[Feature] Implement Heroic Strikethrough to NPCs (#2395)

* [Feature] Implement Heroic Strikethrough to NPCs

* Removed virtual from inline int32 GetHeroicStrikethrough()

* Fix formatting

* Removed unnecessary function
This commit is contained in:
Aeadoin 2022-08-31 00:29:41 -04:00 committed by GitHub
parent edda5ef811
commit 149fa54cfa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 63 additions and 14 deletions

View File

@ -143,6 +143,7 @@ public:
int flymode;
int always_aggro;
int exp_mod;
int heroic_strikethrough;
};
static std::string PrimaryKey()
@ -277,6 +278,7 @@ public:
"flymode",
"always_aggro",
"exp_mod",
"heroic_strikethrough",
};
}
@ -407,6 +409,7 @@ public:
"flymode",
"always_aggro",
"exp_mod",
"heroic_strikethrough",
};
}
@ -571,6 +574,7 @@ public:
e.flymode = -1;
e.always_aggro = 0;
e.exp_mod = 100;
e.heroic_strikethrough = 0;
return e;
}
@ -730,6 +734,7 @@ public:
e.flymode = atoi(row[121]);
e.always_aggro = atoi(row[122]);
e.exp_mod = atoi(row[123]);
e.heroic_strikethrough = atoi(row[124]);
return e;
}
@ -886,6 +891,7 @@ public:
v.push_back(columns[121] + " = " + std::to_string(e.flymode));
v.push_back(columns[122] + " = " + std::to_string(e.always_aggro));
v.push_back(columns[123] + " = " + std::to_string(e.exp_mod));
v.push_back(columns[124] + " = " + std::to_string(e.heroic_strikethrough));
auto results = db.QueryDatabase(
fmt::format(
@ -1184,6 +1190,7 @@ public:
v.push_back(std::to_string(e.flymode));
v.push_back(std::to_string(e.always_aggro));
v.push_back(std::to_string(e.exp_mod));
v.push_back(std::to_string(e.heroic_strikethrough));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
@ -1341,6 +1348,7 @@ public:
e.flymode = atoi(row[121]);
e.always_aggro = atoi(row[122]);
e.exp_mod = atoi(row[123]);
e.heroic_strikethrough = atoi(row[124]);
all_entries.push_back(e);
}
@ -1489,6 +1497,7 @@ public:
e.flymode = atoi(row[121]);
e.always_aggro = atoi(row[122]);
e.exp_mod = atoi(row[123]);
e.heroic_strikethrough = atoi(row[124]);
all_entries.push_back(e);
}

View File

@ -34,7 +34,7 @@
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
#define CURRENT_BINARY_DATABASE_VERSION 9200
#define CURRENT_BINARY_DATABASE_VERSION 9201
#ifdef BOTS
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9029

View File

@ -454,6 +454,7 @@
9198|2022_08_14_exp_modifier_instance_versions.sql|SHOW COLUMNS FROM `character_exp_modifiers` LIKE 'instance_version'|empty|
9199|2022_08_08_task_req_activity_id.sql|SHOW COLUMNS FROM `task_activities` LIKE 'req_activity_id'|empty|
9200|2022_08_19_zone_expansion_consistency.sql|SELECT * FROM db_version WHERE version >= 9200|empty|
9201|2022_08_22_npc_types_heroic_strikethrough.sql|SHOW COLUMNS FROM `npc_types` LIKE 'heroic_strikethrough'|empty|
# Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not

View File

@ -0,0 +1,2 @@
ALTER TABLE `npc_types`
ADD COLUMN `heroic_strikethrough` INT NOT NULL DEFAULT 0 AFTER `exp_mod`;

View File

