From a73bf221ed9f97f58fd0d175a1364222be0de49b Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Thu, 13 Sep 2018 19:25:05 -0400 Subject: [PATCH 01/11] Make SPA 112 affect fizzle rate not effective caster level Per dev quote, SPA 112 "Modifies casting skills of the affected entity by BaseEffect for the purposes of determining whether or not a fizzle occurs when casting spells." Fixes issues caused by having a spell with this effect on caster such as wrong target debuff durations and buff refreshes not taking hold. --- zone/bonuses.cpp | 20 +++++++++++++++++--- zone/common.h | 1 + zone/lua_stat_bonuses.cpp | 6 ++++++ zone/lua_stat_bonuses.h | 1 + zone/spells.cpp | 2 ++ 5 files changed, 27 insertions(+), 3 deletions(-) diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index b6de679d1..7965e016e 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1217,8 +1217,12 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) break; } - case SE_CastingLevel2: case SE_CastingLevel: { + newbon->adjusted_casting_skill += base1; + break; + } + + case SE_CastingLevel2: { newbon->effective_casting_level += base1; break; } @@ -1917,8 +1921,13 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne break; } - case SE_CastingLevel2: case SE_CastingLevel: // Brilliance of Ro + { + new_bonus->adjusted_casting_skill += effect_value; + break; + } + + case SE_CastingLevel2: { new_bonus->effective_casting_level += effect_value; break; @@ -3867,8 +3876,13 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) aabonuses.Corrup = effect_value; break; - case SE_CastingLevel2: case SE_CastingLevel: // Brilliance of Ro + spellbonuses.adjusted_casting_skill = effect_value; + aabonuses.adjusted_casting_skill = effect_value; + itembonuses.adjusted_casting_skill = effect_value; + break; + + case SE_CastingLevel2: spellbonuses.effective_casting_level = effect_value; aabonuses.effective_casting_level = effect_value; itembonuses.effective_casting_level = effect_value; diff --git a/zone/common.h b/zone/common.h index df6b22637..9db776e4b 100644 --- a/zone/common.h +++ b/zone/common.h @@ -354,6 +354,7 @@ struct StatBonuses { int32 skillmod[EQEmu::skills::HIGHEST_SKILL + 1]; int32 skillmodmax[EQEmu::skills::HIGHEST_SKILL + 1]; int effective_casting_level; + int adjusted_casting_skill; // SPA 112 for fizzles int reflect_chance; // chance to reflect incoming spell uint32 singingMod; uint32 Amplification; // stacks with singingMod diff --git a/zone/lua_stat_bonuses.cpp b/zone/lua_stat_bonuses.cpp index e1fb329cc..282aaad38 100644 --- a/zone/lua_stat_bonuses.cpp +++ b/zone/lua_stat_bonuses.cpp @@ -333,6 +333,11 @@ int Lua_StatBonuses::Geteffective_casting_level() const { return self->effective_casting_level; } +int Lua_StatBonuses::Getadjusted_casting_skill() const { + Lua_Safe_Call_Int(); + return self->adjusted_casting_skill; +} + int Lua_StatBonuses::Getreflect_chance() const { Lua_Safe_Call_Int(); return self->reflect_chance; @@ -1347,6 +1352,7 @@ luabind::scope lua_register_stat_bonuses() { .def("skillmod", &Lua_StatBonuses::Getskillmod) .def("skillmodmax", &Lua_StatBonuses::Getskillmodmax) .def("effective_casting_level", &Lua_StatBonuses::Geteffective_casting_level) + .def("adjusted_casting_skill", &Lua_StatBonuses::Getadjusted_casting_skill) .def("reflect_chance", &Lua_StatBonuses::Getreflect_chance) .def("singingMod", &Lua_StatBonuses::GetsingingMod) .def("Amplification", &Lua_StatBonuses::GetAmplification) diff --git a/zone/lua_stat_bonuses.h b/zone/lua_stat_bonuses.h index 9ad04d681..c65c589a4 100644 --- a/zone/lua_stat_bonuses.h +++ b/zone/lua_stat_bonuses.h @@ -91,6 +91,7 @@ public: int32 Getskillmod(int idx) const; int32 Getskillmodmax(int idx) const; int Geteffective_casting_level() const; + int Getadjusted_casting_skill() const; int Getreflect_chance() const; uint32 GetsingingMod() const; uint32 GetAmplification() const; diff --git a/zone/spells.cpp b/zone/spells.cpp index 60591b7b1..990b26d60 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -752,6 +752,8 @@ bool Client::CheckFizzle(uint16 spell_id) act_skill = GetSkill(spells[spell_id].skill); act_skill += GetLevel(); // maximum of whatever the client can cheat + act_skill += itembonuses.adjusted_casting_skill + spellbonuses.adjusted_casting_skill + aabonuses.adjusted_casting_skill; + Log(Logs::Detail, Logs::Spells, "Adjusted casting skill: %d+%d+%d+%d+%d=%d", GetSkill(spells[spell_id].skill), GetLevel(), itembonuses.adjusted_casting_skill, spellbonuses.adjusted_casting_skill, aabonuses.adjusted_casting_skill, act_skill); //spell specialization float specialize = GetSpecializeSkillValue(spell_id); From 44f85f140cab8ddf8eb398377204d7bfe9db617d Mon Sep 17 00:00:00 2001 From: Xackery Xtal Date: Mon, 21 Jan 2019 20:29:12 -0800 Subject: [PATCH 02/11] Added Spells:CharmDisablesSpecialAbilities --- common/ruletypes.h | 1 + zone/npc.cpp | 5 +++++ zone/npc.h | 1 + 3 files changed, 7 insertions(+) diff --git a/common/ruletypes.h b/common/ruletypes.h index 623260af4..7076eda69 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -365,6 +365,7 @@ RULE_INT(Spells, CharismaEffectiveness, 10) // Deterimes how much resist modific RULE_INT(Spells, CharismaEffectivenessCap, 255) // Deterimes how much resist modification charisma applies to charm/pacify checks. Default 10 CHA = -1 resist mod. RULE_BOOL(Spells, CharismaCharmDuration, false) // Allow CHA resist mod to extend charm duration. RULE_INT(Spells, CharmBreakCheckChance, 25) //Determines chance for a charm break check to occur each buff tick. +RULE_BOOL(Spells, CharmDisablesSpecialAbilities, false) //When charm is cast on an NPC, strip their special abilities RULE_INT(Spells, MaxCastTimeReduction, 50) //Max percent your spell cast time can be reduced by spell haste RULE_INT(Spells, RootBreakFromSpells, 55) //Chance for root to break when cast on. RULE_INT(Spells, DeathSaveCharismaMod, 3) //Determines how much charisma effects chance of death save firing. diff --git a/zone/npc.cpp b/zone/npc.cpp index 8de819e0c..ad7bbd74d 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -208,6 +208,7 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi default_accuracy_rating = npc_type_data->accuracy_rating; default_avoidance_rating = npc_type_data->avoidance_rating; default_atk = npc_type_data->ATK; + strn0cpy(default_special_abilities, npc_type_data->special_abilities, 512); // used for when getting charmed, if 0, doesn't swap charm_ac = npc_type_data->charm_ac; @@ -2840,6 +2841,8 @@ void NPC::ModifyStatsOnCharm(bool bRemoved) 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); } else { if (charm_ac) AC = charm_ac; @@ -2855,6 +2858,8 @@ void NPC::ModifyStatsOnCharm(bool bRemoved) base_damage = round((charm_max_dmg - charm_min_dmg) / 1.9); min_damage = charm_min_dmg - round(base_damage / 10.0); } + if (RuleB(Spells, CharmDisablesSpecialAbilities)) + ClearSpecialAbilities(); } // the rest of the stats aren't cached, so lets just do these two instead of full CalcBonuses() SetAttackTimer(); diff --git a/zone/npc.h b/zone/npc.h index b9e0458e6..ec60f80d2 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -538,6 +538,7 @@ protected: int default_accuracy_rating; int default_avoidance_rating; int default_atk; + char default_special_abilities[512]; // when charmed, switch to these int charm_ac; From 1aab23098a7910c27e2b70a8bad059e0975eda84 Mon Sep 17 00:00:00 2001 From: Trust Date: Tue, 18 Jun 2019 23:59:37 +0000 Subject: [PATCH 03/11] Container lock inconsistent state fix --- zone/object.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/zone/object.cpp b/zone/object.cpp index 4306c6642..df143085c 100644 --- a/zone/object.cpp +++ b/zone/object.cpp @@ -443,6 +443,14 @@ bool Object::Process(){ if(m_ground_spawn && respawn_timer.Check()){ RandomSpawn(true); } + + if (user != nullptr && !entity_list.GetClientByCharID(user->CharacterID())) { + m_inuse = false; + last_user = user; + user->SetTradeskillObject(nullptr); + user = nullptr; + } + return true; } @@ -554,7 +562,6 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object) ClickObjectAction_Struct* coa = (ClickObjectAction_Struct*)outapp->pBuffer; //TODO: there is prolly a better way to do this. - m_inuse = true; coa->type = m_type; coa->unknown16 = 0x0a; @@ -576,12 +583,6 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object) } } - if(sender->IsLooting()) - { - coa->open = 0x00; - user = sender; - } - sender->QueuePacket(outapp); safe_delete(outapp); @@ -590,6 +591,7 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object) return(false); // Starting to use this object + m_inuse = true; sender->SetTradeskillObject(this); user = sender; From 682054970cfb0295ea6905d05d170fa5b59e1ad8 Mon Sep 17 00:00:00 2001 From: Justin Wienckowski Date: Mon, 15 Apr 2019 02:06:48 -0700 Subject: [PATCH 04/11] Zone::IsSpellBlocked should correctly handle spellid 0 (all spells) blocked in a region (type 2). --- zone/zone.cpp | 55 +++++++++++++++++++++------------------------------ 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/zone/zone.cpp b/zone/zone.cpp index 923e37a39..90e7fafc7 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1952,46 +1952,37 @@ bool Zone::IsSpellBlocked(uint32 spell_id, const glm::vec3& location) } } + // If all spells are blocked and this is an exception, it is not blocked + if (block_all && exception) + { + return false; + } + for (int x = 0; x < totalBS; x++) { - // If spellid is 0, block all spells in the zone - if (block_all) + // Spellid of 0 matches all spells + if (0 != blocked_spells[x].spellid && spell_id != blocked_spells[x].spellid) { - // If the same zone has entries other than spellid 0, they act as exceptions and are allowed - if (exception) - { - return false; - } - else + continue; + } + + switch (blocked_spells[x].type) + { + case 1: { return true; + break; } - } - else - { - if (spell_id != blocked_spells[x].spellid) + case 2: + { + if (IsWithinAxisAlignedBox(location, blocked_spells[x].m_Location - blocked_spells[x].m_Difference, blocked_spells[x].m_Location + blocked_spells[x].m_Difference)) + return true; + break; + } + default: { continue; - } - - switch (blocked_spells[x].type) - { - case 1: - { - return true; - break; - } - case 2: - { - if (IsWithinAxisAlignedBox(location, blocked_spells[x].m_Location - blocked_spells[x].m_Difference, blocked_spells[x].m_Location + blocked_spells[x].m_Difference)) - return true; - break; - } - default: - { - continue; - break; - } + break; } } } From 06fbd7103e8859aa5ba59ced74bf102f8e43ef86 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Mon, 5 Aug 2019 09:24:22 -0400 Subject: [PATCH 05/11] Update client_packet.cpp --- zone/client_packet.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index f0eb00d13..728d687b6 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -2851,12 +2851,17 @@ void Client::Handle_OP_ApplyPoison(const EQApplicationPacket *app) // Poisons that don't proc until a level higher than the // rogue simply won't apply at all, no skill check done. - if (ChanceRoll < (.9 + GetLevel()/1000)) { + uint16 applyskill=GetSkill(EQEmu::skills::SkillApplyPoison); + + if (ChanceRoll < (.75 + applyskill/1000)) { ApplyPoisonSuccessResult = 1; AddProcToWeapon(poison->Proc.Effect, false, (GetDEX() / 100) + 103); } } + else { + Message(13, "A piercing weapon must be wielded to apply poison."); + } // Live always deletes the item, success or failure. Even if too high. DeleteItemInInventory(ApplyPoisonData->inventorySlot, 1, true); From d3641be6c0476e8f0a00bc4dce5942f49dbf1e6e Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Mon, 5 Aug 2019 09:30:57 -0400 Subject: [PATCH 06/11] Update loottables.cpp --- zone/loottables.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/loottables.cpp b/zone/loottables.cpp index e7c9624f1..816eb2ee4 100644 --- a/zone/loottables.cpp +++ b/zone/loottables.cpp @@ -352,7 +352,7 @@ void NPC::AddLootDrop(const EQEmu::ItemData *item2, ItemList* itemlist, int16 ch } else if (foundslot == EQEmu::invslot::slotSecondary && (GetOwner() != nullptr || (CanThisClassDualWield() && zone->random.Roll(NPC_DW_CHANCE)) || (item2->Damage==0)) && - (item2->IsType1HWeapon() || item2->ItemType == EQEmu::item::ItemTypeShield)) + (item2->IsType1HWeapon() || item2->ItemType == EQEmu::item::ItemTypeShield || item2->ItemType == EQEmu::item::ItemTypeLight)) { if (item2->Proc.Effect!=0) CastToMob()->AddProcToWeapon(item2->Proc.Effect, true); From 49094bcfbccf914f165f49ae3ebba622afb7f1e6 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Fri, 16 Aug 2019 10:28:06 -0400 Subject: [PATCH 07/11] Fix attack to not attack with a non-weapon (expand cancel to include cancel of light) I found a single item in the db, Flickering Watchtower Torch, that is a light item type but has 1 damage/100 delay. This code would make that weapon not operate, if we care. --- zone/attack.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index a51cd3163..ffb90074a 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1386,12 +1386,12 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b || (GetHP() < 0) || (!IsAttackAllowed(other)) ) { - Log(Logs::Detail, Logs::Combat, "Attack canceled, invalid circumstances."); + Log(Logs::Detail, Logs::Combat, "Attack cancelled, invalid circumstances."); return false; // Only bards can attack while casting } if (DivineAura() && !GetGM()) {//cant attack while invulnerable unless your a gm - Log(Logs::Detail, Logs::Combat, "Attack canceled, Divine Aura is in effect."); + Log(Logs::Detail, Logs::Combat, "Attack cancelled, Divine Aura is in effect."); Message_StringID(MT_DefaultText, DIVINE_AURA_NO_ATK); //You can't attack while invulnerable! return false; } @@ -1411,7 +1411,7 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b if (weapon != nullptr) { if (!weapon->IsWeapon()) { - Log(Logs::Detail, Logs::Combat, "Attack canceled, Item %s (%d) is not a weapon.", weapon->GetItem()->Name, weapon->GetID()); + Log(Logs::Detail, Logs::Combat, "Attack cancelled, Item %s (%d) is not a weapon.", weapon->GetItem()->Name, weapon->GetID()); return(false); } Log(Logs::Detail, Logs::Combat, "Attacking with weapon: %s (%d)", weapon->GetItem()->Name, weapon->GetID()); @@ -1951,8 +1951,8 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool if (weapon) { Log(Logs::Detail, Logs::Combat, "Attacking with weapon: %s (%d) (too bad im not using it for much)", weapon->Name, weapon->ID); - if (Hand == EQEmu::invslot::slotSecondary && weapon->ItemType == EQEmu::item::ItemTypeShield) { - Log(Logs::Detail, Logs::Combat, "Attack with shield canceled."); + if (Hand == EQEmu::invslot::slotSecondary && !weapon->IsType1HWeapon()) { + Log(Logs::Detail, Logs::Combat, "Attack with non-weapon cancelled."); return false; } @@ -3940,7 +3940,7 @@ void Mob::TryWeaponProc(const EQEmu::ItemInstance* weapon_g, Mob *on, uint16 han } if (DivineAura()) { - Log(Logs::Detail, Logs::Combat, "Procs canceled, Divine Aura is in effect."); + Log(Logs::Detail, Logs::Combat, "Procs cancelled, Divine Aura is in effect."); return; } From 0d6d78ebdaf906af6cd14ff150e084c0e12cd431 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 1 Sep 2019 15:37:21 -0500 Subject: [PATCH 08/11] Update client_packet.cpp --- zone/client_packet.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 728d687b6..8b417b4bf 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -2851,16 +2851,15 @@ void Client::Handle_OP_ApplyPoison(const EQApplicationPacket *app) // Poisons that don't proc until a level higher than the // rogue simply won't apply at all, no skill check done. - uint16 applyskill=GetSkill(EQEmu::skills::SkillApplyPoison); + uint16 poison_skill = GetSkill(EQEmu::skills::SkillApplyPoison); - if (ChanceRoll < (.75 + applyskill/1000)) { + if (ChanceRoll < (.75 + poison_skill / 1000)) { ApplyPoisonSuccessResult = 1; - AddProcToWeapon(poison->Proc.Effect, false, - (GetDEX() / 100) + 103); + AddProcToWeapon(poison->Proc.Effect, false, (GetDEX() / 100) + 103); } } else { - Message(13, "A piercing weapon must be wielded to apply poison."); + Message(Chat::Red, "A piercing weapon must be wielded to apply poison."); } // Live always deletes the item, success or failure. Even if too high. From 5381dcead139458166687961dbfe2555b3f0d79f Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 1 Sep 2019 16:07:48 -0500 Subject: [PATCH 09/11] Formatting: NPC::ModifyStatsOnCharm --- zone/npc.cpp | 68 +++++++++++++++++++++++++++++++++------------------- zone/npc.h | 2 +- 2 files changed, 44 insertions(+), 26 deletions(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index f67dc5581..759d6ffe0 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -2839,43 +2839,61 @@ void NPC::DepopSwarmPets() } } -void NPC::ModifyStatsOnCharm(bool bRemoved) +void NPC::ModifyStatsOnCharm(bool is_charm_removed) { - if (bRemoved) { - if (charm_ac) + if (is_charm_removed) { + if (charm_ac) { AC = default_ac; - if (charm_attack_delay) + } + if (charm_attack_delay) { attack_delay = default_attack_delay; - if (charm_accuracy_rating) + } + if (charm_accuracy_rating) { accuracy_rating = default_accuracy_rating; - if (charm_avoidance_rating) + } + if (charm_avoidance_rating) { avoidance_rating = default_avoidance_rating; - if (charm_atk) + } + 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); + min_damage = default_min_dmg - round(base_damage / 10.0); } - if (RuleB(Spells, CharmDisablesSpecialAbilities)) + if (RuleB(Spells, CharmDisablesSpecialAbilities)) { ProcessSpecialAbilities(default_special_abilities); - } else { - if (charm_ac) - AC = charm_ac; - if (charm_attack_delay) - attack_delay = charm_attack_delay; - if (charm_accuracy_rating) - accuracy_rating = charm_accuracy_rating; - if (charm_avoidance_rating) - avoidance_rating = charm_avoidance_rating; - if (charm_atk) - ATK = charm_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 (RuleB(Spells, CharmDisablesSpecialAbilities)) - ClearSpecialAbilities(); + + SetAttackTimer(); + CalcAC(); + + return; } + + if (charm_ac) { + AC = charm_ac; + } + if (charm_attack_delay) { + attack_delay = charm_attack_delay; + } + if (charm_accuracy_rating) { + accuracy_rating = charm_accuracy_rating; + } + if (charm_avoidance_rating) { + avoidance_rating = charm_avoidance_rating; + } + if (charm_atk) { + ATK = charm_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 (RuleB(Spells, CharmDisablesSpecialAbilities)) { + 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 af9855103..5d7a7bd73 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -294,7 +294,7 @@ public: int32 GetNPCHPRegen() const { return hp_regen + itembonuses.HPRegen + spellbonuses.HPRegen; } inline const char* GetAmmoIDfile() const { return ammo_idfile; } - void ModifyStatsOnCharm(bool bRemoved); + void ModifyStatsOnCharm(bool is_charm_removed); //waypoint crap int GetMaxWp() const { return max_wp; } From b8624d04887eb550bcf38d631b08f153a0c7f830 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 1 Sep 2019 16:51:39 -0500 Subject: [PATCH 10/11] Post PR blocked spells formatting --- common/eq_constants.h | 5 ++ zone/api_service.cpp | 2 +- zone/zone.cpp | 128 +++++++++++++++++++++--------------------- zone/zone.h | 6 +- 4 files changed, 72 insertions(+), 69 deletions(-) diff --git a/common/eq_constants.h b/common/eq_constants.h index 6c3e28da0..4cc61c0b4 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -459,4 +459,9 @@ enum ChatChannelNames : uint16 ChatChannel_Emotes = 22 }; +namespace ZoneBlockedSpellTypes { + const uint8 ZoneWide = 1; + const uint8 Region = 2; +}; + #endif /*COMMON_EQ_CONSTANTS_H*/ diff --git a/zone/api_service.cpp b/zone/api_service.cpp index b8ae1149e..807b7fd6c 100644 --- a/zone/api_service.cpp +++ b/zone/api_service.cpp @@ -803,7 +803,7 @@ Json::Value ApiGetZoneAttributes(EQ::Net::WebsocketServerConnection *connection, row["mobs_aggro_count"] = zone->MobsAggroCount(); row["save_zone_cfg"] = zone->SaveZoneCFG(); row["short_name"] = zone->GetShortName(); - row["total_blocked_spells"] = zone->GetTotalBlockedSpells(); + row["total_blocked_spells"] = zone->GetZoneTotalBlockedSpells(); row["zone_id"] = zone->GetZoneID(); row["zone_type"] = zone->GetZoneType(); diff --git a/zone/zone.cpp b/zone/zone.cpp index 789f400ea..1804e311f 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -822,11 +822,11 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) Weather_Timer = new Timer(60000); Weather_Timer->Start(); Log(Logs::General, Logs::None, "The next weather check for zone: %s will be in %i seconds.", short_name, Weather_Timer->GetRemainingTime()/1000); - zone_weather = 0; - weather_intensity = 0; - blocked_spells = nullptr; - totalBS = 0; - zone_has_current_time = false; + zone_weather = 0; + weather_intensity = 0; + blocked_spells = nullptr; + zone_total_blocked_spells = 0; + zone_has_current_time = false; Instance_Shutdown_Timer = nullptr; bool is_perma = false; @@ -972,7 +972,7 @@ bool Zone::Init(bool iStaticZone) { //load up the zone's doors (prints inside) zone->LoadZoneDoors(zone->GetShortName(), zone->GetInstanceVersion()); - zone->LoadBlockedSpells(zone->GetZoneID()); + zone->LoadZoneBlockedSpells(zone->GetZoneID()); //clear trader items if we are loading the bazaar if(strncasecmp(short_name,"bazaar",6)==0) { @@ -1880,17 +1880,21 @@ bool ZoneDatabase::GetDecayTimes(npcDecayTimes_Struct *npcCorpseDecayTimes) return true; } -void Zone::weatherSend(Client* client) +void Zone::weatherSend(Client *client) { auto outapp = new EQApplicationPacket(OP_Weather, 8); - if(zone_weather>0) - outapp->pBuffer[0] = zone_weather-1; - if(zone_weather>0) + if (zone_weather > 0) { + outapp->pBuffer[0] = zone_weather - 1; + } + if (zone_weather > 0) { outapp->pBuffer[4] = zone->weather_intensity; - if (client) + } + if (client) { client->QueuePacket(outapp); - else + } + else { entity_list.QueueClients(0, outapp); + } safe_delete(outapp); } @@ -1908,15 +1912,13 @@ void Zone::SetGraveyard(uint32 zoneid, const glm::vec4& graveyardPosition) { m_Graveyard = graveyardPosition; } -void Zone::LoadBlockedSpells(uint32 zoneid) +void Zone::LoadZoneBlockedSpells(uint32 zone_id) { - if(!blocked_spells) - { - totalBS = database.GetBlockedSpellsCount(zoneid); - if(totalBS > 0){ - blocked_spells = new ZoneSpellsBlocked[totalBS]; - if(!database.LoadBlockedSpells(totalBS, blocked_spells, zoneid)) - { + if (!blocked_spells) { + zone_total_blocked_spells = database.GetBlockedSpellsCount(zone_id); + if (zone_total_blocked_spells > 0) { + blocked_spells = new ZoneSpellsBlocked[zone_total_blocked_spells]; + if (!database.LoadBlockedSpells(zone_total_blocked_spells, blocked_spells, zone_id)) { Log(Logs::General, Logs::Error, "... Failed to load blocked spells."); ClearBlockedSpells(); } @@ -1926,93 +1928,89 @@ void Zone::LoadBlockedSpells(uint32 zoneid) void Zone::ClearBlockedSpells() { - if(blocked_spells){ + if (blocked_spells) { safe_delete_array(blocked_spells); - totalBS = 0; + zone_total_blocked_spells = 0; } } -bool Zone::IsSpellBlocked(uint32 spell_id, const glm::vec3& location) +bool Zone::IsSpellBlocked(uint32 spell_id, const glm::vec3 &location) { - if (blocked_spells) - { + if (blocked_spells) { bool exception = false; bool block_all = false; - for (int x = 0; x < totalBS; x++) - { - if (blocked_spells[x].spellid == spell_id) - { + + for (int x = 0; x < GetZoneTotalBlockedSpells(); x++) { + if (blocked_spells[x].spellid == spell_id) { exception = true; } - if (blocked_spells[x].spellid == 0) - { + if (blocked_spells[x].spellid == 0) { block_all = true; } } - // If all spells are blocked and this is an exception, it is not blocked - if (block_all && exception) - { - return false; - } + // If all spells are blocked and this is an exception, it is not blocked + if (block_all && exception) { + return false; + } - for (int x = 0; x < totalBS; x++) - { - // Spellid of 0 matches all spells - if (0 != blocked_spells[x].spellid && spell_id != blocked_spells[x].spellid) - { + for (int x = 0; x < GetZoneTotalBlockedSpells(); x++) { + // Spellid of 0 matches all spells + if (0 != blocked_spells[x].spellid && spell_id != blocked_spells[x].spellid) { continue; } - switch (blocked_spells[x].type) - { - case 1: - { + switch (blocked_spells[x].type) { + case ZoneBlockedSpellTypes::ZoneWide: { return true; break; } - case 2: - { - if (IsWithinAxisAlignedBox(location, blocked_spells[x].m_Location - blocked_spells[x].m_Difference, blocked_spells[x].m_Location + blocked_spells[x].m_Difference)) + case ZoneBlockedSpellTypes::Region: { + if (IsWithinAxisAlignedBox( + location, + blocked_spells[x].m_Location - blocked_spells[x].m_Difference, + blocked_spells[x].m_Location + blocked_spells[x].m_Difference + )) { return true; + } break; } - default: - { + default: { continue; break; } } } } + return false; } -const char* Zone::GetSpellBlockedMessage(uint32 spell_id, const glm::vec3& location) +const char *Zone::GetSpellBlockedMessage(uint32 spell_id, const glm::vec3 &location) { - if(blocked_spells) - { - for(int x = 0; x < totalBS; x++) - { - if(spell_id != blocked_spells[x].spellid && blocked_spells[x].spellid != 0) + if (blocked_spells) { + for (int x = 0; x < GetZoneTotalBlockedSpells(); x++) { + if (spell_id != blocked_spells[x].spellid && blocked_spells[x].spellid != 0) { continue; + } - switch(blocked_spells[x].type) - { - case 1: - { + switch (blocked_spells[x].type) { + case ZoneBlockedSpellTypes::ZoneWide: { return blocked_spells[x].message; break; } - case 2: - { - if(IsWithinAxisAlignedBox(location, blocked_spells[x].m_Location - blocked_spells[x].m_Difference, blocked_spells[x].m_Location + blocked_spells[x].m_Difference)) + case ZoneBlockedSpellTypes::Region: { + if (IsWithinAxisAlignedBox( + location, + blocked_spells[x].m_Location - blocked_spells[x].m_Difference, + blocked_spells[x].m_Location + blocked_spells[x].m_Difference + )) { return blocked_spells[x].message; + } break; } - default: - { + default: { continue; break; } diff --git a/zone/zone.h b/zone/zone.h index 176fe0cb3..c6da2642e 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -162,7 +162,7 @@ public: inline void SetZoneHasCurrentTime(bool time) { zone_has_current_time = time; } inline void ShowNPCGlobalLoot(Client *to, NPC *who) { m_global_loot.ShowNPCGlobalLoot(to, who); } inline void ShowZoneGlobalLoot(Client *to) { m_global_loot.ShowZoneGlobalLoot(to); } - int GetTotalBlockedSpells() { return totalBS; } + int GetZoneTotalBlockedSpells() { return zone_total_blocked_spells; } int SaveTempItem(uint32 merchantid, uint32 npcid, uint32 item, int32 charges, bool sold = false); int32 MobsAggroCount() { return aggroedmobs; } @@ -234,7 +234,7 @@ public: void LoadAdventureFlavor(); void LoadAlternateAdvancement(); void LoadAlternateCurrencies(); - void LoadBlockedSpells(uint32 zoneid); + void LoadZoneBlockedSpells(uint32 zone_id); void LoadLDoNTrapEntries(); void LoadLDoNTraps(); void LoadLevelEXPMods(); @@ -348,7 +348,7 @@ private: glm::vec3 m_SafePoint; glm::vec4 m_Graveyard; int default_ruleset; - int totalBS; + int zone_total_blocked_spells; int npc_position_update_distance; int32 aggroedmobs; uint8 zone_type; From 2e98de39239b25c3186ed81bda6b3a1df8f47cf2 Mon Sep 17 00:00:00 2001 From: Noudess Date: Mon, 2 Sep 2019 10:34:09 -0400 Subject: [PATCH 11/11] Changes to allow individual spawn2 locations to vary in npc selection (cond) --- common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + .../2019_09_02_required_spawn_filter.sql | 1 + zone/questmgr.cpp | 11 ++++++++- zone/spawn2.cpp | 24 ++++++++++++------- zone/spawngroup.cpp | 23 +++++++++++------- zone/spawngroup.h | 5 ++-- 7 files changed, 46 insertions(+), 21 deletions(-) create mode 100644 utils/sql/git/required/2019_09_02_required_spawn_filter.sql diff --git a/common/version.h b/common/version.h index c9bb6d361..0c6dea2b5 100644 --- a/common/version.h +++ b/common/version.h @@ -31,7 +31,7 @@ */ -#define CURRENT_BINARY_DATABASE_VERSION 9141 +#define CURRENT_BINARY_DATABASE_VERSION 9142 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9025 diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 4addfe6ce..a2035a7da 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -395,6 +395,7 @@ 9139|2019_03_25_optional_npc_model.sql|SHOW COLUMNS FROM `npc_types` LIKE 'model'|empty| 9140|2019_07_03_update_range.sql|SHOW COLUMNS FROM `zone` LIKE 'max_movement_update_range'|empty| 9141|2019_07_10_npc_flymode.sql|SHOW COLUMNS FROM `npc_types` LIKE 'flymode'|empty| +9142|2019_09_02_required_spawn_filter.sql|SHOW COLUMNS FROM `spawnentry` LIKE 'condition_value_filter'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2019_09_02_required_spawn_filter.sql b/utils/sql/git/required/2019_09_02_required_spawn_filter.sql new file mode 100644 index 000000000..d89cdfa4a --- /dev/null +++ b/utils/sql/git/required/2019_09_02_required_spawn_filter.sql @@ -0,0 +1 @@ +ALTER TABLE `spawnentry` ADD COLUMN `condition_value_filter` MEDIUMINT(9) NOT NULL DEFAULT '1' AFTER `chance`; diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 64e5bd008..21cb6d4b1 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -258,7 +258,16 @@ Mob *QuestManager::spawn_from_spawn2(uint32 spawn2_id) return nullptr; } } - uint32 npcid = spawn_group->GetNPCType(); + + uint16 condition_value=1; + uint16 condition_id=found_spawn->GetSpawnCondition(); + + if (condition_id > 0) { + condition_value = zone->spawn_conditions.GetCondition(zone->GetShortName(), zone->GetInstanceID(), condition_id); + } + + uint32 npcid = spawn_group->GetNPCType(condition_value); + if (npcid == 0) { return nullptr; } diff --git a/zone/spawn2.cpp b/zone/spawn2.cpp index 93c955d4e..19a6f161d 100644 --- a/zone/spawn2.cpp +++ b/zone/spawn2.cpp @@ -184,12 +184,18 @@ bool Spawn2::Process() { return false; } + uint16 condition_value=1; + + if (condition_id > 0) { + condition_value = zone->spawn_conditions.GetCondition(zone->GetShortName(), zone->GetInstanceID(), condition_id); + } + //have the spawn group pick an NPC for us - uint32 npcid = spawn_group->GetNPCType(); + uint32 npcid = spawn_group->GetNPCType(condition_value); if (npcid == 0) { Log(Logs::Detail, Logs::Spawns, - "Spawn2 %d: Spawn group %d did not yeild an NPC! not spawning.", + "Spawn2 %d: Spawn group %d did not yield an NPC! not spawning.", spawn2_id, spawngroup_id_); @@ -202,7 +208,7 @@ bool Spawn2::Process() { if (tmp == nullptr) { Log(Logs::Detail, Logs::Spawns, - "Spawn2 %d: Spawn group %d yeilded an invalid NPC type %d", + "Spawn2 %d: Spawn group %d yielded an invalid NPC type %d", spawn2_id, spawngroup_id_, npcid); @@ -214,7 +220,7 @@ bool Spawn2::Process() { if (!entity_list.LimitCheckName(tmp->name)) { Log(Logs::Detail, Logs::Spawns, - "Spawn2 %d: Spawn group %d yeilded NPC type %d, which is unique and one already exists.", + "Spawn2 %d: Spawn group %d yielded NPC type %d, which is unique and one already exists.", spawn2_id, spawngroup_id_, npcid); @@ -227,7 +233,7 @@ bool Spawn2::Process() { if (!entity_list.LimitCheckType(npcid, tmp->spawn_limit)) { Log(Logs::Detail, Logs::Spawns, - "Spawn2 %d: Spawn group %d yeilded NPC type %d, which is over its spawn limit (%d)", + "Spawn2 %d: Spawn group %d yielded NPC type %d, which is over its spawn limit (%d)", spawn2_id, spawngroup_id_, npcid, @@ -881,19 +887,19 @@ void SpawnConditionManager::ExecEvent(SpawnEvent &event, bool send_update) { break; case SpawnEvent::ActionAdd: new_value += event.argument; - Log(Logs::Detail, Logs::Spawns, "Event %d: Executing. Adding %d to condition %d, yeilding %d.", event.id, event.argument, event.condition_id, new_value); + Log(Logs::Detail, Logs::Spawns, "Event %d: Executing. Adding %d to condition %d, yielding %d.", event.id, event.argument, event.condition_id, new_value); break; case SpawnEvent::ActionSubtract: new_value -= event.argument; - Log(Logs::Detail, Logs::Spawns, "Event %d: Executing. Subtracting %d from condition %d, yeilding %d.", event.id, event.argument, event.condition_id, new_value); + Log(Logs::Detail, Logs::Spawns, "Event %d: Executing. Subtracting %d from condition %d, yielding %d.", event.id, event.argument, event.condition_id, new_value); break; case SpawnEvent::ActionMultiply: new_value *= event.argument; - Log(Logs::Detail, Logs::Spawns, "Event %d: Executing. Multiplying condition %d by %d, yeilding %d.", event.id, event.condition_id, event.argument, new_value); + Log(Logs::Detail, Logs::Spawns, "Event %d: Executing. Multiplying condition %d by %d, yielding %d.", event.id, event.condition_id, event.argument, new_value); break; case SpawnEvent::ActionDivide: new_value /= event.argument; - Log(Logs::Detail, Logs::Spawns, "Event %d: Executing. Dividing condition %d by %d, yeilding %d.", event.id, event.condition_id, event.argument, new_value); + Log(Logs::Detail, Logs::Spawns, "Event %d: Executing. Dividing condition %d by %d, yielding %d.", event.id, event.condition_id, event.argument, new_value); break; default: Log(Logs::Detail, Logs::Spawns, "Event %d: Invalid event action type %d", event.id, event.action); diff --git a/zone/spawngroup.cpp b/zone/spawngroup.cpp index 41cd14060..8bff77bff 100644 --- a/zone/spawngroup.cpp +++ b/zone/spawngroup.cpp @@ -28,11 +28,12 @@ extern EntityList entity_list; extern Zone *zone; -SpawnEntry::SpawnEntry(uint32 in_NPCType, int in_chance, uint8 in_npc_spawn_limit) +SpawnEntry::SpawnEntry(uint32 in_NPCType, int in_chance, uint16 in_filter, uint8 in_npc_spawn_limit) { - NPCType = in_NPCType; - chance = in_chance; - npc_spawn_limit = in_npc_spawn_limit; + NPCType = in_NPCType; + chance = in_chance; + condition_value_filter = in_filter; + npc_spawn_limit = in_npc_spawn_limit; } SpawnGroup::SpawnGroup( @@ -64,7 +65,7 @@ SpawnGroup::SpawnGroup( despawn_timer = despawn_timer_in; } -uint32 SpawnGroup::GetNPCType() +uint32 SpawnGroup::GetNPCType(uint16 in_filter) { #if EQDEBUG >= 10 Log(Logs::General, Logs::None, "SpawnGroup[%08x]::GetNPCType()", (uint32) this); @@ -87,6 +88,9 @@ uint32 SpawnGroup::GetNPCType() continue; } + if (se->condition_value_filter != in_filter) + continue; + totalchance += se->chance; possible.push_back(se); } @@ -94,7 +98,6 @@ uint32 SpawnGroup::GetNPCType() return 0; } - int32 roll = 0; roll = zone->random.Int(0, totalchance - 1); @@ -242,6 +245,7 @@ bool ZoneDatabase::LoadSpawnGroups(const char *zone_name, uint16 version, SpawnG spawnentry.spawngroupID, npcid, chance, + condition_value_filter, npc_types.spawn_limit AS sl FROM @@ -266,7 +270,8 @@ bool ZoneDatabase::LoadSpawnGroups(const char *zone_name, uint16 version, SpawnG auto new_spawn_entry = new SpawnEntry( atoi(row[1]), atoi(row[2]), - (row[3] ? atoi(row[3]) : 0) + atoi(row[3]), + (row[4] ? atoi(row[4]) : 0) ); SpawnGroup *spawn_group = spawn_group_list->GetSpawnGroup(atoi(row[0])); @@ -342,6 +347,7 @@ bool ZoneDatabase::LoadSpawnGroupsByID(int spawn_group_id, SpawnGroupList *spawn (spawnentry.spawngroupID), spawnentry.npcid, spawnentry.chance, + spawnentry.condition_value_filter, spawngroup.spawn_limit FROM spawnentry, @@ -362,7 +368,8 @@ bool ZoneDatabase::LoadSpawnGroupsByID(int spawn_group_id, SpawnGroupList *spawn auto new_spawn_entry = new SpawnEntry( atoi(row[1]), atoi(row[2]), - (row[3] ? atoi(row[3]) : 0) + atoi(row[3]), + (row[4] ? atoi(row[4]) : 0) ); SpawnGroup *spawn_group = spawn_group_list->GetSpawnGroup(atoi(row[0])); diff --git a/zone/spawngroup.h b/zone/spawngroup.h index 4b68a35eb..2a4c1af6c 100644 --- a/zone/spawngroup.h +++ b/zone/spawngroup.h @@ -25,10 +25,11 @@ class SpawnEntry { public: - SpawnEntry(uint32 in_NPCType, int in_chance, uint8 in_npc_spawn_limit); + SpawnEntry(uint32 in_NPCType, int in_chance, uint16 in_filter, uint8 in_npc_spawn_limit); ~SpawnEntry() {} uint32 NPCType; int chance; + uint16 condition_value_filter; //this is a cached value from npc_types, for speed uint8 npc_spawn_limit; //max # of this entry which can be spawned in this zone @@ -52,7 +53,7 @@ public: ); ~SpawnGroup(); - uint32 GetNPCType(); + uint32 GetNPCType(uint16 condition_value_filter=1); void AddSpawnEntry(SpawnEntry *newEntry); uint32 id; float roamdist;