From 2ef5ade596aafa5de0e2c36fe05840c22a2e30c7 Mon Sep 17 00:00:00 2001 From: Joey Wendt Date: Thu, 12 Oct 2017 00:43:25 -0500 Subject: [PATCH 01/24] Change weapon proc req. level to Level2 property --- zone/attack.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index a87f504b7..a6baffc8d 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3922,10 +3922,10 @@ void Mob::TryWeaponProc(const EQEmu::ItemInstance *inst, const EQEmu::ItemData * float WPC = ProcChance * (100.0f + // Proc chance for this weapon static_cast(weapon->ProcRate)) / 100.0f; if (zone->random.Roll(WPC)) { // 255 dex = 0.084 chance of proc. No idea what this number should be really. - if (weapon->Proc.Level > ourlevel) { + if (weapon->Proc.Level2 > ourlevel) { Log(Logs::Detail, Logs::Combat, "Tried to proc (%s), but our level (%d) is lower than required (%d)", - weapon->Name, ourlevel, weapon->Proc.Level); + weapon->Name, ourlevel, weapon->Proc.Level2); if (IsPet()) { Mob *own = GetOwner(); if (own) @@ -3962,7 +3962,7 @@ void Mob::TryWeaponProc(const EQEmu::ItemInstance *inst, const EQEmu::ItemData * float APC = ProcChance * (100.0f + // Proc chance for this aug static_cast(aug->ProcRate)) / 100.0f; if (zone->random.Roll(APC)) { - if (aug->Proc.Level > ourlevel) { + if (aug->Proc.Level2 > ourlevel) { if (IsPet()) { Mob *own = GetOwner(); if (own) From 3bcfcc6308dca4c1df03a3690c1bc47de3a82d23 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 16 Oct 2017 12:56:00 -0500 Subject: [PATCH 02/24] ZoneDatabase::GetKarma crash fix --- zone/zonedb.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index bd28bcba3..a9a0c7042 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -2953,9 +2953,11 @@ uint32 ZoneDatabase::GetKarma(uint32 acct_id) if (!results.Success()) return 0; - auto row = results.begin(); + for (auto row = results.begin(); row != results.end(); ++row) { + return atoi(row[0]); + } - return atoi(row[0]); + return 0; } void ZoneDatabase::UpdateKarma(uint32 acct_id, uint32 amount) From a7d0251b7711a798103c35296e2407a3fb5c9c76 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 16 Oct 2017 13:15:03 -0500 Subject: [PATCH 03/24] Mob::TryFadeEffect sanity checks for potential crashing --- zone/mob.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/zone/mob.cpp b/zone/mob.cpp index efb2922f0..d08804410 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3949,10 +3949,17 @@ int16 Mob::GetHealRate(uint16 spell_id, Mob* caster) { bool Mob::TryFadeEffect(int slot) { + if (!buffs[slot].spellid) + return false; + if(IsValidSpell(buffs[slot].spellid)) { for(int i = 0; i < EFFECT_COUNT; i++) { + + if (!spells[buffs[slot].spellid].effectid[i]) + continue; + if (spells[buffs[slot].spellid].effectid[i] == SE_CastOnFadeEffectAlways || spells[buffs[slot].spellid].effectid[i] == SE_CastOnRuneFadeEffect) { From e80f3c87e97e0f9ab9fa1b768f1da71166750077 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 16 Oct 2017 13:20:33 -0500 Subject: [PATCH 04/24] Zone: Main loop crash fix --- zone/net.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/zone/net.cpp b/zone/net.cpp index eafdde807..80411af7b 100644 --- a/zone/net.cpp +++ b/zone/net.cpp @@ -542,8 +542,10 @@ int main(int argc, char** argv) { process_timer.Stop(); process_timer.Start(1000, true); - uint32 shutdown_timer = database.getZoneShutDownDelay(zone->GetZoneID(), zone->GetInstanceVersion()); - zone->StartShutdownTimer(shutdown_timer); + if (zone && zone->GetZoneID() && zone->GetInstanceVersion()) { + uint32 shutdown_timer = database.getZoneShutDownDelay(zone->GetZoneID(), zone->GetInstanceVersion()); + zone->StartShutdownTimer(shutdown_timer); + } } else if (!previous_loaded && current_loaded) { process_timer.Stop(); From f29fe17496ba34f4818e0a1812a903a5b79e75d7 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 16 Oct 2017 13:46:09 -0500 Subject: [PATCH 05/24] Potential pathing crash fix --- zone/mob_ai.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 8efdba38c..a6d6c992a 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1298,7 +1298,7 @@ void Mob::AI_Process() { if (AI_PursueCastCheck()) { //we did something, so do not process movement. } - else if (AI_movement_timer->Check()) + else if (AI_movement_timer->Check() && target) { if (!IsRooted()) { Log(Logs::Detail, Logs::AI, "Pursuing %s while engaged.", target->GetName()); From 00b2debb3217ed13cd4924f8eebcddf681b9e497 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 16 Oct 2017 13:52:01 -0500 Subject: [PATCH 06/24] Raid::UpdateGroupAAs out of range crash in memset --- zone/raids.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zone/raids.cpp b/zone/raids.cpp index 9b33056c0..d86d6c3fc 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -323,6 +323,10 @@ void Raid::SaveRaidLeaderAA() void Raid::UpdateGroupAAs(uint32 gid) { + + if (gid <= 0 || gid > MAX_RAID_GROUPS) + return; + Client *gl = GetGroupLeader(gid); if (gl) From b9d2c1b9f61f9c2176396f22a3de98bf1dfd1ae1 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 16 Oct 2017 13:56:02 -0500 Subject: [PATCH 07/24] Client::Handle_OP_RaidCommand crash fix --- zone/client_packet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index fe5ab8d92..3b91a280c 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -11480,7 +11480,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) Client *client_moved = entity_list.GetClientByName(raid_command_packet->leader_name); - if (client_moved) { + if (client_moved && client_moved->GetRaid()) { client_moved->GetRaid()->SendHPManaEndPacketsTo(client_moved); client_moved->GetRaid()->SendHPManaEndPacketsFrom(client_moved); From b10f5d374583053bda91e15ad3e0266051fcd50f Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 16 Oct 2017 15:52:59 -0500 Subject: [PATCH 08/24] Group ID can be zero --- zone/raids.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/raids.cpp b/zone/raids.cpp index d86d6c3fc..2bbd83064 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -324,7 +324,7 @@ void Raid::SaveRaidLeaderAA() void Raid::UpdateGroupAAs(uint32 gid) { - if (gid <= 0 || gid > MAX_RAID_GROUPS) + if (gid < 0 || gid > MAX_RAID_GROUPS) return; Client *gl = GetGroupLeader(gid); From 199dd7d6183728490b2373e8bb18ef4769b69fc1 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 20 Oct 2017 17:01:39 -0400 Subject: [PATCH 09/24] Fix missing round kick dmg calc --- zone/special_attacks.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 57ea1c781..03b144b5a 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -37,6 +37,7 @@ int Mob::GetBaseSkillDamage(EQEmu::skills::SkillType skill, Mob *target) case EQEmu::skills::SkillDragonPunch: case EQEmu::skills::SkillEagleStrike: case EQEmu::skills::SkillTigerClaw: + case EQEmu::skills::SkillRoundKick: if (skill_level >= 25) base++; if (skill_level >= 75) From c8b75e982ef24617c8ba99aff9fccd990d2f11b7 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Fri, 20 Oct 2017 18:36:11 -0500 Subject: [PATCH 10/24] Safeguard characters losing their account_id --- zone/zonedb.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index a9a0c7042..e23d534c5 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1433,6 +1433,11 @@ bool ZoneDatabase::SaveCharacterInventorySnapshot(uint32 character_id){ } bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp){ + + /* If this is ever zero - the client hasn't fully loaded and potentially crashed during zone */ + if (account_id <= 0) + return false; + clock_t t = std::clock(); /* Function timer start */ std::string query = StringFormat( "REPLACE INTO `character_data` (" From 9856df20fbf402208b0caf45cbf02f6cc603bba3 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 23 Oct 2017 17:24:18 -0500 Subject: [PATCH 11/24] Fix SQL syntax in Adventure::MoveCorpsesToGraveyard() --- world/adventure.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/world/adventure.cpp b/world/adventure.cpp index cbea9079c..c984fa831 100644 --- a/world/adventure.cpp +++ b/world/adventure.cpp @@ -380,7 +380,7 @@ void Adventure::MoveCorpsesToGraveyard() std::list dbid_list; std::list charid_list; - std::string query = StringFormat("SELECT id, charid FROM character_corpses WHERE instanceid=%d", GetInstanceID()); + std::string query = StringFormat("SELECT id, charid FROM character_corpses WHERE instance_id=%d", GetInstanceID()); auto results = database.QueryDatabase(query); if(!results.Success()) @@ -395,8 +395,8 @@ void Adventure::MoveCorpsesToGraveyard() float z = GetTemplate()->graveyard_z; query = StringFormat("UPDATE character_corpses " - "SET zoneid = %d, instanceid = 0, " - "x = %f, y = %f, z = %f WHERE instanceid = %d", + "SET zone_id = %d, instance_id = 0, " + "x = %f, y = %f, z = %f WHERE instance_id = %d", GetTemplate()->graveyard_zone_id, x, y, z, GetInstanceID()); database.QueryDatabase(query); From 50d5f3785c253fd4ca80e3885ba26464d5ca8bf8 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 24 Oct 2017 00:02:49 -0400 Subject: [PATCH 12/24] Send caster name in buff packets for UF+ Currently the names only show for inspecting via Target Window because we send buff packets in the wrong order and there is a dependency on them being correct for self that isn't present for target window. --- common/eq_packet_structs.h | 2 ++ common/patches/rof.cpp | 4 ++-- common/patches/rof2.cpp | 4 ++-- common/patches/uf.cpp | 4 ++-- zone/spell_effects.cpp | 17 +++++++++-------- zone/spells.cpp | 9 +++++++-- 6 files changed, 24 insertions(+), 16 deletions(-) diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 4876b4211..8452813f5 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -4767,6 +4767,7 @@ struct BuffIconEntry_Struct uint32 spell_id; int32 tics_remaining; uint32 num_hits; + char caster[64]; }; struct BuffIcon_Struct @@ -4776,6 +4777,7 @@ struct BuffIcon_Struct uint16 count; uint8 type; // 0 = self buff window, 1 = self target window, 4 = group, 5 = PC, 7 = NPC int32 tic_timer; + int32 name_lengths; // so ahh we kind of do these packets hacky, this is the total length of all the names to make creating the real packets in the translators easier BuffIconEntry_Struct entries[0]; }; diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 1df8d1aa5..b655cffb0 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -460,7 +460,7 @@ namespace RoF { SETUP_VAR_ENCODE(BuffIcon_Struct); - uint32 sz = 12 + (17 * emu->count); + uint32 sz = 12 + (17 * emu->count) + emu->name_lengths; // 17 includes nullterm __packet->size = sz; __packet->pBuffer = new unsigned char[sz]; memset(__packet->pBuffer, 0, sz); @@ -476,7 +476,7 @@ namespace RoF __packet->WriteUInt32(emu->entries[i].spell_id); __packet->WriteUInt32(emu->entries[i].tics_remaining); __packet->WriteUInt32(emu->entries[i].num_hits); // Unknown - __packet->WriteString(""); + __packet->WriteString(emu->entries[i].caster); } __packet->WriteUInt8(emu->type); // Unknown diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 57a8d9eb6..baf6d6b09 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -528,7 +528,7 @@ namespace RoF2 { SETUP_VAR_ENCODE(BuffIcon_Struct); - uint32 sz = 12 + (17 * emu->count); + uint32 sz = 12 + (17 * emu->count) + emu->name_lengths; // 17 includes nullterm __packet->size = sz; __packet->pBuffer = new unsigned char[sz]; memset(__packet->pBuffer, 0, sz); @@ -544,7 +544,7 @@ namespace RoF2 __packet->WriteUInt32(emu->entries[i].spell_id); __packet->WriteUInt32(emu->entries[i].tics_remaining); __packet->WriteUInt32(emu->entries[i].num_hits); // Unknown - __packet->WriteString(""); + __packet->WriteString(emu->entries[i].caster); } __packet->WriteUInt8(emu->type); // Unknown diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index 87cd6c700..cf0c17176 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -391,7 +391,7 @@ namespace UF { SETUP_VAR_ENCODE(BuffIcon_Struct); - uint32 sz = 12 + (17 * emu->count); + uint32 sz = 12 + (17 * emu->count) + emu->name_lengths; // 17 includes nullterm __packet->size = sz; __packet->pBuffer = new unsigned char[sz]; memset(__packet->pBuffer, 0, sz); @@ -407,7 +407,7 @@ namespace UF __packet->WriteUInt32(emu->entries[i].spell_id); __packet->WriteUInt32(emu->entries[i].tics_remaining); __packet->WriteUInt32(emu->entries[i].num_hits); - __packet->WriteString(""); + __packet->WriteString(emu->entries[i].caster); } __packet->WriteUInt8(emu->type); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 375572014..9f78cb35d 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -137,6 +137,15 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove buffs[buffslot].magic_rune = 0; buffs[buffslot].numhits = 0; + if (spells[spell_id].numhits > 0) { + + int numhit = spells[spell_id].numhits; + + numhit += numhit * caster->GetFocusEffect(focusFcLimitUse, spell_id) / 100; + numhit += caster->GetFocusEffect(focusIncreaseNumHits, spell_id); + buffs[buffslot].numhits = numhit; + } + if (spells[spell_id].EndurUpkeep > 0) SetEndurUpkeep(true); @@ -184,14 +193,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove } } - if(spells[spell_id].numhits > 0 && buffslot >= 0){ - - int numhit = spells[spell_id].numhits; - - numhit += numhit*caster->GetFocusEffect(focusFcLimitUse, spell_id)/100; - numhit += caster->GetFocusEffect(focusIncreaseNumHits, spell_id); - buffs[buffslot].numhits = numhit; - } if (!IsPowerDistModSpell(spell_id)) SetSpellPowerDistanceMod(0); diff --git a/zone/spells.cpp b/zone/spells.cpp index 877886757..c5a5a0b48 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3309,8 +3309,8 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid buffs[emptyslot].spellid = spell_id; buffs[emptyslot].casterlevel = caster_level; - if (caster && caster->IsClient()) - strcpy(buffs[emptyslot].caster_name, caster->GetName()); + if (caster && !caster->IsAura()) // maybe some other things we don't want to ... + strcpy(buffs[emptyslot].caster_name, caster->GetCleanName()); else memset(buffs[emptyslot].caster_name, 0, 64); buffs[emptyslot].casterid = caster ? caster->GetID() : 0; @@ -5548,6 +5548,8 @@ void Client::SendBuffNumHitPacket(Buffs_Struct &buff, int slot) bi->entries[0].spell_id = buff.spellid; bi->entries[0].tics_remaining = buff.ticsremaining; bi->entries[0].num_hits = buff.numhits; + strn0cpy(bi->entries[0].caster, buff.caster_name, 64); + bi->name_lengths = strlen(bi->entries[0].caster); FastQueuePacket(&outapp); } @@ -5633,6 +5635,7 @@ EQApplicationPacket *Mob::MakeBuffsPacket(bool for_target) else buff->type = 0; + buff->name_lengths = 0; // hacky shit uint32 index = 0; for(int i = 0; i < buff_count; ++i) { @@ -5642,6 +5645,8 @@ EQApplicationPacket *Mob::MakeBuffsPacket(bool for_target) buff->entries[index].spell_id = buffs[i].spellid; buff->entries[index].tics_remaining = buffs[i].ticsremaining; buff->entries[index].num_hits = buffs[i].numhits; + strn0cpy(buff->entries[index].caster, buffs[i].caster_name, 64); + buff->name_lengths += strlen(buff->entries[index].caster); ++index; } } From ed98aa45d2677840bdb7566c358861dc96138e84 Mon Sep 17 00:00:00 2001 From: regneq Date: Fri, 27 Oct 2017 21:24:24 -0700 Subject: [PATCH 13/24] Traps overhaul. New functionality has been added, while preserving the old functionality. Numerous bug fixes occurred as well. Added column triggered_number. If this is set, then the trap will despawn after it has been triggered this number of times. If 0, the trap will never despawn on its own. Added group column. This allows developers to group traps together in a similar way as spawngroups for NPCs. When a trap that is grouped is despawned in anyway, a random trap in the group will take its place. Grouped traps do not have to be at the same coords or have the same type. This can allow for some spawning diversity if so required. If set to 0, the trap is not grouped and will always respawn. Added column despawn_when_triggered. If set to 1, then a trap will despawn when a player triggers it. If 0, then there will be a 5 second reset time and then the same trap will again be active. (Assuming triggered_number has not been reached.) The player that triggered the trap will not re-trigger it until they have left and re-enetered the trap's radius. Traps will no longer trigger on players that are currently zoning. This fixes some weirdness and at least one crash. The trap can trigger however after the connection is been completed. If a player camped out in a trap radius they can potentially still be hit. Alarm type traps were not using effectvalue2 to determine who should be aggroed. This is now fixed. Traps will no longer be broken by #repop, #depopzone, or #reloadworld. All 3 commands will now have the same effect on traps as they do for NPCs. Added command #reloadtraps. This reloads all of the traps in the zone. Added command #trapinfo. This gives some information about the traps currently spawned in the zone. Added Traps logsys category Required SQL: utils/sql/git/required/2017_10_26_traps.sql --- common/eqemu_logsys.h | 4 +- utils/sql/git/required/2017_10_28_traps.sql | 3 + zone/client.cpp | 2 + zone/client.h | 2 + zone/client_packet.cpp | 4 +- zone/command.cpp | 12 + zone/command.h | 2 + zone/entity.h | 4 + zone/trap.cpp | 271 ++++++++++++++++++-- zone/trap.h | 13 +- zone/zone.cpp | 7 +- zone/zonedb.h | 3 +- 12 files changed, 297 insertions(+), 30 deletions(-) create mode 100644 utils/sql/git/required/2017_10_28_traps.sql diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 10d9e460c..a54edc234 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -89,6 +89,7 @@ enum LogCategory { HP_Update, FixZ, Food, + Traps, MaxCategoryID /* Don't Remove this*/ }; @@ -142,7 +143,8 @@ static const char* LogCategoryName[LogCategory::MaxCategoryID] = { "Headless Client", "HP Update", "FixZ", - "Food" + "Food", + "Traps" }; } diff --git a/utils/sql/git/required/2017_10_28_traps.sql b/utils/sql/git/required/2017_10_28_traps.sql new file mode 100644 index 000000000..20c249ede --- /dev/null +++ b/utils/sql/git/required/2017_10_28_traps.sql @@ -0,0 +1,3 @@ +alter table `traps` add column `triggered_number` tinyint(4) not null default 0; +alter table `traps` add column `group` tinyint(4) not null default 0; +alter table `traps` add column `despawn_when_triggered` tinyint(4) not null default 0; diff --git a/zone/client.cpp b/zone/client.cpp index ce084747a..893a6b51b 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -331,6 +331,8 @@ Client::Client(EQStreamInterface* ieqs) interrogateinv_flag = false; + trapid = 0; + for (int i = 0; i < InnateSkillMax; ++i) m_pp.InnateSkills[i] = InnateDisabled; diff --git a/zone/client.h b/zone/client.h index e334f8fd9..88622fcff 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1302,6 +1302,8 @@ public: int32 CalcATK(); + uint32 trapid; //ID of trap player has triggered. This is cleared when the player leaves the trap's radius, or it despawns. + protected: friend class Mob; void CalcItemBonuses(StatBonuses* newbon); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 6710fdfec..4271ec33f 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -5337,8 +5337,8 @@ void Client::Handle_OP_DisarmTraps(const EQApplicationPacket *app) { Message(MT_Skills, "You disarm a trap."); trap->disarmed = true; - trap->chkarea_timer.Disable(); - trap->respawn_timer.Start((trap->respawn_time + zone->random.Int(0, trap->respawn_var)) * 1000); + Log(Logs::General, Logs::Traps, "Trap %d is disarmed.", trap->trap_id); + trap->UpdateTrap(); } else { diff --git a/zone/command.cpp b/zone/command.cpp index 15b09b9dc..a65f9005e 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -321,6 +321,7 @@ int command_init(void) command_add("reloadqst", " - Clear quest cache (any argument causes it to also stop all timers)", 150, command_reloadqst) || command_add("reloadrulesworld", "Executes a reload of all rules in world specifically.", 80, command_reloadworldrules) || command_add("reloadstatic", "- Reload Static Zone Data", 150, command_reloadstatic) || + command_add("reloadtraps", "- Repops all traps in the current zone.", 80, command_reloadtraps) || command_add("reloadtitles", "- Reload player titles from the database", 150, command_reloadtitles) || command_add("reloadworld", "[0|1] - Clear quest cache (0 - no repop, 1 - repop)", 255, command_reloadworld) || command_add("reloadzps", "- Reload zone points from database", 150, command_reloadzps) || @@ -382,6 +383,7 @@ int command_init(void) command_add("title", "[text] [1 = create title table row] - Set your or your player target's title", 50, command_title) || command_add("titlesuffix", "[text] [1 = create title table row] - Set your or your player target's title suffix", 50, command_titlesuffix) || command_add("traindisc", "[level] - Trains all the disciplines usable by the target, up to level specified. (may freeze client for a few seconds)", 150, command_traindisc) || + command_add("trapinfo", "- Gets infomation about the traps currently spawned in the zone.", 81, command_trapinfo) || command_add("tune", "Calculate ideal statical values related to combat.", 100, command_tune) || command_add("undyeme", "- Remove dye from all of your armor slots", 0, command_undyeme) || command_add("unfreeze", "- Unfreeze your target", 80, command_unfreeze) || @@ -10851,6 +10853,16 @@ void command_reloadperlexportsettings(Client *c, const Seperator *sep) } } +void command_trapinfo(Client *c, const Seperator *sep) +{ + entity_list.GetTrapInfo(c); +} + +void command_reloadtraps(Client *c, const Seperator *sep) +{ + entity_list.UpdateAllTraps(true, true); + c->Message(CC_Default, "Traps reloaded for %s.", zone->GetShortName()); +} // All new code added to command.cpp should be BEFORE this comment line. Do no append code to this file below the BOTS code block. #ifdef BOTS diff --git a/zone/command.h b/zone/command.h index 0a850fbca..dca70d767 100644 --- a/zone/command.h +++ b/zone/command.h @@ -228,6 +228,7 @@ void command_reloadperlexportsettings(Client *c, const Seperator *sep); void command_reloadqst(Client *c, const Seperator *sep); void command_reloadstatic(Client *c, const Seperator *sep); void command_reloadtitles(Client *c, const Seperator *sep); +void command_reloadtraps(Client* c, const Seperator *sep); void command_reloadworld(Client *c, const Seperator *sep); void command_reloadworldrules(Client *c, const Seperator *sep); void command_reloadzps(Client *c, const Seperator *sep); @@ -295,6 +296,7 @@ void command_timezone(Client *c, const Seperator *sep); void command_title(Client *c, const Seperator *sep); void command_titlesuffix(Client *c, const Seperator *sep); void command_traindisc(Client *c, const Seperator *sep); +void command_trapinfo(Client* c, const Seperator *sep); void command_tune(Client *c, const Seperator *sep); void command_undye(Client *c, const Seperator *sep); void command_undyeme(Client *c, const Seperator *sep); diff --git a/zone/entity.h b/zone/entity.h index 29075d9fe..996580aa6 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -473,6 +473,10 @@ public: void RefreshClientXTargets(Client *c); void SendAlternateAdvancementStats(); + void GetTrapInfo(Client* client); + bool IsTrapGroupSpawned(uint32 trap_id, uint8 group); + void UpdateAllTraps(bool respawn, bool repopnow = false); + void ClearTrapPointers(); protected: friend class Zone; void Depop(bool StartSpawnTimer = false); diff --git a/zone/trap.cpp b/zone/trap.cpp index 87000f940..f563048f8 100644 --- a/zone/trap.cpp +++ b/zone/trap.cpp @@ -52,10 +52,12 @@ CREATE TABLE traps ( Trap::Trap() : Entity(), respawn_timer(600000), - chkarea_timer(500), + chkarea_timer(1000), + reset_timer(5000), m_Position(glm::vec3()) { trap_id = 0; + db_id = 0; maxzdiff = 0; radius = 0; effect = 0; @@ -64,12 +66,19 @@ Trap::Trap() : skill = 0; level = 0; respawn_timer.Disable(); + reset_timer.Disable(); detected = false; disarmed = false; respawn_time = 0; respawn_var = 0; hiddenTrigger = nullptr; ownHiddenTrigger = false; + chance = 0; + triggered_number = 0; + times_triggered = 0; + group = 0; + despawn_when_triggered = false; + charid = 0; } Trap::~Trap() @@ -80,8 +89,7 @@ Trap::~Trap() bool Trap::Process() { - if (chkarea_timer.Enabled() && chkarea_timer.Check() - /*&& zone->GetClientCount() > 0*/ ) + if (chkarea_timer.Enabled() && chkarea_timer.Check() && !reset_timer.Enabled()) { Mob* trigger = entity_list.GetTrapTrigger(this); if (trigger && !(trigger->IsClient() && trigger->CastToClient()->GetGM())) @@ -89,6 +97,13 @@ bool Trap::Process() Trigger(trigger); } } + else if (reset_timer.Enabled() && reset_timer.Check()) + { + Log(Logs::General, Logs::Traps, "Reset timer disabled in Reset Check Process for trap %d.", trap_id); + reset_timer.Disable(); + charid = 0; + } + if (respawn_timer.Enabled() && respawn_timer.Check()) { detected = false; @@ -96,11 +111,15 @@ bool Trap::Process() chkarea_timer.Enable(); respawn_timer.Disable(); } + + return true; } void Trap::Trigger(Mob* trigger) { + Log(Logs::General, Logs::Traps, "Trap %d triggered by %s for the %d time!", trap_id, trigger->GetName(), times_triggered + 1); + int i = 0; const NPCType* tmp = 0; switch (effect) @@ -128,7 +147,7 @@ void Trap::Trigger(Mob* trigger) entity_list.MessageClose(trigger,false,effectvalue,13,"%s",message.c_str()); } - entity_list.SendAlarm(this,trigger,effectvalue); + entity_list.SendAlarm(this,trigger, effectvalue2); break; case trapTypeMysticSpawn: if (message.empty()) @@ -201,12 +220,41 @@ void Trap::Trigger(Mob* trigger) safe_delete(outapp); } } - respawn_timer.Start((respawn_time + zone->random.Int(0, respawn_var)) * 1000); - chkarea_timer.Disable(); - disarmed = true; + + if (trigger && trigger->IsClient()) + { + trigger->CastToClient()->trapid = trap_id; + charid = trigger->CastToClient()->CharacterID(); + } + + bool update = false; + if (despawn_when_triggered) + { + Log(Logs::General, Logs::Traps, "Trap %d is despawning after being triggered.", trap_id); + update = true; + } + else + { + reset_timer.Start(5000); + } + + if (triggered_number > 0) + ++times_triggered; + + if (triggered_number > 0 && triggered_number <= times_triggered) + { + Log(Logs::General, Logs::Traps, "Triggered number for trap %d reached. %d/%d", trap_id, times_triggered, triggered_number); + update = true; + } + + if (update) + { + UpdateTrap(); + } } -Trap* EntityList::FindNearbyTrap(Mob* searcher, float max_dist) { +Trap* EntityList::FindNearbyTrap(Mob* searcher, float max_dist) +{ float dist = 999999; Trap* current_trap = nullptr; @@ -231,45 +279,135 @@ Trap* EntityList::FindNearbyTrap(Mob* searcher, float max_dist) { return current_trap; } -Mob* EntityList::GetTrapTrigger(Trap* trap) { - Mob* savemob = 0; +Mob* EntityList::GetTrapTrigger(Trap* trap) +{ float maxdist = trap->radius * trap->radius; - - for (auto it = client_list.begin(); it != client_list.end(); ++it) { + for (auto it = client_list.begin(); it != client_list.end(); ++it) + { Client* cur = it->second; auto diff = glm::vec3(cur->GetPosition()) - trap->m_Position; diff.z = std::abs(diff.z); if ((diff.x*diff.x + diff.y*diff.y) <= maxdist - && diff.z < trap->maxzdiff) + && diff.z <= trap->maxzdiff) { - if (zone->random.Roll(trap->chance)) - return(cur); - else - savemob = cur; - } + //This prevents the trap from triggering on players while zoning. + if (strcmp(cur->GetName(), "No name") == 0) + continue; + if (cur->trapid == 0 && !cur->GetGM() && (trap->chance == 0 || zone->random.Roll(trap->chance))) + { + Log(Logs::General, Logs::Traps, "%s is about to trigger trap %d of chance %d. diff: %0.2f maxdist: %0.2f zdiff: %0.2f maxzdiff: %0.2f", cur->GetName(), trap->trap_id, trap->chance, (diff.x*diff.x + diff.y*diff.y), maxdist, diff.z, trap->maxzdiff); + return cur; + } + } + else + { + if (cur->trapid == trap->trap_id) + { + Log(Logs::General, Logs::Traps, "%s is clearing trapid for trap %d", cur->GetName(), trap->trap_id); + cur->trapid = 0; + } + } } - return savemob; + return nullptr; } -//todo: rewrite this to not need direct access to trap members. +bool EntityList::IsTrapGroupSpawned(uint32 trap_id, uint8 group) +{ + auto it = trap_list.begin(); + while (it != trap_list.end()) + { + Trap* cur = it->second; + if (cur->IsTrap() && cur->group == group && cur->trap_id != trap_id) + { + return true; + } + ++it; + } + + return false; +} + +void EntityList::UpdateAllTraps(bool respawn, bool repopnow) +{ + auto it = trap_list.begin(); + while (it != trap_list.end()) + { + Trap* cur = it->second; + if (cur->IsTrap()) + { + cur->UpdateTrap(respawn, repopnow); + } + ++it; + } + + Log(Logs::General, Logs::Traps, "All traps updated."); +} + +void EntityList::GetTrapInfo(Client* client) +{ + uint8 count = 0; + auto it = trap_list.begin(); + while (it != trap_list.end()) + { + Trap* cur = it->second; + if (cur->IsTrap()) + { + bool isset = (cur->chkarea_timer.Enabled() && !cur->reset_timer.Enabled()); + client->Message(CC_Default, " Trap: (%d) found at %0.2f,%0.2f,%0.2f. Times Triggered: %d Is Active: %d Group: %d Message: %s", cur->trap_id, cur->m_Position.x, cur->m_Position.y, cur->m_Position.z, cur->times_triggered, isset, cur->group, cur->message.c_str()); + ++count; + } + ++it; + } + + client->Message(CC_Default, "%d traps found.", count); +} + +void EntityList::ClearTrapPointers() +{ + auto it = trap_list.begin(); + while (it != trap_list.end()) + { + Trap* cur = it->second; + if (cur->IsTrap()) + { + cur->DestroyHiddenTrigger(); + } + ++it; + } +} + + bool ZoneDatabase::LoadTraps(const char* zonename, int16 version) { std::string query = StringFormat("SELECT id, x, y, z, effect, effectvalue, effectvalue2, skill, " - "maxzdiff, radius, chance, message, respawn_time, respawn_var, level " - "FROM traps WHERE zone='%s' AND version=%u", zonename, version); + "maxzdiff, radius, chance, message, respawn_time, respawn_var, level, " + "`group`, triggered_number, despawn_when_triggered FROM traps WHERE zone='%s' AND version=%u", zonename, version); + auto results = QueryDatabase(query); if (!results.Success()) { return false; } for (auto row = results.begin(); row != results.end(); ++row) { + uint32 tid = atoi(row[0]); + uint8 grp = atoi(row[15]); + + if (grp > 0) + { + // If a member of our group is already spawned skip loading this trap. + if (entity_list.IsTrapGroupSpawned(tid, grp)) + { + continue; + } + } auto trap = new Trap(); - trap->trap_id = atoi(row[0]); + trap->trap_id = tid; + trap->db_id = tid; trap->m_Position = glm::vec3(atof(row[1]), atof(row[2]), atof(row[3])); trap->effect = atoi(row[4]); trap->effectvalue = atoi(row[5]); @@ -282,8 +420,12 @@ bool ZoneDatabase::LoadTraps(const char* zonename, int16 version) { trap->respawn_time = atoi(row[12]); trap->respawn_var = atoi(row[13]); trap->level = atoi(row[14]); + trap->group = grp; + trap->triggered_number = atoi(row[16]); + trap->despawn_when_triggered = atoi(row[17]); entity_list.AddTrap(trap); trap->CreateHiddenTrigger(); + Log(Logs::General, Logs::Traps, "Trap %d successfully loaded.", trap->trap_id); } return true; @@ -318,3 +460,86 @@ void Trap::CreateHiddenTrigger() hiddenTrigger = npca; ownHiddenTrigger = true; } +bool ZoneDatabase::SetTrapData(Trap* trap, bool repopnow) { + + uint32 dbid = trap->db_id; + std::string query; + + if (trap->group > 0) + { + query = StringFormat("SELECT id, x, y, z, effect, effectvalue, effectvalue2, skill, " + "maxzdiff, radius, chance, message, respawn_time, respawn_var, level, " + "triggered_number, despawn_when_triggered FROM traps WHERE zone='%s' AND `group`=%d AND id != %d ORDER BY RAND() LIMIT 1", zone->GetShortName(), trap->group, dbid); + } + else + { + // We could just use the existing data here, but querying the DB is not expensive, and allows content developers to change traps without rebooting. + query = StringFormat("SELECT id, x, y, z, effect, effectvalue, effectvalue2, skill, " + "maxzdiff, radius, chance, message, respawn_time, respawn_var, level, " + "triggered_number, despawn_when_triggered FROM traps WHERE zone='%s' AND id = %d", zone->GetShortName(), dbid); + } + + auto results = QueryDatabase(query); + if (!results.Success()) { + return false; + } + + for (auto row = results.begin(); row != results.end(); ++row) { + + trap->db_id = atoi(row[0]); + trap->m_Position = glm::vec3(atof(row[1]), atof(row[2]), atof(row[3])); + trap->effect = atoi(row[4]); + trap->effectvalue = atoi(row[5]); + trap->effectvalue2 = atoi(row[6]); + trap->skill = atoi(row[7]); + trap->maxzdiff = atof(row[8]); + trap->radius = atof(row[9]); + trap->chance = atoi(row[10]); + trap->message = row[11]; + trap->respawn_time = atoi(row[12]); + trap->respawn_var = atoi(row[13]); + trap->level = atoi(row[14]); + trap->triggered_number = atoi(row[15]); + trap->despawn_when_triggered = atoi(row[16]); + trap->CreateHiddenTrigger(); + + if (repopnow) + { + trap->chkarea_timer.Enable(); + } + else + { + trap->respawn_timer.Start((trap->respawn_time + zone->random.Int(0, trap->respawn_var)) * 1000); + } + + if (trap->trap_id != trap->db_id) + Log(Logs::General, Logs::Traps, "Trap (%d) DBID has changed from %d to %d", trap->trap_id, dbid, trap->db_id); + + return true; + } + + return false; +} + +void Trap::UpdateTrap(bool respawn, bool repopnow) +{ + respawn_timer.Disable(); + chkarea_timer.Disable(); + reset_timer.Disable(); + if (hiddenTrigger) + { + hiddenTrigger->Depop(); + hiddenTrigger = nullptr; + } + times_triggered = 0; + Client* trigger = entity_list.GetClientByCharID(charid); + if (trigger) + { + trigger->trapid = 0; + } + charid = 0; + if (respawn) + { + database.SetTrapData(this, repopnow); + } +} \ No newline at end of file diff --git a/zone/trap.h b/zone/trap.h index 4adab320a..4c0d53106 100644 --- a/zone/trap.h +++ b/zone/trap.h @@ -49,11 +49,15 @@ public: NPC * GetHiddenTrigger() { return hiddenTrigger; } void SetHiddenTrigger(NPC* n) { hiddenTrigger = n; } void CreateHiddenTrigger(); - + void DestroyHiddenTrigger() { hiddenTrigger = nullptr; } + void SetTrapData(); + void UpdateTrap(bool respawn = true, bool repopnow = false); //Trap data, leave this unprotected Timer respawn_timer; //Respawn Time when Trap's been disarmed Timer chkarea_timer; - uint32 trap_id; //Database ID of trap + Timer reset_timer; //How long a trap takes to reset before triggering again. + uint32 trap_id; //Original ID of the trap from DB. This value never changes. + uint32 db_id; //The DB ID of the trap that currently is spawned. glm::vec3 m_Position; float maxzdiff; //maximum z diff to be triggerable float radius; //radius around trap to be triggerable @@ -67,6 +71,11 @@ public: bool disarmed; uint32 respawn_time; uint32 respawn_var; + uint8 triggered_number; + uint8 times_triggered; + uint8 group; + bool despawn_when_triggered; + uint32 charid; //ID of character that triggered trap. This is cleared when the trap despawns are resets. std::string message; protected: diff --git a/zone/zone.cpp b/zone/zone.cpp index 8441f7d82..3a8f1e938 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1428,7 +1428,8 @@ void Zone::StartShutdownTimer(uint32 set_time) { bool Zone::Depop(bool StartSpawnTimer) { std::map::iterator itr; entity_list.Depop(StartSpawnTimer); - + entity_list.ClearTrapPointers(); + entity_list.UpdateAllTraps(false); /* Refresh npctable (cache), getting current info from database. */ while(!npctable.empty()) { itr = npctable.begin(); @@ -1496,6 +1497,8 @@ void Zone::Repop(uint32 delay) { iterator.RemoveCurrent(); } + entity_list.ClearTrapPointers(); + quest_manager.ClearAllTimers(); if (!database.PopulateZoneSpawnList(zoneid, spawn2_list, GetInstanceVersion(), delay)) @@ -1503,6 +1506,8 @@ void Zone::Repop(uint32 delay) { initgrids_timer.Start(); + entity_list.UpdateAllTraps(true, true); + //MODDING HOOK FOR REPOP mod_repop(); } diff --git a/zone/zonedb.h b/zone/zonedb.h index a8ab3659f..c134dade3 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -16,6 +16,7 @@ class NPC; class Petition; class Spawn2; class SpawnGroupList; +class Trap; struct CharacterEventLog_Struct; struct Door; struct ExtendedProfile_Struct; @@ -478,7 +479,7 @@ public: /* Traps */ bool LoadTraps(const char* zonename, int16 version); - char* GetTrapMessage(uint32 trap_id); + bool SetTrapData(Trap* trap, bool repopnow = false); /* Time */ uint32 GetZoneTZ(uint32 zoneid, uint32 version); From cd748e7d8bb865298d9b58963a75a6766f11411b Mon Sep 17 00:00:00 2001 From: regneq Date: Sat, 28 Oct 2017 10:02:31 -0700 Subject: [PATCH 14/24] Fixed an issue that would cause traps to not function correctly if skill is 0 in the database. Added undetectable column, to allow content developers to make a trap undetectable and not able to be disarmed. Pets will no longer try to aggro traps its owner triggers. Traps will now use the radius column to determine disarm range, instead of using a hardcoded value which may not be appropriate in all cases. Decreased the scan range for traps to disarm. Fixed some typos, and removed some unused code. --- utils/sql/git/required/2017_10_28_traps.sql | 1 + zone/attack.cpp | 5 ++- zone/client_packet.cpp | 50 +++++++++++++-------- zone/common.h | 5 +++ zone/entity.h | 2 +- zone/string_ids.h | 5 +++ zone/trap.cpp | 31 +++++++++---- zone/trap.h | 2 +- 8 files changed, 71 insertions(+), 30 deletions(-) diff --git a/utils/sql/git/required/2017_10_28_traps.sql b/utils/sql/git/required/2017_10_28_traps.sql index 20c249ede..dddc00d1b 100644 --- a/utils/sql/git/required/2017_10_28_traps.sql +++ b/utils/sql/git/required/2017_10_28_traps.sql @@ -1,3 +1,4 @@ alter table `traps` add column `triggered_number` tinyint(4) not null default 0; alter table `traps` add column `group` tinyint(4) not null default 0; alter table `traps` add column `despawn_when_triggered` tinyint(4) not null default 0; +alter table `traps` add column `undetectable` tinyint(4) not null default 0; diff --git a/zone/attack.cpp b/zone/attack.cpp index a6baffc8d..d12d8d36c 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2533,6 +2533,9 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b if (other == this) return; + if (other->IsTrap()) + return; + if (damage < 0) { hate = 1; } @@ -3364,7 +3367,7 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const // pets that have GHold will never automatically add NPCs // pets that have Hold and no Focus will add NPCs if they're engaged // pets that have Hold and Focus will not add NPCs - if (pet && !pet->IsFamiliar() && !pet->GetSpecialAbility(IMMUNE_AGGRO) && !pet->IsEngaged() && attacker && attacker != this && !attacker->IsCorpse() && !pet->IsGHeld()) + if (pet && !pet->IsFamiliar() && !pet->GetSpecialAbility(IMMUNE_AGGRO) && !pet->IsEngaged() && attacker && attacker != this && !attacker->IsCorpse() && !pet->IsGHeld() && !attacker->IsTrap()) { if (!pet->IsHeld()) { Log(Logs::Detail, Logs::Aggro, "Sending pet %s into battle due to attack.", pet->GetName()); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 4271ec33f..8efe55f2b 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -5329,31 +5329,44 @@ void Client::Handle_OP_DisarmTraps(const EQApplicationPacket *app) p_timers.Start(pTimerDisarmTraps, reuse - 1); - Trap* trap = entity_list.FindNearbyTrap(this, 60); + uint8 success = SKILLUP_FAILURE; + float curdist = 0; + Trap* trap = entity_list.FindNearbyTrap(this, 250, curdist, true); if (trap && trap->detected) { - int uskill = GetSkill(EQEmu::skills::SkillDisarmTraps); - if ((zone->random.Int(0, 49) + uskill) >= (zone->random.Int(0, 49) + trap->skill)) + float max_radius = (trap->radius * 2) * (trap->radius * 2); // radius is used to trigger trap, so disarm radius should be a bit bigger. + Log(Logs::General, Logs::Traps, "%s is attempting to disarm trap %d. Curdist is %0.2f maxdist is %0.2f", GetName(), trap->trap_id, curdist, max_radius); + if (curdist <= max_radius) { - Message(MT_Skills, "You disarm a trap."); - trap->disarmed = true; - Log(Logs::General, Logs::Traps, "Trap %d is disarmed.", trap->trap_id); - trap->UpdateTrap(); + int uskill = GetSkill(EQEmu::skills::SkillDisarmTraps); + if ((zone->random.Int(0, 49) + uskill) >= (zone->random.Int(0, 49) + trap->skill)) + { + success = SKILLUP_SUCCESS; + Message_StringID(MT_Skills, DISARMED_TRAP); + trap->disarmed = true; + Log(Logs::General, Logs::Traps, "Trap %d is disarmed.", trap->trap_id); + trap->UpdateTrap(); + } + else + { + Message_StringID(MT_Skills, FAIL_DISARM_DETECTED_TRAP); + if (zone->random.Int(0, 99) < 25) { + trap->Trigger(this); + } + } + CheckIncreaseSkill(EQEmu::skills::SkillDisarmTraps, nullptr); + return; } else { - if (zone->random.Int(0, 99) < 25) { - Message(MT_Skills, "You set off the trap while trying to disarm it!"); - trap->Trigger(this); - } - else { - Message(MT_Skills, "You failed to disarm a trap."); - } + Message_StringID(MT_Skills, TRAP_TOO_FAR); } - CheckIncreaseSkill(EQEmu::skills::SkillDisarmTraps, nullptr); - return; } - Message(MT_Skills, "You did not find any traps close enough to disarm."); + else + { + Message_StringID(MT_Skills, LDON_SENSE_TRAP2); + } + return; } @@ -12107,7 +12120,8 @@ void Client::Handle_OP_SenseTraps(const EQApplicationPacket *app) p_timers.Start(pTimerSenseTraps, reuse - 1); - Trap* trap = entity_list.FindNearbyTrap(this, 800); + float trap_curdist = 0; + Trap* trap = entity_list.FindNearbyTrap(this, 800, trap_curdist); CheckIncreaseSkill(EQEmu::skills::SkillSenseTraps, nullptr); diff --git a/zone/common.h b/zone/common.h index bcc9ff305..f7b157115 100644 --- a/zone/common.h +++ b/zone/common.h @@ -604,6 +604,11 @@ enum { //type arguments to DoAnim }; +enum { + SKILLUP_UNKNOWN = 0, + SKILLUP_SUCCESS = 1, + SKILLUP_FAILURE = 2 +}; typedef enum { petFamiliar, //only listens to /pet get lost diff --git a/zone/entity.h b/zone/entity.h index 996580aa6..dccbb45b3 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -365,7 +365,7 @@ public: //trap stuff Mob* GetTrapTrigger(Trap* trap); void SendAlarm(Trap* trap, Mob* currenttarget, uint8 kos); - Trap* FindNearbyTrap(Mob* searcher, float max_dist); + Trap* FindNearbyTrap(Mob* searcher, float max_dist, float &curdist, bool detected = false); void AddHealAggro(Mob* target, Mob* caster, uint16 hate); Mob* FindDefenseNPC(uint32 npcid); diff --git a/zone/string_ids.h b/zone/string_ids.h index c8175c8b6..997858bed 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -19,6 +19,7 @@ #define PROC_TOOLOW 126 //Your will is not sufficient to command this weapon. #define PROC_PETTOOLOW 127 //Your pet's will is not sufficient to command its weapon. #define YOU_FLURRY 128 //You unleash a flurry of attacks. +#define FAILED_DISARM_TRAP 129 //You failed to disarm the trap. #define DOORS_LOCKED 130 //It's locked and you're not holding the key. #define DOORS_CANT_PICK 131 //This lock cannot be picked. #define DOORS_INSUFFICIENT_SKILL 132 //You are not sufficiently skilled to pick this lock. @@ -98,6 +99,7 @@ #define DUP_LORE 290 //Duplicate lore items are not allowed. #define TGB_ON 293 //Target other group buff is *ON*. #define TGB_OFF 294 //Target other group buff is *OFF*. +#define DISARMED_TRAP 305 //You have disarmed the trap. #define LDON_SENSE_TRAP1 306 //You do not Sense any traps. #define TRADESKILL_NOCOMBINE 334 //You cannot combine these items in this container type! #define TRADESKILL_FAILED 336 //You lacked the skills to fashion the items together. @@ -114,6 +116,8 @@ #define MEND_WORSEN 351 //You have worsened your wounds! #define MEND_FAIL 352 //You have failed to mend your wounds. #define LDON_SENSE_TRAP2 367 //You have not detected any traps. +#define TRAP_TOO_FAR 368 //You are too far away from that trap to affect it. +#define FAIL_DISARM_DETECTED_TRAP 370 //You fail to disarm the detected trap. #define LOOT_LORE_ERROR 371 //You cannot loot this Lore Item. You already have one. #define PICK_LORE 379 //You cannot pick up a lore item you already possess. #define CONSENT_DENIED 390 //You do not have consent to summon that corpse. @@ -421,6 +425,7 @@ #define SENSE_ANIMAL 12472 //You sense an animal in this direction. #define SENSE_SUMMONED 12473 //You sense a summoned being in this direction. #define SENSE_NOTHING 12474 //You don't sense anything. +#define SENSE_TRAP 12475 //You sense a trap in this direction. #define LDON_SENSE_TRAP3 12476 //You don't sense any traps. #define INTERRUPT_SPELL_OTHER 12478 //%1's casting is interrupted! #define YOU_HIT_NONMELEE 12481 //You were hit by non-melee for %1 damage. diff --git a/zone/trap.cpp b/zone/trap.cpp index f563048f8..d5bcfbeb8 100644 --- a/zone/trap.cpp +++ b/zone/trap.cpp @@ -79,6 +79,7 @@ Trap::Trap() : group = 0; despawn_when_triggered = false; charid = 0; + undetectable = false; } Trap::~Trap() @@ -253,7 +254,7 @@ void Trap::Trigger(Mob* trigger) } } -Trap* EntityList::FindNearbyTrap(Mob* searcher, float max_dist) +Trap* EntityList::FindNearbyTrap(Mob* searcher, float max_dist, float &trap_curdist, bool detected) { float dist = 999999; Trap* current_trap = nullptr; @@ -263,19 +264,29 @@ Trap* EntityList::FindNearbyTrap(Mob* searcher, float max_dist) for (auto it = trap_list.begin(); it != trap_list.end(); ++it) { cur = it->second; - if(cur->disarmed) + if(cur->disarmed || (detected && !cur->detected) || cur->undetectable) continue; auto diff = glm::vec3(searcher->GetPosition()) - cur->m_Position; - float curdist = diff.x * diff.x + diff.y * diff.y + diff.z * diff.z; + float curdist = diff.x*diff.x + diff.y*diff.y; + diff.z = std::abs(diff.z); - if (curdist < max_dist2 && curdist < dist) + if (curdist < max_dist2 && curdist < dist && diff.z <= cur->maxzdiff) { + Log(Logs::General, Logs::Traps, "Trap %d is curdist %0.1f", cur->db_id, curdist); dist = curdist; current_trap = cur; } } + if (current_trap != nullptr) + { + Log(Logs::General, Logs::Traps, "Trap %d is the closest trap.", current_trap->db_id); + trap_curdist = dist; + } + else + trap_curdist = INVALID_INDEX; + return current_trap; } @@ -386,7 +397,7 @@ bool ZoneDatabase::LoadTraps(const char* zonename, int16 version) { std::string query = StringFormat("SELECT id, x, y, z, effect, effectvalue, effectvalue2, skill, " "maxzdiff, radius, chance, message, respawn_time, respawn_var, level, " - "`group`, triggered_number, despawn_when_triggered FROM traps WHERE zone='%s' AND version=%u", zonename, version); + "`group`, triggered_number, despawn_when_triggered, undetectable FROM traps WHERE zone='%s' AND version=%u", zonename, version); auto results = QueryDatabase(query); if (!results.Success()) { @@ -422,7 +433,8 @@ bool ZoneDatabase::LoadTraps(const char* zonename, int16 version) { trap->level = atoi(row[14]); trap->group = grp; trap->triggered_number = atoi(row[16]); - trap->despawn_when_triggered = atoi(row[17]); + trap->despawn_when_triggered = atobool(row[17]); + trap->undetectable = atobool(row[18]); entity_list.AddTrap(trap); trap->CreateHiddenTrigger(); Log(Logs::General, Logs::Traps, "Trap %d successfully loaded.", trap->trap_id); @@ -469,14 +481,14 @@ bool ZoneDatabase::SetTrapData(Trap* trap, bool repopnow) { { query = StringFormat("SELECT id, x, y, z, effect, effectvalue, effectvalue2, skill, " "maxzdiff, radius, chance, message, respawn_time, respawn_var, level, " - "triggered_number, despawn_when_triggered FROM traps WHERE zone='%s' AND `group`=%d AND id != %d ORDER BY RAND() LIMIT 1", zone->GetShortName(), trap->group, dbid); + "triggered_number, despawn_when_triggered, undetectable FROM traps WHERE zone='%s' AND `group`=%d AND id != %d ORDER BY RAND() LIMIT 1", zone->GetShortName(), trap->group, dbid); } else { // We could just use the existing data here, but querying the DB is not expensive, and allows content developers to change traps without rebooting. query = StringFormat("SELECT id, x, y, z, effect, effectvalue, effectvalue2, skill, " "maxzdiff, radius, chance, message, respawn_time, respawn_var, level, " - "triggered_number, despawn_when_triggered FROM traps WHERE zone='%s' AND id = %d", zone->GetShortName(), dbid); + "triggered_number, despawn_when_triggered, undetectable FROM traps WHERE zone='%s' AND id = %d", zone->GetShortName(), dbid); } auto results = QueryDatabase(query); @@ -500,7 +512,8 @@ bool ZoneDatabase::SetTrapData(Trap* trap, bool repopnow) { trap->respawn_var = atoi(row[13]); trap->level = atoi(row[14]); trap->triggered_number = atoi(row[15]); - trap->despawn_when_triggered = atoi(row[16]); + trap->despawn_when_triggered = atobool(row[16]); + trap->undetectable = atobool(row[17]); trap->CreateHiddenTrigger(); if (repopnow) diff --git a/zone/trap.h b/zone/trap.h index 4c0d53106..e20e2314f 100644 --- a/zone/trap.h +++ b/zone/trap.h @@ -50,7 +50,6 @@ public: void SetHiddenTrigger(NPC* n) { hiddenTrigger = n; } void CreateHiddenTrigger(); void DestroyHiddenTrigger() { hiddenTrigger = nullptr; } - void SetTrapData(); void UpdateTrap(bool respawn = true, bool repopnow = false); //Trap data, leave this unprotected Timer respawn_timer; //Respawn Time when Trap's been disarmed @@ -76,6 +75,7 @@ public: uint8 group; bool despawn_when_triggered; uint32 charid; //ID of character that triggered trap. This is cleared when the trap despawns are resets. + bool undetectable; std::string message; protected: From 15f7440af24d0d52e381b707d5ad8b163ed0b56e Mon Sep 17 00:00:00 2001 From: regneq Date: Sat, 28 Oct 2017 10:48:22 -0700 Subject: [PATCH 15/24] Update version and manifest for traps.sql --- common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/common/version.h b/common/version.h index 52af87872..108ce784b 100644 --- a/common/version.h +++ b/common/version.h @@ -30,7 +30,7 @@ Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9114 +#define CURRENT_BINARY_DATABASE_VERSION 9115 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9017 #else diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index e385b9f9e..c9b6a7b78 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -368,6 +368,7 @@ 9112|2017_06_24_rule_values_expand.sql|SHOW COLUMNS FROM rule_values WHERE Field = 'rule_value' and Type = 'varchar(30)'|empty| 9113|2017_07_19_show_name.sql|SHOW COLUMNS FROM `npc_types` LIKE 'show_name'|empty| 9114|2017_07_22_aura.sql|SHOW TABLES LIKE 'auras'|empty| +9115|2017_10_28_traps.sql|SHOW COLUMNS FROM `traps` LIKE 'triggered_number'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not From 7d1238233347e2a0ea955053e6ab41fa442fcbdc Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 4 Nov 2017 00:32:30 -0400 Subject: [PATCH 16/24] Fix NPC/Pet haste caps --- zone/mob.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index d08804410..782cb4d92 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3397,13 +3397,18 @@ int Mob::GetHaste() else // 1-25 h += itembonuses.haste > 10 ? 10 : itembonuses.haste; - // 60+ 100, 51-59 85, 1-50 level+25 - if (level > 59) // 60+ - cap = RuleI(Character, HasteCap); - else if (level > 50) // 51-59 - cap = 85; - else // 1-50 - cap = level + 25; + // mobs are different! + Mob *owner = nullptr; + if (IsPet()) + owner = GetOwner(); + else if (IsNPC() && CastToNPC()->GetSwarmTarget()) + owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner()); + if (owner) { + cap = 110; + cap += std::max(0, owner->GetLevel() - 39) + std::max(0, owner->GetLevel() - 60); + } else { + cap = 250; + } if(h > cap) h = cap; From 24e4730204b6f29f6c7fd9cef400519c15b78839 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 4 Nov 2017 00:39:54 -0400 Subject: [PATCH 17/24] Whoops, we do haste differently --- zone/mob.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index 782cb4d92..31baf7ae6 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3404,10 +3404,10 @@ int Mob::GetHaste() else if (IsNPC() && CastToNPC()->GetSwarmTarget()) owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner()); if (owner) { - cap = 110; + cap = 10; cap += std::max(0, owner->GetLevel() - 39) + std::max(0, owner->GetLevel() - 60); } else { - cap = 250; + cap = 150; } if(h > cap) From df0004c1b09194b7cb9e1d4e26d2de0cdec236e9 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 4 Nov 2017 01:08:55 -0400 Subject: [PATCH 18/24] Another fix for GetHaste for pets --- zone/mob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index 31baf7ae6..67a6ffa14 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3404,7 +3404,7 @@ int Mob::GetHaste() else if (IsNPC() && CastToNPC()->GetSwarmTarget()) owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner()); if (owner) { - cap = 10; + cap = 10 + level; cap += std::max(0, owner->GetLevel() - 39) + std::max(0, owner->GetLevel() - 60); } else { cap = 150; From 37bedfe9bab8cbb27cf392945b799dfd8432774a Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 4 Nov 2017 01:27:41 -0400 Subject: [PATCH 19/24] Move PetHoTT to NPC::SetTarget for charmed pets --- zone/npc.cpp | 20 ++++++++++++++++++++ zone/pets.cpp | 16 ---------------- zone/pets.h | 1 - 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index a4e6c4214..c13841358 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -436,6 +436,26 @@ void NPC::SetTarget(Mob* mob) { //attack_timer.Disable(); attack_dw_timer.Disable(); } + + // either normal pet and owner is client or charmed pet and owner is client + Mob *owner = nullptr; + if (IsPet() && IsPetOwnerClient()) { + owner = GetOwner(); + } else if (IsCharmed()) { + owner = GetOwner(); + if (owner && !owner->IsClient()) + owner = nullptr; + } + + if (owner) { + auto client = owner->CastToClient(); + if (client->ClientVersionBit() & EQEmu::versions::bit_UFAndLater) { + auto app = new EQApplicationPacket(OP_PetHoTT, sizeof(ClientTarget_Struct)); + auto ct = (ClientTarget_Struct *)app->pBuffer; + ct->new_target = mob ? mob->GetID() : 0; + client->FastQueuePacket(&app); + } + } Mob::SetTarget(mob); } diff --git a/zone/pets.cpp b/zone/pets.cpp index 0c150f743..ef895410b 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -475,22 +475,6 @@ Pet::Pet(NPCType *type_data, Mob *owner, PetType type, uint16 spell_id, int16 po // Class should use npc constructor to set light properties } -void Pet::SetTarget(Mob *mob) -{ - if (mob == GetTarget()) - return; - - auto owner = GetOwner(); - if (owner && owner->IsClient() && owner->CastToClient()->ClientVersionBit() & EQEmu::versions::bit_UFAndLater) { - auto app = new EQApplicationPacket(OP_PetHoTT, sizeof(ClientTarget_Struct)); - auto ct = (ClientTarget_Struct *)app->pBuffer; - ct->new_target = mob ? mob->GetID() : 0; - owner->CastToClient()->QueuePacket(app); - safe_delete(app); - } - NPC::SetTarget(mob); -} - bool ZoneDatabase::GetPetEntry(const char *pet_type, PetRecord *into) { return GetPoweredPetEntry(pet_type, 0, into); } diff --git a/zone/pets.h b/zone/pets.h index edb6dbe95..1b9811149 100644 --- a/zone/pets.h +++ b/zone/pets.h @@ -7,7 +7,6 @@ struct NPCType; class Pet : public NPC { public: Pet(NPCType *type_data, Mob *owner, PetType type, uint16 spell_id, int16 power); - virtual void SetTarget(Mob *mob); virtual bool CheckSpellLevelRestriction(uint16 spell_id); }; From 3eaa0b4fb18c42752acd14b7082d63a083bcefe1 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 9 Nov 2017 13:31:04 -0500 Subject: [PATCH 20/24] Don't need to garble for language, client does Maybe older clients need it? --- zone/client.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 893a6b51b..319752e36 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -1223,11 +1223,6 @@ void Client::ChannelMessageSend(const char* from, const char* to, uint8 chan_num EffSkill = 100; cm->skill_in_language = EffSkill; - // Garble the message based on listener skill - if (ListenerSkill < 100) { - GarbleMessage(buffer, (100 - ListenerSkill)); - } - cm->chan_num = chan_num; strcpy(&cm->message[0], buffer); QueuePacket(&app); From e928046a959d78d9aa5a284e1c9e40c0bb3cf371 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 10 Nov 2017 00:04:33 -0500 Subject: [PATCH 21/24] Add guild tribute slot for legacy stuff --- common/emu_legacy.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/emu_legacy.h b/common/emu_legacy.h index a624be883..0f3cf0a62 100644 --- a/common/emu_legacy.h +++ b/common/emu_legacy.h @@ -78,6 +78,8 @@ namespace EQEmu SLOT_CURSOR_BAG_END = 340, SLOT_TRIBUTE_BEGIN = 400, SLOT_TRIBUTE_END = 404, + SLOT_GUILD_TRIBUTE_BEGIN = 450, + SLOT_GUILD_TRIBUTE_END = 451, SLOT_BANK_BEGIN = 2000, SLOT_BANK_END = 2023, SLOT_BANK_BAGS_BEGIN = 2031, From da163be8dbe56a3d4ba95ed2c08143542dea6433 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 10 Nov 2017 00:06:50 -0500 Subject: [PATCH 22/24] Fix auto complete error --- zone/mob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index 67a6ffa14..66e221b0b 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3401,7 +3401,7 @@ int Mob::GetHaste() Mob *owner = nullptr; if (IsPet()) owner = GetOwner(); - else if (IsNPC() && CastToNPC()->GetSwarmTarget()) + else if (IsNPC() && CastToNPC()->GetSwarmOwner()) owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner()); if (owner) { cap = 10 + level; From 52d31a68467decee078bbe30b2517d501dc16166 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 19 Nov 2017 01:45:05 -0500 Subject: [PATCH 23/24] Make high STR race rez effects a rule They stopped using this one at some point it seems --- common/ruletypes.h | 1 + zone/client_process.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index c3a8fd5dc..ed5ae8e23 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -44,6 +44,7 @@ RULE_INT(Character, DeathExpLossMaxLevel, 255) // Any level greater than this wi RULE_INT(Character, DeathItemLossLevel, 10) RULE_INT(Character, DeathExpLossMultiplier, 3) //Adjust how much exp is lost RULE_BOOL(Character, UseDeathExpLossMult, false) //Adjust to use the above multiplier or to use code default. +RULE_BOOL(Character, UseOldRaceRezEffects, false) // older clients had ID 757 for races with high starting STR, but it doesn't seem used anymore RULE_INT(Character, CorpseDecayTimeMS, 10800000) RULE_INT(Character, CorpseResTimeMS, 10800000) // time before cant res corpse(3 hours) RULE_BOOL(Character, LeaveCorpses, true) diff --git a/zone/client_process.cpp b/zone/client_process.cpp index a0bdeead2..ab694659b 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -1055,7 +1055,8 @@ void Client::OPRezzAnswer(uint32 Action, uint32 SpellID, uint16 ZoneID, uint16 I SetMana(0); SetHP(GetMaxHP()/5); int rez_eff = 756; - if (GetRace() == BARBARIAN || GetRace() == DWARF || GetRace() == TROLL || GetRace() == OGRE) + if (RuleB(Character, UseOldRaceRezEffects) && + (GetRace() == BARBARIAN || GetRace() == DWARF || GetRace() == TROLL || GetRace() == OGRE)) rez_eff = 757; SpellOnTarget(rez_eff, this); // Rezz effects } From b03e9af597e694a437b036fe9d80e7d76179484a Mon Sep 17 00:00:00 2001 From: Akkadius Date: Tue, 21 Nov 2017 21:25:20 -0600 Subject: [PATCH 24/24] Fix issues with NPC's ghosting who path for long distances, this should wrap up the small remainder of ghosting edge cases --- zone/client_process.cpp | 13 +++- zone/mob.cpp | 142 ++++++++++++++++++++-------------------- zone/mob.h | 7 +- 3 files changed, 88 insertions(+), 74 deletions(-) diff --git a/zone/client_process.cpp b/zone/client_process.cpp index ab694659b..355dd2c1f 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -269,8 +269,17 @@ bool Client::Process() { } } - if (force_spawn_updates && mob != this && distance <= client_update_range) - mob->SendPositionUpdateToClient(this); + if (force_spawn_updates && mob != this) { + + if (mob->is_distance_roamer) { + Log(Logs::General, Logs::Debug, "Updating distance roamer %s", mob->GetCleanName()); + mob->SendPositionUpdateToClient(this); + continue; + } + + if (distance <= client_update_range) + mob->SendPositionUpdateToClient(this); + } } } diff --git a/zone/mob.cpp b/zone/mob.cpp index 66e221b0b..af0bd97c8 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -75,7 +75,6 @@ Mob::Mob(const char* in_name, uint32 in_drakkin_tattoo, uint32 in_drakkin_details, EQEmu::TintProfile in_armor_tint, - uint8 in_aa_title, uint8 in_see_invis, // see through invis/ivu uint8 in_see_invis_undead, @@ -91,24 +90,24 @@ Mob::Mob(const char* in_name, uint8 in_handtexture, uint8 in_legtexture, uint8 in_feettexture - ) : + ) : attack_timer(2000), attack_dw_timer(2000), ranged_timer(2000), tic_timer(6000), mana_timer(2000), spellend_timer(0), - rewind_timer(30000), //Timer used for determining amount of time between actual player position updates for /rewind. + rewind_timer(30000), bindwound_timer(10000), stunned_timer(0), spun_timer(0), bardsong_timer(6000), gravity_timer(1000), viral_timer(0), - m_FearWalkTarget(-999999.0f,-999999.0f,-999999.0f), + m_FearWalkTarget(-999999.0f, -999999.0f, -999999.0f), m_TargetLocation(glm::vec3()), m_TargetV(glm::vec3()), - flee_timer(FLEE_CHECK_TIMER), + flee_timer(FLEE_CHECK_TIMER), m_Position(position), tmHidden(-1), mitigation_ac(0), @@ -119,47 +118,48 @@ Mob::Mob(const char* in_name, position_update_melee_push_timer(1000) { targeted = 0; - tar_ndx=0; - tar_vector=0; + tar_ndx = 0; + tar_vector = 0; currently_fleeing = false; last_z = 0; last_major_update_position = m_Position; + is_distance_roamer = false; AI_Init(); SetMoving(false); - moved=false; + moved = false; m_RewindLocation = glm::vec3(); _egnode = nullptr; - name[0]=0; - orig_name[0]=0; - clean_name[0]=0; - lastname[0]=0; - if(in_name) { - strn0cpy(name,in_name,64); - strn0cpy(orig_name,in_name,64); + name[0] = 0; + orig_name[0] = 0; + clean_name[0] = 0; + lastname[0] = 0; + if (in_name) { + strn0cpy(name, in_name, 64); + strn0cpy(orig_name, in_name, 64); } - if(in_lastname) - strn0cpy(lastname,in_lastname,64); - cur_hp = in_cur_hp; - max_hp = in_max_hp; - base_hp = in_max_hp; - gender = in_gender; - race = in_race; - base_gender = in_gender; - base_race = in_race; - class_ = in_class; - bodytype = in_bodytype; + if (in_lastname) + strn0cpy(lastname, in_lastname, 64); + cur_hp = in_cur_hp; + max_hp = in_max_hp; + base_hp = in_max_hp; + gender = in_gender; + race = in_race; + base_gender = in_gender; + base_race = in_race; + class_ = in_class; + bodytype = in_bodytype; orig_bodytype = in_bodytype; - deity = in_deity; - level = in_level; + deity = in_deity; + level = in_level; orig_level = in_level; - npctype_id = in_npctype_id; - size = in_size; - base_size = size; - runspeed = in_runspeed; + npctype_id = in_npctype_id; + size = in_size; + base_size = size; + runspeed = in_runspeed; // neotokyo: sanity check if (runspeed < 0 || runspeed > 20) runspeed = 1.25f; @@ -172,7 +172,8 @@ Mob::Mob(const char* in_name, fearspeed = 0.625f; base_fearspeed = 25; // npcs - } else { + } + else { base_walkspeed = base_runspeed * 100 / 265; walkspeed = ((float)base_walkspeed) * 0.025f; base_fearspeed = base_runspeed * 100 / 127; @@ -184,7 +185,7 @@ Mob::Mob(const char* in_name, current_speed = base_runspeed; - m_PlayerState = 0; + m_PlayerState = 0; // sanity check @@ -196,8 +197,8 @@ Mob::Mob(const char* in_name, m_Light.Type[EQEmu::lightsource::LightActive] = m_Light.Type[EQEmu::lightsource::LightInnate]; m_Light.Level[EQEmu::lightsource::LightActive] = m_Light.Level[EQEmu::lightsource::LightInnate]; - texture = in_texture; - helmtexture = in_helmtexture; + texture = in_texture; + helmtexture = in_helmtexture; armtexture = in_armtexture; bracertexture = in_bracertexture; handtexture = in_handtexture; @@ -205,21 +206,21 @@ Mob::Mob(const char* in_name, feettexture = in_feettexture; multitexture = (armtexture || bracertexture || handtexture || legtexture || feettexture); - haircolor = in_haircolor; - beardcolor = in_beardcolor; - eyecolor1 = in_eyecolor1; - eyecolor2 = in_eyecolor2; - hairstyle = in_hairstyle; - luclinface = in_luclinface; - beard = in_beard; - drakkin_heritage = in_drakkin_heritage; - drakkin_tattoo = in_drakkin_tattoo; - drakkin_details = in_drakkin_details; + haircolor = in_haircolor; + beardcolor = in_beardcolor; + eyecolor1 = in_eyecolor1; + eyecolor2 = in_eyecolor2; + hairstyle = in_hairstyle; + luclinface = in_luclinface; + beard = in_beard; + drakkin_heritage = in_drakkin_heritage; + drakkin_tattoo = in_drakkin_tattoo; + drakkin_details = in_drakkin_details; attack_speed = 0; attack_delay = 0; slow_mitigation = 0; - findable = false; - trackable = true; + findable = false; + trackable = true; has_shieldequiped = false; has_twohandbluntequiped = false; has_twohanderequipped = false; @@ -230,19 +231,19 @@ Mob::Mob(const char* in_name, SpellPowerDistanceMod = 0; last_los_check = false; - if(in_aa_title>0) - aa_title = in_aa_title; + if (in_aa_title > 0) + aa_title = in_aa_title; else - aa_title =0xFF; - AC = in_ac; - ATK = in_atk; - STR = in_str; - STA = in_sta; - DEX = in_dex; - AGI = in_agi; - INT = in_int; - WIS = in_wis; - CHA = in_cha; + aa_title = 0xFF; + AC = in_ac; + ATK = in_atk; + STR = in_str; + STA = in_sta; + DEX = in_dex; + AGI = in_agi; + INT = in_int; + WIS = in_wis; + CHA = in_cha; MR = CR = FR = DR = PR = Corrup = 0; ExtraHaste = 0; @@ -263,8 +264,8 @@ Mob::Mob(const char* in_name, hidden = false; improved_hidden = false; invulnerable = false; - IsFullHP = (cur_hp == max_hp); - qglobal=0; + IsFullHP = (cur_hp == max_hp); + qglobal = 0; spawned = false; InitializeBuffSlots(); @@ -305,7 +306,7 @@ Mob::Mob(const char* in_name, logging_enabled = false; isgrouped = false; israidgrouped = false; - + IsHorse = false; entity_id_being_looted = 0; @@ -376,13 +377,13 @@ Mob::Mob(const char* in_name, } destructibleobject = false; - wandertype=0; - pausetype=0; + wandertype = 0; + pausetype = 0; cur_wp = 0; m_CurrentWayPoint = glm::vec4(); cur_wp_pause = 0; - patrol=0; - follow=0; + patrol = 0; + follow = 0; follow_dist = 100; // Default Distance for Follow no_target_hotkey = false; flee_mode = false; @@ -396,7 +397,7 @@ Mob::Mob(const char* in_name, rooted = false; charmed = false; has_virus = false; - for (i=0; i= (100 * 100)) { entity_list.QueueClients(this, app, true, true); last_major_update_position = m_Position; + is_distance_roamer = true; } else { entity_list.QueueCloseClients(this, app, true, RuleI(Range, MobPositionUpdates), nullptr, false); diff --git a/zone/mob.h b/zone/mob.h index 058774c54..005021e40 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -162,6 +162,8 @@ public: inline virtual bool IsMob() const { return true; } inline virtual bool InZone() const { return true; } + bool is_distance_roamer; + //Somewhat sorted: needs documenting! //Attack @@ -954,7 +956,7 @@ public: void SendTo(float new_x, float new_y, float new_z); void SendToFixZ(float new_x, float new_y, float new_z); float GetZOffset() const; - void FixZ(int32 z_find_offset = 5); + void FixZ(int32 z_find_offset = 5); void NPCSpecialAttacks(const char* parse, int permtag, bool reset = true, bool remove = false); inline uint32 DontHealMeBefore() const { return pDontHealMeBefore; } inline uint32 DontBuffMeBefore() const { return pDontBuffMeBefore; } @@ -1225,7 +1227,8 @@ protected: uint32 npctype_id; glm::vec4 m_Position; /* Used to determine when an NPC has traversed so many units - to send a zone wide pos update */ - glm::vec4 last_major_update_position; + glm::vec4 last_major_update_position; + int animation; // this is really what MQ2 calls SpeedRun just packed like (int)(SpeedRun * 40.0f) float base_size; float size;