@ -385,7 +385,7 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit)
*
* Formula (all int math)
* (posted for parry, assume rest at the same)
* Chance = (((SKILL + 100) + [((SKILL+100) * SPA(175).Base1) / 100]) / 45) + [(hDex / 25) - min([hDex / 25], hStrikethrough)].
* Chance = (((SKILL + 100) + [((SKILL+100) * SPA(175).Base1) / 100]) / 45) + [(hDex / 25) - min([hStrikethrough, hDex / 25])].
* hStrikethrough is a mob stat that was added to counter the bonuses of heroic stats
* Number rolled against 100, if the chance is greater than 100 it happens 100% of time
*
@ -398,8 +398,7 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit)
/*
This special ability adds a negative modifer to the defenders riposte/block/parry/chance
therefore reducing the defenders chance to successfully avoid the melee attack. At present
time this is the only way to fine tune counter these mods on players. This may
therefore reducing the defenders chance to successfully avoid the melee attack. Works in tandem with Heroic Strikethrough. This may
ultimately end up being more useful as fields in npc_types.
*/
@ -431,6 +430,24 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit)
modify_dodge = GetSpecialAbilityParam(MODIFY_AVOID_DAMAGE, 4);
}
/* Heroic Strikethrough Implementation per Dev Quotes (2018):
* https://forums.daybreakgames.com/eq/index.php?threads/illusions-benefit-neza-10-dodge.246757/#post-3622670
* Step1 = HeroicStrikethrough(NPC)
* Step2 = HeroicAgility / 25
* Step3 = MIN( Step1, Step2 )
* Step4 = DodgeSkill + 100
* Step5 = Step4 + ( DodgeSkill * DodgeSPA ) / 100
* Step6 = Step5 / 45
* DodgeChance = Step6 + ( Step2 - Step3 )
* Formula (all int math)
* (posted for parry, dodge appears to be the same as confirmed per Dev, assuming Riposte/Block are the same)
* Chance = (((SKILL + 100) + [((SKILL+100) * SPA(175).Base1) / 100]) / 45) + [(hDex / 25) - min([hStrikethrough, hDex / 25])].
If an NPC's Heroic Strikethrough is higher than your character's Heroic Agility bonus, you are disqualified from the "bonus" you would have gotten at the end.
*/
int hstrikethrough = attacker->GetHeroicStrikethrough();
// riposte -- it may seem crazy, but if the attacker has SPA 173 on them, they are immune to Ripo
bool ImmuneRipo = false;
if (!RuleB(Combat, UseLiveRiposteMechanics)) {
@ -465,7 +482,7 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit)
int chance = GetSkill(EQ::skills::SkillRiposte) + 100;
chance += (chance * (aabonuses.RiposteChance + spellbonuses.RiposteChance + itembonuses.RiposteChance)) / 100;
chance /= 50;
chance += itembonuses.HeroicDEX / 25; // live has "heroic strickthrough" here to counter
chance += (itembonuses.HeroicDEX / 25) - std::min(hstrikethrough,(itembonuses.HeroicDEX / 25)); // "Heroic Strikethrough" subtracted here to counter HeroicDEX
if (counter_riposte || counter_all) {
float counter = (counter_riposte + counter_all) / 100.0f;
chance -= chance * counter;
@ -508,7 +525,7 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit)
int chance = GetSkill(EQ::skills::SkillBlock) + 100;
chance += (chance * (aabonuses.IncreaseBlockChance + spellbonuses.IncreaseBlockChance + itembonuses.IncreaseBlockChance)) / 100;
chance /= 25;
chance += itembonuses.HeroicDEX / 25; // live has "heroic strickthrough" here to counter
chance += (itembonuses.HeroicDEX / 25) - std::min(hstrikethrough,(itembonuses.HeroicDEX / 25)); // "Heroic Strikethrough" subtracted here to counter HeroicDEX
if (counter_block || counter_all) {
float counter = (counter_block + counter_all) / 100.0f;
chance -= chance * counter;
@ -535,7 +552,7 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit)
int chance = GetSkill(EQ::skills::SkillParry) + 100;
chance += (chance * (aabonuses.ParryChance + spellbonuses.ParryChance + itembonuses.ParryChance)) / 100;
chance /= 45;
chance += itembonuses.HeroicDEX / 25; // live has "heroic strickthrough" here to counter
chance += (itembonuses.HeroicDEX / 25) - std::min(hstrikethrough,(itembonuses.HeroicDEX / 25)); // "Heroic Strikethrough" subtracted here to counter HeroicDEX
if (counter_parry || counter_all) {
float counter = (counter_parry + counter_all) / 100.0f;
chance -= chance * counter;
@ -562,7 +579,7 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit)
int chance = GetSkill(EQ::skills::SkillDodge) + 100;
chance += (chance * (aabonuses.DodgeChance + spellbonuses.DodgeChance + itembonuses.DodgeChance)) / 100;
chance /= 45;
chance += itembonuses.HeroicAGI / 25; // live has "heroic strickthrough" here to counter
chance += (itembonuses.HeroicAGI / 25) - std::min(hstrikethrough,(itembonuses.HeroicAGI / 25)); // "Heroic Strikethrough" subtracted here to counter HeroicAGI
if (counter_dodge || counter_all) {
float counter = (counter_dodge + counter_all) / 100.0f;
chance -= chance * counter;

View File

@ -56,7 +56,7 @@ Beacon::Beacon(const glm::vec4 &in_pos, int lifetime)
:Mob
(
nullptr, nullptr, 0, 0, 0, INVISIBLE_MAN, 0, BT_NoTarget, 0, 0, 0, 0, 0, in_pos, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, EQ::TintProfile(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, EQ::TintProfile(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false
),
remove_timer(lifetime),
spell_timer(0)

View File

@ -135,6 +135,7 @@ Client::Client(EQStreamInterface* ieqs)
0,
0,
0,
0,
false
),
hpupdate_timer(2000),

View File

@ -154,7 +154,7 @@ Corpse::Corpse(NPC* in_npc, ItemList* in_itemlist, uint32 in_npctypeid, const NP
in_npc->GetDeity(),in_npc->GetLevel(),in_npc->GetNPCTypeID(),in_npc->GetSize(),0,
in_npc->GetPosition(), in_npc->GetInnateLightType(), in_npc->GetTexture(),in_npc->GetHelmTexture(),
0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,EQ::TintProfile(),0xff,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,EQ::TintProfile(),0xff,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
(*in_npctypedata)->use_model, false),
corpse_decay_timer(in_decaytime),
corpse_rez_timer(0),
@ -263,7 +263,8 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob (
0, // uint8 in_legtexture,
0, // uint8 in_feettexture,
0, // uint8 in_usemodel,
0 // bool in_always_aggro
0, // bool in_always_aggro,
0 // Int32 in_heroic_strikethrough
),
corpse_decay_timer(RuleI(Character, CorpseDecayTimeMS)),
corpse_rez_timer(RuleI(Character, CorpseResTimeMS)),
@ -503,6 +504,7 @@ EQ::TintProfile(),
0,
0,
0,
0,
false),
corpse_decay_timer(RuleI(Character, CorpseDecayTimeMS)),
corpse_rez_timer(RuleI(Character, CorpseResTimeMS)),

View File

@ -36,7 +36,7 @@ Encounter::Encounter(const char* enc_name)
:Mob
(
nullptr, nullptr, 0, 0, 0, INVISIBLE_MAN, 0, BT_NoTarget, 0, 0, 0, 0, 0, glm::vec4(0,0,0,0), 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, EQ::TintProfile(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, EQ::TintProfile(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false
)
{
encounter_name[0] = 0;

View File

@ -97,6 +97,7 @@ Mob::Mob(
uint8 in_feettexture,
uint16 in_usemodel,
bool in_always_aggro,
int32 in_heroic_strikethrough,
int64 in_hp_regen_per_second
) :
attack_timer(2000),
@ -283,6 +284,7 @@ Mob::Mob(
spawned = false;
rare_spawn = false;
always_aggro = in_always_aggro;
heroic_strikethrough = in_heroic_strikethrough;
InitializeBuffSlots();

View File

@ -164,6 +164,7 @@ public:
uint8 in_feettexture,
uint16 in_usemodel,
bool in_always_aggros_foes,
int32 in_heroic_strikethrough,
int64 in_hp_regen_per_second = 0
);
virtual ~Mob();
@ -640,6 +641,7 @@ public:
void SetIsBoat(bool boat) { is_boat = boat; }
bool IsControllableBoat() const;
inline const bool AlwaysAggro() const { return always_aggro; }
inline int32 GetHeroicStrikethrough() const { return heroic_strikethrough; }
//Group
virtual bool HasRaid() = 0;
@ -1473,7 +1475,8 @@ protected:
bool follow_run;
bool no_target_hotkey;
bool rare_spawn;
int32 heroic_strikethrough;
uint32 m_PlayerState;
uint32 GetPlayerState() { return m_PlayerState; }
void AddPlayerState(uint32 new_state) { m_PlayerState |= new_state; }

View File

@ -115,7 +115,8 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi
npc_type_data->feettexture,
npc_type_data->use_model,
npc_type_data->always_aggro,
npc_type_data->hp_regen_per_second
npc_type_data->hp_regen_per_second,
npc_type_data->heroic_strikethrough
),
attacked_timer(CombatEventTimer_expire),
swarm_timer(100),
@ -203,6 +204,7 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi
accuracy_rating = npc_type_data->accuracy_rating;
avoidance_rating = npc_type_data->avoidance_rating;
ATK = npc_type_data->ATK;
heroic_strikethrough = npc_type_data->heroic_strikethrough;
// used for when switch back to charm
default_ac = npc_type_data->AC;
@ -2646,6 +2648,10 @@ void NPC::ModifyNPCStat(const char *identifier, const char *new_value)
AI_AddNPCSpellsEffects(atoi(val.c_str()));
CalcBonuses();
return;
}
else if (id == "heroic_strikethrough") {
heroic_strikethrough = atoi(val.c_str());
return;
}
}
@ -2786,6 +2792,9 @@ float NPC::GetNPCStat(const char *identifier)
else if (id == "npc_spells_effects_id") {
return npc_spells_effects_id;
}
else if (id == "heroic_strikethrough") {
return heroic_strikethrough;
}
//default values
else if (id == "default_ac") {
return default_ac;

View File

@ -2561,6 +2561,8 @@ const NPCType *ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load
t->exp_mod = n.exp_mod;
t->skip_auto_scale = false; // hardcoded here for now
t->hp_regen_per_second = n.hp_regen_per_second;
t->heroic_strikethrough = n.heroic_strikethrough;
// If NPC with duplicate NPC id already in table,
// free item we attempted to add.

View File

@ -150,6 +150,7 @@ struct NPCType
int8 flymode;
bool always_aggro;
int exp_mod;
int heroic_strikethrough;
};
namespace player_lootitem {