mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 21:01:29 +00:00
[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:
parent
edda5ef811
commit
149fa54cfa
@ -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);
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
ALTER TABLE `npc_types`
|
||||
ADD COLUMN `heroic_strikethrough` INT NOT NULL DEFAULT 0 AFTER `exp_mod`;
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -135,6 +135,7 @@ Client::Client(EQStreamInterface* ieqs)
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
false
|
||||
),
|
||||
hpupdate_timer(2000),
|
||||
|
||||
@ -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)),
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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; }
|
||||
|
||||
11
zone/npc.cpp
11
zone/npc.cpp
@ -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;
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -150,6 +150,7 @@ struct NPCType
|
||||
int8 flymode;
|
||||
bool always_aggro;
|
||||
int exp_mod;
|
||||
int heroic_strikethrough;
|
||||
};
|
||||
|
||||
namespace player_lootitem {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user