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/common/ruletypes.h b/common/ruletypes.h index 0e3f3cdc5..9085fafea 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -342,6 +342,7 @@ RULE_INT(Spells, CharismaEffectiveness, 10, "Deterimes how much resist modificat 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/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/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/attack.cpp b/zone/attack.cpp index 3dca5f8c3..a530594e8 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1387,13 +1387,13 @@ 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."); - MessageString(Chat::DefaultText, DIVINE_AURA_NO_ATK); //You can't attack while invulnerable! + Log(Logs::Detail, Logs::Combat, "Attack cancelled, Divine Aura is in effect."); + MessageString(Chat::DefaultText, DIVINE_AURA_NO_ATK); //You can't attack while invulnerable return false; } @@ -1412,7 +1412,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()); @@ -1952,8 +1952,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; } @@ -3941,7 +3941,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; } diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 9b7354190..630b75c22 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1205,8 +1205,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; } @@ -1914,8 +1918,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; @@ -3850,8 +3859,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/client_packet.cpp b/zone/client_packet.cpp index ccb4ee96a..d5dde9750 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -2852,12 +2852,16 @@ 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 poison_skill = GetSkill(EQEmu::skills::SkillApplyPoison); + + 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(Chat::Red, "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); diff --git a/zone/common.h b/zone/common.h index 0ffa67468..e06fa134a 100644 --- a/zone/common.h +++ b/zone/common.h @@ -388,6 +388,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/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); 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/npc.cpp b/zone/npc.cpp index 755664f49..759d6ffe0 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -209,6 +209,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; @@ -2838,39 +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); } - } 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)) { + ProcessSpecialAbilities(default_special_abilities); } + + 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 4c913f70d..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; } @@ -541,6 +541,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; diff --git a/zone/object.cpp b/zone/object.cpp index 241d3a3f1..b976b878a 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; 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; diff --git a/zone/spells.cpp b/zone/spells.cpp index 9392ab7af..406739acf 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -757,6 +757,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); diff --git a/zone/zone.cpp b/zone/zone.cpp index 8ca78735f..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,102 +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; } } - for (int x = 0; x < totalBS; x++) - { - // If spellid is 0, block all spells in the zone - if (block_all) - { - // If the same zone has entries other than spellid 0, they act as exceptions and are allowed - if (exception) - { - return false; - } - else - { - return true; - } - } - else - { - if (spell_id != blocked_spells[x].spellid) - { - continue; - } + // If all spells are blocked and this is an exception, it is not blocked + if (block_all && exception) { + return false; + } - switch (blocked_spells[x].type) - { - case 1: - { + 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 ZoneBlockedSpellTypes::ZoneWide: { + return true; + break; + } + 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; - } - 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; + } + 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;