From 6506ad5b512e2d05e673afbdddc5cf57208fac60 Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 15 Sep 2025 09:14:46 -0400 Subject: [PATCH] [Quest API] Add EVENT_CHARM_START and EVENT_CHARM_END (#5013) --- zone/embparser.cpp | 2 ++ zone/event_codes.h | 2 ++ zone/lua_general.cpp | 4 ++- zone/lua_parser.cpp | 6 +++- zone/npc.cpp | 81 +++++++++++++++++++----------------------- zone/npc.h | 2 +- zone/spell_effects.cpp | 14 ++++---- 7 files changed, 56 insertions(+), 55 deletions(-) diff --git a/zone/embparser.cpp b/zone/embparser.cpp index 6d220b8d8..ad9708d52 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -208,6 +208,8 @@ const char* QuestEventSubroutines[_LargestEventID] = { "EVENT_SPELL_BLOCKED", "EVENT_READ_ITEM", "EVENT_PET_COMMAND", + "EVENT_CHARM_START", + "EVENT_CHARM_END", // Add new events before these or Lua crashes "EVENT_SPELL_EFFECT_BOT", diff --git a/zone/event_codes.h b/zone/event_codes.h index 0d14f565d..ff345abff 100644 --- a/zone/event_codes.h +++ b/zone/event_codes.h @@ -146,6 +146,8 @@ typedef enum { EVENT_SPELL_BLOCKED, EVENT_READ_ITEM, EVENT_PET_COMMAND, + EVENT_CHARM_START, + EVENT_CHARM_END, // Add new events before these or Lua crashes EVENT_SPELL_EFFECT_BOT, diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index ae8c4b2af..1873aec0b 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -6992,7 +6992,9 @@ luabind::scope lua_register_events() { luabind::value("entity_variable_update", static_cast(EVENT_ENTITY_VARIABLE_UPDATE)), luabind::value("aa_loss", static_cast(EVENT_AA_LOSS)), luabind::value("read", static_cast(EVENT_READ_ITEM)), - luabind::value("pet_command", static_cast(EVENT_PET_COMMAND)) + luabind::value("pet_command", static_cast(EVENT_PET_COMMAND)), + luabind::value("charm_start", static_cast(EVENT_CHARM_START)), + luabind::value("charm_end", static_cast(EVENT_CHARM_END)) )]; } diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index c3f37aec5..3e3a5257f 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -189,7 +189,9 @@ const char *LuaEvents[_LargestEventID] = { "event_aa_loss", "event_spell_blocked", "event_read_item", - "event_pet_command" + "event_pet_command", + "event_charm_start", + "event_charm_end" }; extern Zone *zone; @@ -266,6 +268,8 @@ LuaParser::LuaParser() { NPCArgumentDispatch[EVENT_ENTITY_VARIABLE_UPDATE] = handle_npc_entity_variable; NPCArgumentDispatch[EVENT_SPELL_BLOCKED] = handle_npc_spell_blocked; NPCArgumentDispatch[EVENT_PET_COMMAND] = handle_npc_pet_command; + NPCArgumentDispatch[EVENT_CHARM_START] = handle_npc_single_mob; + NPCArgumentDispatch[EVENT_CHARM_END] = handle_npc_single_mob; PlayerArgumentDispatch[EVENT_SAY] = handle_player_say; PlayerArgumentDispatch[EVENT_ENVIRONMENTAL_DAMAGE] = handle_player_environmental_damage; diff --git a/zone/npc.cpp b/zone/npc.cpp index 9f8631f4f..0a91d8e52 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -3285,62 +3285,55 @@ void NPC::DepopSwarmPets() } } -void NPC::ModifyStatsOnCharm(bool is_charm_removed) +void NPC::ModifyStatsOnCharm(bool remove_charm, Mob* charmer) { - if (is_charm_removed) { - if (charm_ac) { - AC = default_ac; - } - if (charm_attack_delay) { - attack_delay = default_attack_delay; - } - if (charm_accuracy_rating) { - accuracy_rating = default_accuracy_rating; - } - if (charm_avoidance_rating) { - avoidance_rating = default_avoidance_rating; - } - if (charm_atk) { - ATK = default_atk; - } - if (charm_min_dmg || charm_max_dmg) { - base_damage = round((default_max_dmg - default_min_dmg) / 1.9); - min_damage = default_min_dmg - round(base_damage / 10.0); - } - if (RuleB(Spells, CharmDisablesSpecialAbilities)) { - ProcessSpecialAbilities(default_special_abilities); - } - - SetAttackTimer(); - CalcAC(); - - return; + if (!remove_charm && parse->HasQuestSub(GetNPCTypeID(), EVENT_CHARM_START)) { + parse->EventNPC(EVENT_CHARM_START, this, charmer, "", 0); + } else if (remove_charm && parse->HasQuestSub(GetNPCTypeID(), EVENT_CHARM_END)) { + parse->EventNPC(EVENT_CHARM_END, this, charmer, "", 0); } - if (charm_ac) { - AC = charm_ac; + const int new_ac = remove_charm ? default_ac : charm_ac; + const int new_attack_delay = remove_charm ? default_attack_delay : charm_attack_delay; + const int new_accuracy_rating = remove_charm ? default_accuracy_rating : charm_accuracy_rating; + const int new_avoidance_rating = remove_charm ? default_avoidance_rating : charm_avoidance_rating; + const int new_atk = remove_charm ? default_atk : charm_atk; + const int new_min_dmg = remove_charm ? default_min_dmg : charm_min_dmg; + const int new_max_dmg = remove_charm ? default_max_dmg : charm_max_dmg; + + if (new_ac) { + AC = new_ac; } - if (charm_attack_delay) { - attack_delay = charm_attack_delay; + + if (new_attack_delay) { + attack_delay = new_attack_delay; } - if (charm_accuracy_rating) { - accuracy_rating = charm_accuracy_rating; + + if (new_accuracy_rating) { + accuracy_rating = new_accuracy_rating; } - if (charm_avoidance_rating) { - avoidance_rating = charm_avoidance_rating; + + if (new_avoidance_rating) { + avoidance_rating = new_avoidance_rating; } - if (charm_atk) { - ATK = charm_atk; + + if (new_atk) { + ATK = new_atk; } - if (charm_min_dmg || charm_max_dmg) { - base_damage = round((charm_max_dmg - charm_min_dmg) / 1.9); - min_damage = charm_min_dmg - round(base_damage / 10.0); + + if (new_min_dmg || new_max_dmg) { + base_damage = std::round((new_max_dmg - new_min_dmg) / 1.9); + min_damage = new_min_dmg - std::round(base_damage / 10.0); } + if (RuleB(Spells, CharmDisablesSpecialAbilities)) { - ClearSpecialAbilities(); + if (remove_charm) { + ProcessSpecialAbilities(default_special_abilities); + } else { + ClearSpecialAbilities(); + } } - // the rest of the stats aren't cached, so lets just do these two instead of full CalcBonuses() SetAttackTimer(); CalcAC(); } diff --git a/zone/npc.h b/zone/npc.h index 4c4d3cb1b..90569d12b 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -346,7 +346,7 @@ public: int64 GetNPCHPRegen() const { return hp_regen + itembonuses.HPRegen + spellbonuses.HPRegen; } inline const char* GetAmmoIDfile() const { return ammo_idfile; } - void ModifyStatsOnCharm(bool is_charm_removed); + void ModifyStatsOnCharm(bool remove_charm, Mob* charmer); //waypoint crap int GetMaxWp() const { return max_wp; } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 145c4f67a..42078e322 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -835,12 +835,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove SendAppearancePacket(AppearanceType::Pet, caster->GetID(), true, true); } - if (IsClient()) - { + if (IsClient()) { CastToClient()->AI_Start(); - } else if(IsNPC()) { - CastToNPC()->SetPetSpellID(0); //not a pet spell. - CastToNPC()->ModifyStatsOnCharm(false); + } else if (IsNPC()) { + CastToNPC()->SetPetSpellID(0); //not a pet spell. + CastToNPC()->ModifyStatsOnCharm(false, caster); } bool bBreak = false; @@ -4418,10 +4417,9 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) case SpellEffect::Charm: { - if(IsNPC()) - { + if (IsNPC()) { CastToNPC()->RestoreGuardSpotCharm(); - CastToNPC()->ModifyStatsOnCharm(true); + CastToNPC()->ModifyStatsOnCharm(true, GetOwner()); } SendAppearancePacket(AppearanceType::Pet, 0, true, true);