From a5872b165fbe8582d8f779774291eaee16893aa4 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Wed, 1 Apr 2015 13:00:38 -0400 Subject: [PATCH 001/129] Zoning into a new zone did not properly display PCs with tree/object illusions and NPCs wearing gear in non-weapon slots. The illusion thing: Not sure why, but te opcode for BulkZoneSpawn doesn't display the tree/object illusions. I did notice that even OP_Illusion gets rejected by the client if sent before Client_Ready. Maybe that is why. The BULKSpawns cannot be sent that late, I tried moving it in the sequence but it never did the illusions correctly, at any point. So, we new new the single spawn OP code for PCs with those illusions. This works. The NPC gear thing. Same story with BulkZoneSpawn, Not sure why. The data is sent correctly. So now we update the client zoning in (only them) with what the NPCs are wearing. Every othe client already is up to date. --- common/races.h | 2 ++ zone/entity.cpp | 37 ++++++++++++++++++++++++++++++++++--- zone/mob.cpp | 12 ++++++++++-- zone/mob.h | 2 +- 4 files changed, 47 insertions(+), 6 deletions(-) diff --git a/common/races.h b/common/races.h index 2c360e908..68add5cca 100644 --- a/common/races.h +++ b/common/races.h @@ -47,6 +47,8 @@ #define IKSAR 128 #define VAHSHIR 130 #define CONTROLLED_BOAT 141 +#define MINOR_ILL_OBJ 142 +#define TREE 143 #define IKSAR_SKELETON 161 #define FROGLOK 330 #define FROGLOK2 74 // Not sure why /who all reports race as 74 for frogloks diff --git a/zone/entity.cpp b/zone/entity.cpp index a28a4b846..a2fb2657d 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1149,19 +1149,50 @@ void EntityList::SendZoneSpawnsBulk(Client *client) NewSpawn_Struct ns; Mob *spawn; uint32 maxspawns = 100; + EQApplicationPacket *app; if (maxspawns > mob_list.size()) maxspawns = mob_list.size(); BulkZoneSpawnPacket *bzsp = new BulkZoneSpawnPacket(client, maxspawns); + + int32 race=-1; for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { spawn = it->second; if (spawn && spawn->InZone()) { if (spawn->IsClient() && (spawn->CastToClient()->GMHideMe(client) || spawn->CastToClient()->IsHoveringForRespawn())) continue; - memset(&ns, 0, sizeof(NewSpawn_Struct)); - spawn->FillSpawnStruct(&ns, client); - bzsp->AddSpawn(&ns); + + race = spawn->GetRace(); + + // Illusion races on PCs don't work as a mass spawn + // But they will work as an add_spawn AFTER CLIENT_CONNECTED. + if (spawn->IsClient() && (race == MINOR_ILL_OBJ || race == TREE)) { + app = new EQApplicationPacket; + spawn->CreateSpawnPacket(app); + client->QueuePacket(app, true, Client::CLIENT_CONNECTED); + safe_delete(app); + } + else { + memset(&ns, 0, sizeof(NewSpawn_Struct)); + spawn->FillSpawnStruct(&ns, client); + bzsp->AddSpawn(&ns); + } + + // On NPCs wearing gear from loottable or previously traded + // to them, mass spawn does not properly update the visual look. + // (Bulk packet sends the same info - client just doesn't use it. + // except on primary/secondary - tested on multiple client types) + // Do that using a Wear Change now. + if (!spawn->IsClient()) { + const Item_Struct *item; + for (int i=0; i< 7 ; ++i) { + item=database.GetItem(spawn->GetEquipment(i)); + if (item != 0) { + spawn->SendWearChange(i,client); + } + } + } } } safe_delete(bzsp); diff --git a/zone/mob.cpp b/zone/mob.cpp index 2053594be..f21ed3d6e 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -2540,7 +2540,7 @@ uint32 NPC::GetEquipment(uint8 material_slot) const return equipment[invslot]; } -void Mob::SendWearChange(uint8 material_slot) +void Mob::SendWearChange(uint8 material_slot, Client *one_client) { EQApplicationPacket* outapp = new EQApplicationPacket(OP_WearChange, sizeof(WearChange_Struct)); WearChange_Struct* wc = (WearChange_Struct*)outapp->pBuffer; @@ -2552,7 +2552,15 @@ void Mob::SendWearChange(uint8 material_slot) wc->color.Color = GetEquipmentColor(material_slot); wc->wear_slot_id = material_slot; - entity_list.QueueClients(this, outapp); + if (!one_client) + { + entity_list.QueueClients(this, outapp); + } + else + { + one_client->QueuePacket(outapp, false, Client::CLIENT_CONNECTED); + } + safe_delete(outapp); } diff --git a/zone/mob.h b/zone/mob.h index 979d9728a..f35857823 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -166,7 +166,7 @@ public: void SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32 parm4, uint32 parm5, Client *specific_target=nullptr); void SendTargetable(bool on, Client *specific_target = nullptr); - virtual void SendWearChange(uint8 material_slot); + virtual void SendWearChange(uint8 material_slot, Client *one_client = nullptr); virtual void SendTextureWC(uint8 slot, uint16 texture, uint32 hero_forge_model = 0, uint32 elite_material = 0, uint32 unknown06 = 0, uint32 unknown18 = 0); virtual void SetSlotTint(uint8 material_slot, uint8 red_tint, uint8 green_tint, uint8 blue_tint); From bf93d72a431a3037e7105a2a1c8fb42f1751f9fa Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Thu, 2 Apr 2015 13:25:12 -0400 Subject: [PATCH 002/129] Added more changes so mobs armor will appear correctly (pc races only) when the spawn vie gm command or normally with loot tables that equip. Refined previous changes that fixed the issue with zoning in and not seeing previosuly spawned armor by sharing the same module. --- zone/entity.cpp | 26 +++++++++++--------------- zone/mob.cpp | 28 ++++++++++++++++++++++++++++ zone/mob.h | 1 + 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/zone/entity.cpp b/zone/entity.cpp index a2fb2657d..c6de7b33e 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -618,6 +618,7 @@ void EntityList::AddNPC(NPC *npc, bool SendSpawnPacket, bool dontqueue) EQApplicationPacket *app = new EQApplicationPacket; npc->CreateSpawnPacket(app, npc); QueueClients(npc, app); + npc->SendArmorAppearance(); safe_delete(app); } else { NewSpawn_Struct *ns = new NewSpawn_Struct; @@ -726,10 +727,16 @@ void EntityList::CheckSpawnQueue() EQApplicationPacket *outapp = 0; iterator.Reset(); + NewSpawn_Struct *ns; + while(iterator.MoreElements()) { outapp = new EQApplicationPacket; - Mob::CreateSpawnPacket(outapp, iterator.GetData()); + ns = iterator.GetData(); + Mob::CreateSpawnPacket(outapp, ns); QueueClients(0, outapp); + auto it = npc_list.find(ns->spawn.spawnId); + NPC *pnpc = it->second; + pnpc->SendArmorAppearance(); safe_delete(outapp); iterator.RemoveCurrent(); } @@ -1179,20 +1186,9 @@ void EntityList::SendZoneSpawnsBulk(Client *client) bzsp->AddSpawn(&ns); } - // On NPCs wearing gear from loottable or previously traded - // to them, mass spawn does not properly update the visual look. - // (Bulk packet sends the same info - client just doesn't use it. - // except on primary/secondary - tested on multiple client types) - // Do that using a Wear Change now. - if (!spawn->IsClient()) { - const Item_Struct *item; - for (int i=0; i< 7 ; ++i) { - item=database.GetItem(spawn->GetEquipment(i)); - if (item != 0) { - spawn->SendWearChange(i,client); - } - } - } + // Despite being sent in the OP_ZoneSpawns packet, the client + // does not display worn armor correctly so display it. + spawn->SendArmorAppearance(client); } } safe_delete(bzsp); diff --git a/zone/mob.cpp b/zone/mob.cpp index 9e9bcbd5d..9b4c2f88e 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -2552,6 +2552,34 @@ uint32 NPC::GetEquipment(uint8 material_slot) const return equipment[invslot]; } +void Mob::SendArmorAppearance(Client *one_client) +{ + // one_client of 0 means sent to all clients + // + // Despite the fact that OP_NewSpawn and OP_ZoneSpawns include the + // armor being worn and its mats, the client doesn't update the display + // on arrival of these packets reliably. + // + // Send Wear changes if mob is a PC race and item is an armor slot. + // The other packets work for primary/secondary. + + if (IsPlayerRace(race)) + { + if (!IsClient()) + { + const Item_Struct *item; + for (int i=0; i< 7 ; ++i) + { + item=database.GetItem(GetEquipment(i)); + if (item != 0) + { + SendWearChange(i,one_client); + } + } + } + } +} + void Mob::SendWearChange(uint8 material_slot, Client *one_client) { EQApplicationPacket* outapp = new EQApplicationPacket(OP_WearChange, sizeof(WearChange_Struct)); diff --git a/zone/mob.h b/zone/mob.h index 22f9cb5f1..ada2c56dd 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -171,6 +171,7 @@ public: void SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32 parm4, uint32 parm5, Client *specific_target=nullptr); void SendTargetable(bool on, Client *specific_target = nullptr); + virtual void SendArmorAppearance(Client *one_client = nullptr); virtual void SendWearChange(uint8 material_slot, Client *one_client = nullptr); virtual void SendTextureWC(uint8 slot, uint16 texture, uint32 hero_forge_model = 0, uint32 elite_material = 0, uint32 unknown06 = 0, uint32 unknown18 = 0); From 0d4775a9dfbcfbaaacbb36cf916e503ce2f999d8 Mon Sep 17 00:00:00 2001 From: JJ Date: Tue, 7 Apr 2015 19:57:36 -0400 Subject: [PATCH 003/129] Adjust to safe_delete packets. --- world/zoneserver.cpp | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index ffc22352a..b40f3dc04 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -190,7 +190,7 @@ bool ZoneServer::Process() { Log.Out(Logs::Detail, Logs::World_Server,"Zone authorization failed."); auto pack = new ServerPacket(ServerOP_ZAAuthFailed); SendPacket(pack); - delete pack; + safe_delete(pack); Disconnect(); return false; } @@ -201,7 +201,7 @@ bool ZoneServer::Process() { Log.Out(Logs::Detail, Logs::World_Server,"Zone authorization failed."); auto pack = new ServerPacket(ServerOP_ZAAuthFailed); SendPacket(pack); - delete pack; + safe_delete(pack); Disconnect(); return false; } @@ -779,7 +779,7 @@ bool ZoneServer::Process() { whom->wrace = whoall->wrace; strcpy(whom->whom,whoall->whom); client_list.SendWhoAll(whoall->fromid,whoall->from, whoall->admin, whom, this); - delete whom; + safe_delete(whom); break; } case ServerOP_RequestOnlineGuildMembers: { @@ -958,7 +958,7 @@ bool ZoneServer::Process() { tod->start_eqtime=zoneserver_list.worldclock.getStartEQTime(); tod->start_realtime=zoneserver_list.worldclock.getStartRealTime(); SendPacket(pack); - delete pack; + safe_delete(pack); break; } case ServerOP_SetWorldTime: { @@ -1059,8 +1059,7 @@ bool ZoneServer::Process() { } else { - delete pack; - pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct)); + auto pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct)); ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; strcpy(scs->grantname, s->grantname); strcpy(scs->ownername, s->ownername); @@ -1076,6 +1075,7 @@ bool ZoneServer::Process() { else { Log.Out(Logs::Detail, Logs::World_Server, "Unable to locate zone record for instance id %u in zoneserver list for ServerOP_Consent_Response operation.", s->instance_id); } + safe_delete(pack); } } else @@ -1091,8 +1091,7 @@ bool ZoneServer::Process() { } else { // send target not found back to requester - delete pack; - pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct)); + auto pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct)); ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; strcpy(scs->grantname, s->grantname); strcpy(scs->ownername, s->ownername); @@ -1107,13 +1106,13 @@ bool ZoneServer::Process() { else { Log.Out(Logs::Detail, Logs::World_Server, "Unable to locate zone record for zone id %u in zoneserver list for ServerOP_Consent_Response operation.", s->zone_id); } + safe_delete(pack); } } } else { // send target not found back to requester - delete pack; - pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct)); + auto pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct)); ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; strcpy(scs->grantname, s->grantname); strcpy(scs->ownername, s->ownername); @@ -1128,6 +1127,7 @@ bool ZoneServer::Process() { else { Log.Out(Logs::Detail, Logs::World_Server, "Unable to locate zone record for zone id %u in zoneserver list for ServerOP_Consent_Response operation.", s->zone_id); } + safe_delete(pack); } break; } @@ -1313,15 +1313,17 @@ bool ZoneServer::Process() { } default: { - Log.Out(Logs::Detail, Logs::World_Server,"Unknown ServerOPcode from zone 0x%04x, size %d",pack->opcode,pack->size); + Log.Out(Logs::Detail, Logs::World_Server, "Unknown ServerOPcode from zone 0x%04x, size %d", pack->opcode, pack->size); DumpPacket(pack->pBuffer, pack->size); break; } } - if (pack) - delete pack; - else - Log.Out(Logs::Detail, Logs::World_Server, "Zoneserver process tried to delete pack when pack does not exist."); + if (pack) { + safe_delete(pack); + } + else { + Log.Out(Logs::Detail, Logs::World_Server, "Zoneserver process attempted to delete pack when pack does not exist."); + } } return true; } From ea240f781406118858b61d67c85ca99b45705b48 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 10 Apr 2015 03:06:05 -0400 Subject: [PATCH 004/129] Fix sign issue with hate redux spells --- zone/spells.cpp | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index a4bef767c..ab8b44408 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -82,6 +82,7 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) #include #include +#include #ifndef WIN32 #include @@ -2149,7 +2150,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 // caster if they're not using TGB // NOTE: this will always hit the caster, plus the target's group so // it can affect up to 7 people if the targeted group is not our own - + // Allow pets who cast group spells to affect the group. if (spell_target->IsPetOwnerClient() && IsPetOwnerClient()){ Mob* owner = spell_target->GetOwner(); @@ -2157,7 +2158,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 if (owner) spell_target = owner; } - + if(spell_target->IsGrouped()) { Group *target_group = entity_list.GetGroupByMob(spell_target); @@ -3669,22 +3670,16 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r spelltar->Message_StringID(MT_SpellFailure, YOU_RESIST, spells[spell_id].name); } - if(spelltar->IsAIControlled()){ + if (spelltar->IsAIControlled()) { int32 aggro = CheckAggroAmount(spell_id); - if(aggro > 0) { - if(!IsHarmonySpell(spell_id)) - spelltar->AddToHateList(this, aggro); - else - if(!spelltar->PassCharismaCheck(this, spell_id)) - spelltar->AddToHateList(this, aggro); - } - else{ - uint32 newhate = spelltar->GetHateAmount(this) + aggro; - if (newhate < 1) { - spelltar->SetHateAmountOnEnt(this,1); - } else { - spelltar->SetHateAmountOnEnt(this,newhate); - } + if (aggro > 0) { + if (!IsHarmonySpell(spell_id)) + spelltar->AddToHateList(this, aggro); + else if (!spelltar->PassCharismaCheck(this, spell_id)) + spelltar->AddToHateList(this, aggro); + } else { + int newhate = spelltar->GetHateAmount(this) + aggro; + spelltar->SetHateAmountOnEnt(this, std::max(1, newhate)); } } @@ -4404,7 +4399,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use level_mod += (2 * level_diff); } } - + if (CharismaCheck) { /* @@ -5053,7 +5048,7 @@ bool Mob::FindType(uint16 type, bool bOffensive, uint16 threshold) { spells[buffs[i].spellid].base[j], spells[buffs[i].spellid].max[j], buffs[i].casterlevel, buffs[i].spellid); - Log.Out(Logs::General, Logs::Normal, + Log.Out(Logs::General, Logs::Normal, "FindType: type = %d; value = %d; threshold = %d", type, value, threshold); if (value < threshold) From a4ac2b3831d610324c7443b840e330a918a7bcbb Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 10 Apr 2015 21:23:29 -0400 Subject: [PATCH 005/129] Added some comments about powersource [skip ci] --- common/item_struct.h | 2 +- zone/attack.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/item_struct.h b/common/item_struct.h index 3ef26db94..1fcb49f51 100644 --- a/common/item_struct.h +++ b/common/item_struct.h @@ -185,7 +185,7 @@ struct Item_Struct { uint32 AugType; uint8 AugSlotType[EmuConstants::ITEM_COMMON_SIZE]; // RoF: Augment Slot 1-6 Type uint8 AugSlotVisible[EmuConstants::ITEM_COMMON_SIZE]; // RoF: Augment Slot 1-6 Visible - uint8 AugSlotUnk2[EmuConstants::ITEM_COMMON_SIZE]; // RoF: Augment Slot 1-6 Unknown + uint8 AugSlotUnk2[EmuConstants::ITEM_COMMON_SIZE]; // RoF: Augment Slot 1-6 Unknown Most likely Powersource related uint32 LDoNTheme; uint32 LDoNPrice; uint32 LDoNSold; diff --git a/zone/attack.cpp b/zone/attack.cpp index e573b12cc..2ba068010 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3968,7 +3968,7 @@ void Mob::TryWeaponProc(const ItemInst* weapon_g, Mob *on, uint16 hand) { } // Innate + aug procs from weapons - // TODO: powersource procs + // TODO: powersource procs -- powersource procs are on invis augs, so shouldn't need anything extra TryWeaponProc(weapon_g, weapon_g->GetItem(), on, hand); // Procs from Buffs and AA both melee and range TrySpellProc(weapon_g, weapon_g->GetItem(), on, hand); @@ -4054,7 +4054,7 @@ void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on } } } - // TODO: Powersource procs + // TODO: Powersource procs -- powersource procs are from augs so shouldn't need anything extra return; } From 0eda3efe6a1aebbc1c1b9cb09b3b6d9264468bb4 Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Sat, 11 Apr 2015 22:48:49 -0700 Subject: [PATCH 006/129] Ignore procs when setting recast timers --- zone/spells.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index ab8b44408..643b70ca8 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2253,7 +2253,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 } //set our reuse timer on long ass reuse_time spells... - if(IsClient()) + if(IsClient() && !isproc) { if(spell_id == casting_spell_id && casting_spell_timer != 0xFFFFFFFF) { From 23dd560a720a737de0affc6347352fd5ea1b16a7 Mon Sep 17 00:00:00 2001 From: JJ Date: Mon, 20 Apr 2015 19:48:52 -0400 Subject: [PATCH 007/129] Don't delete packet when it is still referenced. Create a new packet instead for deconfliction. --- zone/worldserver.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index ec2893e05..aa3e32d43 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1406,17 +1406,16 @@ void WorldServer::Process() { // CONSENT_INVALID_NAME = 397 // TARGET_NOT_FOUND = 101 - safe_delete(pack); - pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct)); - ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; + ServerPacket *scs_pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct)); + ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)scs_pack->pBuffer; strcpy(scs->grantname, s->grantname); strcpy(scs->ownername, s->ownername); scs->permission = s->permission; scs->zone_id = s->zone_id; scs->instance_id = s->instance_id; scs->message_string_id = TARGET_NOT_FOUND; - worldserver.SendPacket(pack); - safe_delete(pack); + worldserver.SendPacket(scs_pack); + safe_delete(scs_pack); } break; } From c974b30192d89e7eacf67c13916e4f2f0442c911 Mon Sep 17 00:00:00 2001 From: Uleat Date: Wed, 22 Apr 2015 12:29:35 -0400 Subject: [PATCH 008/129] Probable fix for 'Debug Assertion Failure' in Client::GarbleMessage() --- changelog.txt | 4 ++++ zone/client.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 2dca36203..29caca258 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,9 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 04/22/2015 == +Uleat: Probable fix for 'Debug Assertion Failure' in Client::GarbleMessage() when calling the 'isalpha' macro. +ref: https://connect.microsoft.com/VisualStudio/feedback/details/932876/calling-isdigit-with-a-signed-char-1-results-in-a-assert-failure-in-debug-compiles + == 03/29/2015 == Secrets: Identified the Target Ring fields for RoF/RoF2. Secrets: Added a perl accessor for the last target ring position received from the client. Usage: $client->GetTargetRingX(), $client->GetTargetRingY(), $client->GetTargetRingZ() diff --git a/zone/client.cpp b/zone/client.cpp index 677884171..ad1863a05 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -7500,7 +7500,7 @@ void Client::GarbleMessage(char *message, uint8 variance) } uint8 chance = (uint8)zone->random.Int(0, 115); // variation just over worst possible scrambling - if (isalpha(message[i]) && (chance <= variance)) { + if (isalpha((unsigned char)message[i]) && (chance <= variance)) { uint8 rand_char = (uint8)zone->random.Int(0,51); // choose a random character from the alpha list message[i] = alpha_list[rand_char]; } From de57c94d3e0bae5b6e481c21073b9c494b559199 Mon Sep 17 00:00:00 2001 From: JJ Date: Thu, 23 Apr 2015 18:42:17 -0400 Subject: [PATCH 009/129] Blocked spell negation fix. --- zone/zone.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/zone.cpp b/zone/zone.cpp index 6cd81929f..eb22b0a6e 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1900,7 +1900,7 @@ bool Zone::IsSpellBlocked(uint32 spell_id, const glm::vec3& location) } 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)) + 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; } @@ -1934,7 +1934,7 @@ const char* Zone::GetSpellBlockedMessage(uint32 spell_id, const glm::vec3& locat } 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)) + 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; } From 47c9690a32ad60bad667b9a5320f48c3b626d2a3 Mon Sep 17 00:00:00 2001 From: JJ Date: Sat, 25 Apr 2015 11:46:43 -0400 Subject: [PATCH 010/129] Don't garble # commands. --- zone/client.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zone/client.cpp b/zone/client.cpp index ad1863a05..fdab00a3c 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -7491,6 +7491,10 @@ void Client::GarbleMessage(char *message, uint8 variance) const char delimiter = 0x12; int delimiter_count = 0; + // Don't garble # commands + if (message[0] == '#') + return; + for (size_t i = 0; i < strlen(message); i++) { // Client expects hex values inside of a text link body if (message[i] == delimiter) { From c84f56f1f53df3f890ae8abce430146fa359f326 Mon Sep 17 00:00:00 2001 From: GPanula Date: Sun, 26 Apr 2015 09:43:05 -0500 Subject: [PATCH 011/129] Avoid returning nulls when looking up if the server is trusted. Nulls will in the query results will cause the loginserver to crash --- loginserver/database_mysql.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/loginserver/database_mysql.cpp b/loginserver/database_mysql.cpp index 9cf08563b..d1bcff9b3 100644 --- a/loginserver/database_mysql.cpp +++ b/loginserver/database_mysql.cpp @@ -113,8 +113,8 @@ bool DatabaseMySQL::GetWorldRegistration(string long_name, string short_name, un length = mysql_real_escape_string(db, escaped_short_name, short_name.substr(0, 100).c_str(), short_name.substr(0, 100).length()); escaped_short_name[length+1] = 0; stringstream query(stringstream::in | stringstream::out); - query << "SELECT WSR.ServerID, WSR.ServerTagDescription, WSR.ServerTrusted, SLT.ServerListTypeID, "; - query << "SLT.ServerListTypeDescription, WSR.ServerAdminID FROM " << server.options.GetWorldRegistrationTable(); + query << "SELECT ifnull(WSR.ServerID,999999) AS ServerID, WSR.ServerTagDescription, ifnull(WSR.ServerTrusted,0) AS ServerTrusted, ifnull(SLT.ServerListTypeID,999999) AS ServerListTypeID, "; + query << "SLT.ServerListTypeDescription, ifnull(WSR.ServerAdminID,0) AS ServerAdminID FROM " << server.options.GetWorldRegistrationTable(); query << " AS WSR JOIN " << server.options.GetWorldServerTypeTable() << " AS SLT ON WSR.ServerListTypeID = SLT.ServerListTypeID"; query << " WHERE WSR.ServerShortName = '"; query << escaped_short_name; From d7e3a33179c747ff1bd3917e14b58c811563ef57 Mon Sep 17 00:00:00 2001 From: GPanula Date: Sun, 26 Apr 2015 09:56:46 -0500 Subject: [PATCH 012/129] opps, lets use a valid ServeLisTypeID --- loginserver/database_mysql.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loginserver/database_mysql.cpp b/loginserver/database_mysql.cpp index d1bcff9b3..aefe4b094 100644 --- a/loginserver/database_mysql.cpp +++ b/loginserver/database_mysql.cpp @@ -113,7 +113,7 @@ bool DatabaseMySQL::GetWorldRegistration(string long_name, string short_name, un length = mysql_real_escape_string(db, escaped_short_name, short_name.substr(0, 100).c_str(), short_name.substr(0, 100).length()); escaped_short_name[length+1] = 0; stringstream query(stringstream::in | stringstream::out); - query << "SELECT ifnull(WSR.ServerID,999999) AS ServerID, WSR.ServerTagDescription, ifnull(WSR.ServerTrusted,0) AS ServerTrusted, ifnull(SLT.ServerListTypeID,999999) AS ServerListTypeID, "; + query << "SELECT ifnull(WSR.ServerID,999999) AS ServerID, WSR.ServerTagDescription, ifnull(WSR.ServerTrusted,0) AS ServerTrusted, ifnull(SLT.ServerListTypeID,3) AS ServerListTypeID, "; query << "SLT.ServerListTypeDescription, ifnull(WSR.ServerAdminID,0) AS ServerAdminID FROM " << server.options.GetWorldRegistrationTable(); query << " AS WSR JOIN " << server.options.GetWorldServerTypeTable() << " AS SLT ON WSR.ServerListTypeID = SLT.ServerListTypeID"; query << " WHERE WSR.ServerShortName = '"; From 0d84ede3d62e8a978f608f157145487fe7ab7ef7 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 26 Apr 2015 13:35:36 -0400 Subject: [PATCH 013/129] Allow /pet attack by mob name to work ex. /pet attack a_snake --- common/eq_packet_structs.h | 2 +- common/patches/rof.cpp | 2 +- common/patches/rof2.cpp | 2 +- common/patches/rof2_structs.h | 2 +- common/patches/rof_structs.h | 2 +- common/patches/sod.cpp | 2 +- common/patches/sod_structs.h | 2 +- common/patches/sof.cpp | 2 +- common/patches/sof_structs.h | 2 +- common/patches/titanium.cpp | 16 ++++++++-------- common/patches/titanium_structs.h | 2 +- common/patches/uf.cpp | 6 +++--- common/patches/uf_structs.h | 2 +- zone/client_packet.cpp | 17 +++++++++-------- 14 files changed, 31 insertions(+), 30 deletions(-) diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 1481fce8c..8aafc0a62 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -1160,7 +1160,7 @@ struct TargetReject_Struct { struct PetCommand_Struct { /*000*/ uint32 command; -/*004*/ uint32 unknown; +/*004*/ uint32 target; }; /* diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 7bb64735b..ac6301c75 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -4717,7 +4717,7 @@ namespace RoF SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct); IN(command); - emu->unknown = eq->unknown04; + IN(target); FINISH_DIRECT_DECODE(); } diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 06c524053..16115d881 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -4864,7 +4864,7 @@ namespace RoF2 SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct); IN(command); - emu->unknown = eq->unknown04; + IN(target); FINISH_DIRECT_DECODE(); } diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index 21681df5e..4034e359a 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -1293,7 +1293,7 @@ struct TargetReject_Struct { struct PetCommand_Struct { /*00*/ uint32 command; -/*04*/ uint32 unknown04; +/*04*/ uint32 target; /*08*/ uint32 unknown08; }; diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index 622305833..344d0e386 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -1323,7 +1323,7 @@ struct TargetReject_Struct { struct PetCommand_Struct { /*00*/ uint32 command; -/*04*/ uint32 unknown04; +/*04*/ uint32 target; /*08*/ uint32 unknown08; }; diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index adf4ec06f..88e52a70a 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -3349,7 +3349,7 @@ namespace SoD default: emu->command = eq->command; } - OUT(unknown); + IN(target); FINISH_DIRECT_DECODE(); } diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 55730423b..8ccb58937 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -1091,7 +1091,7 @@ struct TargetReject_Struct { struct PetCommand_Struct { /*000*/ uint32 command; -/*004*/ uint32 unknown; +/*004*/ uint32 target; }; /* diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index a7553e7f9..42ebeb392 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -2687,7 +2687,7 @@ namespace SoF default: emu->command = eq->command; } - OUT(unknown); + IN(target); FINISH_DIRECT_DECODE(); } diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index fff207893..c969c4b56 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -1068,7 +1068,7 @@ struct TargetReject_Struct { struct PetCommand_Struct { /*000*/ uint32 command; -/*004*/ uint32 unknown; +/*004*/ uint32 target; }; /* diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index c8eeea36a..beb338431 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -122,7 +122,7 @@ namespace Titanium EAT_ENCODE(OP_GuildMemberLevelUpdate); // added ; EAT_ENCODE(OP_ZoneServerReady); // added ; - + ENCODE(OP_Action) { ENCODE_LENGTH_EXACT(Action_Struct); @@ -326,7 +326,7 @@ namespace Titanium { SETUP_VAR_ENCODE(ExpeditionCompass_Struct); ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count); - + OUT(count); for (uint32 i = 0; i < emu->count; ++i) @@ -1308,7 +1308,7 @@ namespace Titanium VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, emu->unknown12[11]); VARSTRUCT_ENCODE_STRING(OutBuffer, new_message.c_str()); - + delete[] __emu_buffer; dest->FastQueuePacket(&in, ack_req); } @@ -1351,7 +1351,7 @@ namespace Titanium InBuffer += strlen(InBuffer) + 1; memcpy(OutBuffer, InBuffer, sizeof(TaskDescriptionTrailer_Struct)); - + delete[] __emu_buffer; dest->FastQueuePacket(&in, ack_req); } @@ -1621,7 +1621,7 @@ namespace Titanium FINISH_DIRECT_DECODE(); } - + DECODE(OP_ApplyPoison) { DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); @@ -1942,7 +1942,7 @@ namespace Titanium default: emu->command = eq->command; } - OUT(unknown); + IN(target); FINISH_DIRECT_DECODE(); } @@ -2151,7 +2151,7 @@ namespace Titanium return serverSlot; // deprecated } - + static inline int16 ServerToTitaniumCorpseSlot(uint32 serverCorpseSlot) { //int16 TitaniumCorpse; @@ -2166,7 +2166,7 @@ namespace Titanium return titaniumSlot; // deprecated } - + static inline uint32 TitaniumToServerCorpseSlot(int16 titaniumCorpseSlot) { //uint32 ServerCorpse; diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index 5ad997d5b..12042ed31 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -950,7 +950,7 @@ struct TargetReject_Struct { struct PetCommand_Struct { /*000*/ uint32 command; -/*004*/ uint32 unknown; +/*004*/ uint32 target; }; /* diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index aafef5896..038dcf548 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -2213,7 +2213,7 @@ namespace UF FINISH_ENCODE(); return; } - + unsigned char *emu_ptr = __emu_buffer; emu_ptr += sizeof(CharacterSelect_Struct); CharacterSelectEntry_Struct *emu_cse = (CharacterSelectEntry_Struct *)nullptr; @@ -3599,7 +3599,7 @@ namespace UF SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct); IN(command); - IN(unknown); + IN(target); FINISH_DIRECT_DECODE(); } @@ -3861,7 +3861,7 @@ namespace UF UF::structs::ItemSerializationHeaderFinish hdrf; hdrf.ornamentIcon = ornaIcon; - hdrf.unknown060 = 0; //This is Always 0.. or it breaks shit.. + hdrf.unknown060 = 0; //This is Always 0.. or it breaks shit.. hdrf.unknown061 = 0; //possibly ornament / special ornament hdrf.isCopied = 0; //Flag for item to be 'Copied' hdrf.ItemClass = item->ItemClass; diff --git a/common/patches/uf_structs.h b/common/patches/uf_structs.h index a82bfc4e3..b39bc5b19 100644 --- a/common/patches/uf_structs.h +++ b/common/patches/uf_structs.h @@ -1146,7 +1146,7 @@ struct TargetReject_Struct { struct PetCommand_Struct { /*000*/ uint32 command; -/*004*/ uint32 unknown; +/*004*/ uint32 target; }; /* diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 201b0d13e..12b16610e 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -9752,6 +9752,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) char val1[20] = { 0 }; PetCommand_Struct* pet = (PetCommand_Struct*)app->pBuffer; Mob* mypet = this->GetPet(); + Mob *target = entity_list.GetMob(pet->target); if (!mypet || pet->command == PET_LEADER) { @@ -9799,22 +9800,22 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) switch (PetCommand) { case PET_ATTACK: { - if (!GetTarget()) + if (!target) break; - if (GetTarget()->IsMezzed()) { - Message_StringID(10, CANNOT_WAKE, mypet->GetCleanName(), GetTarget()->GetCleanName()); + if (target->IsMezzed()) { + Message_StringID(10, CANNOT_WAKE, mypet->GetCleanName(), target->GetCleanName()); break; } if (mypet->IsFeared()) break; //prevent pet from attacking stuff while feared - if (!mypet->IsAttackAllowed(GetTarget())) { + if (!mypet->IsAttackAllowed(target)) { mypet->Say_StringID(NOT_LEGAL_TARGET); break; } if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 2) || mypet->GetPetType() != petAnimation) { - if (GetTarget() != this && DistanceSquaredNoZ(mypet->GetPosition(), GetTarget()->GetPosition()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) { + if (target != this && DistanceSquaredNoZ(mypet->GetPosition(), target->GetPosition()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) { if (mypet->IsHeld()) { if (!mypet->IsFocused()) { mypet->SetHeld(false); //break the hold and guard if we explicitly tell the pet to attack. @@ -9822,12 +9823,12 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) mypet->SetPetOrder(SPO_Follow); } else { - mypet->SetTarget(GetTarget()); + mypet->SetTarget(target); } } zone->AddAggroMob(); - mypet->AddToHateList(GetTarget(), 1); - Message_StringID(MT_PetResponse, PET_ATTACKING, mypet->GetCleanName(), GetTarget()->GetCleanName()); + mypet->AddToHateList(target, 1); + Message_StringID(MT_PetResponse, PET_ATTACKING, mypet->GetCleanName(), target->GetCleanName()); } } break; From 46d70199099a93d73dd28017d736a84539784eca Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Wed, 29 Apr 2015 08:26:59 -0400 Subject: [PATCH 014/129] Spells like flame_lick were not requiring flame lick. Noexpend for flame lick was not working. Also fixed a log message with arguments reversed. --- zone/spells.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index a4bef767c..020064637 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1164,12 +1164,14 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot, } else if (!bard_song_mode) { + int noexpend; for(int t_count = 0; t_count < 4; t_count++) { component = spells[spell_id].components[t_count]; - if (component == -1) + noexpend = spells[spell_id].NoexpendReagent[t_count]; + if (component == -1 || noexpend == component) continue; component_count = spells[spell_id].component_counts[t_count]; - Log.Out(Logs::Detail, Logs::Spells, "Spell %d: Consuming %d of spell component item id %d", spell_id, component, component_count); + Log.Out(Logs::Detail, Logs::Spells, "Spell %d: Consuming %d of spell component item id %d", spell_id, component_count, component); // Components found, Deleting // now we go looking for and deleting the items one by one for(int s = 0; s < component_count; s++) From 2c4ca77ffc38bdbd0bbcafbfbea66f95ecbbab46 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Wed, 29 Apr 2015 19:18:17 -0400 Subject: [PATCH 015/129] Monk wearing magical gloves can hit creatures that need a magical weapon when fighting hand to hand. --- zone/attack.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 950663a38..da0504902 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -982,14 +982,22 @@ int Mob::GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate return 0; } else{ - if((GetClass() == MONK || GetClass() == BEASTLORD) && GetLevel() >= 30){ - dmg = GetMonkHandToHandDamage(); - if (hate) *hate += dmg; + bool MagicGloves=false; + ItemInst *gloves=CastToClient()->GetInv().GetItem(MainHands); + if (gloves != nullptr) { + MagicGloves = gloves->GetItem()->Magic; + } + + if((GetClass() == MONK || GetClass() == BEASTLORD)) { + if(MagicGloves || GetLevel() >= 30){ + dmg = GetMonkHandToHandDamage(); + if (hate) *hate += dmg; + } } else if(GetOwner() && GetLevel() >= RuleI(Combat, PetAttackMagicLevel)){ //pets wouldn't actually use this but... dmg = 1; //it gives us an idea if we can hit } - else if(GetSpecialAbility(SPECATK_MAGICAL)){ + else if(MagicGloves || GetSpecialAbility(SPECATK_MAGICAL)){ dmg = 1; } else From a1960d4a4ab37ad9864cd77023c5b6beea76e76a Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Thu, 30 Apr 2015 08:00:36 -0400 Subject: [PATCH 016/129] Npcs won't respond to hails if they can't see you. --- zone/client.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 677884171..01d3de558 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -1051,12 +1051,12 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s if(quest_manager.ProximitySayInUse()) entity_list.ProcessProximitySay(message, this, language); - if (GetTarget() != 0 && GetTarget()->IsNPC()) { + if (GetTarget() != 0 && GetTarget()->IsNPC() && + !IsInvisible(GetTarget())) { if(!GetTarget()->CastToNPC()->IsEngaged()) { CheckLDoNHail(GetTarget()); CheckEmoteHail(GetTarget(), message); - if(DistanceSquaredNoZ(m_Position, GetTarget()->GetPosition()) <= 200) { NPC *tar = GetTarget()->CastToNPC(); parse->EventNPC(EVENT_SAY, tar->CastToNPC(), this, message, language); From eea667e22dc574f0115572f653c19409e1862b02 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Thu, 30 Apr 2015 09:33:11 -0400 Subject: [PATCH 017/129] Check to make sure we're a client before a CastToClient(). Missed this on first patch. --- zone/attack.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index da0504902..8689f9b85 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -983,9 +983,11 @@ int Mob::GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate } else{ bool MagicGloves=false; - ItemInst *gloves=CastToClient()->GetInv().GetItem(MainHands); - if (gloves != nullptr) { - MagicGloves = gloves->GetItem()->Magic; + if (IsClient()) { + ItemInst *gloves=CastToClient()->GetInv().GetItem(MainHands); + if (gloves != nullptr) { + MagicGloves = gloves->GetItem()->Magic; + } } if((GetClass() == MONK || GetClass() == BEASTLORD)) { From 06f4fd49efc1dde83c4f18b0f20a93b709798436 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 30 Apr 2015 19:36:21 -0400 Subject: [PATCH 018/129] Implement mob and client melee push New rules: Combat:MeleePush turns melee push on/off Combat:MeleePushChance is the chance that an NPC will be pushed Clients are pushed every successful hit, need to verify or disprove this --- changelog.txt | 5 + common/eq_packet_structs.h | 6 +- common/patches/rof.cpp | 6 +- common/patches/rof2.cpp | 6 +- common/patches/rof2_structs.h | 7 +- common/patches/rof_structs.h | 7 +- common/patches/sod.cpp | 4 +- common/patches/sod_structs.h | 7 +- common/patches/sof.cpp | 4 +- common/patches/sof_structs.h | 7 +- common/patches/titanium_structs.h | 6 +- common/patches/uf.cpp | 6 +- common/patches/uf_structs.h | 7 +- common/ruletypes.h | 8 +- common/skills.cpp | 37 +++ common/skills.h | 1 + .../sql/git/optional/2015_4_30_MeleePush.sql | 2 + zone/attack.cpp | 17 ++ zone/spell_effects.cpp | 286 +++++++++--------- zone/spells.cpp | 4 +- zone/trap.cpp | 2 +- 21 files changed, 257 insertions(+), 178 deletions(-) create mode 100644 utils/sql/git/optional/2015_4_30_MeleePush.sql diff --git a/changelog.txt b/changelog.txt index 29caca258..a90c903bd 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,10 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 04/30/2015 == +demonstar55: Implement mob and client melee push + You can set Combat:MeleePush to false to turn off or change Combat:MeleePushChance to increase the chance an NPC can be pushed + PCs are always pushed, need to do more testing to verify. + == 04/22/2015 == Uleat: Probable fix for 'Debug Assertion Failure' in Client::GarbleMessage() when calling the 'isalpha' macro. ref: https://connect.microsoft.com/VisualStudio/feedback/details/932876/calling-isdigit-with-a-signed-char-1-results-in-a-assert-failure-in-debug-compiles diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 8aafc0a62..c203dad53 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -1318,9 +1318,9 @@ struct CombatDamage_Struct /* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells /* 05 */ uint16 spellid; /* 07 */ uint32 damage; -/* 11 */ uint32 unknown11; -/* 15 */ uint32 sequence; // see above notes in Action_Struct -/* 19 */ uint32 unknown19; +/* 11 */ float force; +/* 15 */ float meleepush_xy; // see above notes in Action_Struct +/* 19 */ float meleepush_z; /* 23 */ }; diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index ac6301c75..b4a6e73d3 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -658,7 +658,9 @@ namespace RoF OUT(type); OUT(spellid); OUT(damage); - eq->sequence = emu->sequence; + OUT(force) + OUT(meleepush_xy); + OUT(meleepush_z) FINISH_ENCODE(); } @@ -4389,7 +4391,7 @@ namespace RoF IN(type); IN(spellid); IN(damage); - emu->sequence = eq->sequence; + IN(meleepush_xy); FINISH_DIRECT_DECODE(); } diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 16115d881..e8b98e7af 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -729,7 +729,9 @@ namespace RoF2 OUT(type); OUT(spellid); OUT(damage); - eq->sequence = emu->sequence; + OUT(force) + OUT(meleepush_xy); + OUT(meleepush_z) FINISH_ENCODE(); } @@ -4538,7 +4540,7 @@ namespace RoF2 IN(type); IN(spellid); IN(damage); - emu->sequence = eq->sequence; + IN(meleepush_xy); FINISH_DIRECT_DECODE(); } diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index 4034e359a..92ff47fda 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -1484,9 +1484,10 @@ struct CombatDamage_Struct /* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells /* 05 */ uint32 spellid; /* 09 */ int32 damage; -/* 13 */ float unknown11; // cd cc cc 3d -/* 17 */ float sequence; // see above notes in Action_Struct -/* 21 */ uint8 unknown19[9]; // was [9] +/* 13 */ float force; // cd cc cc 3d +/* 17 */ float meleepush_xy; // see above notes in Action_Struct +/* 21 */ float meleepush_z; +/* 25 */ uint8 unknown25[5]; // was [9] /* 30 */ }; diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index 344d0e386..55c86a557 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -1514,9 +1514,10 @@ struct CombatDamage_Struct /* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells /* 05 */ uint32 spellid; /* 09 */ int32 damage; -/* 13 */ float unknown11; // cd cc cc 3d -/* 17 */ float sequence; // see above notes in Action_Struct -/* 21 */ uint8 unknown19[9]; // was [9] +/* 13 */ float force; // cd cc cc 3d +/* 17 */ float meleepush_xy; // see above notes in Action_Struct +/* 21 */ float meleepush_z; +/* 25 */ uint8 unknown25[5]; // was [9] /* 30 */ }; diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 88e52a70a..8e5656b4b 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -446,7 +446,9 @@ namespace SoD OUT(type); OUT(spellid); OUT(damage); - eq->sequence = emu->sequence; + OUT(force) + OUT(meleepush_xy); + OUT(meleepush_z) FINISH_ENCODE(); } diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 8ccb58937..024814bfd 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -1272,9 +1272,10 @@ struct CombatDamage_Struct /* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells /* 05 */ uint16 spellid; /* 07 */ int32 damage; -/* 11 */ float unknown11; // cd cc cc 3d -/* 15 */ float sequence; // see above notes in Action_Struct -/* 19 */ uint8 unknown19[9]; // was [9] +/* 11 */ float force; // cd cc cc 3d +/* 15 */ float meleepush_xy; // see above notes in Action_Struct +/* 19 */ float meleepush_z; +/* 23 */ uint8 unknown23[5]; // was [9] /* 28 */ }; diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 42ebeb392..1cd5ee7ed 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -426,7 +426,9 @@ namespace SoF OUT(type); OUT(spellid); OUT(damage); - eq->sequence = emu->sequence; + OUT(force) + OUT(meleepush_xy); + OUT(meleepush_z) FINISH_ENCODE(); } diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index c969c4b56..4dd0e7d80 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -1249,9 +1249,10 @@ struct CombatDamage_Struct /* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells /* 05 */ uint16 spellid; /* 07 */ int32 damage; -/* 11 */ float unknown11; // cd cc cc 3d -/* 15 */ float sequence; // see above notes in Action_Struct -/* 19 */ uint8 unknown19[9]; // was [9] +/* 11 */ float force; // cd cc cc 3d +/* 15 */ float meleepush_xy; // see above notes in Action_Struct +/* 19 */ float meleepush_z; +/* 23 */ uint8 unknown23[5]; // was [9] /* 28 */ }; diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index 12042ed31..53bcb38da 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -1101,9 +1101,9 @@ struct CombatDamage_Struct /* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells /* 05 */ uint16 spellid; /* 07 */ uint32 damage; -/* 11 */ uint32 unknown11; -/* 15 */ uint32 sequence; // see above notes in Action_Struct -/* 19 */ uint32 unknown19; +/* 11 */ float force; +/* 15 */ float meleepush_xy; // see above notes in Action_Struct +/* 19 */ float meleepush_z; /* 23 */ }; diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index 038dcf548..c2b3dba4a 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -581,7 +581,9 @@ namespace UF OUT(type); OUT(spellid); OUT(damage); - eq->sequence = emu->sequence; + OUT(force) + OUT(meleepush_xy); + OUT(meleepush_z) FINISH_ENCODE(); } @@ -3356,7 +3358,7 @@ namespace UF IN(type); IN(spellid); IN(damage); - emu->sequence = eq->sequence; + IN(meleepush_xy); FINISH_DIRECT_DECODE(); } diff --git a/common/patches/uf_structs.h b/common/patches/uf_structs.h index b39bc5b19..49fb9a155 100644 --- a/common/patches/uf_structs.h +++ b/common/patches/uf_structs.h @@ -1330,9 +1330,10 @@ struct CombatDamage_Struct /* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells /* 05 */ uint16 spellid; /* 07 */ int32 damage; -/* 11 */ float unknown11; // cd cc cc 3d -/* 15 */ float sequence; // see above notes in Action_Struct -/* 19 */ uint8 unknown19[9]; // was [9] +/* 11 */ float force; // cd cc cc 3d +/* 15 */ float meleepush_xy; // see above notes in Action_Struct +/* 19 */ float meleepush_z; +/* 23 */ uint8 unknown23[5]; // was [9] /* 28 */ }; diff --git a/common/ruletypes.h b/common/ruletypes.h index c8f9557f5..5c4154cf9 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -377,7 +377,7 @@ RULE_REAL ( Combat, HitBonusPerLevel, 1.2) //You gain this % of hit for every le RULE_REAL ( Combat, WeaponSkillFalloff, 0.33) //For every weapon skill point that's not maxed you lose this % of hit RULE_REAL ( Combat, ArcheryHitPenalty, 0.25) //Archery has a hit penalty to try to help balance it with the plethora of long term +hit modifiers for it RULE_REAL ( Combat, AgiHitFactor, 0.01) -RULE_REAL ( Combat, MinChancetoHit, 5.0) //Minimum % chance to hit with regular melee/ranged +RULE_REAL ( Combat, MinChancetoHit, 5.0) //Minimum % chance to hit with regular melee/ranged RULE_REAL ( Combat, MaxChancetoHit, 95.0) //Maximum % chance to hit with regular melee/ranged RULE_INT ( Combat, MinRangedAttackDist, 25) //Minimum Distance to use Ranged Attacks RULE_BOOL ( Combat, ArcheryBonusRequiresStationary, true) //does the 2x archery bonus chance require a stationary npc @@ -438,6 +438,8 @@ RULE_INT ( Combat, BerserkerFrenzyStart, 35) RULE_INT ( Combat, BerserkerFrenzyEnd, 45) RULE_BOOL ( Combat, OneProcPerWeapon, true) //If enabled, One proc per weapon per round RULE_BOOL ( Combat, ProjectileDmgOnImpact, true) //If enabled, projectiles (ie arrows) will hit on impact, instead of instantly. +RULE_BOOL ( Combat, MeleePush, true) // enable melee push +RULE_INT ( Combat, MeleePushChance, 50) // (NPCs) chance the target will be pushed. Made up, 100 actually isn't that bad RULE_CATEGORY_END() RULE_CATEGORY( NPC ) @@ -577,7 +579,7 @@ RULE_INT ( Console, SessionTimeOut, 600000 ) // Amount of time in ms for the con RULE_CATEGORY_END() RULE_CATEGORY( QueryServ ) -RULE_BOOL( QueryServ, PlayerLogChat, false) // Logs Player Chat +RULE_BOOL( QueryServ, PlayerLogChat, false) // Logs Player Chat RULE_BOOL( QueryServ, PlayerLogTrades, false) // Logs Player Trades RULE_BOOL( QueryServ, PlayerLogHandins, false) // Logs Player Handins RULE_BOOL( QueryServ, PlayerLogNPCKills, false) // Logs Player NPC Kills @@ -590,7 +592,7 @@ RULE_BOOL( QueryServ, PlayerLogZone, false) // Logs Player Zone Events RULE_BOOL( QueryServ, PlayerLogDeaths, false) // Logs Player Deaths RULE_BOOL( QueryServ, PlayerLogConnectDisconnect, false) // Logs Player Connect Disconnect State RULE_BOOL( QueryServ, PlayerLogLevels, false) // Logs Player Leveling/Deleveling -RULE_BOOL( QueryServ, PlayerLogAARate, false) // Logs Player AA Experience Rates +RULE_BOOL( QueryServ, PlayerLogAARate, false) // Logs Player AA Experience Rates RULE_BOOL( QueryServ, PlayerLogQGlobalUpdate, false) // Logs Player QGlobal Updates RULE_BOOL( QueryServ, PlayerLogTaskUpdates, false) // Logs Player Task Updates RULE_BOOL( QueryServ, PlayerLogKeyringAddition, false) // Log PLayer Keyring additions diff --git a/common/skills.cpp b/common/skills.cpp index ceb726d8c..363c077d8 100644 --- a/common/skills.cpp +++ b/common/skills.cpp @@ -55,3 +55,40 @@ bool EQEmu::IsSpecializedSkill(SkillUseTypes skill) return false; } } + +float EQEmu::GetSkillMeleePushForce(SkillUseTypes skill) +{ + // This is the force/magnitude of the push from an attack of this skill type + // You can find these numbers in the clients skill struct + switch (skill) { + case Skill1HBlunt: + case Skill1HSlashing: + case SkillHandtoHand: + case SkillThrowing: + return 0.1f; + case Skill2HBlunt: + case Skill2HSlashing: + case SkillEagleStrike: + case SkillKick: + case SkillTigerClaw: + //case Skill2HPiercing: + return 0.2f; + case SkillArchery: + return 0.15f; + case SkillBackstab: + case SkillBash: + return 0.3f; + case SkillDragonPunch: + case SkillRoundKick: + return 0.25f; + case SkillFlyingKick: + return 0.4f; + case Skill1HPiercing: + case SkillFrenzy: + return 0.05f; + case SkillIntimidation: + return 2.5f; + default: + return 0.0f; + } +} diff --git a/common/skills.h b/common/skills.h index f7f66b49c..aafb1abce 100644 --- a/common/skills.h +++ b/common/skills.h @@ -270,6 +270,7 @@ typedef enum { namespace EQEmu { bool IsTradeskill(SkillUseTypes skill); bool IsSpecializedSkill(SkillUseTypes skill); + float GetSkillMeleePushForce(SkillUseTypes skill); } #endif diff --git a/utils/sql/git/optional/2015_4_30_MeleePush.sql b/utils/sql/git/optional/2015_4_30_MeleePush.sql new file mode 100644 index 000000000..e640d4237 --- /dev/null +++ b/utils/sql/git/optional/2015_4_30_MeleePush.sql @@ -0,0 +1,2 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:MeleePush', 'true', 'Turns on Melee Push.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:MeleePushChance', '50', 'Chance that an NPC can be pushed from melee.'); diff --git a/zone/attack.cpp b/zone/attack.cpp index a8cff65f5..d89c9932b 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3708,6 +3708,23 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons a->type = SkillDamageTypes[skill_used]; // was 0x1c a->damage = damage; a->spellid = spell_id; + a->meleepush_xy = attacker->GetHeading() * 2.0f; + if (RuleB(Combat, MeleePush) && damage > 0 && !IsRooted() && + (IsClient() || zone->random.Roll(RuleI(Combat, MeleePushChance)))) { + a->force = EQEmu::GetSkillMeleePushForce(skill_used); + // update NPC stuff + auto new_pos = glm::vec3(m_Position.x + (a->force * std::sin(a->meleepush_xy) + m_Delta.x), + m_Position.y + (a->force * std::cos(a->meleepush_xy) + m_Delta.y), m_Position.z); + if (zone->zonemap->CheckLoS(glm::vec3(m_Position), new_pos)) { // If we have LoS on the new loc it should be reachable. + if (IsNPC()) { + // Is this adequate? + Teleport(new_pos); + SendPosUpdate(); + } + } else { + a->force = 0.0f; // we couldn't move there, so lets not + } + } //Note: if players can become pets, they will not receive damage messages of their own //this was done to simplify the code here (since we can only effectively skip one mob on queue) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index b159fbabc..8644ac918 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -189,8 +189,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if (!IsPowerDistModSpell(spell_id)) SetSpellPowerDistanceMod(0); - - bool SE_SpellTrigger_HasCast = false; + + bool SE_SpellTrigger_HasCast = false; // iterate through the effects in the spell for (i = 0; i < EFFECT_COUNT; i++) @@ -424,11 +424,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) } case SE_Succor: - { - + { + float x, y, z, heading; const char *target_zone; - + x = static_cast(spell.base[1]); y = static_cast(spell.base[0]); z = static_cast(spell.base[2]); @@ -872,7 +872,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if(buffs[buffslot].ticsremaining > RuleI(Character, MaxFearDurationForPlayerCharacter)) buffs[buffslot].ticsremaining = RuleI(Character, MaxFearDurationForPlayerCharacter); } - + if(RuleB(Combat, EnableFearPathing)){ if(IsClient()) @@ -921,7 +921,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) cd->source = action->source; cd->type = action->type; cd->spellid = action->spell; - cd->sequence = action->sequence; + cd->meleepush_xy = action->sequence; CastToClient()->QueuePacket(action_packet); if(caster->IsClient() && caster != this) @@ -970,7 +970,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) cd->source = action->source; cd->type = action->type; cd->spellid = action->spell; - cd->sequence = action->sequence; + cd->meleepush_xy = action->sequence; CastToClient()->QueuePacket(action_packet); if(caster->IsClient() && caster != this) @@ -1006,7 +1006,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) cd->source = action->source; cd->type = action->type; cd->spellid = action->spell; - cd->sequence = action->sequence; + cd->meleepush_xy = action->sequence; CastToClient()->QueuePacket(action_packet); if(caster->IsClient() && caster != this) @@ -1291,7 +1291,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Spell Absorb Rune: %+i", effect_value); #endif - if(effect_value > 0) + if(effect_value > 0) buffs[buffslot].magic_rune = effect_value; break; @@ -1329,12 +1329,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_DistanceRemoval: { - buffs[buffslot].caston_x = int(GetX()); - buffs[buffslot].caston_y = int(GetY()); - buffs[buffslot].caston_z = int(GetZ()); + buffs[buffslot].caston_x = int(GetX()); + buffs[buffslot].caston_y = int(GetY()); + buffs[buffslot].caston_z = int(GetZ()); break; } - + case SE_Levitate: { #ifdef SPELL_EFFECT_SPAM @@ -1349,13 +1349,13 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_DeathSave: { int16 mod = 0; - + if(caster) { mod = caster->aabonuses.UnfailingDivinity + caster->itembonuses.UnfailingDivinity + caster->spellbonuses.UnfailingDivinity; } - + buffs[buffslot].ExtraDIChance = mod; break; } @@ -1440,7 +1440,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) for(int x = EmuConstants::MATERIAL_BEGIN; x <= EmuConstants::MATERIAL_TINT_END; x++) SendWearChange(x); - + if(caster && (caster->spellbonuses.IllusionPersistence || caster->aabonuses.IllusionPersistence || caster->itembonuses.IllusionPersistence)) buffs[buffslot].persistant_buff = 1; @@ -1555,8 +1555,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) { uint16 pet_spellid = CastToNPC()->GetPetSpellID(); uint16 pet_ActSpellCost = caster->GetActSpellCost(pet_spellid, spells[pet_spellid].mana); - int16 ImprovedReclaimMod = caster->spellbonuses.ImprovedReclaimEnergy + - caster->itembonuses.ImprovedReclaimEnergy + + int16 ImprovedReclaimMod = caster->spellbonuses.ImprovedReclaimEnergy + + caster->itembonuses.ImprovedReclaimEnergy + caster->aabonuses.ImprovedReclaimEnergy; if (!ImprovedReclaimMod) @@ -1666,9 +1666,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) // Only allow 2 size changes from Base Size float modifyAmount = (static_cast(effect_value) / 100.0f); float maxModAmount = GetBaseSize() * modifyAmount * modifyAmount; - if ((GetSize() <= GetBaseSize() && GetSize() > maxModAmount) || + if ((GetSize() <= GetBaseSize() && GetSize() > maxModAmount) || (GetSize() >= GetBaseSize() && GetSize() < maxModAmount) || - (GetSize() <= GetBaseSize() && maxModAmount > 1.0f) || + (GetSize() <= GetBaseSize() && maxModAmount > 1.0f) || (GetSize() >= GetBaseSize() && maxModAmount < 1.0f)) { ChangeSize(GetSize() * modifyAmount); @@ -1684,7 +1684,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) rooted = true; if (caster){ - buffs[buffslot].RootBreakChance = caster->aabonuses.RootBreakChance + + buffs[buffslot].RootBreakChance = caster->aabonuses.RootBreakChance + caster->itembonuses.RootBreakChance + caster->spellbonuses.RootBreakChance; } @@ -2249,7 +2249,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) float range = 0.0f; if (spells[spell_id].base2[i]) range = (float)spells[spell_id].base[i]; - + entity_list.AETaunt(caster->CastToClient(), range); } break; @@ -2540,7 +2540,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) int mana_damage = 0; int32 mana_to_use = GetMana() - spell.base[i]; if(mana_to_use > -1) { - SetMana(GetMana() - spell.base[i]); + SetMana(GetMana() - spell.base[i]); TryTriggerOnValueAmount(false, true); // we take full dmg(-10 to make the damage the right sign) mana_damage = spell.base[i] / -10 * spell.base2[i]; @@ -2661,7 +2661,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) { if (IsNPC()){ caster->Taunt(this->CastToNPC(), false, static_cast(spell.base[i])); - + if (spell.base2[i] > 0) CastToNPC()->SetHateAmountOnEnt(caster, (CastToNPC()->GetHateAmount(caster) + spell.base2[i])); } @@ -2972,7 +2972,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_TriggerOnReqTarget: case SE_LimitRace: case SE_FcLimitUse: - case SE_FcMute: + case SE_FcMute: case SE_LimitUseType: case SE_FcStunTimeMod: case SE_StunBashChance: @@ -2981,9 +2981,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_LimitCastTimeMax: case SE_TriggerOnReqCaster: case SE_FrenziedDevastation: - case SE_AStacker: - case SE_BStacker: - case SE_CStacker: + case SE_AStacker: + case SE_BStacker: + case SE_CStacker: case SE_DStacker: case SE_DoubleRiposte: case SE_Berserk: @@ -3473,7 +3473,7 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste effect_value = CalcSpellEffectValue(spell_id, i, caster_level, caster, ticsremaining); //Handle client cast DOTs here. if (caster && effect_value < 0){ - + if (IsDetrimentalSpell(spell_id)){ if (caster->IsClient()){ if (!caster->CastToClient()->GetFeigned()) @@ -3715,7 +3715,7 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste { if (spellbonuses.DistanceRemoval){ - int distance = ((int(GetX()) - buffs[slot].caston_x) * (int(GetX()) - buffs[slot].caston_x)) + + int distance = ((int(GetX()) - buffs[slot].caston_x) * (int(GetX()) - buffs[slot].caston_x)) + ((int(GetY()) - buffs[slot].caston_y) * (int(GetY()) - buffs[slot].caston_y)) + ((int(GetZ()) - buffs[slot].caston_z) * (int(GetZ()) - buffs[slot].caston_z)); @@ -3729,12 +3729,12 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste } case SE_AddHateOverTimePct: - { + { if (IsNPC()){ uint32 new_hate = CastToNPC()->GetHateAmount(caster) * (100 + spell.base[i]) / 100; if (new_hate <= 0) new_hate = 1; - + CastToNPC()->SetHateAmountOnEnt(caster, new_hate); } break; @@ -4136,9 +4136,9 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) uint32 buff_max = GetMaxTotalSlots(); bool found_numhits = false; - + for(uint32 d = 0; d < buff_max; d++) { - + if(IsValidSpell(buffs[d].spellid) && (buffs[d].numhits > 0)) { Numhits(true); found_numhits = true; @@ -4148,7 +4148,7 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) if (!found_numhits) Numhits(false); } - + if (spells[buffs[slot].spellid].NimbusEffect > 0) RemoveNimbusEffect(spells[buffs[slot].spellid].NimbusEffect); @@ -4204,15 +4204,15 @@ int32 Client::GetAAEffectDataBySlot(uint32 aa_ID, uint32 slot_id, bool GetEffect base1 = iter->second.base1; base2 = iter->second.base2; slot = iter->second.slot; - + if (slot && slot == slot_id) { if (GetEffect) return effect; - + if (GetBase1) return base1; - + if (GetBase2) return base2; } @@ -4236,7 +4236,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) uint32 slot = 0; bool LimitFailure = false; - bool LimitInclude[MaxLimitInclude] = { false }; + bool LimitInclude[MaxLimitInclude] = { false }; /* Certain limits require only one of several Include conditions to be true. Ie. Add damage to fire OR ice spells. 0/1 SE_LimitResist 2/3 SE_LimitSpell @@ -4247,7 +4247,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) 12/13 SE_LimitSpellClass: 14/15 SE_LimitSpellSubClass: Remember: Update MaxLimitInclude in spdat.h if adding new limits that require Includes - */ + */ int FocusCount = 0; std::map >::const_iterator find_iter = aa_effects.find(aa_ID); @@ -4262,7 +4262,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) base1 = iter->second.base1; base2 = iter->second.base2; slot = iter->second.slot; - + /* AA Foci's can contain multiple focus effects within the same AA. To handle this we will not automatically return zero if a limit is found. @@ -4283,7 +4283,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) if (LimitFailure){ value = 0; LimitFailure = false; - + for(int e = 0; e < MaxLimitInclude; e++) { LimitInclude[e] = false; //Reset array } @@ -4322,7 +4322,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) LimitFailure = true; break; - + case SE_LimitMaxLevel: spell_level = spell.classes[(GetClass()%16) - 1]; lvldiff = spell_level - base1; @@ -4333,7 +4333,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) if(lvlModifier < 1) LimitFailure = true; } - else + else LimitFailure = true; } break; @@ -4357,7 +4357,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) if(base1 < 0) { //Exclude if (spell_id == -base1) LimitFailure = true; - } + } else { LimitInclude[2] = true; if (spell_id == base1) //Include @@ -4450,7 +4450,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) if(base1 < 0) { //Exclude if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)) return(0); - } + } else { LimitInclude[12] = true; if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)) //Include @@ -4462,7 +4462,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) if(base1 < 0) { //Exclude if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)) return(0); - } + } else { LimitInclude[14] = true; if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)) //Include @@ -4599,7 +4599,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) //Note if using these as AA, make sure this is first focus used. case SE_SympatheticProc: - if(type == focusSympatheticProc) + if(type == focusSympatheticProc) value = base2; break; @@ -4657,12 +4657,12 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) if(type == focusIncreaseNumHits) value = base1; break; - + case SE_FcLimitUse: if(type == focusFcLimitUse) value = base1; break; - + case SE_FcMute: if(type == focusFcMute) value = base1; @@ -4683,7 +4683,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) if (LimitFailure) return 0; - + return(value*lvlModifier/100); } @@ -4694,7 +4694,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo if(!IsValidSpell(focus_id) || !IsValidSpell(spell_id)) return 0; - + const SPDat_Spell_Struct &focus_spell = spells[focus_id]; const SPDat_Spell_Struct &spell = spells[spell_id]; @@ -4704,7 +4704,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo int lvldiff = 0; uint32 Caston_spell_id = 0; - bool LimitInclude[MaxLimitInclude] = { false }; + bool LimitInclude[MaxLimitInclude] = { false }; /* Certain limits require only one of several Include conditions to be true. Ie. Add damage to fire OR ice spells. 0/1 SE_LimitResist 2/3 SE_LimitSpell @@ -4715,15 +4715,15 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo 12/13 SE_LimitSpellClass: 14/15 SE_LimitSpellSubClass: Remember: Update MaxLimitInclude in spdat.h if adding new limits that require Includes - */ - + */ + for (int i = 0; i < EFFECT_COUNT; i++) { switch (focus_spell.effectid[i]) { - + case SE_Blank: break; - + case SE_LimitResist: if(focus_spell.base[i] < 0){ if (spell.resisttype == -focus_spell.base[i]) //Exclude @@ -4735,7 +4735,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo LimitInclude[1] = true; } break; - + case SE_LimitInstant: if(focus_spell.base[i] == 1 && spell.buffduration) //Fail if not instant return 0; @@ -4760,7 +4760,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo return 0; } break; - + case SE_LimitMinLevel: if (IsNPC()) break; @@ -4777,12 +4777,12 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo if (spells[spell_id].cast_time > (uint16)focus_spell.base[i]) return(0); break; - + case SE_LimitSpell: if(focus_spell.base[i] < 0) { //Exclude if (spell_id == -focus_spell.base[i]) return(0); - } + } else { LimitInclude[2] = true; if (spell_id == focus_spell.base[i]) //Include @@ -4871,7 +4871,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo LimitInclude[11] = true; } break; - + case SE_LimitClass: //Do not use this limit more then once per spell. If multiple class, treat value like items would. if (!PassLimitClass(focus_spell.base[i], GetClass())) @@ -4989,7 +4989,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo if (type == focusSpellHaste && focus_spell.base[i] > value) value = focus_spell.base[i]; break; - + case SE_IncreaseSpellDuration: if (type == focusSpellDuration && focus_spell.base[i] > value) value = focus_spell.base[i]; @@ -5161,14 +5161,14 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo Log.Out(Logs::General, Logs::Normal, "CalcFocusEffect: unknown effectid %d", focus_spell.effectid[i]); #endif } - + } for(int e = 0; e < MaxLimitInclude; e+=2) { if (LimitInclude[e] && !LimitInclude[e+1]) return 0; } - + if (Caston_spell_id){ if(IsValidSpell(Caston_spell_id) && (Caston_spell_id != spell_id)) SpellFinished(Caston_spell_id, this, 10, 0, -1, spells[Caston_spell_id].ResistDiff); @@ -5260,7 +5260,7 @@ uint16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { } } - /*Note: At present, ff designing custom AA to have a sympathetic proc effect, only use one focus + /*Note: At present, ff designing custom AA to have a sympathetic proc effect, only use one focus effect within the aa_effects data for each AA*[No live AA's use this effect to my knowledge]*/ if (aabonuses.FocusEffects[type]){ @@ -5557,10 +5557,10 @@ int16 NPC::GetFocusEffect(focusType type, uint16 spell_id) { //item focus for(int i = 0; i < EmuConstants::EQUIPMENT_SIZE; i++){ const Item_Struct *cur = database.GetItem(equipment[i]); - + if(!cur) continue; - + TempItem = cur; if (TempItem && TempItem->Focus.Effect > 0 && TempItem->Focus.Effect != SPELL_UNKNOWN) { @@ -5590,7 +5590,7 @@ int16 NPC::GetFocusEffect(focusType type, uint16 spell_id) { } } } - + if(UsedItem && rand_effectiveness && focus_max_real != 0) realTotal = CalcFocusEffect(type, UsedFocusID, spell_id); } @@ -5909,21 +5909,21 @@ float Mob::GetSympatheticProcChances(uint16 spell_id, int16 ProcRateMod, int32 I int32 total_cast_time = 0; float cast_time_mod = 0.0f; ProcRateMod -= 100; - + if (spells[spell_id].recast_time >= spells[spell_id].recovery_time) total_cast_time = spells[spell_id].recast_time + spells[spell_id].cast_time; else total_cast_time = spells[spell_id].recovery_time + spells[spell_id].cast_time; - - if (total_cast_time > 0 && total_cast_time <= 2500) - cast_time_mod = 0.25f; - else if (total_cast_time > 2500 && total_cast_time < 7000) - cast_time_mod = 0.167f*((static_cast(total_cast_time) - 1000.0f)/1000.0f); - else - cast_time_mod = static_cast(total_cast_time) / 7000.0f; - ProcChance = (RuleR(Casting, AvgSpellProcsPerMinute)/100.0f) * (static_cast(100.0f + ProcRateMod) / 10.0f) + if (total_cast_time > 0 && total_cast_time <= 2500) + cast_time_mod = 0.25f; + else if (total_cast_time > 2500 && total_cast_time < 7000) + cast_time_mod = 0.167f*((static_cast(total_cast_time) - 1000.0f)/1000.0f); + else + cast_time_mod = static_cast(total_cast_time) / 7000.0f; + + ProcChance = (RuleR(Casting, AvgSpellProcsPerMinute)/100.0f) * (static_cast(100.0f + ProcRateMod) / 10.0f) * cast_time_mod * (static_cast(100.0f + ItemProcRate)/100.0f); return ProcChance; @@ -6063,7 +6063,7 @@ int32 Mob::GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spel CheckNumHitsRemaining(NumHit::MatchingSpells, tmp_buffslot); } - + return value; } @@ -6147,8 +6147,8 @@ uint16 Mob::GetSpellEffectResistChance(uint16 spell_id) bool Mob::TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier){ - /*Live 5-20-14 Patch Note: Updated all spells which use Remove Detrimental and - Cancel Beneficial spell effects to use a new method. The chances for those spells to + /*Live 5-20-14 Patch Note: Updated all spells which use Remove Detrimental and + Cancel Beneficial spell effects to use a new method. The chances for those spells to affect their targets have not changed unless otherwise noted.*/ /*This should provide a somewhat accurate conversion between pre 5/14 base values and post. @@ -6160,7 +6160,7 @@ bool Mob::TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier){ //Effect value of dispels are treated as a level modifier. //Values for scaling were obtain from live parses, best estimates. - caster_level += level_modifier - 1; + caster_level += level_modifier - 1; int dispel_chance = 32; //Baseline chance if no level difference and no modifier int level_diff = caster_level - buff_level; @@ -6201,7 +6201,7 @@ bool Mob::ImprovedTaunt(){ else { if(!TryFadeEffect(spellbonuses.ImprovedTaunt[2])) BuffFadeBySlot(spellbonuses.ImprovedTaunt[2], true); //If caster killed removed effect. - } + } } } @@ -6267,7 +6267,7 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama Range 845 - 847 : UNKNOWN Range 10000 - 11000 : Limit to Race [base2 - 10000 = Race] (*Not on live: Too useful a function to not implement) THIS IS A WORK IN PROGRESS - */ + */ if (value <= 0) return true; @@ -6276,174 +6276,174 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama switch(value) { - case 100: + case 100: if ((GetBodyType() == BT_Animal) || (GetBodyType() == BT_Humanoid)) return true; break; - case 101: + case 101: if (GetBodyType() == BT_Dragon || GetBodyType() == BT_VeliousDragon || GetBodyType() == BT_Dragon3) return true; break; - case 102: + case 102: if ((GetBodyType() == BT_Animal) || (GetBodyType() == BT_Insect)) return true; break; - case 104: + case 104: if (GetBodyType() == BT_Animal) return true; break; - case 105: + case 105: if (GetBodyType() == BT_Plant) return true; break; - case 106: + case 106: if (GetBodyType() == BT_Giant) return true; break; - case 108: + case 108: if ((GetBodyType() != BT_Animal) || (GetBodyType() != BT_Humanoid)) return true; break; - case 109: + case 109: if ((GetRace() == 520) ||(GetRace() == 79)) return true; break; - case 111: + case 111: if ((GetRace() == 527) ||(GetRace() == 11)) return true; break; - case 112: + case 112: if ((GetRace() == 456) ||(GetRace() == 28)) return true; break; - case 113: + case 113: if ((GetRace() == 456) ||(GetRace() == 48)) return true; break; - case 114: + case 114: if (GetRace() == 526) return true; break; - case 115: + case 115: if (GetRace() == 522) return true; break; - case 117: + case 117: if ((GetBodyType() == BT_Animal) || (GetBodyType() == BT_Plant)) return true; break; - case 118: + case 118: if (GetBodyType() == BT_Summoned) return true; break; - case 119: + case 119: if (IsPet() && ((GetRace() == 212) || ((GetRace() == 75) && GetTexture() == 1))) return true; break; - case 120: + case 120: if (GetBodyType() == BT_Undead) return true; break; - case 121: + case 121: if (GetBodyType() != BT_Undead) return true; break; - case 122: + case 122: if ((GetRace() == 473) || (GetRace() == 425)) return true; break; - case 123: + case 123: if (GetBodyType() == BT_Humanoid) return true; break; - case 124: + case 124: if ((GetBodyType() == BT_Undead) && (GetHPRatio() < 10)) return true; break; - case 125: + case 125: if ((GetRace() == 457 || GetRace() == 88) && (GetHPRatio() < 10)) return true; break; - case 126: + case 126: if ((GetRace() == 581 || GetRace() == 69) && (GetHPRatio() < 10)) return true; break; - case 201: + case 201: if (GetHPRatio() > 75) return true; break; - case 204: + case 204: if (GetHPRatio() < 20) return true; break; - case 216: + case 216: if (!IsEngaged()) return true; break; - case 250: + case 250: if (GetHPRatio() < 35) return true; break; - case 304: - if (IsClient() && + case 304: + if (IsClient() && ((GetClass() == WARRIOR) || (GetClass() == BARD) || (GetClass() == SHADOWKNIGHT) || (GetClass() == PALADIN) || (GetClass() == CLERIC) || (GetClass() == RANGER) || (GetClass() == SHAMAN) || (GetClass() == ROGUE) || (GetClass() == BERSERKER))) return true; break; - case 701: + case 701: if (!IsPet()) return true; break; - case 818: + case 818: if (GetBodyType() == BT_Undead) return true; break; - case 819: + case 819: if (GetBodyType() != BT_Undead) return true; break; - case 842: + case 842: if (GetBodyType() == BT_Humanoid && GetLevel() <= 84) return true; break; - case 843: + case 843: if (GetBodyType() == BT_Humanoid && GetLevel() <= 86) return true; break; - case 844: + case 844: if (GetBodyType() == BT_Humanoid && GetLevel() <= 88) return true; break; @@ -6452,7 +6452,7 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama //Limit to amount of pets if (value >= 221 && value <= 249){ int count = hate_list.GetSummonedPetCountOnHateList(this); - + for (int base2_value = 221; base2_value <= 249; ++base2_value){ if (value == base2_value){ if (count >= (base2_value - 220)){ @@ -6476,12 +6476,12 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama } //End Damage if (!IsDamage || UseCastRestriction) { - + //Heal only if HP within specified range. [Doesn't follow a set forumla for all values...] if (value >= 400 && value <= 408){ for (int base2_value = 400; base2_value <= 408; ++base2_value){ if (value == base2_value){ - + if (value == 400 && GetHPRatio() <= 25) return true; @@ -6492,11 +6492,11 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama } } } - + else if (value >= 500 && value <= 549){ for (int base2_value = 500; base2_value <= 520; ++base2_value){ if (value == base2_value){ - if (GetHPRatio() < (base2_value - 500)*5) + if (GetHPRatio() < (base2_value - 500)*5) return true; } } @@ -6507,8 +6507,8 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama return true; } } // End Heal - - + + return false; } @@ -6521,18 +6521,18 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed){ and you lose your mana. If there is LOS the bolt will lock onto your target and the damage is applied when it hits the target. -If your target moves the bolt moves with it in any direction or angle (consistent with other projectiles). -The way this is written once a bolt is cast a the distance from the initial cast to the target repeatedly - check and if target is moving recalculates at what predicted time the bolt should hit that target in client_process + check and if target is moving recalculates at what predicted time the bolt should hit that target in client_process When bolt hits its predicted point the damage is then done to target. Note: Projectile speed of 1 takes 3 seconds to go 100 distance units. Calculations are based on this constant. - Live Bolt speed: Projectile speed of X takes 5 seconds to go 300 distance units. + Live Bolt speed: Projectile speed of X takes 5 seconds to go 300 distance units. Pending Implementation: What this code can not do is prevent damage if the bolt hits a barrier after passing the initial LOS check because the target has moved while the bolt is in motion. (it is rare to actual get this to occur on live in normal game play) */ if (!spell_target) return false; - - uint8 anim = spells[spell_id].CastingAnim; + + uint8 anim = spells[spell_id].CastingAnim; int slot = -1; //Make sure there is an avialable bolt to be cast. @@ -6545,9 +6545,9 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed){ if (slot < 0) return false; - + if (CheckLosFN(spell_target)) { - + float speed_mod = speed * 0.45f; //Constant for adjusting speeds to match calculated impact time. float distance = spell_target->CalculateDistance(GetX(), GetY(), GetZ()); float hit = 60.0f + (distance / speed_mod); @@ -6570,18 +6570,18 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed){ ProjectileAnimation(spell_target,0, false, speed,0,0,0, spells[spell_id].player_1); } - //This allows limited support for server using older spell files that do not contain data for bolt graphics. + //This allows limited support for server using older spell files that do not contain data for bolt graphics. else { //Only use fire graphic for fire spells. if (spells[spell_id].resisttype == RESIST_FIRE) { - + if (IsClient()){ if (CastToClient()->GetClientVersionBit() <= 4) //Titanium needs alternate graphic. ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_Titanium)), false, speed); - else + else ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_SOF)), false, speed); } - + else ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_NPC)), false, speed); } @@ -6627,7 +6627,7 @@ void Mob::ResourceTap(int32 damage, uint16 spellid) } void Mob::TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker){ - + if (damage <= 0) return; @@ -6649,15 +6649,15 @@ void Mob::TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker){ uint16 spell_id = spells[buffs[slot].spellid].base[i]; if (damage > spells[buffs[slot].spellid].base2[i]){ - + BuffFadeBySlot(slot); if (IsValidSpell(spell_id)) { - if (IsBeneficialSpell(spell_id)) + if (IsBeneficialSpell(spell_id)) SpellFinished(spell_id, this, 10, 0, -1, spells[spell_id].ResistDiff); - - else if(attacker) + + else if(attacker) SpellFinished(spell_id, attacker, 10, 0, -1, spells[spell_id].ResistDiff); } } @@ -6734,7 +6734,7 @@ void Mob::CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster) float dist_from_min = distance - spells[spell_id].min_dist; float mod = spells[spell_id].min_dist_mod + (dist_from_min * (dm_mod_interval/dm_range)); mod *= 100.0f; - + SetSpellPowerDistanceMod(static_cast(mod)); } } diff --git a/zone/spells.cpp b/zone/spells.cpp index 98535e638..db2d61f91 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2563,7 +2563,7 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) { cd->source = action->source; cd->type = DamageTypeSpell; cd->spellid = action->spell; - cd->sequence = action->sequence; + cd->meleepush_xy = action->sequence; cd->damage = 0; if(!IsEffectInSpell(spell_id, SE_BindAffinity)) { @@ -3827,7 +3827,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r cd->source = action->source; cd->type = action->type; cd->spellid = action->spell; - cd->sequence = action->sequence; + cd->meleepush_xy = action->sequence; cd->damage = 0; if(!IsEffectInSpell(spell_id, SE_BindAffinity)) { diff --git a/zone/trap.cpp b/zone/trap.cpp index 9462e658a..1cce48e19 100644 --- a/zone/trap.cpp +++ b/zone/trap.cpp @@ -192,7 +192,7 @@ void Trap::Trigger(Mob* trigger) int dmg = zone->random.Int(effectvalue, effectvalue2); trigger->SetHP(trigger->GetHP() - dmg); a->damage = dmg; - a->sequence = zone->random.Int(0, 1234567); + a->meleepush_xy = zone->random.Int(0, 1234567); a->source = GetHiddenTrigger()!=nullptr ? GetHiddenTrigger()->GetID() : trigger->GetID(); a->spellid = 0; a->target = trigger->GetID(); From 399942f6f4423728b5050c901efab6a1f4ebfd4a Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Fri, 1 May 2015 07:02:23 -0400 Subject: [PATCH 019/129] Allow Kerran race illusions to be either gender. --- zone/mob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index 9b4c2f88e..afabeaae4 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1787,7 +1787,7 @@ bool Mob::IsPlayerRace(uint16 in_race) { uint8 Mob::GetDefaultGender(uint16 in_race, uint8 in_gender) { - if (Mob::IsPlayerRace(in_race) || in_race == 15 || in_race == 50 || in_race == 57 || in_race == 70 || in_race == 98 || in_race == 118) { + if (Mob::IsPlayerRace(in_race) || in_race == 15 || in_race == 50 || in_race == 57 || in_race == 70 || in_race == 98 || in_race == 118 || in_race == 23) { if (in_gender >= 2) { // Male default for PC Races return 0; From eda74e66e03dbaeb8d85f8264c0b5a930e053650 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Fri, 1 May 2015 19:22:06 -0400 Subject: [PATCH 020/129] Fix proc messages for undead proc against non-undead. --- zone/spells.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 020064637..891ad64f6 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1479,7 +1479,10 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce { //invalid target Log.Out(Logs::Detail, Logs::Spells, "Spell %d canceled: invalid target of body type %d (undead)", spell_id, mob_body); - Message_StringID(13,SPELL_NEED_TAR); + if(!spell_target) + Message_StringID(13,SPELL_NEED_TAR); + else + Message_StringID(13,CANNOT_AFFECT_NPC); return false; } CastAction = SingleTarget; From 6c8dfbdc4d7eca1a0d7d1bcc0e28a0009ce4f6ef Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Fri, 1 May 2015 20:40:46 -0400 Subject: [PATCH 021/129] Mobs that were blinded were being included in every use of IsFeared() which was bad. Blinded mobs can still cast spells when in melee range. The original fear code had no blind rolled into it, I added that. This was an overright. I changed the macro to use bonues and fleemode instead of looking at curfp. Testing looks good to me. --- zone/mob.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/zone/mob.h b/zone/mob.h index ada2c56dd..1d41f4098 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -803,8 +803,7 @@ public: //old fear function //void SetFeared(Mob *caster, uint32 duration, bool flee = false); float GetFearSpeed(); - bool IsFeared() { return curfp; } // This returns true if the mob is feared or fleeing due to low HP - //old fear: inline void StartFleeing() { SetFeared(GetHateTop(), FLEE_RUN_DURATION, true); } + bool IsFeared() { return (spellbonuses.IsFeared || flee_mode); } // This returns true if the mob is feared or fleeing due to low HP inline void StartFleeing() { flee_mode = true; CalculateNewFearpoint(); } void ProcessFlee(); void CheckFlee(); From 1ab3cf53e2106e56c58d8171d3f70b64fe0566c6 Mon Sep 17 00:00:00 2001 From: GPanula Date: Fri, 1 May 2015 22:53:36 -0500 Subject: [PATCH 022/129] if ServerID is null, it will crash the loginserver when it tries to add the new server to tblWorldServerRegistration table --- loginserver/database_mysql.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loginserver/database_mysql.cpp b/loginserver/database_mysql.cpp index aefe4b094..cffec5548 100644 --- a/loginserver/database_mysql.cpp +++ b/loginserver/database_mysql.cpp @@ -254,7 +254,7 @@ bool DatabaseMySQL::CreateWorldRegistration(string long_name, string short_name, length = mysql_real_escape_string(db, escaped_short_name, short_name.substr(0, 100).c_str(), short_name.substr(0, 100).length()); escaped_short_name[length+1] = 0; stringstream query(stringstream::in | stringstream::out); - query << "SELECT max(ServerID) FROM " << server.options.GetWorldRegistrationTable(); + query << "SELECT ifnull(max(ServerID),0) FROM " << server.options.GetWorldRegistrationTable(); if(mysql_query(db, query.str().c_str()) != 0) { From 59ab7071b7a1525ffc9e8057818683b5b9fcb8ff Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Sat, 2 May 2015 07:00:52 -0400 Subject: [PATCH 023/129] Beginnings of fix to SendBuffDuration. --- zone/spells.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 891ad64f6..72c659271 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -5259,8 +5259,31 @@ void Client::SendBuffDurationPacket(Buffs_Struct &buff) sbf->slot = 2; sbf->spellid = buff.spellid; sbf->slotid = 0; - sbf->effect = 255; sbf->level = buff.casterlevel > 0 ? buff.casterlevel : GetLevel(); + + if (IsEffectInSpell(buff.spellid, SE_TotalHP)) + { + // If any of the lower 6 bits are set, the GUI changes MAX_HP AGAIN. + // If its set to 0 the effect is cancelled. + // 128 seems to work (ie: change only duration). + sbf->effect = 128; + } + else if (IsEffectInSpell(buff.spellid, SE_CurrentHP)) + { + // This is mostly a problem when we try and update duration on a + // dot or a hp->mana conversion. Zero cancels the effect, any + // other value has the GUI doing that value at the same time server + // is doing theirs. This makes the two match. + int index = GetSpellEffectIndex(buff.spellid, SE_CurrentHP); + sbf->effect = abs(spells[buff.spellid].base[index]); + } + else + { + // Default to what old code did until we find a better fix for + // other spell lines. + sbf->effect=sbf->level; + } + sbf->bufffade = 0; sbf->duration = buff.ticsremaining; sbf->num_hits = buff.numhits; From 77dca484fee2ec690ec079679db1b5a12079e6a7 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Wed, 6 May 2015 15:39:36 -0400 Subject: [PATCH 024/129] The mob AT_Anim (as set in spawn2) was not correctly displaying in various situations. First, the set function for mob _appearance optimized sending a message if the new appearance was equal to the old. This cann't be done, as the 1st time the zone runs there is no client when the set function is called. If we're combining set/send, as we are, better to always do both. This fixes several of the cases. Repop also did not work, as no code was being called reliably to set appearance and update the client based on code path and various flags. This is also fixed. --- zone/entity.cpp | 2 ++ zone/mob.cpp | 10 ++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/zone/entity.cpp b/zone/entity.cpp index c6de7b33e..5646f0870 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -619,6 +619,7 @@ void EntityList::AddNPC(NPC *npc, bool SendSpawnPacket, bool dontqueue) npc->CreateSpawnPacket(app, npc); QueueClients(npc, app); npc->SendArmorAppearance(); + npc->SetAppearance(npc->GetGuardPointAnim(),false); safe_delete(app); } else { NewSpawn_Struct *ns = new NewSpawn_Struct; @@ -737,6 +738,7 @@ void EntityList::CheckSpawnQueue() auto it = npc_list.find(ns->spawn.spawnId); NPC *pnpc = it->second; pnpc->SendArmorAppearance(); + pnpc->SetAppearance(pnpc->GetGuardPointAnim(),false); safe_delete(outapp); iterator.RemoveCurrent(); } diff --git a/zone/mob.cpp b/zone/mob.cpp index afabeaae4..66fbc7809 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -2040,12 +2040,10 @@ const int32& Mob::SetMana(int32 amount) void Mob::SetAppearance(EmuAppearance app, bool iIgnoreSelf) { - if (_appearance != app) { - _appearance = app; - SendAppearancePacket(AT_Anim, GetAppearanceValue(app), true, iIgnoreSelf); - if (this->IsClient() && this->IsAIControlled()) - SendAppearancePacket(AT_Anim, ANIM_FREEZE, false, false); - } + _appearance = app; + SendAppearancePacket(AT_Anim, GetAppearanceValue(app), true, iIgnoreSelf); + if (this->IsClient() && this->IsAIControlled()) + SendAppearancePacket(AT_Anim, ANIM_FREEZE, false, false); } bool Mob::UpdateActiveLight() From 4a4a0c5e8bdcbe5cb3e49b8422e396dc21bf65d7 Mon Sep 17 00:00:00 2001 From: SecretsOTheP Date: Wed, 6 May 2015 18:50:08 -0400 Subject: [PATCH 025/129] * -Exported additional entity IDs for dropped items to perl upon EVENT_CLICK_OBJECT (clicker_id) and EVENT_PLAYER_PICKUP ($picked_up_entity_id) -Identified Size / SolidType fields in newer clients and properly exported it to EQEmu for use in UF, RoF, RoF2 via perl accessors. (Should work in LUA, no testing was done though for LUA) -Added a sanity check for size to objects. Any size over 5000.f seems to crash the newer clients' graphical engines and PEQ has some containers filled in with bogus values. -Added the ability to return a value on perl function EVENT_PLAYER_PICKUP which sends a fake dropped item ID to the client to generate the appropriate client response so the item can stay on the ground and not be 'picked up'. Should also work in LUA, didn't test LUA. -Renamed unknown008 and unknown010 to size and solidtype respectively for objects. --- common/patches/rof.cpp | 4 ++-- common/patches/rof2.cpp | 4 ++-- common/patches/uf.cpp | 4 ++-- zone/client_packet.cpp | 2 +- zone/command.cpp | 46 ++++++++++++++++++++--------------------- zone/embparser.cpp | 2 ++ zone/object.cpp | 25 +++++++++++++++++----- zone/zone.cpp | 4 ++-- 8 files changed, 54 insertions(+), 37 deletions(-) diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index b4a6e73d3..da9ed18f9 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -975,8 +975,8 @@ namespace RoF VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Same for all objects in the zone VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->heading); VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0); // Normally 0, but seen (float)255.0 as well - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 1); // Need to add emu->size to struct + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->solidtype); // Unknown + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->size != 0 && (float)emu->size < 5000.f ? (float)((float)emu->size / 100.0f) : 1.f ); // This appears to be the size field. Hackish logic because some PEQ DB items were corrupt. VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->y); VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->x); VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->z); diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index e8b98e7af..2041a3c3a 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -1046,8 +1046,8 @@ namespace RoF2 VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Same for all objects in the zone VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->heading); VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0); // Normally 0, but seen (float)255.0 as well - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 1); // Need to add emu->size to struct + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->solidtype); // Unknown + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->size != 0 && (float)emu->size < 5000.f ? (float)((float)emu->size / 100.0f) : 1.f ); // This appears to be the size field. Hackish logic because some PEQ DB items were corrupt. VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->y); VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->x); VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->z); diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index c2b3dba4a..4fbc7b6b8 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -857,8 +857,8 @@ namespace UF // field to be set to (float)255.0 to appear at all, and also the size field below to be 5, to be the correct size. I think SoD has the same // issue. VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown, observed 0 - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // This appears to be the size field. + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->solidtype); // Unknown, observed 0 + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->size != 0 && (float)emu->size < 5000.f ? (float)((float)emu->size / 100.0f) : 1.f ); // This appears to be the size field. Hackish logic because some PEQ DB items were corrupt. VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->y); VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->x); VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->z); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 12b16610e..e2c63c232 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4126,7 +4126,7 @@ void Client::Handle_OP_ClickObject(const EQApplicationPacket *app) char buf[10]; snprintf(buf, 9, "%u", click_object->drop_id); buf[9] = '\0'; - parse->EventPlayer(EVENT_CLICK_OBJECT, this, buf, 0, &args); + parse->EventPlayer(EVENT_CLICK_OBJECT, this, buf, GetID(), &args); } // Observed in RoF after OP_ClickObjectAction: diff --git a/zone/command.cpp b/zone/command.cpp index b383e09b3..3f3d4b6c1 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -8549,20 +8549,20 @@ void command_object(Client *c, const Seperator *sep) od.object_type = atoi(row[7]); icon = atoi(row[8]); - od.unknown008 = atoi(row[9]); - od.unknown010 = atoi(row[10]); + od.size = atoi(row[9]); + od.solidtype = atoi(row[10]); od.unknown020 = atoi(row[11]); switch (od.object_type) { case 0: // Static Object case staticType: // Static Object unlocked for changes - if (od.unknown008 == 0) // Unknown08 field is optional Size parameter for static objects - od.unknown008 = 100; // Static object default Size is 100% + if (od.size == 0) // Unknown08 field is optional Size parameter for static objects + od.size = 100; // Static object default Size is 100% c->Message(0, "- STATIC Object (%s): id %u, x %.1f, y %.1f, z %.1f, h %.1f, model %s, " "size %u, solidtype %u, incline %u", (od.object_type == 0) ? "locked" : "unlocked", id, od.x, od.y, od.z, - od.heading, od.object_name, od.unknown008, od.unknown010, od.unknown020); + od.heading, od.object_name, od.size, od.solidtype, od.unknown020); break; case OT_DROPPEDITEM: // Ground Spawn @@ -8620,10 +8620,10 @@ void command_object(Client *c, const Seperator *sep) switch (od.object_type) { case 0: // Static Object if ((sep->argnum - col) > 3) { - od.unknown008 = atoi(sep->arg[4 + col]); // Size specified + od.size = atoi(sep->arg[4 + col]); // Size specified if ((sep->argnum - col) > 4) { - od.unknown010 = atoi(sep->arg[5 + col]); // SolidType specified + od.solidtype = atoi(sep->arg[5 + col]); // SolidType specified if ((sep->argnum - col) > 5) od.unknown020 = atoi(sep->arg[6 + col]); // Incline specified @@ -8922,16 +8922,16 @@ void command_object(Client *c, const Seperator *sep) return; } - od.unknown008 = atoi(sep->arg[4]); + od.size = atoi(sep->arg[4]); o->SetObjectData(&od); - if (od.unknown008 == 0) // 0 == unspecified == 100% - od.unknown008 = 100; + if (od.size == 0) // 0 == unspecified == 100% + od.size = 100; c->Message(0, "Static Object %u set to %u%% size. Size will take effect when you commit to the " "database with '#object Save', after which the object will be unchangeable until " "you unlock it again with '#object Edit' and zone out and back in.", - id, od.unknown008); + id, od.size); } else if (strcmp(sep->arg[3], "solidtype") == 0) { if (od.object_type != staticType) { @@ -8946,13 +8946,13 @@ void command_object(Client *c, const Seperator *sep) return; } - od.unknown010 = atoi(sep->arg[4]); + od.solidtype = atoi(sep->arg[4]); o->SetObjectData(&od); c->Message(0, "Static Object %u set to SolidType %u. Change will take effect when you commit " "to the database with '#object Save'. Support for this property is on a " "per-model basis, mostly seen in smaller objects such as chests and tables.", - id, od.unknown010); + id, od.solidtype); } else if (strcmp(sep->arg[3], "icon") == 0) { if ((od.object_type < 2) || (od.object_type == staticType)) { @@ -9239,24 +9239,24 @@ void command_object(Client *c, const Seperator *sep) "unknown08 = %u, unknown10 = %u, unknown20 = %u " "WHERE ID = %u", zone->GetZoneID(), zone->GetInstanceVersion(), od.x, od.y, od.z, - od.heading, od.object_name, od.object_type, icon, od.unknown008, - od.unknown010, od.unknown020, id); + od.heading, od.object_name, od.object_type, icon, od.size, + od.solidtype, od.unknown020, id); else if (id == 0) query = StringFormat("INSERT INTO object " "(zoneid, version, xpos, ypos, zpos, heading, objectname, " "type, icon, unknown08, unknown10, unknown20) " "VALUES (%u, %u, %.1f, %.1f, %.1f, %.1f, '%s', %u, %u, %u, %u, %u)", zone->GetZoneID(), zone->GetInstanceVersion(), od.x, od.y, od.z, - od.heading, od.object_name, od.object_type, icon, od.unknown008, - od.unknown010, od.unknown020); + od.heading, od.object_name, od.object_type, icon, od.size, + od.solidtype, od.unknown020); else query = StringFormat("INSERT INTO object " "(id, zoneid, version, xpos, ypos, zpos, heading, objectname, " "type, icon, unknown08, unknown10, unknown20) " "VALUES (%u, %u, %u, %.1f, %.1f, %.1f, %.1f, '%s', %u, %u, %u, %u, %u)", id, zone->GetZoneID(), zone->GetInstanceVersion(), od.x, od.y, od.z, - od.heading, od.object_name, od.object_type, icon, od.unknown008, - od.unknown010, od.unknown020); + od.heading, od.object_name, od.object_type, icon, od.size, + od.solidtype, od.unknown020); results = database.QueryDatabase(query); if (!results.Success()) { @@ -9314,12 +9314,12 @@ void command_object(Client *c, const Seperator *sep) memcpy(door.dest_zone, "NONE", 5); - if ((door.size = od.unknown008) == 0) // unknown08 = optional size percentage + if ((door.size = od.size) == 0) // unknown08 = optional size percentage door.size = 100; switch ( door.opentype = - od.unknown010) // unknown10 = optional request_nonsolid (0 or 1 or experimental number) + od.solidtype) // unknown10 = optional request_nonsolid (0 or 1 or experimental number) { case 0: door.opentype = 31; @@ -9576,8 +9576,8 @@ void command_object(Client *c, const Seperator *sep) strn0cpy(od.object_name, row[4], sizeof(od.object_name)); od.object_type = atoi(row[5]); icon = atoi(row[6]); - od.unknown008 = atoi(row[7]); - od.unknown010 = atoi(row[8]); + od.size = atoi(row[7]); + od.solidtype = atoi(row[8]); od.unknown020 = atoi(row[9]); if (od.object_type == 0) diff --git a/zone/embparser.cpp b/zone/embparser.cpp index 3bfb2e90a..db017750a 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -1277,6 +1277,7 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID case EVENT_PLAYER_PICKUP:{ ExportVar(package_name.c_str(), "picked_up_id", data); + ExportVar(package_name.c_str(), "picked_up_entity_id", extradata); break; } @@ -1367,6 +1368,7 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID case EVENT_CLICK_OBJECT: { ExportVar(package_name.c_str(), "objectid", data); + ExportVar(package_name.c_str(), "clicker_id", extradata); break; } diff --git a/zone/object.cpp b/zone/object.cpp index 7dd97d355..7255cd9e9 100644 --- a/zone/object.cpp +++ b/zone/object.cpp @@ -485,7 +485,22 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object) buf[9] = '\0'; std::vector args; args.push_back(m_inst); - parse->EventPlayer(EVENT_PLAYER_PICKUP, sender, buf, 0, &args); + if(parse->EventPlayer(EVENT_PLAYER_PICKUP, sender, buf, this->GetID(), &args)) + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ClickObject, sizeof(ClickObject_Struct)); + memcpy(outapp->pBuffer, click_object, sizeof(ClickObject_Struct)); + ClickObject_Struct* co = (ClickObject_Struct*)outapp->pBuffer; + co->drop_id = 0; + entity_list.QueueClients(nullptr, outapp, false); + safe_delete(outapp); + + // No longer using a tradeskill object + sender->SetTradeskillObject(nullptr); + user = nullptr; + + return true; + } + // Transfer item to client sender->PutItemInInventory(MainCursor, *m_inst, false); @@ -809,7 +824,7 @@ void Object::SetModelName(const char* modelname) void Object::SetSize(uint16 size) { - m_data.unknown008 = size; + m_data.size = size; EQApplicationPacket* app = new EQApplicationPacket(); EQApplicationPacket* app2 = new EQApplicationPacket(); this->CreateDeSpawnPacket(app); @@ -822,7 +837,7 @@ void Object::SetSize(uint16 size) void Object::SetSolidType(uint16 solidtype) { - m_data.unknown010 = solidtype; + m_data.solidtype = solidtype; EQApplicationPacket* app = new EQApplicationPacket(); EQApplicationPacket* app2 = new EQApplicationPacket(); this->CreateDeSpawnPacket(app); @@ -835,12 +850,12 @@ void Object::SetSolidType(uint16 solidtype) uint16 Object::GetSize() { - return m_data.unknown008; + return m_data.size; } uint16 Object::GetSolidType() { - return m_data.unknown010; + return m_data.solidtype; } const char* Object::GetModelName() diff --git a/zone/zone.cpp b/zone/zone.cpp index eb22b0a6e..56181695d 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -241,8 +241,8 @@ bool Zone::LoadZoneObjects() { data.object_type = type; data.linked_list_addr[0] = 0; data.linked_list_addr[1] = 0; - data.unknown008 = (uint32)atoi(row[11]); - data.unknown010 = (uint32)atoi(row[12]); + data.size = (uint32)atoi(row[11]); + data.solidtype = (uint32)atoi(row[12]); data.unknown020 = (uint32)atoi(row[13]); data.unknown024 = (uint32)atoi(row[14]); data.unknown076 = (uint32)atoi(row[15]); From cfedf53dc081864262263cb1f577b510fa4dd804 Mon Sep 17 00:00:00 2001 From: SecretsOTheP Date: Wed, 6 May 2015 18:53:41 -0400 Subject: [PATCH 026/129] *cone of shame* forgot a file --- common/eq_packet_structs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index c203dad53..980fcc419 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -2535,8 +2535,8 @@ struct BookRequest_Struct { */ struct Object_Struct { /*00*/ uint32 linked_list_addr[2];// They are, get this, prev and next, ala linked list -/*08*/ uint16 unknown008; // -/*10*/ uint16 unknown010; // +/*08*/ uint16 size; // +/*10*/ uint16 solidtype; // /*12*/ uint32 drop_id; // Unique object id for zone /*16*/ uint16 zone_id; // Redudant, but: Zone the object appears in /*18*/ uint16 zone_instance; // From 8224a9e7762c77396ba11bc750ec0713fd377f91 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 6 May 2015 23:40:01 -0400 Subject: [PATCH 027/129] Fix bards not playing their instruments This is a rather naive implementation, we should really save the PlayerState server side so we can have newly zoned in clients after the equip happened to see the animation. But until we find all the places the PlayerState is sent, this is fine. --- zone/client_packet.cpp | 24 ++++++++++++++++++++++++ zone/client_packet.h | 2 ++ 2 files changed, 26 insertions(+) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index e2c63c232..4424d1f47 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -381,6 +381,8 @@ void MapOpcodes() ConnectedOpcodes[OP_VetClaimRequest] = &Client::Handle_OP_VetClaimRequest; ConnectedOpcodes[OP_VoiceMacroIn] = &Client::Handle_OP_VoiceMacroIn; ConnectedOpcodes[OP_WearChange] = &Client::Handle_OP_WearChange; + ConnectedOpcodes[OP_WeaponEquip2] = &Client::Handle_OP_WeaponEquip2; + ConnectedOpcodes[OP_WeaponUnequip2] = &Client::Handle_OP_WeaponUnequip2; ConnectedOpcodes[OP_WhoAllRequest] = &Client::Handle_OP_WhoAllRequest; ConnectedOpcodes[OP_WorldUnknown001] = &Client::Handle_OP_Ignore; ConnectedOpcodes[OP_XTargetAutoAddHaters] = &Client::Handle_OP_XTargetAutoAddHaters; @@ -13889,6 +13891,28 @@ void Client::Handle_OP_WearChange(const EQApplicationPacket *app) return; } +void Client::Handle_OP_WeaponEquip2(const EQApplicationPacket *app) +{ + if (app->size != 8) { + std::cout << "Wrong size: OP_WeaponEquip2, size=" << app->size << ", expected " << 8 << std::endl; + return; + } + + // We should probably save it server side, but for now this works + entity_list.QueueClients(this, app, false); +} + +void Client::Handle_OP_WeaponUnequip2(const EQApplicationPacket *app) +{ + if (app->size != 8) { + std::cout << "Wrong size: OP_WeaponUnequip2, size=" << app->size << ", expected " << 8 << std::endl; + return; + } + + // We should probably save it server side, but for now this works + entity_list.QueueClients(this, app, false); +} + void Client::Handle_OP_WhoAllRequest(const EQApplicationPacket *app) { if (app->size != sizeof(Who_All_Struct)) { diff --git a/zone/client_packet.h b/zone/client_packet.h index 51b6713b7..0f09cf574 100644 --- a/zone/client_packet.h +++ b/zone/client_packet.h @@ -288,6 +288,8 @@ void Handle_OP_VetClaimRequest(const EQApplicationPacket *app); void Handle_OP_VoiceMacroIn(const EQApplicationPacket *app); void Handle_OP_WearChange(const EQApplicationPacket *app); + void Handle_OP_WeaponEquip2(const EQApplicationPacket *app); + void Handle_OP_WeaponUnequip2(const EQApplicationPacket *app); void Handle_OP_WhoAllRequest(const EQApplicationPacket *app); void Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app); void Handle_OP_XTargetRequest(const EQApplicationPacket *app); From ebe2ea697ebb6301abf1c128e7c11ae53141b4ac Mon Sep 17 00:00:00 2001 From: hateborne Date: Thu, 7 May 2015 16:06:06 -0400 Subject: [PATCH 028/129] Exporting ConnectNodeToNode and AddNode (from Pathing) to Perl Exporting ConnectNodeToNode and AddNode from pathing to Perl so devs can more quickly build grids with Perl script(s). --- zone/embparser_api.cpp | 48 +++++++++++++++++++++++++++ zone/questmgr.cpp | 73 ++++++++++++++++++++++++++++++++++++++++++ zone/questmgr.h | 2 ++ 3 files changed, 123 insertions(+) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 1729978b0..800ed9f83 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -1917,6 +1917,52 @@ XS(XS__repopzone) XSRETURN_EMPTY; } +XS(XS__ConnectNodeToNode); +XS(XS__ConnectNodeToNode) +{ + dXSARGS; + if (items != 4) + Perl_croak(aTHX_ "Usage: ConnectNodeToNode(node1, node2, teleport, doorid)"); + + int node1 = (int)SvIV(ST(0)); + int node2 = (int)SvIV(ST(1)); + int teleport = (int)SvIV(ST(2)); + int doorid = (int)SvIV(ST(3)); + + quest_manager.ConnectNodeToNode(node1, node2, teleport, doorid); + + XSRETURN_EMPTY; +} + +XS(XS__AddNode); +XS(XS__AddNode) +{ + dXSARGS; + //void QuestManager::AddNode(float x, float y, float z, float best_z, int32 requested_id); + if (items < 3 || items > 5) + Perl_croak(aTHX_ "Usage: AddNode(x, y, z, [best_z], [requested_id])"); + + int x = (int)SvIV(ST(0)); + int y = (int)SvIV(ST(1)); + int z = (int)SvIV(ST(2)); + int best_z = 0; + int requested_id = 0; + + if (items == 4) + { + best_z = (int)SvIV(ST(3)); + } + else if (items == 5) + { + best_z = (int)SvIV(ST(3)); + requested_id = (int)SvIV(ST(4)); + } + + quest_manager.AddNode(x, y, z, best_z, requested_id); + + XSRETURN_EMPTY; +} + XS(XS__npcrace); XS(XS__npcrace) { @@ -3699,6 +3745,8 @@ EXTERN_C XS(boot_quest) newXS(strcpy(buf, "reloadzonestaticdata"), XS__reloadzonestaticdata, file); newXS(strcpy(buf, "removetitle"), XS__removetitle, file); newXS(strcpy(buf, "repopzone"), XS__repopzone, file); + newXS(strcpy(buf, "ConnectNodeToNode"), XS__ConnectNodeToNode, file); + newXS(strcpy(buf, "AddNode"), XS__AddNode, file); newXS(strcpy(buf, "resettaskactivity"), XS__resettaskactivity, file); newXS(strcpy(buf, "respawn"), XS__respawn, file); newXS(strcpy(buf, "resume"), XS__resume, file); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 47d92b68e..b9025956e 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -668,6 +668,79 @@ void QuestManager::repopzone() { } } +void QuestManager::ConnectNodeToNode(int node1, int node2, int teleport, int doorid) { + if (!node1 || !node2) + { + Log.Out(Logs::General, Logs::Quests, "QuestManager::ConnectNodeToNode called without node1 or node2. Probably syntax error in quest file."); + } + else + { + if (!teleport) + { + teleport = 0; + } + else if (teleport == 1 || teleport == -1) + { + teleport = -1; + } + + if (!doorid) + { + doorid = 0; + } + + if (!zone->pathing) + { + // if no pathing bits available, make them available. + zone->pathing = new PathManager(); + } + + if (zone->pathing) + { + zone->pathing->ConnectNodeToNode(node1, node2, teleport, doorid); + Log.Out(Logs::Moderate, Logs::Quests, "QuestManager::ConnectNodeToNode connecting node %i to node %i.", node1, node2); + } + } +} + +void QuestManager::AddNode(float x, float y, float z, float best_z, int32 requested_id) +{ + if (!x || !y || !z) + { + Log.Out(Logs::General, Logs::Quests, "QuestManager::AddNode called without x, y, z. Probably syntax error in quest file."); + } + + if (!best_z || best_z == 0) + { + if (zone->zonemap) + { + glm::vec3 loc(x, y, z); + best_z = zone->zonemap->FindBestZ(loc, nullptr); + } + else + { + best_z = z; + } + } + + if (!requested_id) + { + requested_id = 0; + } + + if (!zone->pathing) + { + // if no pathing bits available, make them available. + zone->pathing = new PathManager(); + } + + if (zone->pathing) + { + zone->pathing->AddNode(x, y, z, best_z, requested_id); + Log.Out(Logs::Moderate, Logs::Quests, "QuestManager::AddNode adding node at (%i, %i, %i).", x, y, z); + } +} + void QuestManager::settarget(const char *type, int target_id) { QuestManagerCurrentQuestVars(); if (!owner || !owner->IsNPC()) diff --git a/zone/questmgr.h b/zone/questmgr.h index d2f018b8c..b278e2c2a 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -87,6 +87,8 @@ public: void depopall(int npc_type = 0); void depopzone(bool StartSpawnTimer = true); void repopzone(); + void ConnectNodeToNode(int node1, int node2, int teleport, int doorid); + void AddNode(float x, float y, float z, float best_z, int32 requested_id); void settarget(const char *type, int target_id); void follow(int entity_id, int distance); void sfollow(); From 7bcfaf60ab89fee489bea279a513f45df531279e Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 7 May 2015 18:34:19 -0400 Subject: [PATCH 029/129] Save PlayerState server side We now send the PlayerState in the spawn struct to allow clients to see other bard animations with instrument to be played if they zone in after the bard equipped the instrument OP_WeaponEquip2 and OP_WeaponUnequip2 renamed to OP_PlayerStateAdd and OP_PlayerStateRemove Still needs work: Get AI controlled mobs sending the correct PlayerStates. (stunned, attacking, etc) --- common/emu_oplist.h | 4 +-- common/eq_packet_structs.h | 8 ++++- common/patches/rof.cpp | 2 +- common/patches/rof2.cpp | 2 +- common/patches/rof2_structs.h | 2 +- common/patches/rof_structs.h | 2 +- common/patches/sod.cpp | 2 +- common/patches/sod_structs.h | 2 +- common/patches/sof.cpp | 1 + common/patches/sof_structs.h | 3 +- common/patches/titanium.cpp | 2 +- common/patches/titanium_structs.h | 2 +- common/patches/uf.cpp | 2 +- common/patches/uf_structs.h | 2 +- utils/patches/patch_RoF.conf | 4 +-- utils/patches/patch_RoF2.conf | 4 +-- utils/patches/patch_SoD.conf | 4 +-- utils/patches/patch_SoF.conf | 4 +-- utils/patches/patch_Titanium.conf | 4 +-- utils/patches/patch_UF.conf | 4 +-- zone/client.cpp | 30 +++++++++--------- zone/client_packet.cpp | 52 +++++++++++++++++-------------- zone/client_packet.h | 4 +-- zone/lua_packet.cpp | 6 ++-- zone/mob.cpp | 26 +++++++++------- zone/mob.h | 5 +++ 26 files changed, 101 insertions(+), 82 deletions(-) diff --git a/common/emu_oplist.h b/common/emu_oplist.h index 62978655a..c51e4b600 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -364,6 +364,8 @@ N(OP_PetitionUnCheckout), N(OP_PetitionUpdate), N(OP_PickPocket), N(OP_PlayerProfile), +N(OP_PlayerStateAdd), +N(OP_PlayerStateRemove), N(OP_PlayEverquestRequest), N(OP_PlayEverquestResponse), N(OP_PlayMP3), @@ -519,8 +521,6 @@ N(OP_VetRewardsAvaliable), N(OP_VoiceMacroIn), N(OP_VoiceMacroOut), N(OP_WeaponEquip1), -N(OP_WeaponEquip2), -N(OP_WeaponUnequip2), N(OP_WearChange), N(OP_Weather), N(OP_Weblink), diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 980fcc419..ac6528860 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -273,7 +273,8 @@ struct Spawn_Struct { /*0146*/ uint8 beard; // Beard style (not totally, sure but maybe!) /*0147*/ uint8 unknown0147[4]; /*0151*/ uint8 level; // Spawn Level -/*0152*/ uint8 unknown0259[4]; // ***Placeholder +// None = 0, Open = 1, WeaponSheathed = 2, Aggressive = 4, ForcedAggressive = 8, InstrumentEquipped = 16, Stunned = 32, PrimaryWeaponEquipped = 64, SecondaryWeaponEquipped = 128 +/*0152*/ uint32 PlayerState; // Controls animation stuff /*0156*/ uint8 beardcolor; // Beard color /*0157*/ char suffix[32]; // Player's suffix (of Veeshan, etc.) /*0189*/ uint32 petOwnerId; // If this is a pet, the spawn id of owner @@ -366,6 +367,11 @@ union }; +struct PlayerState_Struct { +/*00*/ uint32 spawn_id; +/*04*/ uint32 state; +}; + /* ** New Spawn ** Length: 176 Bytes diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index da9ed18f9..bfb47492a 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -3990,7 +3990,7 @@ namespace RoF VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petOwnerId); VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown13 - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown14 - Stance 64 = normal 4 = aggressive 40 = stun/mezzed + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->PlayerState); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown15 VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown16 VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown17 diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 2041a3c3a..da9fa311a 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -4140,7 +4140,7 @@ namespace RoF2 VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petOwnerId); VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown13 - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown14 - Stance 64 = normal 4 = aggressive 40 = stun/mezzed + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->PlayerState); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown15 VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown16 VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown17 diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index 92ff47fda..4aac9e7de 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -416,7 +416,7 @@ struct Spawn_Struct /*0000*/ uint8 unknown12; /*0000*/ uint32 petOwnerId; /*0000*/ uint8 unknown13; -/*0000*/ uint32 unknown14; // Stance 64 = normal 4 = aggressive 40 = stun/mezzed +/*0000*/ uint32 PlayerState; // Stance 64 = normal 4 = aggressive 40 = stun/mezzed /*0000*/ uint32 unknown15; /*0000*/ uint32 unknown16; /*0000*/ uint32 unknown17; diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index 55c86a557..8fcfdb21d 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -410,7 +410,7 @@ struct Spawn_Struct /*0000*/ uint8 unknown12; /*0000*/ uint32 petOwnerId; /*0000*/ uint8 unknown13; -/*0000*/ uint32 unknown14; // Stance 64 = normal 4 = aggressive 40 = stun/mezzed +/*0000*/ uint32 PlayerState; // Stance 64 = normal 4 = aggressive 40 = stun/mezzed /*0000*/ uint32 unknown15; /*0000*/ uint32 unknown16; /*0000*/ uint32 unknown17; diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 8e5656b4b..a4765939d 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -2737,7 +2737,7 @@ namespace SoD VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown12 VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petOwnerId); VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown13 - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown14 - Stance 64 = normal 4 = aggressive 40 = stun/mezzed + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->PlayerState); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown15 VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown16 VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown17 diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 024814bfd..584318a71 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -286,7 +286,7 @@ struct Spawn_Struct /*0000*/ uint8 unknown12; /*0000*/ uint32 petOwnerId; /*0000*/ uint8 unknown13; -/*0000*/ uint32 unknown14; // Stance 64 = normal 4 = aggressive 40 = stun/mezzed +/*0000*/ uint32 PlayerState; // Stance 64 = normal 4 = aggressive 40 = stun/mezzed /*0000*/ uint32 unknown15; /*0000*/ uint32 unknown16; /*0000*/ uint32 unknown17; diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 1cd5ee7ed..186515b3b 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -2088,6 +2088,7 @@ namespace SoF eq->runspeed = emu->runspeed; eq->light = emu->light; eq->level = emu->level; + eq->PlayerState = emu->PlayerState; eq->lfg = emu->lfg; eq->hairstyle = emu->hairstyle; eq->haircolor = emu->haircolor; diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index 4dd0e7d80..23404e051 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -241,7 +241,8 @@ struct Spawn_Struct { /*0506*/ uint8 light; // Spawn's lightsource /*0507*/ uint8 unknown0507[4]; /*0511*/ uint8 level; // Spawn Level -/*0512*/ uint8 unknown0512[16]; +/*0512*/ uint32 PlayerState; +/*0516*/ uint8 unknown0516[12]; /*0528*/ uint8 lfg; /*0529*/ uint8 unknown0529[4]; /*0533*/ uint8 hairstyle; // Sets the style of hair diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index beb338431..e23202c87 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -1549,7 +1549,7 @@ namespace Titanium eq->beardcolor = emu->beardcolor; // eq->unknown0147[4] = emu->unknown0147[4]; eq->level = emu->level; - // eq->unknown0259[4] = emu->unknown0259[4]; + eq->PlayerState = emu->PlayerState; eq->beard = emu->beard; strcpy(eq->suffix, emu->suffix); eq->petOwnerId = emu->petOwnerId; diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index 53bcb38da..89a3634fd 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -212,7 +212,7 @@ struct Spawn_Struct { /*0146*/ uint8 beardcolor; // Beard color /*0147*/ uint8 unknown0147[4]; /*0151*/ uint8 level; // Spawn Level -/*0152*/ uint8 unknown0259[4]; // ***Placeholder +/*0152*/ uint32 PlayerState; // PlayerState controls some animation stuff /*0156*/ uint8 beard; // Beard style /*0157*/ char suffix[32]; // Player's suffix (of Veeshan, etc.) /*0189*/ uint32 petOwnerId; // If this is a pet, the spawn id of owner diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index 4fbc7b6b8..58131bd35 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -3005,7 +3005,7 @@ namespace UF VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown12 VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petOwnerId); VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown13 - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown14 - Stance 64 = normal 4 = aggressive 40 = stun/mezzed + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->PlayerState); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown15 VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown16 VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown17 diff --git a/common/patches/uf_structs.h b/common/patches/uf_structs.h index 49fb9a155..66cc58d1d 100644 --- a/common/patches/uf_structs.h +++ b/common/patches/uf_structs.h @@ -286,7 +286,7 @@ struct Spawn_Struct /*0000*/ uint8 unknown12; /*0000*/ uint32 petOwnerId; /*0000*/ uint8 unknown13; -/*0000*/ uint32 unknown14; // Stance 64 = normal 4 = aggressive 40 = stun/mezzed +/*0000*/ uint32 PlayerState; // Stance 64 = normal 4 = aggressive 40 = stun/mezzed /*0000*/ uint32 unknown15; /*0000*/ uint32 unknown16; /*0000*/ uint32 unknown17; diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index d144b040e..02d7c9bb8 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -269,8 +269,8 @@ OP_RequestDuel=0x1ea9 OP_MobRename=0x5040 OP_AugmentItem=0x1627 # Was 0x37cb OP_WeaponEquip1=0x35c3 -OP_WeaponEquip2=0x012f # Was 0x6022 -OP_WeaponUnequip2=0x1076 # Was 0x0110 +OP_PlayerStateAdd=0x012f # Was 0x6022 +OP_PlayerStateRemove=0x1076 # Was 0x0110 OP_ApplyPoison=0x1499 OP_Save=0x2e6f OP_TestBuff=0x046e # Was 0x3772 diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index 0cc0c69d0..312f725fe 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -268,8 +268,8 @@ OP_RequestDuel=0x3af1 OP_MobRename=0x2c57 OP_AugmentItem=0x661b OP_WeaponEquip1=0x34a7 -OP_WeaponEquip2=0x559a -OP_WeaponUnequip2=0x2d25 +OP_PlayerStateAdd=0x559a +OP_PlayerStateRemove=0x2d25 OP_ApplyPoison=0x31e6 OP_Save=0x4a39 OP_TestBuff=0x7cb8 diff --git a/utils/patches/patch_SoD.conf b/utils/patches/patch_SoD.conf index a5dee668c..2f01ef89b 100644 --- a/utils/patches/patch_SoD.conf +++ b/utils/patches/patch_SoD.conf @@ -266,8 +266,8 @@ OP_RequestDuel=0x79e0 # C OP_MobRename=0x0a1d # C OP_AugmentItem=0x0370 # C OP_WeaponEquip1=0x719e # C -OP_WeaponEquip2=0x7b6e # C -OP_WeaponUnequip2=0x19a8 # C +OP_PlayerStateAdd=0x7b6e # C +OP_PlayerStateRemove=0x19a8 # C OP_ApplyPoison=0x405b # C OP_Save=0x5c85 # C OP_TestBuff=0x5fc7 # C diff --git a/utils/patches/patch_SoF.conf b/utils/patches/patch_SoF.conf index 80aee8bdc..9c58b5684 100644 --- a/utils/patches/patch_SoF.conf +++ b/utils/patches/patch_SoF.conf @@ -262,8 +262,8 @@ OP_RequestDuel=0x3A2B #Xinu 02/22/09 OP_MobRename=0x6be5 #Trevius 01/16/09 OP_AugmentItem=0x172A #Trevius 03/14/09 OP_WeaponEquip1=0x7260 #Trevius 02/27/09 -OP_WeaponEquip2=0x5C2F #Trevius 02/27/09 -OP_WeaponUnequip2=0x6213 #Trevius 02/27/09 +OP_PlayerStateAdd=0x5C2F #Trevius 02/27/09 +OP_PlayerStateRemove=0x6213 #Trevius 02/27/09 OP_ApplyPoison=0x4543 #WildcardX 03/6/09 OP_Save=0x72F2 #Trevius 03/15/09 OP_TestBuff=0x07BF #/testbuff diff --git a/utils/patches/patch_Titanium.conf b/utils/patches/patch_Titanium.conf index 2660738de..99ef322a0 100644 --- a/utils/patches/patch_Titanium.conf +++ b/utils/patches/patch_Titanium.conf @@ -534,8 +534,8 @@ OP_PVPLeaderBoardDetailsRequest=0x06a2 OP_PVPLeaderBoardDetailsReply=0x246a OP_PickLockSuccess=0x40E7 OP_WeaponEquip1=0x6c5e -OP_WeaponEquip2=0x63da -OP_WeaponUnequip2=0x381d +OP_PlayerStateAdd=0x63da +OP_PlayerStateRemove=0x381d OP_VoiceMacroIn=0x2866 # Client to Server OP_VoiceMacroOut=0x2ec6 # Server to Client OP_CameraEffect=0x0937 # Correct diff --git a/utils/patches/patch_UF.conf b/utils/patches/patch_UF.conf index 09c6a8252..cbe57e399 100644 --- a/utils/patches/patch_UF.conf +++ b/utils/patches/patch_UF.conf @@ -272,8 +272,8 @@ OP_RequestDuel=0x6cfe # C OP_MobRename=0x0507 # C OP_AugmentItem=0x7c87 # C OP_WeaponEquip1=0x4572 # C -OP_WeaponEquip2=0x399b # C -OP_WeaponUnequip2=0x416b # C +OP_PlayerStateAdd=0x399b # C +OP_PlayerStateRemove=0x416b # C OP_ApplyPoison=0x5cd3 # C OP_Save=0x6618 # C OP_TestBuff=0x3415 # C diff --git a/zone/client.cpp b/zone/client.cpp index cd96ed29b..c31de64fa 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -2540,12 +2540,12 @@ void Client::LogMerchant(Client* player, Mob* merchant, uint32 quantity, uint32 bool Client::BindWound(Mob* bindmob, bool start, bool fail){ EQApplicationPacket* outapp = 0; - if(!fail) + if(!fail) { outapp = new EQApplicationPacket(OP_Bind_Wound, sizeof(BindWound_Struct)); BindWound_Struct* bind_out = (BindWound_Struct*) outapp->pBuffer; // Start bind - if(!bindwound_timer.Enabled()) + if(!bindwound_timer.Enabled()) { //make sure we actually have a bandage... and consume it. int16 bslot = m_inv.HasItemByUse(ItemTypeBandage, 1, invWhereWorn|invWherePersonal); @@ -2592,9 +2592,9 @@ bool Client::BindWound(Mob* bindmob, bool start, bool fail){ ; // Binding self } } - } + } else if (bindwound_timer.Check()) // Did the timer finish? - { + { // finish bind // disable complete timer bindwound_timer.Disable(); @@ -5709,8 +5709,8 @@ void Client::ProcessInspectRequest(Client* requestee, Client* requester) { else if (inst && inst->GetOrnamentationIcon()) { insr->itemicons[L] = inst->GetOrnamentationIcon(); - } - else + } + else { insr->itemicons[L] = item->Icon; } @@ -7612,7 +7612,7 @@ void Client::SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, ui // Find out starting faction for this faction // It needs to be used to adj max and min personal // The range is still the same, 1200-3000(4200), but adjusted for base - database.GetFactionData(&fm, GetClass(), GetRace(), GetDeity(), + database.GetFactionData(&fm, GetClass(), GetRace(), GetDeity(), faction_id[i]); // Adjust the amount you can go up or down so the resulting range @@ -7653,7 +7653,7 @@ void Client::SetFactionLevel2(uint32 char_id, int32 faction_id, uint8 char_class // Find out starting faction for this faction // It needs to be used to adj max and min personal // The range is still the same, 1200-3000(4200), but adjusted for base - database.GetFactionData(&fm, GetClass(), GetRace(), GetDeity(), + database.GetFactionData(&fm, GetClass(), GetRace(), GetDeity(), faction_id); // Adjust the amount you can go up or down so the resulting range @@ -7839,14 +7839,14 @@ void Client::SendFactionMessage(int32 tmpvalue, int32 faction_id, int32 faction_ char name[50]; int32 faction_value; - // If we're dropping from MAX or raising from MIN or repairing, + // If we're dropping from MAX or raising from MIN or repairing, // we should base the message on the new updated value so we don't show // a min MAX message // // If we're changing any other place, we use the value before the // hit. For example, if we go from 1199 to 1200 which is the MAX // we still want to say faction got better this time around. - + if ( (faction_before_hit >= this_faction_max) || (faction_before_hit <= this_faction_min)) faction_value = totalvalue; @@ -8382,10 +8382,10 @@ std::string Client::TextLink::GenerateLink() m_Link.clear(); m_LinkBody.clear(); m_LinkText.clear(); - + generate_body(); generate_text(); - + if ((m_LinkBody.length() == EmuConstants::TEXT_LINK_BODY_LENGTH) && (m_LinkText.length() > 0)) { m_Link.push_back(0x12); m_Link.append(m_LinkBody); @@ -8424,7 +8424,7 @@ void Client::TextLink::generate_body() { /* Current server mask: EQClientRoF2 - + RoF2: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%02X" "%05X" "%08X" (56) RoF: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X" (55) SoF: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X" (50) @@ -8432,7 +8432,7 @@ void Client::TextLink::generate_body() */ memset(&m_LinkBodyStruct, 0, sizeof(TextLinkBody_Struct)); - + const Item_Struct* item_data = nullptr; switch (m_LinkType) { @@ -8479,7 +8479,7 @@ void Client::TextLink::generate_body() default: break; } - + if (m_ProxyItemID != NOT_USED) { m_LinkBodyStruct.item_id = m_ProxyItemID; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 4424d1f47..690185744 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -305,6 +305,8 @@ void MapOpcodes() ConnectedOpcodes[OP_PetitionRefresh] = &Client::Handle_OP_PetitionRefresh; ConnectedOpcodes[OP_PetitionResolve] = &Client::Handle_OP_PetitionResolve; ConnectedOpcodes[OP_PetitionUnCheckout] = &Client::Handle_OP_PetitionUnCheckout; + ConnectedOpcodes[OP_PlayerStateAdd] = &Client::Handle_OP_PlayerStateAdd; + ConnectedOpcodes[OP_PlayerStateRemove] = &Client::Handle_OP_PlayerStateRemove; ConnectedOpcodes[OP_PickPocket] = &Client::Handle_OP_PickPocket; ConnectedOpcodes[OP_PopupResponse] = &Client::Handle_OP_PopupResponse; ConnectedOpcodes[OP_PotionBelt] = &Client::Handle_OP_PotionBelt; @@ -381,8 +383,6 @@ void MapOpcodes() ConnectedOpcodes[OP_VetClaimRequest] = &Client::Handle_OP_VetClaimRequest; ConnectedOpcodes[OP_VoiceMacroIn] = &Client::Handle_OP_VoiceMacroIn; ConnectedOpcodes[OP_WearChange] = &Client::Handle_OP_WearChange; - ConnectedOpcodes[OP_WeaponEquip2] = &Client::Handle_OP_WeaponEquip2; - ConnectedOpcodes[OP_WeaponUnequip2] = &Client::Handle_OP_WeaponUnequip2; ConnectedOpcodes[OP_WhoAllRequest] = &Client::Handle_OP_WhoAllRequest; ConnectedOpcodes[OP_WorldUnknown001] = &Client::Handle_OP_Ignore; ConnectedOpcodes[OP_XTargetAutoAddHaters] = &Client::Handle_OP_XTargetAutoAddHaters; @@ -10325,6 +10325,32 @@ void Client::Handle_OP_PetitionUnCheckout(const EQApplicationPacket *app) return; } +void Client::Handle_OP_PlayerStateAdd(const EQApplicationPacket *app) +{ + if (app->size != sizeof(PlayerState_Struct)) { + std::cout << "Wrong size: OP_PlayerStateAdd, size=" << app->size << ", expected " << sizeof(PlayerState_Struct) << std::endl; + return; + } + + PlayerState_Struct *ps = (PlayerState_Struct *)app->pBuffer; + AddPlayerState(ps->state); + + entity_list.QueueClients(this, app, false); +} + +void Client::Handle_OP_PlayerStateRemove(const EQApplicationPacket *app) +{ + if (app->size != sizeof(PlayerState_Struct)) { + std::cout << "Wrong size: OP_PlayerStateRemove, size=" << app->size << ", expected " << sizeof(PlayerState_Struct) << std::endl; + return; + } + PlayerState_Struct *ps = (PlayerState_Struct *)app->pBuffer; + RemovePlayerState(ps->state); + + // We should probably save it server side, but for now this works + entity_list.QueueClients(this, app, false); +} + void Client::Handle_OP_PickPocket(const EQApplicationPacket *app) { if (app->size != sizeof(PickPocket_Struct)) @@ -13891,28 +13917,6 @@ void Client::Handle_OP_WearChange(const EQApplicationPacket *app) return; } -void Client::Handle_OP_WeaponEquip2(const EQApplicationPacket *app) -{ - if (app->size != 8) { - std::cout << "Wrong size: OP_WeaponEquip2, size=" << app->size << ", expected " << 8 << std::endl; - return; - } - - // We should probably save it server side, but for now this works - entity_list.QueueClients(this, app, false); -} - -void Client::Handle_OP_WeaponUnequip2(const EQApplicationPacket *app) -{ - if (app->size != 8) { - std::cout << "Wrong size: OP_WeaponUnequip2, size=" << app->size << ", expected " << 8 << std::endl; - return; - } - - // We should probably save it server side, but for now this works - entity_list.QueueClients(this, app, false); -} - void Client::Handle_OP_WhoAllRequest(const EQApplicationPacket *app) { if (app->size != sizeof(Who_All_Struct)) { diff --git a/zone/client_packet.h b/zone/client_packet.h index 0f09cf574..1a9591f4c 100644 --- a/zone/client_packet.h +++ b/zone/client_packet.h @@ -218,6 +218,8 @@ void Handle_OP_PetitionRefresh(const EQApplicationPacket *app); void Handle_OP_PetitionResolve(const EQApplicationPacket *app); void Handle_OP_PetitionUnCheckout(const EQApplicationPacket *app); + void Handle_OP_PlayerStateAdd(const EQApplicationPacket *app); + void Handle_OP_PlayerStateRemove(const EQApplicationPacket *app); void Handle_OP_PickPocket(const EQApplicationPacket *app); void Handle_OP_PopupResponse(const EQApplicationPacket *app); void Handle_OP_PotionBelt(const EQApplicationPacket *app); @@ -288,8 +290,6 @@ void Handle_OP_VetClaimRequest(const EQApplicationPacket *app); void Handle_OP_VoiceMacroIn(const EQApplicationPacket *app); void Handle_OP_WearChange(const EQApplicationPacket *app); - void Handle_OP_WeaponEquip2(const EQApplicationPacket *app); - void Handle_OP_WeaponUnequip2(const EQApplicationPacket *app); void Handle_OP_WhoAllRequest(const EQApplicationPacket *app); void Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app); void Handle_OP_XTargetRequest(const EQApplicationPacket *app); diff --git a/zone/lua_packet.cpp b/zone/lua_packet.cpp index e16a85c6d..1eef7220d 100644 --- a/zone/lua_packet.cpp +++ b/zone/lua_packet.cpp @@ -18,7 +18,7 @@ Lua_Packet::Lua_Packet(int opcode, int size, bool raw) { if(raw) { SetLuaPtrData(new EQApplicationPacket(OP_Unknown, size)); owned_ = true; - + EQApplicationPacket *self = reinterpret_cast(d_); self->SetOpcodeBypass(opcode); } else { @@ -692,8 +692,8 @@ luabind::scope lua_register_packet_opcodes() { luabind::value("VetClaimRequest", static_cast(OP_VetClaimRequest)), luabind::value("VetClaimReply", static_cast(OP_VetClaimReply)), luabind::value("WeaponEquip1", static_cast(OP_WeaponEquip1)), - luabind::value("WeaponEquip2", static_cast(OP_WeaponEquip2)), - luabind::value("WeaponUnequip2", static_cast(OP_WeaponUnequip2)), + luabind::value("PlayerStateAdd", static_cast(OP_PlayerStateAdd)), + luabind::value("PlayerStateRemove", static_cast(OP_PlayerStateRemove)), luabind::value("WorldLogout", static_cast(OP_WorldLogout)), luabind::value("SessionReady", static_cast(OP_SessionReady)), luabind::value("Login", static_cast(OP_Login)), diff --git a/zone/mob.cpp b/zone/mob.cpp index 0659110cd..1c4c2a01e 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -148,6 +148,7 @@ Mob::Mob(const char* in_name, size = in_size; base_size = size; runspeed = in_runspeed; + PlayerState = 0; // sanity check @@ -160,7 +161,7 @@ Mob::Mob(const char* in_name, m_Light.Level.Spell = m_Light.Type.Spell = 0; m_Light.Type.Active = m_Light.Type.Innate; m_Light.Level.Active = m_Light.Level.Innate; - + texture = in_texture; helmtexture = in_helmtexture; armtexture = in_armtexture; @@ -739,7 +740,7 @@ void Mob::CreateSpawnPacket(EQApplicationPacket* app, Mob* ForWho) { NewSpawn_Struct* ns = (NewSpawn_Struct*)app->pBuffer; FillSpawnStruct(ns, ForWho); - if(strlen(ns->spawn.lastName) == 0) + if(strlen(ns->spawn.lastName) == 0) { switch(ns->spawn.class_) { @@ -915,6 +916,7 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) ns->spawn.class_ = class_; ns->spawn.gender = gender; ns->spawn.level = level; + ns->spawn.PlayerState = PlayerState; ns->spawn.deity = deity; ns->spawn.animation = 0; ns->spawn.findable = findable?1:0; @@ -2566,10 +2568,10 @@ void Mob::SendArmorAppearance(Client *one_client) if (!IsClient()) { const Item_Struct *item; - for (int i=0; i< 7 ; ++i) + for (int i=0; i< 7 ; ++i) { item=database.GetItem(GetEquipment(i)); - if (item != 0) + if (item != 0) { SendWearChange(i,one_client); } @@ -2597,7 +2599,7 @@ void Mob::SendWearChange(uint8 material_slot, Client *one_client) else { one_client->QueuePacket(outapp, false, Client::CLIENT_CONNECTED); - } + } safe_delete(outapp); } @@ -2724,7 +2726,7 @@ int32 Mob::GetHerosForgeModel(uint8 material_slot) const const Item_Struct *item; item = database.GetItem(GetEquipment(material_slot)); int16 invslot = Inventory::CalcSlotFromMaterial(material_slot); - + if (item != 0 && invslot != INVALID_INDEX) { if (IsClient()) @@ -2982,10 +2984,10 @@ uint32 Mob::GetLevelHP(uint8 tlevel) } int32 Mob::GetActSpellCasttime(uint16 spell_id, int32 casttime) { - + int32 cast_reducer = 0; cast_reducer += GetFocusEffect(focusSpellHaste, spell_id); - + if (level >= 60 && casttime > 1000) { casttime = casttime / 2; @@ -3599,7 +3601,7 @@ int16 Mob::GetSkillDmgTaken(const SkillUseTypes skill_used) // All skill dmg mod + Skill specific skilldmg_mod += itembonuses.SkillDmgTaken[HIGHEST_SKILL+1] + spellbonuses.SkillDmgTaken[HIGHEST_SKILL+1] + itembonuses.SkillDmgTaken[skill_used] + spellbonuses.SkillDmgTaken[skill_used]; - + skilldmg_mod += SkillDmgTaken_Mod[skill_used] + SkillDmgTaken_Mod[HIGHEST_SKILL+1]; @@ -5317,7 +5319,7 @@ int32 Mob::GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot) if (slot < 4){ if (id == "components") { return spells[spell_id].components[slot];} - else if (id == "component_counts") { return spells[spell_id].component_counts[slot];} + else if (id == "component_counts") { return spells[spell_id].component_counts[slot];} else if (id == "NoexpendReagent") {return spells[spell_id].NoexpendReagent[slot];} } @@ -5395,7 +5397,7 @@ int32 Mob::GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot) else if (id == "max_dist") {return static_cast(spells[spell_id].max_dist); } else if (id == "min_range") {return static_cast(spells[spell_id].min_range); } else if (id == "DamageShieldType") {return spells[spell_id].DamageShieldType; } - + return stat; } @@ -5415,7 +5417,7 @@ bool Mob::CanClassEquipItem(uint32 item_id) int bitmask = 1; bitmask = bitmask << (GetClass() - 1); - + if(!(itm->Classes & bitmask)) return false; else diff --git a/zone/mob.h b/zone/mob.h index 1d41f4098..fc5e1d8e0 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1026,6 +1026,11 @@ protected: uint32 follow_dist; bool no_target_hotkey; + uint32 PlayerState; + uint32 GetPlayerState() { return PlayerState; } + void AddPlayerState(uint32 new_state) { PlayerState |= new_state; } + void RemovePlayerState(uint32 old_state) { PlayerState &= ~old_state; } + uint8 gender; uint16 race; uint8 base_gender; From 103d808925369785ef4567de6e5157e63e775c67 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 7 May 2015 22:15:43 -0400 Subject: [PATCH 030/129] Whoops, we do want to ignore the sender --- zone/client_packet.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 690185744..317530110 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -10335,7 +10335,7 @@ void Client::Handle_OP_PlayerStateAdd(const EQApplicationPacket *app) PlayerState_Struct *ps = (PlayerState_Struct *)app->pBuffer; AddPlayerState(ps->state); - entity_list.QueueClients(this, app, false); + entity_list.QueueClients(this, app, true); } void Client::Handle_OP_PlayerStateRemove(const EQApplicationPacket *app) @@ -10348,7 +10348,7 @@ void Client::Handle_OP_PlayerStateRemove(const EQApplicationPacket *app) RemovePlayerState(ps->state); // We should probably save it server side, but for now this works - entity_list.QueueClients(this, app, false); + entity_list.QueueClients(this, app, true); } void Client::Handle_OP_PickPocket(const EQApplicationPacket *app) From bf4ff036413c3e3ff49e09109c0aace4ad187c02 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 8 May 2015 00:59:38 -0400 Subject: [PATCH 031/129] Use PlayerState to generate stun particles --- zone/bot.cpp | 11 ++++------- zone/client_packet.cpp | 1 - zone/client_process.cpp | 12 +++++------- zone/common.h | 14 +++++++++++++- zone/merc.cpp | 5 +---- zone/mob.cpp | 31 +++++++++++++++++++++++++++++-- zone/mob.h | 10 ++++++---- zone/npc.cpp | 15 +++++++-------- zone/spells.cpp | 5 +++-- 9 files changed, 68 insertions(+), 36 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index ea48727ec..ba7624158 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2858,10 +2858,7 @@ void Bot::SaveTimers() { bool Bot::Process() { if(IsStunned() && stunned_timer.Check()) - { - this->stunned = false; - this->stunned_timer.Disable(); - } + Mob::UnStun(); if(!GetBotOwner()) return false; @@ -11719,11 +11716,11 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { const char* equipped[EmuConstants::EQUIPMENT_SIZE] = {"Charm", "Left Ear", "Head", "Face", "Right Ear", "Neck", "Shoulders", "Arms", "Back", "Left Wrist", "Right Wrist", "Range", "Hands", "Primary Hand", "Secondary Hand", "Left Finger", "Right Finger", "Chest", "Legs", "Feet", "Waist", "Ammo" }; - + const ItemInst* inst = nullptr; const Item_Struct* item = nullptr; bool is2Hweapon = false; - + std::string item_link; Client::TextLink linker; linker.SetLinkType(linker.linkItemInst); @@ -11753,7 +11750,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { // I could not find a difference between the criteria positive code and the criteria negative code.. // ..so, I deleted the check (old criteria: i = { MainCharm, MainRange, MainPrimary, MainSecondary, MainAmmo }) - + linker.SetItemInst(inst); item_link = linker.GenerateLink(); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 317530110..73d028e68 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -10347,7 +10347,6 @@ void Client::Handle_OP_PlayerStateRemove(const EQApplicationPacket *app) PlayerState_Struct *ps = (PlayerState_Struct *)app->pBuffer; RemovePlayerState(ps->state); - // We should probably save it server side, but for now this works entity_list.QueueClients(this, app, true); } diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 40e8e6d1b..42681340b 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -197,10 +197,8 @@ bool Client::Process() { instalog = true; } - if (IsStunned() && stunned_timer.Check()) { - this->stunned = false; - this->stunned_timer.Disable(); - } + if (IsStunned() && stunned_timer.Check()) + Mob::UnStun(); if(!m_CheatDetectMoved) { @@ -262,7 +260,7 @@ bool Client::Process() { } if(light_update_timer.Check()) { - + UpdateEquipmentLight(); if(UpdateActiveLight()) { SendAppearancePacket(AT_Light, GetActiveLightType()); @@ -562,7 +560,7 @@ bool Client::Process() { } ProjectileAttack(); - + if(spellbonuses.GravityEffect == 1) { if(gravity_timer.Check()) DoGravityEffect(); @@ -793,7 +791,7 @@ void Client::OnDisconnect(bool hard_disconnect) { Mob *Other = trade->With(); if(Other) { - Log.Out(Logs::Detail, Logs::Trading, "Client disconnected during a trade. Returning their items."); + Log.Out(Logs::Detail, Logs::Trading, "Client disconnected during a trade. Returning their items."); FinishTrade(this); if(Other->IsClient()) diff --git a/zone/common.h b/zone/common.h index 56ab6f819..a7d9e2d99 100644 --- a/zone/common.h +++ b/zone/common.h @@ -173,6 +173,18 @@ enum class NumHit { // Numhits type OffensiveSpellProcs = 11 // Offensive buff procs }; +enum class PlayerState : uint32 { + None = 0, + Open = 1, + WeaponSheathed = 2, + Aggressive = 4, + ForcedAggressive = 8, + InstrumentEquipped = 16, + Stunned = 32, + PrimaryWeaponEquipped = 64, + SecondaryWeaponEquipped = 128 +}; + //this is our internal representation of the BUFF struct, can put whatever we want in it struct Buffs_Struct { uint16 spellid; @@ -438,7 +450,7 @@ struct StatBonuses { int32 ShieldEquipHateMod; // Hate mod when shield equiped. int32 ShieldEquipDmgMod[2]; // Damage mod when shield equiped. 0 = damage modifier 1 = Unknown bool TriggerOnValueAmount; // Triggers off various different conditions, bool to check if client has effect. - int8 StunBashChance; // chance to stun with bash. + int8 StunBashChance; // chance to stun with bash. int8 IncreaseChanceMemwipe; // increases chance to memory wipe int8 CriticalMend; // chance critical monk mend int32 ImprovedReclaimEnergy; // Modifies amount of mana returned from reclaim energy diff --git a/zone/merc.cpp b/zone/merc.cpp index 4609487cf..73f6b1fbf 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -1236,10 +1236,7 @@ void Merc::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { bool Merc::Process() { if(IsStunned() && stunned_timer.Check()) - { - this->stunned = false; - this->stunned_timer.Disable(); - } + Mob::UnStun(); if (GetDepop()) { diff --git a/zone/mob.cpp b/zone/mob.cpp index 1c4c2a01e..b4c3370cb 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -148,7 +148,7 @@ Mob::Mob(const char* in_name, size = in_size; base_size = size; runspeed = in_runspeed; - PlayerState = 0; + m_PlayerState = 0; // sanity check @@ -916,7 +916,7 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) ns->spawn.class_ = class_; ns->spawn.gender = gender; ns->spawn.level = level; - ns->spawn.PlayerState = PlayerState; + ns->spawn.PlayerState = m_PlayerState; ns->spawn.deity = deity; ns->spawn.animation = 0; ns->spawn.findable = findable?1:0; @@ -5423,3 +5423,30 @@ bool Mob::CanClassEquipItem(uint32 item_id) else return true; } + +void Mob::SendAddPlayerState(PlayerState new_state) +{ + auto app = new EQApplicationPacket(OP_PlayerStateAdd, sizeof(PlayerState_Struct)); + auto ps = (PlayerState_Struct *)app->pBuffer; + + ps->spawn_id = GetID(); + ps->state = static_cast(new_state); + + AddPlayerState(ps->state); + entity_list.QueueClients(nullptr, app); + safe_delete(app); +} + +void Mob::SendRemovePlayerState(PlayerState old_state) +{ + auto app = new EQApplicationPacket(OP_PlayerStateRemove, sizeof(PlayerState_Struct)); + auto ps = (PlayerState_Struct *)app->pBuffer; + + ps->spawn_id = GetID(); + ps->state = static_cast(old_state); + + RemovePlayerState(ps->state); + entity_list.QueueClients(nullptr, app); + safe_delete(app); +} + diff --git a/zone/mob.h b/zone/mob.h index fc5e1d8e0..3b0702e60 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1026,10 +1026,12 @@ protected: uint32 follow_dist; bool no_target_hotkey; - uint32 PlayerState; - uint32 GetPlayerState() { return PlayerState; } - void AddPlayerState(uint32 new_state) { PlayerState |= new_state; } - void RemovePlayerState(uint32 old_state) { PlayerState &= ~old_state; } + uint32 m_PlayerState; + uint32 GetPlayerState() { return m_PlayerState; } + void AddPlayerState(uint32 new_state) { m_PlayerState |= new_state; } + void RemovePlayerState(uint32 old_state) { m_PlayerState &= ~old_state; } + void SendAddPlayerState(PlayerState new_state); + void SendRemovePlayerState(PlayerState old_state); uint8 gender; uint16 race; diff --git a/zone/npc.cpp b/zone/npc.cpp index 4e69657cd..0ece7ab6c 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -522,7 +522,7 @@ void NPC::QueryLoot(Client* to) linker.SetItemData(item); auto item_link = linker.GenerateLink(); - + to->Message(0, "%s, ID: %u, Level: (min: %u, max: %u)", item_link.c_str(), item->ID, (*cur)->min_level, (*cur)->max_level); } @@ -569,8 +569,7 @@ bool NPC::Process() { if (IsStunned() && stunned_timer.Check()) { - this->stunned = false; - this->stunned_timer.Disable(); + Mob::UnStun(); this->spun_timer.Disable(); } @@ -724,7 +723,7 @@ void NPC::UpdateEquipmentLight() { m_Light.Type.Equipment = 0; m_Light.Level.Equipment = 0; - + for (int index = MAIN_BEGIN; index < EmuConstants::EQUIPMENT_SIZE; ++index) { if (index == MainAmmo) { continue; } @@ -1933,7 +1932,7 @@ void NPC::ModifyNPCStat(const char *identifier, const char *newValue) else if(id == "special_attacks") { NPCSpecialAttacks(val.c_str(), 0, 1); return; } else if(id == "special_abilities") { ProcessSpecialAbilities(val.c_str()); return; } else if(id == "attack_speed") { attack_speed = (float)atof(val.c_str()); CalcBonuses(); return; } - else if(id == "attack_delay") { attack_delay = atoi(val.c_str()); CalcBonuses(); return; } + else if(id == "attack_delay") { attack_delay = atoi(val.c_str()); CalcBonuses(); return; } else if(id == "atk") { ATK = atoi(val.c_str()); return; } else if(id == "accuracy") { accuracy_rating = atoi(val.c_str()); return; } else if(id == "avoidance") { avoidance_rating = atoi(val.c_str()); return; } @@ -2418,7 +2417,7 @@ void NPC::DoQuestPause(Mob *other) { } -void NPC::ChangeLastName(const char* in_lastname) +void NPC::ChangeLastName(const char* in_lastname) { EQApplicationPacket* outapp = new EQApplicationPacket(OP_GMLastName, sizeof(GMLastName_Struct)); @@ -2468,9 +2467,9 @@ void NPC::DepopSwarmPets() } if (IsPet() && GetPetType() == petTargetLock && GetPetTargetLockID()){ - + Mob *targMob = entity_list.GetMob(GetPetTargetLockID()); - + if(!targMob || (targMob && targMob->IsCorpse())){ Kill(); return; diff --git a/zone/spells.cpp b/zone/spells.cpp index 472a0ac9f..d93050746 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4708,7 +4708,7 @@ void Mob::Stun(int duration) { stunned = true; stunned_timer.Start(duration); - SendStunAppearance(); + SendAddPlayerState(PlayerState::Stunned); } } @@ -4716,6 +4716,7 @@ void Mob::UnStun() { if(stunned && stunned_timer.Enabled()) { stunned = false; stunned_timer.Disable(); + SendRemovePlayerState(PlayerState::Stunned); } } @@ -5259,7 +5260,7 @@ void Client::SendBuffDurationPacket(Buffs_Struct &buff) if (IsEffectInSpell(buff.spellid, SE_TotalHP)) { // If any of the lower 6 bits are set, the GUI changes MAX_HP AGAIN. - // If its set to 0 the effect is cancelled. + // If its set to 0 the effect is cancelled. // 128 seems to work (ie: change only duration). sbf->effect = 128; } From f68952c168d6610f5de8d5984ef38860d191d47b Mon Sep 17 00:00:00 2001 From: JJ Date: Fri, 8 May 2015 22:23:50 -0400 Subject: [PATCH 032/129] Update to some spell duration formulas (Shendare). Filename fixes. --- ...l => 2015_02_05_UseAdditiveFocusFromWornSlot.sql} | 0 ...Type.sql => 2015_02_06_AdditiveBonusWornType.sql} | 0 ...5_4_30_MeleePush.sql => 2015_04_30_MeleePush.sql} | 0 zone/spells.cpp | 12 +++++++----- 4 files changed, 7 insertions(+), 5 deletions(-) rename utils/sql/git/optional/{2015_2_5_UseAdditiveFocusFromWornSlot.sql => 2015_02_05_UseAdditiveFocusFromWornSlot.sql} (100%) rename utils/sql/git/optional/{2015_2_6_AdditiveBonusWornType.sql => 2015_02_06_AdditiveBonusWornType.sql} (100%) rename utils/sql/git/optional/{2015_4_30_MeleePush.sql => 2015_04_30_MeleePush.sql} (100%) diff --git a/utils/sql/git/optional/2015_2_5_UseAdditiveFocusFromWornSlot.sql b/utils/sql/git/optional/2015_02_05_UseAdditiveFocusFromWornSlot.sql similarity index 100% rename from utils/sql/git/optional/2015_2_5_UseAdditiveFocusFromWornSlot.sql rename to utils/sql/git/optional/2015_02_05_UseAdditiveFocusFromWornSlot.sql diff --git a/utils/sql/git/optional/2015_2_6_AdditiveBonusWornType.sql b/utils/sql/git/optional/2015_02_06_AdditiveBonusWornType.sql similarity index 100% rename from utils/sql/git/optional/2015_2_6_AdditiveBonusWornType.sql rename to utils/sql/git/optional/2015_02_06_AdditiveBonusWornType.sql diff --git a/utils/sql/git/optional/2015_4_30_MeleePush.sql b/utils/sql/git/optional/2015_04_30_MeleePush.sql similarity index 100% rename from utils/sql/git/optional/2015_4_30_MeleePush.sql rename to utils/sql/git/optional/2015_04_30_MeleePush.sql diff --git a/zone/spells.cpp b/zone/spells.cpp index d93050746..77a9f691d 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2642,7 +2642,7 @@ int CalcBuffDuration_formula(int level, int formula, int duration) return i < duration ? (i < 1 ? 1 : i) : duration; case 2: - i = (int)ceil(duration / 5.0f * 3); + i = (int)ceil(level / 5.0f * 3); return i < duration ? (i < 1 ? 1 : i) : duration; case 3: @@ -2681,13 +2681,15 @@ int CalcBuffDuration_formula(int level, int formula, int duration) return std::min((level + 3) * 30, duration); case 12: - return duration; - + case 13: + case 14: case 15: // Don't know what the real formula for this should be. Used by Skinspikes potion. return duration; - case 50: // lucy says this is unlimited? - return 72000; // 5 days + case 50: // Permanent. Cancelled by casting/combat for perm invis, non-lev zones for lev, curing poison/curse counters, etc. + return 72000; // 5 days until better method to make permanent + + //case 51: // Permanent. Cancelled when out of range of aura. Placeholder until appropriate duration identified. case 3600: return duration ? duration : 3600; From c360aa9b0f99e34da33386ce474f3cd2f5fa483d Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 8 May 2015 22:42:45 -0400 Subject: [PATCH 033/129] Make use of Aggressive/Weapon PlayerStates I HAVE NO IDEA WHAT THIS DOES, BUT LIVE DOES IT Something to do with the animation system, all I know --- zone/bot.cpp | 6 ++++++ zone/loottables.cpp | 8 ++++++-- zone/merc.cpp | 6 ++++++ zone/mob_ai.cpp | 4 ++++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index ba7624158..48a4dfe55 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3459,6 +3459,9 @@ void Bot::AI_Process() { return; } + if (!(m_PlayerState & static_cast(PlayerState::Aggressive))) + SendAddPlayerState(PlayerState::Aggressive); + bool atCombatRange = false; float meleeDistance = GetMaxMeleeRangeToTarget(GetTarget()); @@ -3717,6 +3720,9 @@ void Bot::AI_Process() { // Not engaged in combat SetTarget(0); + if (m_PlayerState & static_cast(PlayerState::Aggressive)) + SendRemovePlayerState(PlayerState::Aggressive); + if(!IsMoving() && AIthink_timer->Check() && !spellend_timer.Enabled()) { if(GetBotStance() != BotStancePassive) { if(!AI_IdleCastCheck() && !IsCasting()) diff --git a/zone/loottables.cpp b/zone/loottables.cpp index 3170729d0..e3014b12e 100644 --- a/zone/loottables.cpp +++ b/zone/loottables.cpp @@ -58,7 +58,7 @@ void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* ite if(max_cash > 0 && lts->avgcoin > 0 && EQEmu::ValueWithin(lts->avgcoin, min_cash, max_cash)) { float upper_chance = (float)(lts->avgcoin - min_cash) / (float)(max_cash - min_cash); float avg_cash_roll = (float)zone->random.Real(0.0, 1.0); - + if(avg_cash_roll < upper_chance) { cash = zone->random.Int(lts->avgcoin, max_cash); } else { @@ -120,7 +120,7 @@ void ZoneDatabase::AddLootDropToNPC(NPC* npc,uint32 lootdrop_id, ItemList* iteml for(int j = 0; j < charges; ++j) { if(zone->random.Real(0.0, 100.0) <= lds->Entries[i].chance) { const Item_Struct* dbitem = GetItem(lds->Entries[i].item_id); - npc->AddLootDrop(dbitem, itemlist, lds->Entries[i].item_charges, lds->Entries[i].minlevel, + npc->AddLootDrop(dbitem, itemlist, lds->Entries[i].item_charges, lds->Entries[i].minlevel, lds->Entries[i].maxlevel, lds->Entries[i].equip_item > 0 ? true : false, false); } } @@ -332,6 +332,8 @@ void NPC::AddLootDrop(const Item_Struct *item2, ItemList* itemlist, int16 charge CastToMob()->AddProcToWeapon(item2->Proc.Effect, true); eslot = MaterialPrimary; + if (item2->Damage > 0) + SendAddPlayerState(PlayerState::PrimaryWeaponEquipped); } else if (foundslot == MainSecondary && (GetOwner() != nullptr || (GetLevel() >= 13 && zone->random.Roll(NPC_DW_CHANCE)) || (item2->Damage==0)) && @@ -342,6 +344,8 @@ void NPC::AddLootDrop(const Item_Struct *item2, ItemList* itemlist, int16 charge CastToMob()->AddProcToWeapon(item2->Proc.Effect, true); eslot = MaterialSecondary; + if (item2->Damage > 0) + SendAddPlayerState(PlayerState::SecondaryWeaponEquipped); } else if (foundslot == MainHead) { eslot = MaterialHead; diff --git a/zone/merc.cpp b/zone/merc.cpp index 73f6b1fbf..5f06331fa 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -1468,6 +1468,9 @@ void Merc::AI_Process() { return; } + if (!(m_PlayerState & static_cast(PlayerState::Aggressive))) + SendAddPlayerState(PlayerState::Aggressive); + bool atCombatRange = false; float meleeDistance = GetMaxMeleeRangeToTarget(GetTarget()); @@ -1681,6 +1684,9 @@ void Merc::AI_Process() { confidence_timer.Disable(); _check_confidence = false; + if (m_PlayerState & static_cast(PlayerState::Aggressive)) + SendRemovePlayerState(PlayerState::Aggressive); + if(!check_target_timer.Enabled()) check_target_timer.Start(2000, false); diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 452ca71fd..020c833e8 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1084,6 +1084,8 @@ void Mob::AI_Process() { if (engaged) { + if (!(m_PlayerState & static_cast(PlayerState::Aggressive))) + SendAddPlayerState(PlayerState::Aggressive); // we are prevented from getting here if we are blind and don't have a target in range // from above, so no extra blind checks needed if ((IsRooted() && !GetSpecialAbility(IGNORE_ROOT_AGGRO_RULES)) || IsBlind()) @@ -1435,6 +1437,8 @@ void Mob::AI_Process() { } else { + if (m_PlayerState & static_cast(PlayerState::Aggressive)) + SendRemovePlayerState(PlayerState::Aggressive); if(AIfeignremember_timer->Check()) { // 6/14/06 // Improved Feign Death Memory From d1fbd086d73299d088b8ef577fbc8a6b8c625f08 Mon Sep 17 00:00:00 2001 From: regneq Date: Mon, 11 May 2015 11:35:54 -0700 Subject: [PATCH 034/129] Fully implemented QuestReward. (credit to Cavedude on EQMacEmu) Syntax on NPC is: e.other:QuestReward(e.self,copper,silver,gold,platinum,item,experience,factionid,factionvalue); This will give you any or all of the rewards and their messages with one call, including the quest ding sound. Any item is sent to your inventory, like SummonItem does now. The coin message is generated by the client, and will give you a message for each coin type (You recieve 5 copper...). No way around that, but it's still useful if the reward only calls for a single type. --- common/eq_packet_structs.h | 36 +++++++++++++------------- zone/client.cpp | 33 ++++++++++++++++++++++++ zone/client.h | 1 + zone/lua_client.cpp | 50 ++++++++++++++++++++++++++++++++++- zone/lua_client.h | 8 ++++++ zone/lua_mob.cpp | 24 ----------------- zone/lua_mob.h | 4 --- zone/mob.cpp | 16 ------------ zone/mob.h | 1 - zone/perl_client.cpp | 53 +++++++++++++++++++++++++++++++++++++- zone/perl_mob.cpp | 42 ------------------------------ 11 files changed, 161 insertions(+), 107 deletions(-) diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index ac6528860..f91107a14 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -2154,24 +2154,24 @@ struct Illusion_Struct_Old { // OP_Sound - Size: 68 struct QuestReward_Struct { -/*000*/ uint32 from_mob; // ID of mob awarding the client -/*004*/ uint32 unknown004; -/*008*/ uint32 unknown008; -/*012*/ uint32 unknown012; -/*016*/ uint32 unknown016; -/*020*/ uint32 unknown020; -/*024*/ uint32 silver; // Gives silver to the client -/*028*/ uint32 gold; // Gives gold to the client -/*032*/ uint32 platinum; // Gives platinum to the client -/*036*/ uint32 unknown036; -/*040*/ uint32 unknown040; -/*044*/ uint32 unknown044; -/*048*/ uint32 unknown048; -/*052*/ uint32 unknown052; -/*056*/ uint32 unknown056; -/*060*/ uint32 unknown060; -/*064*/ uint32 unknown064; -/*068*/ + /*000*/ uint32 mob_id; // ID of mob awarding the client + /*004*/ uint32 target_id; + /*008*/ uint32 exp_reward; + /*012*/ uint32 faction; + /*016*/ int32 faction_mod; + /*020*/ uint32 copper; // Gives copper to the client + /*024*/ uint32 silver; // Gives silver to the client + /*028*/ uint32 gold; // Gives gold to the client + /*032*/ uint32 platinum; // Gives platinum to the client + /*036*/ uint32 item_id; + /*040*/ uint32 unknown040; + /*044*/ uint32 unknown044; + /*048*/ uint32 unknown048; + /*052*/ uint32 unknown052; + /*056*/ uint32 unknown056; + /*060*/ uint32 unknown060; + /*064*/ uint32 unknown064; + /*068*/ }; // Size: 8 diff --git a/zone/client.cpp b/zone/client.cpp index c31de64fa..f86b641c1 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -8584,3 +8584,36 @@ bool Client::TextLink::GenerateLinkBody(std::string& textLinkBody, const TextLin if (textLinkBody.length() != EmuConstants::TEXT_LINK_BODY_LENGTH) { return false; } return true; } + +void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid, uint32 exp, uint32 factionid, int32 faction) { + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Sound, sizeof(QuestReward_Struct)); + memset(outapp->pBuffer, 0, sizeof(outapp->pBuffer)); + QuestReward_Struct* qr = (QuestReward_Struct*)outapp->pBuffer; + + qr->mob_id = target->GetID(); // Entity ID for the from mob name + qr->target_id = GetID(); // The Client ID (this) + qr->copper = copper; + qr->silver = silver; + qr->gold = gold; + qr->platinum = platinum; + qr->item_id = itemid; + qr->exp_reward = exp; + qr->faction = factionid; + qr->faction_mod = faction; + + if (copper > 0 || silver > 0 || gold > 0 || platinum > 0) + AddMoneyToPP(copper, silver, gold, platinum, false); + + if (itemid > 0) + SummonItem(itemid, 0, 0, 0, 0, 0, 0, false, MainPowerSource); + + if (exp > 0) + AddEXP(exp); + + if (factionid > 0) + SetFactionLevel2(CharacterID(), factionid, GetClass(), GetBaseRace(), GetDeity(), faction, 0); + + QueuePacket(outapp, false, Client::CLIENT_CONNECTED); + safe_delete(outapp); +} \ No newline at end of file diff --git a/zone/client.h b/zone/client.h index 533db7b04..b5a016fdc 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1254,6 +1254,7 @@ public: virtual int32 Tune_GetMeleeMitDmg(Mob* GM, Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating); int32 GetMeleeDamage(Mob* other, bool GetMinDamage = false); + void QuestReward(Mob* target, uint32 copper = 0, uint32 silver = 0, uint32 gold = 0, uint32 platinum = 0, uint32 itemid = 0, uint32 exp = 0, uint32 factionid = 0, int32 faction = 0); protected: friend class Mob; void CalcItemBonuses(StatBonuses* newbon); diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index ac0ddcf99..0242041a6 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -1255,6 +1255,46 @@ void Lua_Client::PlayMP3(std::string file) self->PlayMP3(file.c_str()); } +void Lua_Client::QuestReward(Lua_Mob target) { + Lua_Safe_Call_Void(); + self->QuestReward(target); +} + +void Lua_Client::QuestReward(Lua_Mob target, uint32 copper) { + Lua_Safe_Call_Void(); + self->QuestReward(target, copper); +} + +void Lua_Client::QuestReward(Lua_Mob target, uint32 copper, uint32 silver) { + Lua_Safe_Call_Void(); + self->QuestReward(target, copper, silver); +} + +void Lua_Client::QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold) { + Lua_Safe_Call_Void(); + self->QuestReward(target, copper, silver, gold); +} + +void Lua_Client::QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum) { + Lua_Safe_Call_Void(); + self->QuestReward(target, copper, silver, gold, platinum); +} + +void Lua_Client::QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid) { + Lua_Safe_Call_Void(); + self->QuestReward(target, copper, silver, gold, platinum, itemid); +} + +void Lua_Client::QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid, uint32 exp) { + Lua_Safe_Call_Void(); + self->QuestReward(target, copper, silver, gold, platinum, itemid, exp); +} + +void Lua_Client::QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid, uint32 exp, uint32 factionid, int32 faction) { + Lua_Safe_Call_Void(); + self->QuestReward(target, copper, silver, gold, platinum, itemid, exp, factionid, faction); +} + luabind::scope lua_register_client() { return luabind::class_("Client") .def(luabind::constructor<>()) @@ -1504,7 +1544,15 @@ luabind::scope lua_register_client() { .def("SetConsumption", (void(Lua_Client::*)(int, int))&Lua_Client::SetConsumption) .def("SendMarqueeMessage", (void(Lua_Client::*)(uint32, uint32, uint32, uint32, uint32, std::string))&Lua_Client::SendMarqueeMessage) .def("SendColoredText", (void(Lua_Client::*)(uint32, std::string))&Lua_Client::SendColoredText) - .def("PlayMP3", (void(Lua_Client::*)(std::string))&Lua_Client::PlayMP3); + .def("PlayMP3", (void(Lua_Client::*)(std::string))&Lua_Client::PlayMP3) + .def("QuestReward", (void(Lua_Client::*)(Lua_Mob))&Lua_Client::QuestReward) + .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32))&Lua_Client::QuestReward) + .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32))&Lua_Client::QuestReward) + .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32))&Lua_Client::QuestReward) + .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32, uint32))&Lua_Client::QuestReward) + .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32, uint32, uint32))&Lua_Client::QuestReward) + .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32, uint32, uint32, uint32))&Lua_Client::QuestReward) + .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32, uint32, uint32, uint32, uint32, int32))&Lua_Client::QuestReward); } luabind::scope lua_register_inventory_where() { diff --git a/zone/lua_client.h b/zone/lua_client.h index e2b0a6614..d28da9cdb 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -278,6 +278,14 @@ public: void SendMarqueeMessage(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, std::string msg); void SendColoredText(uint32 type, std::string msg); void PlayMP3(std::string file); + void QuestReward(Lua_Mob target); + void QuestReward(Lua_Mob target, uint32 copper); + void QuestReward(Lua_Mob target, uint32 copper, uint32 silver); + void QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold); + void QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum); + void QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid); + void QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid, uint32 exp); + void QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid, uint32 exp, uint32 factionid, int32 faction); }; #endif diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index 04d11566a..036af2072 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -1590,26 +1590,6 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { beard, aa_title, drakkin_heritage, drakkin_tattoo, drakkin_details, size); } -void Lua_Mob::QuestReward(Lua_Client c) { - Lua_Safe_Call_Void(); - self->QuestReward(c); -} - -void Lua_Mob::QuestReward(Lua_Client c, uint32 silver) { - Lua_Safe_Call_Void(); - self->QuestReward(c, silver); -} - -void Lua_Mob::QuestReward(Lua_Client c, uint32 silver, uint32 gold) { - Lua_Safe_Call_Void(); - self->QuestReward(c, silver, gold); -} - -void Lua_Mob::QuestReward(Lua_Client c, uint32 silver, uint32 gold, uint32 platinum) { - Lua_Safe_Call_Void(); - self->QuestReward(c, silver, gold, platinum); -} - void Lua_Mob::CameraEffect(uint32 duration, uint32 intensity) { Lua_Safe_Call_Void(); self->CameraEffect(duration, intensity); @@ -2132,10 +2112,6 @@ luabind::scope lua_register_mob() { .def("SetRace", (void(Lua_Mob::*)(int))&Lua_Mob::SetRace) .def("SetGender", (void(Lua_Mob::*)(int))&Lua_Mob::SetGender) .def("SendIllusionPacket", (void(Lua_Mob::*)(luabind::adl::object))&Lua_Mob::SendIllusionPacket) - .def("QuestReward", (void(Lua_Mob::*)(Lua_Client))&Lua_Mob::QuestReward) - .def("QuestReward", (void(Lua_Mob::*)(Lua_Client,uint32))&Lua_Mob::QuestReward) - .def("QuestReward", (void(Lua_Mob::*)(Lua_Client,uint32,uint32))&Lua_Mob::QuestReward) - .def("QuestReward", (void(Lua_Mob::*)(Lua_Client,uint32,uint32,uint32))&Lua_Mob::QuestReward) .def("CameraEffect", (void(Lua_Mob::*)(uint32,uint32))&Lua_Mob::CameraEffect) .def("CameraEffect", (void(Lua_Mob::*)(uint32,uint32,Lua_Client))&Lua_Mob::CameraEffect) .def("CameraEffect", (void(Lua_Mob::*)(uint32,uint32,Lua_Client,bool))&Lua_Mob::CameraEffect) diff --git a/zone/lua_mob.h b/zone/lua_mob.h index f272cd440..3caf62839 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -296,10 +296,6 @@ public: void SetRace(int in); void SetGender(int in); void SendIllusionPacket(luabind::adl::object illusion); - void QuestReward(Lua_Client c); - void QuestReward(Lua_Client c, uint32 silver); - void QuestReward(Lua_Client c, uint32 silver, uint32 gold); - void QuestReward(Lua_Client c, uint32 silver, uint32 gold, uint32 platinum); void CameraEffect(uint32 duration, uint32 intensity); void CameraEffect(uint32 duration, uint32 intensity, Lua_Client c); void CameraEffect(uint32 duration, uint32 intensity, Lua_Client c, bool global); diff --git a/zone/mob.cpp b/zone/mob.cpp index b4c3370cb..05ec2015d 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1910,22 +1910,6 @@ void Mob::SendTargetable(bool on, Client *specific_target) { safe_delete(outapp); } -void Mob::QuestReward(Client *c, uint32 silver, uint32 gold, uint32 platinum) { - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Sound, sizeof(QuestReward_Struct)); - QuestReward_Struct* qr = (QuestReward_Struct*) outapp->pBuffer; - - qr->from_mob = GetID(); // Entity ID for the from mob name - qr->silver = silver; - qr->gold = gold; - qr->platinum = platinum; - - if(c) - c->QueuePacket(outapp, false, Client::CLIENT_CONNECTED); - - safe_delete(outapp); -} - void Mob::CameraEffect(uint32 duration, uint32 intensity, Client *c, bool global) { diff --git a/zone/mob.h b/zone/mob.h index 3b0702e60..0d412a666 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -493,7 +493,6 @@ public: inline bool CheckLastLosState() const { return last_los_check; } //Quest - void QuestReward(Client *c = nullptr, uint32 silver = 0, uint32 gold = 0, uint32 platinum = 0); void CameraEffect(uint32 duration, uint32 intensity, Client *c = nullptr, bool global = false); inline bool GetQglobal() const { return qglobal; } diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index a885922e9..785ad04f6 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -6188,6 +6188,57 @@ XS(XS_Client_GetTargetRingZ) XSRETURN(1); } +XS(XS_Client_QuestReward); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_QuestReward) +{ + dXSARGS; + if (items < 1 || items > 9) + Perl_croak(aTHX_ "Usage: Client::QuestReward(THIS, mob, copper, silver, gold, platinum, itemid, exp, factionid, faction)"); + { + Client* THIS; + Mob * mob = nullptr; + int32 copper = 0; + int32 silver = 0; + int32 gold = 0; + int32 platinum = 0; + int32 itemid = 0; + int32 exp = 0; + int32 factionid = 0; + int32 faction = 0; + + if (sv_derived_from(ST(0), "THIS")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Client *, tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type client"); + if (THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + if (items > 1) { + if (sv_derived_from(ST(1), "mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + mob = INT2PTR(Mob *, tmp); + } + else + Perl_croak(aTHX_ "mob is not of type Mob"); + if (mob == nullptr) + Perl_croak(aTHX_ "mob is nullptr, avoiding crash."); + } + if (items > 2) { copper = (int32)SvIV(ST(2)); } + if (items > 3) { silver = (int32)SvIV(ST(3)); } + if (items > 4) { gold = (int32)SvIV(ST(4)); } + if (items > 5) { platinum = (int32)SvIV(ST(5)); } + if (items > 6) { itemid = (int32)SvIV(ST(6)); } + if (items > 7) { exp = (int32)SvIV(ST(7)); } + if (items > 8) { factionid = (int32)SvIV(ST(8)); } + if (items > 9) { faction = (int32)SvIV(ST(9)); } + + THIS->QuestReward(mob, copper, silver, gold, platinum, itemid, exp, factionid, faction); + } + XSRETURN_EMPTY; +} + #ifdef __cplusplus extern "C" #endif @@ -6432,7 +6483,7 @@ XS(boot_Client) newXSproto(strcpy(buf, "GetTargetRingX"), XS_Client_GetTargetRingX, file, "$$"); newXSproto(strcpy(buf, "GetTargetRingY"), XS_Client_GetTargetRingY, file, "$$"); newXSproto(strcpy(buf, "GetTargetRingZ"), XS_Client_GetTargetRingZ, file, "$$"); - + newXSproto(strcpy(buf, "QuestReward"), XS_Client_QuestReward, file, "$$;$$$$$$$$"); XSRETURN_YES; } diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index 5eeb7be1b..f5ab99cd6 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -7027,47 +7027,6 @@ XS(XS_Mob_SendAppearanceEffect) XSRETURN_EMPTY; } -XS(XS_Mob_QuestReward); /* prototype to pass -Wmissing-prototypes */ -XS(XS_Mob_QuestReward) -{ - dXSARGS; - if (items < 1 || items > 5) - Perl_croak(aTHX_ "Usage: Mob::QuestReward(THIS, client, silver, gold, platinum)"); - { - Mob * THIS; - Client* client = nullptr; - int32 silver = 0; - int32 gold = 0; - int32 platinum = 0; - - if (sv_derived_from(ST(0), "Mob")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(Mob *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type Mob"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - if (items > 1) { - if (sv_derived_from(ST(1), "Client")) { - IV tmp = SvIV((SV*)SvRV(ST(1))); - client = INT2PTR(Client *,tmp); - } - else - Perl_croak(aTHX_ "client is not of type Client"); - if(client == nullptr) - Perl_croak(aTHX_ "client is nullptr, avoiding crash."); - } - if (items > 2) { silver = (int32)SvIV(ST(2)); } - if (items > 3) { gold = (int32)SvIV(ST(3)); } - if (items > 4) { platinum = (int32)SvIV(ST(4)); } - - THIS->QuestReward(client, silver, gold, platinum); - } - XSRETURN_EMPTY; -} - XS(XS_Mob_SetFlyMode); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_SetFlyMode) { @@ -8660,7 +8619,6 @@ XS(boot_Mob) newXSproto(strcpy(buf, "SendIllusion"), XS_Mob_SendIllusion, file, "$$;$$$$$$$$$$$$"); newXSproto(strcpy(buf, "MakeTempPet"), XS_Mob_MakeTempPet, file, "$$;$$$$"); newXSproto(strcpy(buf, "TypesTempPet"), XS_Mob_TypesTempPet, file, "$$;$$$$$"); - newXSproto(strcpy(buf, "QuestReward"), XS_Mob_QuestReward, file, "$$;$$$"); newXSproto(strcpy(buf, "CameraEffect"), XS_Mob_CameraEffect, file, "$$;$$$"); newXSproto(strcpy(buf, "SpellEffect"), XS_Mob_SpellEffect, file, "$$;$$$$$$"); newXSproto(strcpy(buf, "TempName"), XS_Mob_TempName, file, "$:$"); From 6fad93aeeef45b308710e0e1f2530702cafe3d81 Mon Sep 17 00:00:00 2001 From: regneq Date: Mon, 11 May 2015 12:42:13 -0700 Subject: [PATCH 035/129] QuestReward now accepts a single bool (true or false) for faction instead of 2 int32s. If true, it will pull the faction hits assigned to the NPC in the DB (reversed, of course) and give you that as part of the reward. Example usage: e.other:QuestReward(e.self,copper,silver,gold,platinum,itemid,exp,faction) (Credit to Cavedude) --- zone/client.cpp | 29 ++++++++++++++++++++++------- zone/client.h | 4 ++-- zone/lua_client.cpp | 6 +++--- zone/lua_client.h | 2 +- zone/perl_client.cpp | 12 +++++------- 5 files changed, 33 insertions(+), 20 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index f86b641c1..0aa5510ff 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -7588,7 +7588,7 @@ FACTION_VALUE Client::GetFactionLevel(uint32 char_id, uint32 npc_id, uint32 p_ra } //Sets the characters faction standing with the specified NPC. -void Client::SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, uint8 char_race, uint8 char_deity) +void Client::SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, uint8 char_race, uint8 char_deity, bool quest) { int32 faction_id[MAX_NPC_FACTIONS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int32 npc_value[MAX_NPC_FACTIONS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -7615,6 +7615,15 @@ void Client::SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, ui database.GetFactionData(&fm, GetClass(), GetRace(), GetDeity(), faction_id[i]); + if (quest) + { + //The ole switcheroo + if (npc_value[i] > 0) + npc_value[i] = -abs(npc_value[i]); + else if (npc_value[i] < 0) + npc_value[i] = abs(npc_value[i]); + } + // Adjust the amount you can go up or down so the resulting range // is PERSONAL_MAX - PERSONAL_MIN // @@ -8585,7 +8594,7 @@ bool Client::TextLink::GenerateLinkBody(std::string& textLinkBody, const TextLin return true; } -void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid, uint32 exp, uint32 factionid, int32 faction) { +void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid, uint32 exp, bool faction) { EQApplicationPacket* outapp = new EQApplicationPacket(OP_Sound, sizeof(QuestReward_Struct)); memset(outapp->pBuffer, 0, sizeof(outapp->pBuffer)); @@ -8599,8 +8608,6 @@ void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold, qr->platinum = platinum; qr->item_id = itemid; qr->exp_reward = exp; - qr->faction = factionid; - qr->faction_mod = faction; if (copper > 0 || silver > 0 || gold > 0 || platinum > 0) AddMoneyToPP(copper, silver, gold, platinum, false); @@ -8608,12 +8615,20 @@ void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold, if (itemid > 0) SummonItem(itemid, 0, 0, 0, 0, 0, 0, false, MainPowerSource); + if (faction) + { + if (target->IsNPC()) + { + int32 nfl_id = target->CastToNPC()->GetNPCFactionID(); + SetFactionLevel(CharacterID(), nfl_id, GetBaseClass(), GetBaseRace(), GetDeity(), true); + qr->faction = target->CastToNPC()->GetPrimaryFaction(); + qr->faction_mod = 1; // Too lazy to get real value, not sure if this is even used by client anyhow. + } + } + if (exp > 0) AddEXP(exp); - if (factionid > 0) - SetFactionLevel2(CharacterID(), factionid, GetClass(), GetBaseRace(), GetDeity(), faction, 0); - QueuePacket(outapp, false, Client::CLIENT_CONNECTED); safe_delete(outapp); } \ No newline at end of file diff --git a/zone/client.h b/zone/client.h index b5a016fdc..38c5911e0 100644 --- a/zone/client.h +++ b/zone/client.h @@ -611,7 +611,7 @@ public: void SendFactionMessage(int32 tmpvalue, int32 faction_id, int32 faction_before_hit, int32 totalvalue, uint8 temp, int32 this_faction_min, int32 this_faction_max); void UpdatePersonalFaction(int32 char_id, int32 npc_value, int32 faction_id, int32 *current_value, int32 temp, int32 this_faction_min, int32 this_faction_max); - void SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, uint8 char_race, uint8 char_deity); + void SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, uint8 char_race, uint8 char_deity, bool quest = false); void SetFactionLevel2(uint32 char_id, int32 faction_id, uint8 char_class, uint8 char_race, uint8 char_deity, int32 value, uint8 temp); int32 GetRawItemAC(); uint16 GetCombinedAC_TEST(); @@ -1254,7 +1254,7 @@ public: virtual int32 Tune_GetMeleeMitDmg(Mob* GM, Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating); int32 GetMeleeDamage(Mob* other, bool GetMinDamage = false); - void QuestReward(Mob* target, uint32 copper = 0, uint32 silver = 0, uint32 gold = 0, uint32 platinum = 0, uint32 itemid = 0, uint32 exp = 0, uint32 factionid = 0, int32 faction = 0); + void QuestReward(Mob* target, uint32 copper = 0, uint32 silver = 0, uint32 gold = 0, uint32 platinum = 0, uint32 itemid = 0, uint32 exp = 0, bool faction = false); protected: friend class Mob; void CalcItemBonuses(StatBonuses* newbon); diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 0242041a6..47607b705 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -1290,9 +1290,9 @@ void Lua_Client::QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint3 self->QuestReward(target, copper, silver, gold, platinum, itemid, exp); } -void Lua_Client::QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid, uint32 exp, uint32 factionid, int32 faction) { +void Lua_Client::QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid, uint32 exp, bool faction) { Lua_Safe_Call_Void(); - self->QuestReward(target, copper, silver, gold, platinum, itemid, exp, factionid, faction); + self->QuestReward(target, copper, silver, gold, platinum, itemid, exp, faction); } luabind::scope lua_register_client() { @@ -1552,7 +1552,7 @@ luabind::scope lua_register_client() { .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32, uint32))&Lua_Client::QuestReward) .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32, uint32, uint32))&Lua_Client::QuestReward) .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32, uint32, uint32, uint32))&Lua_Client::QuestReward) - .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32, uint32, uint32, uint32, uint32, int32))&Lua_Client::QuestReward); + .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32, uint32, uint32, uint32, bool))&Lua_Client::QuestReward); } luabind::scope lua_register_inventory_where() { diff --git a/zone/lua_client.h b/zone/lua_client.h index d28da9cdb..8f930fc26 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -285,7 +285,7 @@ public: void QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum); void QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid); void QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid, uint32 exp); - void QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid, uint32 exp, uint32 factionid, int32 faction); + void QuestReward(Lua_Mob target, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, uint32 itemid, uint32 exp, bool faction); }; #endif diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 785ad04f6..b9ba00622 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -6193,7 +6193,7 @@ XS(XS_Client_QuestReward) { dXSARGS; if (items < 1 || items > 9) - Perl_croak(aTHX_ "Usage: Client::QuestReward(THIS, mob, copper, silver, gold, platinum, itemid, exp, factionid, faction)"); + Perl_croak(aTHX_ "Usage: Client::QuestReward(THIS, mob, copper, silver, gold, platinum, itemid, exp, faction)"); { Client* THIS; Mob * mob = nullptr; @@ -6203,8 +6203,7 @@ XS(XS_Client_QuestReward) int32 platinum = 0; int32 itemid = 0; int32 exp = 0; - int32 factionid = 0; - int32 faction = 0; + bool faction = false; if (sv_derived_from(ST(0), "THIS")) { IV tmp = SvIV((SV*)SvRV(ST(0))); @@ -6231,10 +6230,9 @@ XS(XS_Client_QuestReward) if (items > 5) { platinum = (int32)SvIV(ST(5)); } if (items > 6) { itemid = (int32)SvIV(ST(6)); } if (items > 7) { exp = (int32)SvIV(ST(7)); } - if (items > 8) { factionid = (int32)SvIV(ST(8)); } - if (items > 9) { faction = (int32)SvIV(ST(9)); } + if (items > 8) { faction = (bool)SvIV(ST(8)); } - THIS->QuestReward(mob, copper, silver, gold, platinum, itemid, exp, factionid, faction); + THIS->QuestReward(mob, copper, silver, gold, platinum, itemid, exp, faction); } XSRETURN_EMPTY; } @@ -6483,7 +6481,7 @@ XS(boot_Client) newXSproto(strcpy(buf, "GetTargetRingX"), XS_Client_GetTargetRingX, file, "$$"); newXSproto(strcpy(buf, "GetTargetRingY"), XS_Client_GetTargetRingY, file, "$$"); newXSproto(strcpy(buf, "GetTargetRingZ"), XS_Client_GetTargetRingZ, file, "$$"); - newXSproto(strcpy(buf, "QuestReward"), XS_Client_QuestReward, file, "$$;$$$$$$$$"); + newXSproto(strcpy(buf, "QuestReward"), XS_Client_QuestReward, file, "$$;$$$$$$$"); XSRETURN_YES; } From cc2a60feb226a1c29eb3b2d7bdebab95268e0c5b Mon Sep 17 00:00:00 2001 From: regneq Date: Mon, 11 May 2015 16:34:46 -0700 Subject: [PATCH 036/129] * change the kill faction hits display before the xp message not after. * removed the double level gain messages to display once either the level gained or the level. * implement the message "You will now lose experience when you die" and "Your items will no longer stay with you..." when reach a certain level already sets in the rule table. --- zone/attack.cpp | 8 ++++---- zone/exp.cpp | 28 +++++++++++++++++++--------- zone/string_ids.h | 2 ++ 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 256eefe0f..49b5df187 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2144,6 +2144,10 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack if(give_exp && give_exp->IsClient()) give_exp_client = give_exp->CastToClient(); + //do faction hits even if we are a merchant, so long as a player killed us + if (give_exp_client && !RuleB(NPC, EnableMeritBasedFaction)) + hate_list.DoFactionHits(GetNPCFactionID()); + bool IsLdonTreasure = (this->GetClass() == LDON_TREASURE); if (give_exp_client && !IsCorpse()) { @@ -2287,10 +2291,6 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack } } - //do faction hits even if we are a merchant, so long as a player killed us - if(give_exp_client && !RuleB(NPC, EnableMeritBasedFaction)) - hate_list.DoFactionHits(GetNPCFactionID()); - if (!HasOwner() && !IsMerc() && class_ != MERCHANT && class_ != ADVENTUREMERCHANT && !GetSwarmInfo() && MerchantType == 0 && killer && (killer->IsClient() || (killer->HasOwner() && killer->GetUltimateOwner()->IsClient()) || (killer->IsNPC() && killer->CastToNPC()->GetSwarmInfo() && killer->CastToNPC()->GetSwarmInfo()->GetOwner() && killer->CastToNPC()->GetSwarmInfo()->GetOwner()->IsClient()))) diff --git a/zone/exp.cpp b/zone/exp.cpp index 7e878206a..3a77f30dc 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -268,12 +268,17 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { //this ammount of exp (once these loops complete) uint16 check_level = GetLevel()+1; //see if we gained any levels + bool level_increase = true; + int8 level_count = 0; + while (set_exp >= GetEXPForLevel(check_level)) { check_level++; if (check_level > 127) { //hard level cap check_level = 127; break; } + level_count++; + if(GetMercID()) UpdateMercLevel(); } @@ -284,6 +289,7 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { check_level = 2; break; } + level_increase = false; if(GetMercID()) UpdateMercLevel(); } @@ -364,17 +370,21 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { if ((GetLevel() != check_level) && !(check_level >= maxlevel)) { char val1[20]={0}; - if (GetLevel() == check_level-1){ - Message_StringID(MT_Experience, GAIN_LEVEL,ConvertArray(check_level,val1)); - SendLevelAppearance(); - /* Message(15, "You have gained a level! Welcome to level %i!", check_level); */ - } - if (GetLevel() == check_level){ - Message_StringID(MT_Experience, LOSE_LEVEL,ConvertArray(check_level,val1)); - /* Message(15, "You lost a level! You are now level %i!", check_level); */ + if (level_increase) + { + if (level_count == 1) + Message_StringID(MT_Experience, GAIN_LEVEL, ConvertArray(check_level, val1)); + else + Message(15, "Welcome to level %i!", check_level); + + if (check_level == RuleI(Character, DeathItemLossLevel)) + Message_StringID(15, CORPSE_ITEM_LOST); + + if (check_level == RuleI(Character, DeathExpLossLevel)) + Message_StringID(15, CORPSE_EXP_LOST); } else - Message(15, "Welcome to level %i!", check_level); + Message_StringID(MT_Experience, LOSE_LEVEL, ConvertArray(check_level, val1)); #ifdef BOTS uint8 myoldlevel = GetLevel(); diff --git a/zone/string_ids.h b/zone/string_ids.h index ef758dba6..2db4e34e8 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -231,6 +231,8 @@ #define MISSED_NOTE_OTHER 1219 //A missed note brings %1's song to a close! #define SPELL_LEVEL_REQ 1226 //This spell only works on people who are level %1 and under. #define CORPSE_DECAY_NOW 1227 //This corpse is waiting to expire. +#define CORPSE_ITEM_LOST 1228 //Your items will no longer stay with you when you respawn on death. You will now need to return to your corpse for your items. +#define CORPSE_EXP_LOST 1229 //You will now lose experience when you die. #define SURNAME_REJECTED 1374 //Your new surname was rejected. Please try a different name. #define DUEL_DECLINE 1383 //%1 has declined your challenge to duel to the death. #define DUEL_ACCEPTED 1384 //%1 has already accepted a duel with someone else. From 052f343e4dc0549958e389f588fef9284dd0bd7f Mon Sep 17 00:00:00 2001 From: regneq Date: Mon, 11 May 2015 21:42:48 -0700 Subject: [PATCH 037/129] smoother NPC pathing. (credit to Haynar from EQMacEmu) --- zone/client.h | 1 + zone/map.cpp | 37 +++++++++++- zone/map.h | 1 + zone/mob.cpp | 1 + zone/mob_ai.cpp | 148 ++++++++++++++++++++++++--------------------- zone/npc.h | 1 + zone/waypoints.cpp | 17 +++++- 7 files changed, 133 insertions(+), 73 deletions(-) diff --git a/zone/client.h b/zone/client.h index 38c5911e0..79dcf87ff 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1129,6 +1129,7 @@ public: inline bool IsDraggingCorpse() { return (DraggedCorpses.size() > 0); } void DragCorpses(); inline void ClearDraggedCorpses() { DraggedCorpses.clear(); } + inline void ResetPositionTimer() { position_timer_counter = 0; } void SendAltCurrencies(); void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount); void AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method = 0); diff --git a/zone/map.cpp b/zone/map.cpp index d4bb5a268..37f394626 100644 --- a/zone/map.cpp +++ b/zone/map.cpp @@ -64,7 +64,7 @@ Map::~Map() { float Map::FindBestZ(glm::vec3 &start, glm::vec3 *result) const { if (!imp) - return false; + return BEST_Z_INVALID; glm::vec3 tmp; if(!result) @@ -93,6 +93,41 @@ float Map::FindBestZ(glm::vec3 &start, glm::vec3 *result) const { return BEST_Z_INVALID; } +float Map::FindClosestZ(glm::vec3 &start, glm::vec3 *result) const { + // Unlike FindBestZ, this method finds the closest Z value above or below the specified point. + // + if (!imp) + return false; + + float ClosestZ = BEST_Z_INVALID; + + glm::vec3 tmp; + if (!result) + result = &tmp; + + glm::vec3 from(start.x, start.y, start.z); + glm::vec3 to(start.x, start.y, BEST_Z_INVALID); + float hit_distance; + bool hit = false; + + // first check is below us + hit = imp->rm->raycast((const RmReal*)&from, (const RmReal*)&to, (RmReal*)result, nullptr, &hit_distance); + if (hit) { + ClosestZ = result->z; + + } + + // Find nearest Z above us + to.z = -BEST_Z_INVALID; + hit = imp->rm->raycast((const RmReal*)&from, (const RmReal*)&to, (RmReal*)result, nullptr, &hit_distance); + if (hit) { + if (abs(from.z - result->z) < abs(ClosestZ - from.z)) + return result->z; + } + + return ClosestZ; +} + bool Map::LineIntersectsZone(glm::vec3 start, glm::vec3 end, float step, glm::vec3 *result) const { if(!imp) return false; diff --git a/zone/map.h b/zone/map.h index 2bce1c6db..f3e81fe20 100644 --- a/zone/map.h +++ b/zone/map.h @@ -34,6 +34,7 @@ public: ~Map(); float FindBestZ(glm::vec3 &start, glm::vec3 *result) const; + float FindClosestZ(glm::vec3 &start, glm::vec3 *result) const; bool LineIntersectsZone(glm::vec3 start, glm::vec3 end, float step, glm::vec3 *result) const; bool LineIntersectsZoneNoZLeaps(glm::vec3 start, glm::vec3 end, float step_mag, glm::vec3 *result) const; bool CheckLoS(glm::vec3 myloc, glm::vec3 oloc) const; diff --git a/zone/mob.cpp b/zone/mob.cpp index 05ec2015d..d95b3bea7 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1201,6 +1201,7 @@ void Mob::SendPosition() PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)app->pBuffer; MakeSpawnUpdateNoDelta(spu); move_tic_count = 0; + tar_ndx = 20; entity_list.QueueClients(this, app, true); safe_delete(app); } diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 020c833e8..18618e262 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1666,92 +1666,41 @@ void NPC::AI_DoMovement() { if (gridno > 0 || cur_wp==-2) { if (movetimercompleted==true) { // time to pause at wp is over - - int32 spawn_id = this->GetSpawnPointID(); - LinkedListIterator iterator(zone->spawn2_list); - iterator.Reset(); - Spawn2 *found_spawn = nullptr; - - while(iterator.MoreElements()) - { - Spawn2* cur = iterator.GetData(); - iterator.Advance(); - if(cur->GetID() == spawn_id) - { - found_spawn = cur; - break; - } - } - - if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) { - CastToNPC()->Depop(true); //depop and resart spawn timer - if(found_spawn) - found_spawn->SetNPCPointerNull(); - } - else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) { - CastToNPC()->Depop(false);//depop without spawn timer - if(found_spawn) - found_spawn->SetNPCPointerNull(); - } - else { - movetimercompleted=false; - - Log.Out(Logs::Detail, Logs::Pathing, "We are departing waypoint %d.", cur_wp); - - //if we were under quest control (with no grid), we are done now.. - if(cur_wp == -2) { - Log.Out(Logs::Detail, Logs::Pathing, "Non-grid quest mob has reached its quest ordered waypoint. Leaving pathing mode."); - roamer = false; - cur_wp = 0; - } - - if(GetAppearance() != eaStanding) - SetAppearance(eaStanding, false); - - entity_list.OpenDoorsNear(CastToNPC()); - - if(!DistractedFromGrid) { - //kick off event_waypoint depart - char temp[16]; - sprintf(temp, "%d", cur_wp); - parse->EventNPC(EVENT_WAYPOINT_DEPART, CastToNPC(), nullptr, temp, 0); - - //setup our next waypoint, if we are still on our normal grid - //remember that the quest event above could have done anything it wanted with our grid - if(gridno > 0) { - CastToNPC()->CalculateNewWaypoint(); - } - } - else { - DistractedFromGrid = false; - } - } + AI_SetupNextWaypoint(); } // endif (movetimercompleted==true) else if (!(AIwalking_timer->Enabled())) { // currently moving + bool doMove = true; if (m_CurrentWayPoint.x == GetX() && m_CurrentWayPoint.y == GetY()) { // are we there yet? then stop Log.Out(Logs::Detail, Logs::AI, "We have reached waypoint %d (%.3f,%.3f,%.3f) on grid %d", cur_wp, GetX(), GetY(), GetZ(), GetGrid()); - SetWaypointPause(); - if(GetAppearance() != eaStanding) - SetAppearance(eaStanding, false); - SetMoving(false); - if (m_CurrentWayPoint.w >= 0.0) { - SetHeading(m_CurrentWayPoint.w); + if (cur_wp_pause != 0) { + SetWaypointPause(); + if (GetAppearance() != eaStanding) + SetAppearance(eaStanding, false); + SetMoving(false); + if (m_CurrentWayPoint.w >= 0.0) { + SetHeading(m_CurrentWayPoint.w); + } + SendPosition(); } - SendPosition(); //kick off event_waypoint arrive char temp[16]; sprintf(temp, "%d", cur_wp); parse->EventNPC(EVENT_WAYPOINT_ARRIVE, CastToNPC(), nullptr, temp, 0); + // start moving directly to next waypoint if we're at a 0 pause waypoint and we didn't get quest halted. + if (!AIwalking_timer->Enabled()) + AI_SetupNextWaypoint(); + else + doMove = false; // wipe feign memory since we reached our first waypoint if(cur_wp == 1) ClearFeignMemory(); } - else - { // not at waypoint yet, so keep moving + if (doMove) + { // not at waypoint yet or at 0 pause WP, so keep moving if(!RuleB(Pathing, AggroReturnToGrid) || !zone->pathing || (DistractedFromGrid == 0)) CalculateNewPosition2(m_CurrentWayPoint.x, m_CurrentWayPoint.y, m_CurrentWayPoint.z, walksp, true); else @@ -1829,6 +1778,67 @@ void NPC::AI_DoMovement() { } } } +void NPC::AI_SetupNextWaypoint() { + int32 spawn_id = this->GetSpawnPointID(); + LinkedListIterator iterator(zone->spawn2_list); + iterator.Reset(); + Spawn2 *found_spawn = nullptr; + + while (iterator.MoreElements()) + { + Spawn2* cur = iterator.GetData(); + iterator.Advance(); + if (cur->GetID() == spawn_id) + { + found_spawn = cur; + break; + } + } + + if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) { + CastToNPC()->Depop(true); //depop and restart spawn timer + if (found_spawn) + found_spawn->SetNPCPointerNull(); + } + else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) { + CastToNPC()->Depop(false);//depop without spawn timer + if (found_spawn) + found_spawn->SetNPCPointerNull(); + } + else { + movetimercompleted = false; + + Log.Out(Logs::Detail, Logs::Pathing, "We are departing waypoint %d.", cur_wp); + + //if we were under quest control (with no grid), we are done now.. + if (cur_wp == -2) { + Log.Out(Logs::Detail, Logs::Pathing, "Non-grid quest mob has reached its quest ordered waypoint. Leaving pathing mode."); + roamer = false; + cur_wp = 0; + } + + if (GetAppearance() != eaStanding) + SetAppearance(eaStanding, false); + + entity_list.OpenDoorsNear(CastToNPC()); + + if (!DistractedFromGrid) { + //kick off event_waypoint depart + char temp[16]; + sprintf(temp, "%d", cur_wp); + parse->EventNPC(EVENT_WAYPOINT_DEPART, CastToNPC(), nullptr, temp, 0); + + //setup our next waypoint, if we are still on our normal grid + //remember that the quest event above could have done anything it wanted with our grid + if (GetGrid() > 0) { + CastToNPC()->CalculateNewWaypoint(); + } + } + else { + DistractedFromGrid = false; + } + } +} // Note: Mob that caused this may not get added to the hate list until after this function call completes void Mob::AI_Event_Engaged(Mob* attacker, bool iYellForHelp) { diff --git a/zone/npc.h b/zone/npc.h index 95a857460..f960c736e 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -119,6 +119,7 @@ public: virtual void AI_Start(uint32 iMoveDelay = 0); virtual void AI_Stop(); void AI_DoMovement(); + void AI_SetupNextWaypoint(); bool AI_AddNPCSpells(uint32 iDBSpellsID); bool AI_AddNPCSpellsEffects(uint32 iDBSpellsEffectsID); virtual bool AI_EngagedCastCheck(); diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index d3b85bf03..b81e100d7 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -394,6 +394,7 @@ void NPC::SetWaypointPause() if (cur_wp_pause == 0) { AIwalking_timer->Start(100); + AIwalking_timer->Trigger(); } else { @@ -514,7 +515,9 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b return true; } + bool send_update = false; int compare_steps = IsBoat() ? 1 : 20; + if(tar_ndx < compare_steps && m_TargetLocation.x==x && m_TargetLocation.y==y) { float new_x = m_Position.x + m_TargetV.x*tar_vector; @@ -637,13 +640,14 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b m_Position.y = y; m_Position.z = z; + tar_ndx = 20; Log.Out(Logs::Detail, Logs::AI, "Only a single step to get there... jumping."); } } else { - tar_vector/=20; + tar_vector/=20.0f; float new_x = m_Position.x + m_TargetV.x*tar_vector; float new_y = m_Position.y + m_TargetV.y*tar_vector; @@ -699,12 +703,19 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b m_Delta = glm::vec4(m_Position.x - nx, m_Position.y - ny, m_Position.z - nz, 0.0f); if (IsClient()) + { SendPosUpdate(1); + CastToClient()->ResetPositionTimer(); + } else + { + // force an update now + move_tic_count = RuleI(Zone, NPCPositonUpdateTicCount); SendPosUpdate(); - - SetAppearance(eaStanding, false); + SetAppearance(eaStanding, false); + } pLastChange = Timer::GetCurrentTime(); + return true; } From cbcaead8df30ece74de4ec48ee737a3df55b0e27 Mon Sep 17 00:00:00 2001 From: hateborne Date: Wed, 13 May 2015 18:41:14 -0400 Subject: [PATCH 038/129] GM Output for Casting Blocked Spells Utilizing the logging system to display an alert when a GM casts a blocked spell, giving some notification instead of silent successes on cast. --- zone/spells.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/zone/spells.cpp b/zone/spells.cpp index 77a9f691d..fc5856284 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1939,6 +1939,12 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 } } + if (IsClient() && CastToClient()->GetGM()){ + if (zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))){ + Log.Out(Logs::Detail, Logs::Spells, "GM Cast Blocked Spell: %s (ID %i)", GetSpellName(spell_id), spell_id); + } + } + if ( this->IsClient() && From 2308d3e88021d146aa20e66bfea63fda8438fc08 Mon Sep 17 00:00:00 2001 From: Uleat Date: Fri, 15 May 2015 22:49:59 -0400 Subject: [PATCH 039/129] Fix for EntityList::CheckSpawnQueue() debug assertion failure crash --- changelog.txt | 3 +++ zone/entity.cpp | 12 +++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/changelog.txt b/changelog.txt index a90c903bd..17126ff43 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 05/15/2015 == +Uleat: Added check to EntityList::CheckSpawnQueue() to bypass dereference if returned iterator is npc_list.end() - should fix the debug assertion failure crash + == 04/30/2015 == demonstar55: Implement mob and client melee push You can set Combat:MeleePush to false to turn off or change Combat:MeleePushChance to increase the chance an NPC can be pushed diff --git a/zone/entity.cpp b/zone/entity.cpp index 5646f0870..bf1f6b720 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -736,9 +736,15 @@ void EntityList::CheckSpawnQueue() Mob::CreateSpawnPacket(outapp, ns); QueueClients(0, outapp); auto it = npc_list.find(ns->spawn.spawnId); - NPC *pnpc = it->second; - pnpc->SendArmorAppearance(); - pnpc->SetAppearance(pnpc->GetGuardPointAnim(),false); + if (it == npc_list.end()) { + // We must of despawned, hope that's the reason! + Log.Out(Logs::General, Logs::Error, "Error in EntityList::CheckSpawnQueue: Unable to find NPC for spawnId '%u'", ns->spawn.spawnId); + } + else { + NPC *pnpc = it->second; + pnpc->SendArmorAppearance(); + pnpc->SetAppearance(pnpc->GetGuardPointAnim(), false); + } safe_delete(outapp); iterator.RemoveCurrent(); } From 79a87fac1d2bb9da7748041bc14719c4cf6c1a5c Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 17 May 2015 23:51:24 -0400 Subject: [PATCH 040/129] Guard against eaStanding spam --- zone/waypoints.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index b81e100d7..d1a447a5e 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -712,7 +712,8 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b // force an update now move_tic_count = RuleI(Zone, NPCPositonUpdateTicCount); SendPosUpdate(); - SetAppearance(eaStanding, false); + if (GetAppearance() != eaStanding) + SetAppearance(eaStanding, false); } pLastChange = Timer::GetCurrentTime(); @@ -1050,7 +1051,7 @@ void ZoneDatabase::AssignGrid(Client *client, int grid, int spawn2id) { if (!results.Success()) return; - + if (results.RowsAffected() != 1) { return; } From 553b7c9f8c36009fc5a3ef5c036d0c8a0a0b67a8 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 18 May 2015 00:04:55 -0400 Subject: [PATCH 041/129] Move the extra appearance packet guard to SetAppearance --- zone/mob.cpp | 2 ++ zone/mob_ai.cpp | 14 ++++---------- zone/waypoints.cpp | 3 +-- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index d95b3bea7..a8bed6417 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -2027,6 +2027,8 @@ const int32& Mob::SetMana(int32 amount) void Mob::SetAppearance(EmuAppearance app, bool iIgnoreSelf) { + if (_appearance == app) + return; _appearance = app; SendAppearancePacket(AT_Anim, GetAppearanceValue(app), true, iIgnoreSelf); if (this->IsClient() && this->IsAIControlled()) diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 18618e262..dd20d605c 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1676,8 +1676,7 @@ void NPC::AI_DoMovement() { Log.Out(Logs::Detail, Logs::AI, "We have reached waypoint %d (%.3f,%.3f,%.3f) on grid %d", cur_wp, GetX(), GetY(), GetZ(), GetGrid()); if (cur_wp_pause != 0) { SetWaypointPause(); - if (GetAppearance() != eaStanding) - SetAppearance(eaStanding, false); + SetAppearance(eaStanding, false); SetMoving(false); if (m_CurrentWayPoint.w >= 0.0) { SetHeading(m_CurrentWayPoint.w); @@ -1728,8 +1727,7 @@ void NPC::AI_DoMovement() { SetGrid( 0 - GetGrid()); // revert to AI control Log.Out(Logs::Detail, Logs::Pathing, "Quest pathing is finished. Resuming on grid %d", GetGrid()); - if(GetAppearance() != eaStanding) - SetAppearance(eaStanding, false); + SetAppearance(eaStanding, false); CalculateNewWaypoint(); } @@ -1817,8 +1815,7 @@ void NPC::AI_SetupNextWaypoint() { cur_wp = 0; } - if (GetAppearance() != eaStanding) - SetAppearance(eaStanding, false); + SetAppearance(eaStanding, false); entity_list.OpenDoorsNear(CastToNPC()); @@ -1845,10 +1842,7 @@ void Mob::AI_Event_Engaged(Mob* attacker, bool iYellForHelp) { if (!IsAIControlled()) return; - if(GetAppearance() != eaStanding) - { - SetAppearance(eaStanding); - } + SetAppearance(eaStanding); if (iYellForHelp) { if(IsPet()) { diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index d1a447a5e..f75743a7d 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -712,8 +712,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b // force an update now move_tic_count = RuleI(Zone, NPCPositonUpdateTicCount); SendPosUpdate(); - if (GetAppearance() != eaStanding) - SetAppearance(eaStanding, false); + SetAppearance(eaStanding, false); } pLastChange = Timer::GetCurrentTime(); From 4266f45295c35d1c24fca919b3c9e37ddc89d95c Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 18 May 2015 20:40:57 -0400 Subject: [PATCH 042/129] Added merc pointer initialization to avoid an invalid pointer condition --- zone/zonedb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 1fab4aff2..ef790e9d8 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -2183,7 +2183,7 @@ const NPCType* ZoneDatabase::GetMercType(uint32 id, uint16 raceid, uint32 client return nullptr; } - const NPCType *npc; + const NPCType *npc = nullptr; // Process each row returned. for (auto row = results.begin(); row != results.end(); ++row) { From 2ef0fc93424f03e1eb675d135f34979115b80873 Mon Sep 17 00:00:00 2001 From: KimLS Date: Mon, 18 May 2015 21:45:51 -0700 Subject: [PATCH 043/129] Change to fishing water location algorithim --- changelog.txt | 3 ++ common/ruletypes.h | 3 +- .../optional/2015_05_18_FishingLineLength.sql | 1 + zone/forage.cpp | 41 +++++++++++-------- 4 files changed, 31 insertions(+), 17 deletions(-) create mode 100644 utils/sql/git/optional/2015_05_18_FishingLineLength.sql diff --git a/changelog.txt b/changelog.txt index 17126ff43..4ad457d78 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 05/18/2015 == +KLS: Changed how fishing locates water to hopefully be a bit more accurate at the expense of a bit more cpu power per line cast. + == 05/15/2015 == Uleat: Added check to EntityList::CheckSpawnQueue() to bypass dereference if returned iterator is npc_list.end() - should fix the debug assertion failure crash diff --git a/common/ruletypes.h b/common/ruletypes.h index 5c4154cf9..a581e527d 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -272,7 +272,8 @@ RULE_BOOL ( Watermap, CheckForWaterWhenMoving, false) // Checks if a mob has mo RULE_BOOL ( Watermap, CheckForWaterOnSendTo, false) // Checks if a mob has moved into/out of water on SendTo RULE_BOOL ( Watermap, CheckForWaterWhenFishing, false) // Only lets a player fish near water (if a water map exists for the zone) RULE_REAL ( Watermap, FishingRodLength, 30) // How far in front of player water must be for fishing to work -RULE_REAL ( Watermap, FishingLineLength, 40) // If water is more than this far below the player, it is considered too far to fish +RULE_REAL ( Watermap, FishingLineLength, 100) // If water is more than this far below the player, it is considered too far to fish +RULE_REAL ( Watermap, FishingLineStepSize, 1) // Basic step size for fishing calc, too small and it will eat cpu, too large and it will miss potential water RULE_CATEGORY_END() RULE_CATEGORY( Spells ) diff --git a/utils/sql/git/optional/2015_05_18_FishingLineLength.sql b/utils/sql/git/optional/2015_05_18_FishingLineLength.sql new file mode 100644 index 000000000..f57c4d02e --- /dev/null +++ b/utils/sql/git/optional/2015_05_18_FishingLineLength.sql @@ -0,0 +1 @@ +UPDATE rule_values SET rule_value=100 WHERE rule_name='Watermap:FishingLineLength'; diff --git a/zone/forage.cpp b/zone/forage.cpp index 96b0efcdc..89d4e254f 100644 --- a/zone/forage.cpp +++ b/zone/forage.cpp @@ -185,26 +185,35 @@ bool Client::CanFish() { rodPosition.x = m_Position.x + RodLength * sin(HeadingDegrees * M_PI/180.0f); rodPosition.y = m_Position.y + RodLength * cos(HeadingDegrees * M_PI/180.0f); + rodPosition.z = m_Position.z; - // Do BestZ to find where the line hanging from the rod intersects the water (if it is water). - // and go 1 unit into the water. - glm::vec3 dest; - dest.x = rodPosition.x; - dest.y = rodPosition.y; - dest.z = m_Position.z+10; - - rodPosition.z = zone->zonemap->FindBestZ(dest, nullptr) + 4; - bool in_lava = zone->watermap->InLava(rodPosition); - bool in_water = zone->watermap->InWater(rodPosition) || zone->watermap->InVWater(rodPosition); - //Message(0, "Rod is at %4.3f, %4.3f, %4.3f, InWater says %d, InLava says %d", RodX, RodY, RodZ, in_water, in_lava); - if (in_lava) { - Message_StringID(MT_Skills, FISHING_LAVA); //Trying to catch a fire elemental or something? + float bestz = zone->zonemap->FindBestZ(rodPosition, nullptr); + float len = m_Position.z - bestz; + if(len > LineLength || len < 0.0f) { + Message_StringID(MT_Skills, FISHING_LAND); return false; } - if((!in_water) || (m_Position.z-rodPosition.z)>LineLength) { //Didn't hit the water OR the water is too far below us - Message_StringID(MT_Skills, FISHING_LAND); //Trying to catch land sharks perhaps? - return false; + + float step_size = RuleR(Watermap, FishingLineStepSize); + + for(float i = 0.0f; i < len; i += step_size) { + glm::vec3 dest(rodPosition.x, rodPosition.y, m_Position.z - i); + + bool in_lava = zone->watermap->InLava(dest); + bool in_water = zone->watermap->InWater(dest) || zone->watermap->InVWater(dest); + + if (in_lava) { + Message_StringID(MT_Skills, FISHING_LAVA); //Trying to catch a fire elemental or something? + return false; + } + + if(in_water) { + return true; + } } + + Message_StringID(MT_Skills, FISHING_LAND); + return false; } return true; } From ea5a1dd6f1bc910e5f092081186eb8d7a6095c65 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 20 May 2015 02:01:43 -0400 Subject: [PATCH 044/129] Bard instrument mods should be more consistent with live Changes: Mods are now saved for in the DB so they are loaded on zone This allows long duration buffs from bards that get mods to keep their mods Ex. Selo's, Symphony of Battle Instrument mods are applied to basically anything that is an instrument skill The only exception to this is discs (ex. Puretone is Singing but always 10) Singing spells from procs (Ex. Storm Blade) that are instrument skills should inherit their buffs instrument mod. Doom effects should also. This isn't implemented yet. --- changelog.txt | 4 + common/skills.cpp | 14 + common/skills.h | 1 + common/spdat.cpp | 14 +- common/version.h | 8 +- utils/sql/db_update_manifest.txt | 3 +- .../required/2015_05_20_BuffInstrumentMod.sql | 1 + zone/bonuses.cpp | 61 +- zone/bot.cpp | 4 +- zone/bot.h | 2 +- zone/client_mods.cpp | 158 +++-- zone/client_packet.cpp | 2 +- zone/common.h | 1 + zone/mob.h | 6 +- zone/mob_ai.cpp | 8 +- zone/spell_effects.cpp | 565 +++++++++--------- zone/spells.cpp | 1 + zone/zonedb.cpp | 150 ++--- 18 files changed, 493 insertions(+), 510 deletions(-) create mode 100644 utils/sql/git/required/2015_05_20_BuffInstrumentMod.sql diff --git a/changelog.txt b/changelog.txt index 4ad457d78..204d08d7b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,9 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 05/20/2015 == +demonstar55: Bard instrument mods should be more consistent with live. Zoning will keep instrument mod for long duration buffs (selo's) + Still need to have procs/doom effects to inherit the instrument mods from their source buff/whatever + == 05/18/2015 == KLS: Changed how fishing locates water to hopefully be a bit more accurate at the expense of a bit more cpu power per line cast. diff --git a/common/skills.cpp b/common/skills.cpp index 363c077d8..5edca778b 100644 --- a/common/skills.cpp +++ b/common/skills.cpp @@ -92,3 +92,17 @@ float EQEmu::GetSkillMeleePushForce(SkillUseTypes skill) return 0.0f; } } + +bool EQEmu::IsBardInstrumentSkill(SkillUseTypes skill) +{ + switch (skill) { + case SkillBrassInstruments: + case SkillSinging: + case SkillStringedInstruments: + case SkillWindInstruments: + case SkillPercussionInstruments: + return true; + default: + return false; + } +} diff --git a/common/skills.h b/common/skills.h index aafb1abce..1fad4de3e 100644 --- a/common/skills.h +++ b/common/skills.h @@ -271,6 +271,7 @@ namespace EQEmu { bool IsTradeskill(SkillUseTypes skill); bool IsSpecializedSkill(SkillUseTypes skill); float GetSkillMeleePushForce(SkillUseTypes skill); + bool IsBardInstrumentSkill(SkillUseTypes skill); } #endif diff --git a/common/spdat.cpp b/common/spdat.cpp index e52a06e2e..7dd21479c 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -72,7 +72,7 @@ -#include "../common/eqemu_logsys.h" +#include "../common/eqemu_logsys.h" #include "classes.h" #include "spdat.h" @@ -162,7 +162,7 @@ bool IsCureSpell(uint16 spell_id) bool CureEffect = false; for(int i = 0; i < EFFECT_COUNT; i++){ - if (sp.effectid[i] == SE_DiseaseCounter || sp.effectid[i] == SE_PoisonCounter + if (sp.effectid[i] == SE_DiseaseCounter || sp.effectid[i] == SE_PoisonCounter || sp.effectid[i] == SE_CurseCounter || sp.effectid[i] == SE_CorruptionCounter) CureEffect = true; } @@ -405,7 +405,7 @@ bool IsPartialCapableSpell(uint16 spell_id) { if (spells[spell_id].no_partial_resist) return false; - + if (IsPureNukeSpell(spell_id)) return true; @@ -447,7 +447,7 @@ bool IsTGBCompatibleSpell(uint16 spell_id) bool IsBardSong(uint16 spell_id) { - if (IsValidSpell(spell_id) && spells[spell_id].classes[BARD - 1] < 255) + if (IsValidSpell(spell_id) && spells[spell_id].classes[BARD - 1] < 127 && !spells[spell_id].IsDisciplineBuff) return true; return false; @@ -693,9 +693,9 @@ bool IsCombatSkill(uint16 spell_id) { if (!IsValidSpell(spell_id)) return false; - + //Check if Discipline - if ((spells[spell_id].mana == 0 && (spells[spell_id].EndurCost || spells[spell_id].EndurUpkeep))) + if ((spells[spell_id].mana == 0 && (spells[spell_id].EndurCost || spells[spell_id].EndurUpkeep))) return true; return false; @@ -1040,7 +1040,7 @@ bool IsCastonFadeDurationSpell(uint16 spell_id) bool IsPowerDistModSpell(uint16 spell_id) { - if (IsValidSpell(spell_id) && + if (IsValidSpell(spell_id) && (spells[spell_id].max_dist_mod || spells[spell_id].min_dist_mod) && spells[spell_id].max_dist > spells[spell_id].min_dist) return true; diff --git a/common/version.h b/common/version.h index 7b8cfd78c..9ce7b285c 100644 --- a/common/version.h +++ b/common/version.h @@ -24,13 +24,13 @@ #define CURRENT_VERSION "1.1.3" -/* - Everytime a Database SQL is added to Github, +/* + Everytime a Database SQL is added to Github, increment CURRENT_BINARY_DATABASE_VERSION number and make sure you update the manifest - Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt + Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9077 +#define CURRENT_BINARY_DATABASE_VERSION 9078 #define COMPILE_DATE __DATE__ #define COMPILE_TIME __TIME__ #ifndef WIN32 diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 2ad0b647c..8e01d0420 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -331,9 +331,10 @@ 9075|2015_02_02_logsys_packet_logs_with_dump.sql|SELECT * FROM `logsys_categories` WHERE `log_category_description` LIKE 'Packet: Server -> Client With Dump'|empty| 9076|2015_02_04_average_coin.sql|SHOW COLUMNS FROM `loottable` WHERE Field = 'avgcoin'|contains|smallint 9077|2015_02_12_zone_gravity.sql|SHOW COLUMNS FROM `zone` LIKE 'gravity'|empty| +9078|2015_05_20_BuffInstrumentMod.sql|SHOW COLUMNS FROM `character_buffs` LIKE `instrument_mod`|empty| # Upgrade conditions: -# This won't be needed after this system is implemented, but it is used database that are not +# This won't be needed after this system is implemented, but it is used database that are not # yet using the versioning system to figure out where the database is schema wise to determine # which updates are necessary to run # diff --git a/utils/sql/git/required/2015_05_20_BuffInstrumentMod.sql b/utils/sql/git/required/2015_05_20_BuffInstrumentMod.sql new file mode 100644 index 000000000..140fe5b66 --- /dev/null +++ b/utils/sql/git/required/2015_05_20_BuffInstrumentMod.sql @@ -0,0 +1 @@ +ALTER TABLE `character_buffs` ADD COLUMN `instrument_mod` int(10) DEFAULT 10 NOT NULL; diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index c17980297..ab4afeab4 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -428,7 +428,7 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu newbon->DSMitigation += item->DSMitigation; } if (item->Worn.Effect > 0 && item->Worn.Type == ET_WornEffect) {// latent effects - ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type); + ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type); } if (item->Focus.Effect>0 && (item->Focus.Type == ET_Focus)) { // focus effects @@ -559,7 +559,7 @@ void Client::AdditiveWornBonuses(const ItemInst *inst, StatBonuses* newbon, bool /* Powerful Non-live like option allows developers to add worn effects on items that can stack with other worn effects of the same spell effect type, instead of only taking the highest value. - Ie Cleave I = 40 pct cleave - So if you equip 3 cleave I items you will have a 120 pct cleave bonus. + Ie Cleave I = 40 pct cleave - So if you equip 3 cleave I items you will have a 120 pct cleave bonus. To enable use RuleI(Spells, AdditiveBonusWornType) Setting value = 2 Will force all live items to automatically be calculated additivily Setting value to anything else will indicate the item 'worntype' that if set to the same, will cause the bonuses to use this calculation @@ -579,7 +579,7 @@ void Client::AdditiveWornBonuses(const ItemInst *inst, StatBonuses* newbon, bool if(GetLevel() < item->ReqLevel) return; - + if (item->Worn.Effect > 0 && item->Worn.Type == RuleI(Spells, AdditiveBonusWornType)) ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type);// Non-live like - Addititive latent effects @@ -691,7 +691,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) continue; Log.Out(Logs::Detail, Logs::AA, "Applying Effect %d from AA %u in slot %d (base1: %d, base2: %d) on %s", effect, aaid, slot, base1, base2, this->GetCleanName()); - + uint8 focus = IsFocusEffect(0, 0, true,effect); if (focus) { @@ -1007,7 +1007,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) case SE_BlockBehind: newbon->BlockBehind += base1; break; - + case SE_StrikeThrough: case SE_StrikeThrough2: newbon->StrikeThrough += base1; @@ -1313,7 +1313,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) case SE_Vampirism: newbon->Vampirism += base1; - break; + break; case SE_FrenziedDevastation: newbon->FrenziedDevastation += base2; @@ -1416,7 +1416,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) } case SE_SkillProcSuccess:{ - + for(int e = 0; e < MAX_SKILL_PROCS; e++) { if(newbon->SkillProcSuccess[e] && newbon->SkillProcSuccess[e] == aaid) @@ -1449,7 +1449,7 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon) int buff_count = GetMaxTotalSlots(); for(i = 0; i < buff_count; i++) { if(buffs[i].spellid != SPELL_UNKNOWN){ - ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, 0, buffs[i].ticsremaining,i); + ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, 0, buffs[i].ticsremaining, i, buffs[i].instrument_mod); if (buffs[i].numhits > 0) Numhits(true); @@ -1472,8 +1472,9 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon) if (GetClass() == BARD) newbon->ManaRegen = 0; // Bards do not get mana regen from spells. } -void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* new_bonus, uint16 casterId, uint8 WornType, uint32 ticsremaining, int buffslot, - bool IsAISpellEffect, uint16 effect_id, int32 se_base, int32 se_limit, int32 se_max) +void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *new_bonus, uint16 casterId, + uint8 WornType, uint32 ticsremaining, int buffslot, int instrument_mod, + bool IsAISpellEffect, uint16 effect_id, int32 se_base, int32 se_limit, int32 se_max) { int i, effect_value, base2, max, effectid; bool AdditiveWornBonus = false; @@ -1509,9 +1510,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne if (WornType && (RuleI(Spells, AdditiveBonusWornType) == WornType)) AdditiveWornBonus = true; - + effectid = spells[spell_id].effectid[i]; - effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, caster, ticsremaining); + effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, instrument_mod, caster, ticsremaining); base2 = spells[spell_id].base2[i]; max = spells[spell_id].max[i]; } @@ -1620,10 +1621,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne if (effect_value > 0 && effect_value > new_bonus->inhibitmelee) { effect_value -= ((effect_value * GetSlowMitigation()/100)); - if (effect_value > new_bonus->inhibitmelee) + if (effect_value > new_bonus->inhibitmelee) new_bonus->inhibitmelee = effect_value; } - + break; } @@ -1839,7 +1840,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne new_bonus->DamageShieldType = GetDamageShieldType(spell_id, max); else new_bonus->DamageShieldType = GetDamageShieldType(spell_id); - + break; } @@ -2020,7 +2021,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_Vampirism: new_bonus->Vampirism += effect_value; - break; + break; case SE_AllInstrumentMod: { @@ -2263,7 +2264,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_CriticalSpellChance: { new_bonus->CriticalSpellChance += effect_value; - + if (base2 > new_bonus->SpellCritDmgIncNoStack) new_bonus->SpellCritDmgIncNoStack = base2; break; @@ -2473,7 +2474,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_NegateAttacks: { - if (!new_bonus->NegateAttacks[0] || + if (!new_bonus->NegateAttacks[0] || ((new_bonus->NegateAttacks[0] && new_bonus->NegateAttacks[2]) && (new_bonus->NegateAttacks[2] < max))){ new_bonus->NegateAttacks[0] = 1; new_bonus->NegateAttacks[1] = buffslot; @@ -2493,7 +2494,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne break; } - + case SE_MeleeThresholdGuard: { if (new_bonus->MeleeThresholdGuard[0] < effect_value){ @@ -2860,17 +2861,17 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne new_bonus->NegateIfCombat = true; break; - case SE_Screech: + case SE_Screech: new_bonus->Screech = effect_value; break; case SE_AlterNPCLevel: if (IsNPC()){ - if (!new_bonus->AlterNPCLevel - || ((effect_value < 0) && (new_bonus->AlterNPCLevel > effect_value)) + if (!new_bonus->AlterNPCLevel + || ((effect_value < 0) && (new_bonus->AlterNPCLevel > effect_value)) || ((effect_value > 0) && (new_bonus->AlterNPCLevel < effect_value))) { - + int tmp_lv = GetOrigLevel() + effect_value; if (tmp_lv < 1) tmp_lv = 1; @@ -2908,7 +2909,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne new_bonus->BerserkSPA = true; break; - + case SE_Metabolism: new_bonus->Metabolism += effect_value; break; @@ -3009,7 +3010,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne } case SE_SkillProc:{ - + for(int e = 0; e < MAX_SKILL_PROCS; e++) { if(new_bonus->SkillProc[e] && new_bonus->SkillProc[e] == spell_id) @@ -3024,7 +3025,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne } case SE_SkillProcSuccess:{ - + for(int e = 0; e < MAX_SKILL_PROCS; e++) { if(new_bonus->SkillProcSuccess[e] && new_bonus->SkillProcSuccess[e] == spell_id) @@ -3040,9 +3041,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne //Special custom cases for loading effects on to NPC from 'npc_spels_effects' table if (IsAISpellEffect) { - + //Non-Focused Effect to modify incoming spell damage by resist type. - case SE_FcSpellVulnerability: + case SE_FcSpellVulnerability: ModVulnerability(base2, effect_value); break; } @@ -4394,7 +4395,7 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) aabonuses.SlayUndead[0] = effect_value; aabonuses.SlayUndead[1] = effect_value; break; - + case SE_DoubleRangedAttack: spellbonuses.DoubleRangedAttack = effect_value; aabonuses.DoubleRangedAttack = effect_value; @@ -4414,7 +4415,7 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) aabonuses.ShieldEquipDmgMod[1] = effect_value; itembonuses.ShieldEquipDmgMod[0] = effect_value; itembonuses.ShieldEquipDmgMod[1] = effect_value; - break; + break; case SE_TriggerMeleeThreshold: spellbonuses.TriggerMeleeThreshold = false; diff --git a/zone/bot.cpp b/zone/bot.cpp index 48a4dfe55..e2c506575 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9092,8 +9092,8 @@ bool Bot::SpellEffect(Mob* caster, uint16 spell_id, float partial) { return Result; } -void Bot::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caster_level, Mob* caster) { - Mob::DoBuffTic(spell_id, slot, ticsremaining, caster_level, caster); +void Bot::DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster) { + Mob::DoBuffTic(buff, slot, caster); } bool Bot::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish, uint32 item_slot, int16 *resist_adjust) { diff --git a/zone/bot.h b/zone/bot.h index 42d0b7f65..871d96df4 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -310,7 +310,7 @@ public: virtual int32 GetActSpellDuration(uint16 spell_id, int32 duration); virtual float GetAOERange(uint16 spell_id); virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100); - virtual void DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caster_level, Mob* caster = 0); + virtual void DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster = nullptr); virtual bool CastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, int16 *resist_adjust = nullptr); virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar); virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster); diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp index 6aae0d0e9..f12c20064 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -1974,101 +1974,87 @@ int32 Client::CalcATK() uint32 Mob::GetInstrumentMod(uint16 spell_id) const { - if (GetClass() != BARD) { + if (GetClass() != BARD || spells[spell_id].IsDisciplineBuff) // Puretone is Singing but doesn't get any mod return 10; - } + uint32 effectmod = 10; int effectmodcap = RuleI(Character, BaseInstrumentSoftCap); - //this should never use spell modifiers... - //if a spell grants better modifers, they are copied into the item mods - //because the spells are supposed to act just like having the intrument. - //item mods are in 10ths of percent increases + // this should never use spell modifiers... + // if a spell grants better modifers, they are copied into the item mods + // because the spells are supposed to act just like having the intrument. + // item mods are in 10ths of percent increases + // clickies (Symphony of Battle) that have a song skill don't get AA bonus for some reason + // but clickies that are songs (selo's on Composers Greaves) do get AA mod as well switch (spells[spell_id].skill) { - case SkillPercussionInstruments: - if (itembonuses.percussionMod == 0 && spellbonuses.percussionMod == 0) { - effectmod = 10; - } - else if (GetSkill(SkillPercussionInstruments) == 0) { - effectmod = 10; - } - else if (itembonuses.percussionMod > spellbonuses.percussionMod) { - effectmod = itembonuses.percussionMod; - } - else { - effectmod = spellbonuses.percussionMod; - } - effectmod += aabonuses.percussionMod; - break; - case SkillStringedInstruments: - if (itembonuses.stringedMod == 0 && spellbonuses.stringedMod == 0) { - effectmod = 10; - } - else if (GetSkill(SkillStringedInstruments) == 0) { - effectmod = 10; - } - else if (itembonuses.stringedMod > spellbonuses.stringedMod) { - effectmod = itembonuses.stringedMod; - } - else { - effectmod = spellbonuses.stringedMod; - } - effectmod += aabonuses.stringedMod; - break; - case SkillWindInstruments: - if (itembonuses.windMod == 0 && spellbonuses.windMod == 0) { - effectmod = 10; - } - else if (GetSkill(SkillWindInstruments) == 0) { - effectmod = 10; - } - else if (itembonuses.windMod > spellbonuses.windMod) { - effectmod = itembonuses.windMod; - } - else { - effectmod = spellbonuses.windMod; - } - effectmod += aabonuses.windMod; - break; - case SkillBrassInstruments: - if (itembonuses.brassMod == 0 && spellbonuses.brassMod == 0) { - effectmod = 10; - } - else if (GetSkill(SkillBrassInstruments) == 0) { - effectmod = 10; - } - else if (itembonuses.brassMod > spellbonuses.brassMod) { - effectmod = itembonuses.brassMod; - } - else { - effectmod = spellbonuses.brassMod; - } - effectmod += aabonuses.brassMod; - break; - case SkillSinging: - if (itembonuses.singingMod == 0 && spellbonuses.singingMod == 0) { - effectmod = 10; - } - else if (itembonuses.singingMod > spellbonuses.singingMod) { - effectmod = itembonuses.singingMod; - } - else { - effectmod = spellbonuses.singingMod; - } - effectmod += aabonuses.singingMod + spellbonuses.Amplification; - break; - default: + case SkillPercussionInstruments: + if (itembonuses.percussionMod == 0 && spellbonuses.percussionMod == 0) effectmod = 10; - break; + else if (GetSkill(SkillPercussionInstruments) == 0) + effectmod = 10; + else if (itembonuses.percussionMod > spellbonuses.percussionMod) + effectmod = itembonuses.percussionMod; + else + effectmod = spellbonuses.percussionMod; + if (IsBardSong(spell_id)) + effectmod += aabonuses.percussionMod; + break; + case SkillStringedInstruments: + if (itembonuses.stringedMod == 0 && spellbonuses.stringedMod == 0) + effectmod = 10; + else if (GetSkill(SkillStringedInstruments) == 0) + effectmod = 10; + else if (itembonuses.stringedMod > spellbonuses.stringedMod) + effectmod = itembonuses.stringedMod; + else + effectmod = spellbonuses.stringedMod; + if (IsBardSong(spell_id)) + effectmod += aabonuses.stringedMod; + break; + case SkillWindInstruments: + if (itembonuses.windMod == 0 && spellbonuses.windMod == 0) + effectmod = 10; + else if (GetSkill(SkillWindInstruments) == 0) + effectmod = 10; + else if (itembonuses.windMod > spellbonuses.windMod) + effectmod = itembonuses.windMod; + else + effectmod = spellbonuses.windMod; + if (IsBardSong(spell_id)) + effectmod += aabonuses.windMod; + break; + case SkillBrassInstruments: + if (itembonuses.brassMod == 0 && spellbonuses.brassMod == 0) + effectmod = 10; + else if (GetSkill(SkillBrassInstruments) == 0) + effectmod = 10; + else if (itembonuses.brassMod > spellbonuses.brassMod) + effectmod = itembonuses.brassMod; + else + effectmod = spellbonuses.brassMod; + if (IsBardSong(spell_id)) + effectmod += aabonuses.brassMod; + break; + case SkillSinging: + if (itembonuses.singingMod == 0 && spellbonuses.singingMod == 0) + effectmod = 10; + else if (itembonuses.singingMod > spellbonuses.singingMod) + effectmod = itembonuses.singingMod; + else + effectmod = spellbonuses.singingMod; + if (IsBardSong(spell_id)) + effectmod += aabonuses.singingMod + spellbonuses.Amplification; + break; + default: + effectmod = 10; + return effectmod; } effectmodcap += aabonuses.songModCap + spellbonuses.songModCap + itembonuses.songModCap; - if (effectmod < 10) { + if (effectmod < 10) effectmod = 10; - } - if (effectmod > effectmodcap) { + if (effectmod > effectmodcap) effectmod = effectmodcap; - } - Log.Out(Logs::Detail, Logs::Spells, "%s::GetInstrumentMod() spell=%d mod=%d modcap=%d\n", - GetName(), spell_id, effectmod, effectmodcap); + Log.Out(Logs::Detail, Logs::Spells, "%s::GetInstrumentMod() spell=%d mod=%d modcap=%d\n", GetName(), spell_id, + effectmod, effectmodcap); return effectmod; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 73d028e68..3a2c6e2c9 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1494,7 +1494,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) for (int i = 0; i < max_slots; i++) { if (buffs[i].spellid != SPELL_UNKNOWN) { m_pp.buffs[i].spellid = buffs[i].spellid; - m_pp.buffs[i].bard_modifier = 10; + m_pp.buffs[i].bard_modifier = buffs[i].instrument_mod; m_pp.buffs[i].slotid = 2; m_pp.buffs[i].player_id = 0x2211; m_pp.buffs[i].level = buffs[i].casterlevel; diff --git a/zone/common.h b/zone/common.h index a7d9e2d99..b2e4d5ed5 100644 --- a/zone/common.h +++ b/zone/common.h @@ -202,6 +202,7 @@ struct Buffs_Struct { int32 caston_z; int32 ExtraDIChance; int16 RootBreakChance; //Not saved to dbase + uint32 instrument_mod; bool persistant_buff; bool client; //True if the caster is a client bool UpdateClient; diff --git a/zone/mob.h b/zone/mob.h index 0d412a666..579ec75a8 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -200,7 +200,7 @@ public: bool IsBeneficialAllowed(Mob *target); virtual int GetCasterLevel(uint16 spell_id); void ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* newbon, uint16 casterID = 0, - uint8 WornType = 0, uint32 ticsremaining = 0, int buffslot = -1, + uint8 WornType = 0, uint32 ticsremaining = 0, int buffslot = -1, int instrument_mod = 10, bool IsAISpellEffect = false, uint16 effect_id = 0, int32 se_base = 0, int32 se_limit = 0, int32 se_max = 0); void NegateSpellsBonuses(uint16 spell_id); virtual float GetActSpellRange(uint16 spell_id, float range, bool IsBard = false); @@ -253,7 +253,7 @@ public: //Buff void BuffProcess(); - virtual void DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caster_level, Mob* caster = 0); + virtual void DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster = nullptr); void BuffFadeBySpellID(uint16 spell_id); void BuffFadeByEffect(int effectid, int skipslot = -1); void BuffFadeAll(); @@ -857,7 +857,7 @@ public: virtual uint32 GetAA(uint32 aa_id) const { return(0); } uint32 GetInstrumentMod(uint16 spell_id) const; - int CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level = 1, Mob *caster = nullptr, int ticsremaining = 0); + int CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level = 1, uint32 instrument_mod = 10, Mob *caster = nullptr, int ticsremaining = 0); int CalcSpellEffectValue_formula(int formula, int base, int max, int caster_level, uint16 spell_id, int ticsremaining = 0); virtual int CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, int caster_level2, Mob* caster1 = nullptr, Mob* caster2 = nullptr, int buffslot = -1); uint32 GetCastedSpellInvSlot() const { return casting_spell_inventory_slot; } diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index dd20d605c..355b6ba48 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -2565,11 +2565,9 @@ void NPC::ApplyAISpellEffects(StatBonuses* newbon) if (!AI_HasSpellsEffects()) return; - for(int i=0; i < AIspellsEffects.size(); i++) - { - ApplySpellsBonuses(0, 0, newbon, 0, 0, 0,-1, - true, AIspellsEffects[i].spelleffectid, AIspellsEffects[i].base, AIspellsEffects[i].limit,AIspellsEffects[i].max); - } + for (int i = 0; i < AIspellsEffects.size(); i++) + ApplySpellsBonuses(0, 0, newbon, 0, 0, 0, -1, 10, true, AIspellsEffects[i].spelleffectid, + AIspellsEffects[i].base, AIspellsEffects[i].limit, AIspellsEffects[i].max); return; } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 8644ac918..19e83c4df 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -199,7 +199,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) continue; effect = spell.effectid[i]; - effect_value = CalcSpellEffectValue(spell_id, i, caster_level, caster ? caster : this); + effect_value = CalcSpellEffectValue(spell_id, i, caster_level, buffslot > -1 ? buffs[buffslot].instrument_mod : 10, caster ? caster : this); if(spell_id == SPELL_LAY_ON_HANDS && caster && caster->GetAA(aaImprovedLayOnHands)) effect_value = GetMaxHP(); @@ -3029,48 +3029,44 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) return true; } -int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level, Mob *caster, int ticsremaining) +int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level, uint32 instrument_mod, Mob *caster, + int ticsremaining) { int formula, base, max, effect_value; - if - ( - !IsValidSpell(spell_id) || - effect_id < 0 || - effect_id >= EFFECT_COUNT - ) + if (!IsValidSpell(spell_id) || effect_id < 0 || effect_id >= EFFECT_COUNT) return 0; formula = spells[spell_id].formula[effect_id]; base = spells[spell_id].base[effect_id]; max = spells[spell_id].max[effect_id]; - if(IsBlankSpellEffect(spell_id, effect_id)) + if (IsBlankSpellEffect(spell_id, effect_id)) return 0; effect_value = CalcSpellEffectValue_formula(formula, base, max, caster_level, spell_id, ticsremaining); - if(caster && IsBardSong(spell_id) && - (spells[spell_id].effectid[effect_id] != SE_AttackSpeed) && - (spells[spell_id].effectid[effect_id] != SE_AttackSpeed2) && - (spells[spell_id].effectid[effect_id] != SE_AttackSpeed3) && - (spells[spell_id].effectid[effect_id] != SE_Lull) && - (spells[spell_id].effectid[effect_id] != SE_ChangeFrenzyRad) && - (spells[spell_id].effectid[effect_id] != SE_Harmony) && - (spells[spell_id].effectid[effect_id] != SE_CurrentMana)&& - (spells[spell_id].effectid[effect_id] != SE_ManaRegen_v2)) - { + // this doesn't actually need to be a song to get mods, just the right skill + if (EQEmu::IsBardInstrumentSkill(spells[spell_id].skill) && + spells[spell_id].effectid[effect_id] != SE_AttackSpeed && + spells[spell_id].effectid[effect_id] != SE_AttackSpeed2 && + spells[spell_id].effectid[effect_id] != SE_AttackSpeed3 && + spells[spell_id].effectid[effect_id] != SE_Lull && + spells[spell_id].effectid[effect_id] != SE_ChangeFrenzyRad && + spells[spell_id].effectid[effect_id] != SE_Harmony && + spells[spell_id].effectid[effect_id] != SE_CurrentMana && + spells[spell_id].effectid[effect_id] != SE_ManaRegen_v2) { int oval = effect_value; - int mod = caster->GetInstrumentMod(spell_id); - mod = ApplySpellEffectiveness(caster, spell_id, mod, true); + int mod = ApplySpellEffectiveness(caster, spell_id, instrument_mod, true); effect_value = effect_value * mod / 10; - Log.Out(Logs::Detail, Logs::Spells, "Effect value %d altered with bard modifier of %d to yeild %d", oval, mod, effect_value); + Log.Out(Logs::Detail, Logs::Spells, "Effect value %d altered with bard modifier of %d to yeild %d", + oval, mod, effect_value); } effect_value = mod_effect_value(effect_value, spell_id, spells[spell_id].effectid[effect_id], caster); - return(effect_value); + return effect_value; } // generic formula calculations @@ -3365,7 +3361,7 @@ void Mob::BuffProcess() { if (buffs[buffs_i].spellid != SPELL_UNKNOWN) { - DoBuffTic(buffs[buffs_i].spellid, buffs_i, buffs[buffs_i].ticsremaining, buffs[buffs_i].casterlevel, entity_list.GetMob(buffs[buffs_i].casterid)); + DoBuffTic(buffs[buffs_i], buffs_i, entity_list.GetMob(buffs[buffs_i].casterid)); // If the Mob died during DoBuffTic, then the buff we are currently processing will have been removed if(buffs[buffs_i].spellid == SPELL_UNKNOWN) continue; @@ -3418,333 +3414,308 @@ void Mob::BuffProcess() } } -void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caster_level, Mob* caster) { +void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) +{ int effect, effect_value; - if(!IsValidSpell(spell_id)) + if (!IsValidSpell(buff.spellid)) return; - const SPDat_Spell_Struct &spell = spells[spell_id]; + const SPDat_Spell_Struct &spell = spells[buff.spellid]; - if (spell_id == SPELL_UNKNOWN) - return; - - if(IsNPC()) - { + if (IsNPC()) { std::vector args; - args.push_back(&ticsremaining); - args.push_back(&caster_level); + args.push_back(&buff.ticsremaining); + args.push_back(&buff.casterlevel); args.push_back(&slot); - int i = parse->EventSpell(EVENT_SPELL_BUFF_TIC_NPC, CastToNPC(), nullptr, spell_id, caster ? caster->GetID() : 0, &args); - if(i != 0) { + int i = parse->EventSpell(EVENT_SPELL_BUFF_TIC_NPC, CastToNPC(), nullptr, buff.spellid, + caster ? caster->GetID() : 0, &args); + if (i != 0) { return; } - } - else - { + } else { std::vector args; - args.push_back(&ticsremaining); - args.push_back(&caster_level); + args.push_back(&buff.ticsremaining); + args.push_back(&buff.casterlevel); args.push_back(&slot); - int i = parse->EventSpell(EVENT_SPELL_BUFF_TIC_CLIENT, nullptr, CastToClient(), spell_id, caster ? caster->GetID() : 0, &args); - if(i != 0) { + int i = parse->EventSpell(EVENT_SPELL_BUFF_TIC_CLIENT, nullptr, CastToClient(), buff.spellid, + caster ? caster->GetID() : 0, &args); + if (i != 0) { return; } } // Check for non buff spell effects to fade // AE melee effects - if(IsClient()) + if (IsClient()) CastToClient()->CheckAAEffect(aaEffectRampage); - for (int i = 0; i < EFFECT_COUNT; i++) - { - if(IsBlankSpellEffect(spell_id, i)) + for (int i = 0; i < EFFECT_COUNT; i++) { + if (IsBlankSpellEffect(buff.spellid, i)) continue; effect = spell.effectid[i]; - //I copied the calculation into each case which needed it instead of - //doing it every time up here, since most buff effects dont need it + // I copied the calculation into each case which needed it instead of + // doing it every time up here, since most buff effects dont need it - switch(effect) - { - case SE_CurrentHP: - { - effect_value = CalcSpellEffectValue(spell_id, i, caster_level, caster, ticsremaining); - //Handle client cast DOTs here. - if (caster && effect_value < 0){ + switch (effect) { + case SE_CurrentHP: { + effect_value = CalcSpellEffectValue(buff.spellid, i, buff.casterlevel, buff.instrument_mod, + caster, buff.ticsremaining); + // Handle client cast DOTs here. + if (caster && effect_value < 0) { - if (IsDetrimentalSpell(spell_id)){ - if (caster->IsClient()){ - if (!caster->CastToClient()->GetFeigned()) - AddToHateList(caster, -effect_value); - } - else if (!IsClient()) //Allow NPC's to generate hate if casted on other NPC's. + if (IsDetrimentalSpell(buff.spellid)) { + if (caster->IsClient()) { + if (!caster->CastToClient()->GetFeigned()) AddToHateList(caster, -effect_value); - } - - effect_value = caster->GetActDoTDamage(spell_id, effect_value, this); - - caster->ResourceTap(-effect_value, spell_id); - effect_value = -effect_value; - Damage(caster, effect_value, spell_id, spell.skill, false, i, true); - } else if(effect_value > 0) { - // Regen spell... - // handled with bonuses + } else if (!IsClient()) // Allow NPC's to generate hate if casted on other + // NPC's. + AddToHateList(caster, -effect_value); } - break; + + effect_value = caster->GetActDoTDamage(buff.spellid, effect_value, this); + + caster->ResourceTap(-effect_value, buff.spellid); + effect_value = -effect_value; + Damage(caster, effect_value, buff.spellid, spell.skill, false, i, true); + } else if (effect_value > 0) { + // Regen spell... + // handled with bonuses } - case SE_HealOverTime: - { - effect_value = CalcSpellEffectValue(spell_id, i, caster_level); - if(caster) - effect_value = caster->GetActSpellHealing(spell_id, effect_value); + break; + } + case SE_HealOverTime: { + effect_value = CalcSpellEffectValue(buff.spellid, i, buff.casterlevel, buff.instrument_mod); + if (caster) + effect_value = caster->GetActSpellHealing(buff.spellid, effect_value); - HealDamage(effect_value, caster, spell_id); - //healing aggro would go here; removed for now + HealDamage(effect_value, caster, buff.spellid); + // healing aggro would go here; removed for now + break; + } + + case SE_CurrentEndurance: { + // Handled with bonuses + break; + } + + case SE_BardAEDot: { + effect_value = + CalcSpellEffectValue(buff.spellid, i, buff.casterlevel, buff.instrument_mod, caster); + + if ((!RuleB(Spells, PreNerfBardAEDoT) && IsMoving()) || invulnerable || + /*effect_value > 0 ||*/ DivineAura()) break; + + if (effect_value < 0) { + effect_value = -effect_value; + if (caster) { + if (caster->IsClient() && !caster->CastToClient()->GetFeigned()) { + AddToHateList(caster, effect_value); + } else if (!caster->IsClient()) + AddToHateList(caster, effect_value); + } + Damage(caster, effect_value, buff.spellid, spell.skill, false, i, true); + } else if (effect_value > 0) { + // healing spell... + HealDamage(effect_value, caster); + // healing aggro would go here; removed for now } + break; + } - case SE_CurrentEndurance: { - // Handled with bonuses - break; - } - - case SE_BardAEDot: - { - effect_value = CalcSpellEffectValue(spell_id, i, caster_level, caster); - - if ((!RuleB(Spells, PreNerfBardAEDoT) && IsMoving()) || invulnerable || /*effect_value > 0 ||*/ DivineAura()) - break; - - if(effect_value < 0) { - effect_value = -effect_value; - if(caster){ - if(caster->IsClient() && !caster->CastToClient()->GetFeigned()){ + case SE_Hate: { + effect_value = CalcSpellEffectValue(buff.spellid, i, buff.casterlevel, buff.instrument_mod); + if (caster) { + if (effect_value > 0) { + if (caster) { + if (caster->IsClient() && !caster->CastToClient()->GetFeigned()) { AddToHateList(caster, effect_value); - } - else if(!caster->IsClient()) + } else if (!caster->IsClient()) AddToHateList(caster, effect_value); } - Damage(caster, effect_value, spell_id, spell.skill, false, i, true); - } else if(effect_value > 0) { - //healing spell... - HealDamage(effect_value, caster); - //healing aggro would go here; removed for now - } - break; - } - - case SE_Hate:{ - effect_value = CalcSpellEffectValue(spell_id, i, caster_level); - if(caster){ - if(effect_value > 0){ - if(caster){ - if(caster->IsClient() && !caster->CastToClient()->GetFeigned()){ - AddToHateList(caster, effect_value); - } - else if(!caster->IsClient()) - AddToHateList(caster, effect_value); - } - }else{ - int32 newhate = GetHateAmount(caster) + effect_value; - if (newhate < 1) { - SetHateAmountOnEnt(caster,1); - } else { - SetHateAmountOnEnt(caster,newhate); - } + } else { + int32 newhate = GetHateAmount(caster) + effect_value; + if (newhate < 1) { + SetHateAmountOnEnt(caster, 1); + } else { + SetHateAmountOnEnt(caster, newhate); } } + } + break; + } + + case SE_WipeHateList: { + if (IsMezSpell(buff.spellid)) break; + + int wipechance = spells[buff.spellid].base[i]; + int bonus = 0; + + if (caster) { + bonus = caster->spellbonuses.IncreaseChanceMemwipe + + caster->itembonuses.IncreaseChanceMemwipe + + caster->aabonuses.IncreaseChanceMemwipe; } - case SE_WipeHateList: - { - if (IsMezSpell(spell_id)) + wipechance += wipechance * bonus / 100; + + if (zone->random.Roll(wipechance)) { + if (IsAIControlled()) { + WipeHateList(); + } + Message(13, "Your mind fogs. Who are my friends? Who are my enemies?... it was all so " + "clear a moment ago..."); + } + break; + } + + case SE_Charm: { + if (!caster || !PassCharismaCheck(caster, buff.spellid)) { + BuffFadeByEffect(SE_Charm); + } + + break; + } + + case SE_Root: { + /* Root formula derived from extensive personal live parses - Kayen + ROOT has a 70% chance to do a resist check to break. + */ + + if (zone->random.Roll(RuleI(Spells, RootBreakCheckChance))) { + float resist_check = + ResistSpell(spells[buff.spellid].resisttype, buff.spellid, caster, 0, 0, 0, 0, true); + + if (resist_check == 100) break; - - int wipechance = spells[spell_id].base[i]; - int bonus = 0; - - if (caster){ - bonus = caster->spellbonuses.IncreaseChanceMemwipe + - caster->itembonuses.IncreaseChanceMemwipe + - caster->aabonuses.IncreaseChanceMemwipe; - } - - wipechance += wipechance*bonus/100; - - if(zone->random.Roll(wipechance)) - { - if(IsAIControlled()) - { - WipeHateList(); - } - Message(13, "Your mind fogs. Who are my friends? Who are my enemies?... it was all so clear a moment ago..."); - } - break; + else if (!TryFadeEffect(slot)) + BuffFadeBySlot(slot); } - case SE_Charm: { - if (!caster || !PassCharismaCheck(caster, spell_id)) { - BuffFadeByEffect(SE_Charm); - } + break; + } - break; - } + case SE_Fear: { + if (zone->random.Roll(RuleI(Spells, FearBreakCheckChance))) { + float resist_check = ResistSpell(spells[buff.spellid].resisttype, buff.spellid, caster); - case SE_Root: { - /* Root formula derived from extensive personal live parses - Kayen - ROOT has a 70% chance to do a resist check to break. - */ - - if (zone->random.Roll(RuleI(Spells, RootBreakCheckChance))) { - float resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster, 0,0,0,0,true); - - if(resist_check == 100) - break; - else - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot); - } - - break; - } - - case SE_Fear: - { - if (zone->random.Roll(RuleI(Spells, FearBreakCheckChance))) { - float resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster); - - if(resist_check == 100) - break; - else - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot); - } - - break; - } - - case SE_Hunger: { - // this procedure gets called 7 times for every once that the stamina update occurs so we add 1/7 of the subtraction. - // It's far from perfect, but works without any unnecessary buff checks to bog down the server. - if(IsClient()) { - CastToClient()->m_pp.hunger_level += 5; - CastToClient()->m_pp.thirst_level += 5; - } - break; - } - case SE_Invisibility: - case SE_InvisVsAnimals: - case SE_InvisVsUndead: - { - if(ticsremaining > 3) - { - if(!IsBardSong(spell_id)) - { - double break_chance = 2.0; - if(caster) - { - break_chance -= (2 * (((double)caster->GetSkill(SkillDivination) + ((double)caster->GetLevel() * 3.0)) / 650.0)); - } - else - { - break_chance -= (2 * (((double)GetSkill(SkillDivination) + ((double)GetLevel() * 3.0)) / 650.0)); - } - - if(zone->random.Real(0.0, 100.0) < break_chance) - { - BuffModifyDurationBySpellID(spell_id, 3); - } - } - } - } - case SE_Invisibility2: - case SE_InvisVsUndead2: - { - if(ticsremaining <= 3 && ticsremaining > 1) - { - Message_StringID(MT_Spells, INVIS_BEGIN_BREAK); - } - break; - } - case SE_InterruptCasting: - { - if(IsCasting()) - { - if(zone->random.Roll(spells[spell_id].base[i])) - { - InterruptSpell(); - } - } - break; - } - // These effects always trigger when they fade. - case SE_CastOnFadeEffect: - case SE_CastOnFadeEffectNPC: - case SE_CastOnFadeEffectAlways: - { - if (ticsremaining == 1) - { - SpellOnTarget(spells[spell_id].base[i], this); - } - break; - } - case SE_LocateCorpse: - { - // This is handled by the client prior to SoD. - - if(IsClient() && (CastToClient()->GetClientVersionBit() & BIT_SoDAndLater)) - CastToClient()->LocateCorpse(); - } - case SE_TotalHP: - { - if (spell.formula[i] > 1000 && spell.formula[i] < 1999) - { - // These formulas can affect Max HP each tick - // Maybe there is a more efficient way to recalculate this for just Max HP each tic... - //CalcBonuses(); - CalcSpellBonuses(&spellbonuses); - CalcMaxHP(); - } - break; - } - - case SE_DistanceRemoval: - { - if (spellbonuses.DistanceRemoval){ - - int distance = ((int(GetX()) - buffs[slot].caston_x) * (int(GetX()) - buffs[slot].caston_x)) + - ((int(GetY()) - buffs[slot].caston_y) * (int(GetY()) - buffs[slot].caston_y)) + - ((int(GetZ()) - buffs[slot].caston_z) * (int(GetZ()) - buffs[slot].caston_z)); - - if (distance > (spells[spell_id].base[i] * spells[spell_id].base[i])){ - - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot , true); - } + if (resist_check == 100) break; - } + else if (!TryFadeEffect(slot)) + BuffFadeBySlot(slot); } - case SE_AddHateOverTimePct: - { - if (IsNPC()){ - uint32 new_hate = CastToNPC()->GetHateAmount(caster) * (100 + spell.base[i]) / 100; - if (new_hate <= 0) - new_hate = 1; + break; + } - CastToNPC()->SetHateAmountOnEnt(caster, new_hate); + case SE_Hunger: { + // this procedure gets called 7 times for every once that the stamina update occurs so we add + // 1/7 of the subtraction. + // It's far from perfect, but works without any unnecessary buff checks to bog down the server. + if (IsClient()) { + CastToClient()->m_pp.hunger_level += 5; + CastToClient()->m_pp.thirst_level += 5; + } + break; + } + case SE_Invisibility: + case SE_InvisVsAnimals: + case SE_InvisVsUndead: { + if (buff.ticsremaining > 3) { + if (!IsBardSong(buff.spellid)) { + double break_chance = 2.0; + if (caster) { + break_chance -= (2 * (((double)caster->GetSkill(SkillDivination) + + ((double)caster->GetLevel() * 3.0)) / + 650.0)); + } else { + break_chance -= + (2 * + (((double)GetSkill(SkillDivination) + ((double)GetLevel() * 3.0)) / + 650.0)); + } + + if (zone->random.Real(0.0, 100.0) < break_chance) { + BuffModifyDurationBySpellID(buff.spellid, 3); + } + } + } + } + case SE_Invisibility2: + case SE_InvisVsUndead2: { + if (buff.ticsremaining <= 3 && buff.ticsremaining > 1) { + Message_StringID(MT_Spells, INVIS_BEGIN_BREAK); + } + break; + } + case SE_InterruptCasting: { + if (IsCasting()) { + if (zone->random.Roll(spells[buff.spellid].base[i])) { + InterruptSpell(); + } + } + break; + } + // These effects always trigger when they fade. + case SE_CastOnFadeEffect: + case SE_CastOnFadeEffectNPC: + case SE_CastOnFadeEffectAlways: { + if (buff.ticsremaining == 1) { + SpellOnTarget(spells[buff.spellid].base[i], this); + } + break; + } + case SE_LocateCorpse: { + // This is handled by the client prior to SoD. + + if (IsClient() && (CastToClient()->GetClientVersionBit() & BIT_SoDAndLater)) + CastToClient()->LocateCorpse(); + } + case SE_TotalHP: { + if (spell.formula[i] > 1000 && spell.formula[i] < 1999) { + // These formulas can affect Max HP each tick + // Maybe there is a more efficient way to recalculate this for just Max HP each tic... + // CalcBonuses(); + CalcSpellBonuses(&spellbonuses); + CalcMaxHP(); + } + break; + } + + case SE_DistanceRemoval: { + if (spellbonuses.DistanceRemoval) { + + int distance = + ((int(GetX()) - buff.caston_x) * (int(GetX()) - buff.caston_x)) + + ((int(GetY()) - buff.caston_y) * (int(GetY()) - buff.caston_y)) + + ((int(GetZ()) - buff.caston_z) * (int(GetZ()) - buff.caston_z)); + + if (distance > (spells[buff.spellid].base[i] * spells[buff.spellid].base[i])) { + + if (!TryFadeEffect(slot)) + BuffFadeBySlot(slot, true); } break; } + } + case SE_AddHateOverTimePct: { + if (IsNPC()) { + uint32 new_hate = CastToNPC()->GetHateAmount(caster) * (100 + spell.base[i]) / 100; + if (new_hate <= 0) + new_hate = 1; - default: - { - // do we need to do anyting here? + CastToNPC()->SetHateAmountOnEnt(caster, new_hate); } + break; + } + + default: { + // do we need to do anyting here? + } } } } diff --git a/zone/spells.cpp b/zone/spells.cpp index fc5856284..525470303 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3189,6 +3189,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid buffs[emptyslot].dot_rune = 0; buffs[emptyslot].ExtraDIChance = 0; buffs[emptyslot].RootBreakChance = 0; + buffs[emptyslot].instrument_mod = caster ? caster->GetInstrumentMod(spell_id) : 10; if (level_override > 0) { buffs[emptyslot].UpdateClient = true; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index ef790e9d8..8369652b7 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -196,7 +196,7 @@ bool ZoneDatabase::GetZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct zone_data->time_type = atoi(row[30]); //not in the DB yet: - zone_data->gravity = atof(row[56]); + zone_data->gravity = atof(row[56]); Log.Out(Logs::General, Logs::Debug, "Zone Gravity is %f", zone_data->gravity); allow_mercs = true; @@ -248,7 +248,7 @@ void ZoneDatabase::UpdateRespawnTime(uint32 spawn2_id, uint16 instance_id, uint3 if(time_left == 0) { std::string query = StringFormat("DELETE FROM `respawn_times` WHERE `id` = %u AND `instance_id` = %u", spawn2_id, instance_id); - QueryDatabase(query); + QueryDatabase(query); return; } @@ -258,14 +258,14 @@ void ZoneDatabase::UpdateRespawnTime(uint32 spawn2_id, uint16 instance_id, uint3 "start, " "duration, " "instance_id) " - "VALUES " + "VALUES " "(%u, " "%u, " "%u, " "%u)", - spawn2_id, + spawn2_id, current_time, - time_left, + time_left, instance_id ); QueryDatabase(query); @@ -1054,8 +1054,8 @@ bool ZoneDatabase::LoadCharacterLanguages(uint32 character_id, PlayerProfile_Str for (i = 0; i < MAX_PP_LANGUAGE; ++i) pp->languages[i] = 0; - for (auto row = results.begin(); row != results.end(); ++row) { - i = atoi(row[0]); + for (auto row = results.begin(); row != results.end(); ++row) { + i = atoi(row[0]); if (i < MAX_PP_LANGUAGE){ pp->languages[i] = atoi(row[1]); } @@ -1102,14 +1102,14 @@ bool ZoneDatabase::LoadCharacterSkills(uint32 character_id, PlayerProfile_Struct "FROM " "`character_skills` " "WHERE `id` = %u ORDER BY `skill_id`", character_id); - auto results = database.QueryDatabase(query); + auto results = database.QueryDatabase(query); int i = 0; /* Initialize Skill */ for (i = 0; i < MAX_PP_SKILL; ++i) pp->skills[i] = 0; - for (auto row = results.begin(); row != results.end(); ++row) { - i = atoi(row[0]); + for (auto row = results.begin(); row != results.end(); ++row) { + i = atoi(row[0]); if (i < MAX_PP_SKILL) pp->skills[i] = atoi(row[1]); } @@ -1259,7 +1259,7 @@ bool ZoneDatabase::LoadCharacterPotions(uint32 character_id, PlayerProfile_Struc bool ZoneDatabase::LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Struct* pp){ std::string query = StringFormat("SELECT `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `is_home` FROM `character_bind` WHERE `id` = %u LIMIT 2", character_id); - auto results = database.QueryDatabase(query); + auto results = database.QueryDatabase(query); for (auto row = results.begin(); row != results.end(); ++row) { @@ -1705,8 +1705,8 @@ bool ZoneDatabase::SaveCharacterCurrency(uint32 character_id, PlayerProfile_Stru pp->careerRadCrystals, pp->currentEbonCrystals, pp->careerEbonCrystals); - auto results = database.QueryDatabase(query); - Log.Out(Logs::General, Logs::None, "Saving Currency for character ID: %i, done", character_id); + auto results = database.QueryDatabase(query); + Log.Out(Logs::General, Logs::None, "Saving Currency for character ID: %i, done", character_id); return true; } @@ -1802,7 +1802,7 @@ const NPCType* ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load std::string where_condition = ""; if (bulk_load){ - Log.Out(Logs::General, Logs::Debug, "Performing bulk NPC Types load"); + Log.Out(Logs::General, Logs::Debug, "Performing bulk NPC Types load"); where_condition = StringFormat( "INNER JOIN spawnentry ON npc_types.id = spawnentry.npcID " "INNER JOIN spawn2 ON spawnentry.spawngroupID = spawn2.spawngroupID " @@ -1910,7 +1910,7 @@ const NPCType* ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load "npc_types.handtexture, " "npc_types.legtexture, " "npc_types.feettexture " - "FROM npc_types %s", + "FROM npc_types %s", where_condition.c_str() ); @@ -2369,7 +2369,7 @@ bool ZoneDatabase::LoadCurrentMerc(Client *client) { if(!results.Success()) return false; - + if(results.RowCount() == 0) return false; @@ -2980,45 +2980,48 @@ void ZoneDatabase::SaveBuffs(Client *client) { query = StringFormat("INSERT INTO `character_buffs` (character_id, slot_id, spell_id, " "caster_level, caster_name, ticsremaining, counters, numhits, melee_rune, " - "magic_rune, persistent, dot_rune, caston_x, caston_y, caston_z, ExtraDIChance) " + "magic_rune, persistent, dot_rune, caston_x, caston_y, caston_z, ExtraDIChance, " + "instrument_mod) " "VALUES('%u', '%u', '%u', '%u', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', " - "'%i', '%i', '%i', '%i')", client->CharacterID(), index, buffs[index].spellid, + "'%i', '%i', '%i', '%i', '%i')", client->CharacterID(), index, buffs[index].spellid, buffs[index].casterlevel, buffs[index].caster_name, buffs[index].ticsremaining, buffs[index].counters, buffs[index].numhits, buffs[index].melee_rune, buffs[index].magic_rune, buffs[index].persistant_buff, buffs[index].dot_rune, buffs[index].caston_x, buffs[index].caston_y, buffs[index].caston_z, - buffs[index].ExtraDIChance); + buffs[index].ExtraDIChance, buffs[index].instrument_mod); QueryDatabase(query); } } -void ZoneDatabase::LoadBuffs(Client *client) { +void ZoneDatabase::LoadBuffs(Client *client) +{ Buffs_Struct *buffs = client->GetBuffs(); uint32 max_slots = client->GetMaxBuffSlots(); - for(int index = 0; index < max_slots; ++index) + for (int index = 0; index < max_slots; ++index) buffs[index].spellid = SPELL_UNKNOWN; std::string query = StringFormat("SELECT spell_id, slot_id, caster_level, caster_name, ticsremaining, " - "counters, numhits, melee_rune, magic_rune, persistent, dot_rune, " - "caston_x, caston_y, caston_z, ExtraDIChance " - "FROM `character_buffs` WHERE `character_id` = '%u'", client->CharacterID()); - auto results = QueryDatabase(query); - if (!results.Success()) { + "counters, numhits, melee_rune, magic_rune, persistent, dot_rune, " + "caston_x, caston_y, caston_z, ExtraDIChance, instrument_mod " + "FROM `character_buffs` WHERE `character_id` = '%u'", + client->CharacterID()); + auto results = QueryDatabase(query); + if (!results.Success()) { return; - } + } - for (auto row = results.begin(); row != results.end(); ++row) { - uint32 slot_id = atoul(row[1]); - if(slot_id >= client->GetMaxBuffSlots()) + for (auto row = results.begin(); row != results.end(); ++row) { + uint32 slot_id = atoul(row[1]); + if (slot_id >= client->GetMaxBuffSlots()) continue; - uint32 spell_id = atoul(row[0]); - if(!IsValidSpell(spell_id)) - continue; + uint32 spell_id = atoul(row[0]); + if (!IsValidSpell(spell_id)) + continue; - Client *caster = entity_list.GetClientByName(row[3]); + Client *caster = entity_list.GetClientByName(row[3]); uint32 caster_level = atoi(row[2]); uint32 ticsremaining = atoul(row[4]); uint32 counters = atoul(row[5]); @@ -3031,53 +3034,54 @@ void ZoneDatabase::LoadBuffs(Client *client) { int32 caston_y = atoul(row[12]); int32 caston_z = atoul(row[13]); int32 ExtraDIChance = atoul(row[14]); + uint32 instrument_mod = atoul(row[15]); buffs[slot_id].spellid = spell_id; - buffs[slot_id].casterlevel = caster_level; + buffs[slot_id].casterlevel = caster_level; - if(caster) { - buffs[slot_id].casterid = caster->GetID(); - strcpy(buffs[slot_id].caster_name, caster->GetName()); - buffs[slot_id].client = true; - } else { - buffs[slot_id].casterid = 0; + if (caster) { + buffs[slot_id].casterid = caster->GetID(); + strcpy(buffs[slot_id].caster_name, caster->GetName()); + buffs[slot_id].client = true; + } else { + buffs[slot_id].casterid = 0; strcpy(buffs[slot_id].caster_name, ""); buffs[slot_id].client = false; - } + } - buffs[slot_id].ticsremaining = ticsremaining; + buffs[slot_id].ticsremaining = ticsremaining; buffs[slot_id].counters = counters; buffs[slot_id].numhits = numhits; buffs[slot_id].melee_rune = melee_rune; buffs[slot_id].magic_rune = magic_rune; - buffs[slot_id].persistant_buff = persistent? true: false; + buffs[slot_id].persistant_buff = persistent ? true : false; buffs[slot_id].dot_rune = dot_rune; buffs[slot_id].caston_x = caston_x; - buffs[slot_id].caston_y = caston_y; - buffs[slot_id].caston_z = caston_z; - buffs[slot_id].ExtraDIChance = ExtraDIChance; - buffs[slot_id].RootBreakChance = 0; - buffs[slot_id].UpdateClient = false; - - } + buffs[slot_id].caston_y = caston_y; + buffs[slot_id].caston_z = caston_z; + buffs[slot_id].ExtraDIChance = ExtraDIChance; + buffs[slot_id].RootBreakChance = 0; + buffs[slot_id].UpdateClient = false; + buffs[slot_id].instrument_mod = instrument_mod; + } max_slots = client->GetMaxBuffSlots(); - for(int index = 0; index < max_slots; ++index) { - if(!IsValidSpell(buffs[index].spellid)) + for (int index = 0; index < max_slots; ++index) { + if (!IsValidSpell(buffs[index].spellid)) continue; - for(int effectIndex = 0; effectIndex < 12; ++effectIndex) { + for (int effectIndex = 0; effectIndex < 12; ++effectIndex) { if (spells[buffs[index].spellid].effectid[effectIndex] == SE_Charm) { - buffs[index].spellid = SPELL_UNKNOWN; - break; - } + buffs[index].spellid = SPELL_UNKNOWN; + break; + } - if (spells[buffs[index].spellid].effectid[effectIndex] == SE_Illusion) { - if(buffs[index].persistant_buff) - break; + if (spells[buffs[index].spellid].effectid[effectIndex] == SE_Illusion) { + if (buffs[index].persistant_buff) + break; - buffs[index].spellid = SPELL_UNKNOWN; + buffs[index].spellid = SPELL_UNKNOWN; break; } } @@ -3389,7 +3393,7 @@ bool ZoneDatabase::SetCharacterFactionLevel(uint32 char_id, int32 faction_id, in "ON DUPLICATE KEY UPDATE `current_value`=%i,`temp`=%i", char_id, faction_id, value, temp, value, temp); auto results = QueryDatabase(query); - + if (!results.Success()) return false; else @@ -3534,7 +3538,7 @@ uint32 ZoneDatabase::GetCharacterCorpseDecayTimer(uint32 corpse_db_id){ auto results = QueryDatabase(query); auto row = results.begin(); if (results.Success() && results.RowsAffected() != 0) - return atoul(row[0]); + return atoul(row[0]); return 0; } @@ -3621,8 +3625,8 @@ uint32 ZoneDatabase::SaveCharacterCorpse(uint32 charid, const char* charname, ui "`wc_6` = %u, " "`wc_7` = %u, " "`wc_8` = %u, " - "`wc_9` = %u ", - EscapeString(charname).c_str(), + "`wc_9` = %u ", + EscapeString(charname).c_str(), zoneid, instanceid, charid, @@ -3675,21 +3679,21 @@ uint32 ZoneDatabase::SaveCharacterCorpse(uint32 charid, const char* charname, ui corpse_items_query = StringFormat("REPLACE INTO `character_corpse_items` \n" " (corpse_id, equip_slot, item_id, charges, aug_1, aug_2, aug_3, aug_4, aug_5, aug_6, attuned) \n" " VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u) \n", - last_insert_id, + last_insert_id, dbpc->items[i].equip_slot, - dbpc->items[i].item_id, - dbpc->items[i].charges, - dbpc->items[i].aug_1, - dbpc->items[i].aug_2, - dbpc->items[i].aug_3, - dbpc->items[i].aug_4, + dbpc->items[i].item_id, + dbpc->items[i].charges, + dbpc->items[i].aug_1, + dbpc->items[i].aug_2, + dbpc->items[i].aug_3, + dbpc->items[i].aug_4, dbpc->items[i].aug_5, dbpc->items[i].aug_6, dbpc->items[i].attuned ); first_entry = 1; } - else{ + else{ corpse_items_query = corpse_items_query + StringFormat(", (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u) \n", last_insert_id, dbpc->items[i].equip_slot, From ea44b4b3b19090125837133df8837e28b47f3ebf Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 21 May 2015 17:25:59 -0400 Subject: [PATCH 045/129] Fix manifest --- utils/sql/db_update_manifest.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 8e01d0420..537f8009b 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -331,7 +331,7 @@ 9075|2015_02_02_logsys_packet_logs_with_dump.sql|SELECT * FROM `logsys_categories` WHERE `log_category_description` LIKE 'Packet: Server -> Client With Dump'|empty| 9076|2015_02_04_average_coin.sql|SHOW COLUMNS FROM `loottable` WHERE Field = 'avgcoin'|contains|smallint 9077|2015_02_12_zone_gravity.sql|SHOW COLUMNS FROM `zone` LIKE 'gravity'|empty| -9078|2015_05_20_BuffInstrumentMod.sql|SHOW COLUMNS FROM `character_buffs` LIKE `instrument_mod`|empty| +9078|2015_05_20_BuffInstrumentMod.sql|SHOW COLUMNS FROM `character_buffs` LIKE 'instrument_mod'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not From 9cbda0f81b85a9fd0d7d202cb1461867029b77fe Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 21 May 2015 18:15:34 -0400 Subject: [PATCH 046/129] Unlink Tiger Claw from other monk skills for RoF2+ Tiger Claw has its own reuse now, which the client expects pTimerCombatAbility2 should be able to be used if they do something similar for other classes. --- common/ptimer.h | 3 +- zone/special_attacks.cpp | 111 ++++++++++++++++++++------------------- 2 files changed, 60 insertions(+), 54 deletions(-) diff --git a/common/ptimer.h b/common/ptimer.h index d12b6779d..8a559a148 100644 --- a/common/ptimer.h +++ b/common/ptimer.h @@ -39,7 +39,8 @@ enum { //values for pTimerType pTimerDisciplineReuseStart = 14, pTimerDisciplineReuseEnd = 24, pTimerCombatAbility = 25, - pTimerBeggingPickPocket = 26, + pTimerCombatAbility2 = 26, // RoF2+ Tiger Claw is unlinked from other monk skills, generic in case other classes ever need it + pTimerBeggingPickPocket = 27, pTimerLayHands = 87, //these IDs are used by client too pTimerHarmTouch = 89, //so dont change them diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index faae0e3b1..0f0a110cd 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -95,7 +95,7 @@ void Mob::ApplySpecialAttackMod(SkillUseTypes skill, int32 &dmg, int32 &mindmg) } } -void Mob::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, int32 min_damage, int32 hate_override,int ReuseTime, +void Mob::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, int32 min_damage, int32 hate_override,int ReuseTime, bool HitChance, bool CanAvoid) { //this really should go through the same code as normal melee damage to //pick up all the special behavior there @@ -181,6 +181,11 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) { return; CombatAbility_Struct* ca_atk = (CombatAbility_Struct*) app->pBuffer; + pTimerType timer = pTimerCombatAbility; + // RoF2+ Tiger Claw is unlinked from other monk skills, if they ever do that for other classes there will need + // to be more checks here + if (GetClientVersion() >= ClientVersion::RoF2 && ca_atk->m_skill == SkillTigerClaw) + timer = pTimerCombatAbility2; /* Check to see if actually have skill */ if (!MaxSkill(static_cast(ca_atk->m_skill))) @@ -218,7 +223,7 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) { if(!CombatRange(GetTarget())) return; - if(!p_timers.Expired(&database, pTimerCombatAbility, false)) { + if(!p_timers.Expired(&database, timer, false)) { Message(13,"Ability recovery time not yet met."); return; } @@ -268,7 +273,7 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) { DoSpecialAttackDamage(GetTarget(), SkillBash, dmg, 1, ht, ReuseTime); if(ReuseTime > 0) { - p_timers.Start(pTimerCombatAbility, ReuseTime); + p_timers.Start(timer, ReuseTime); } } return; @@ -304,7 +309,7 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) { } if(ReuseTime > 0) { - p_timers.Start(pTimerCombatAbility, ReuseTime); + p_timers.Start(timer, ReuseTime); } return; } @@ -390,7 +395,7 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) { ReuseTime = (ReuseTime*HasteMod)/100; if(ReuseTime > 0){ - p_timers.Start(pTimerCombatAbility, ReuseTime); + p_timers.Start(timer, ReuseTime); } } @@ -558,7 +563,7 @@ void Mob::TryBackstab(Mob *other, int ReuseTime) { if (level > 54) { // Check for double attack with main hand assuming maxed DA Skill (MS) - if(IsClient() && CastToClient()->CheckDoubleAttack(false)) + if(IsClient() && CastToClient()->CheckDoubleAttack(false)) if(other->GetHP() > 0) RogueBackstab(other,true, ReuseTime); @@ -650,10 +655,10 @@ void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime) } ndamage = mod_backstab_damage(ndamage); - + uint32 Assassinate_Dmg = 0; Assassinate_Dmg = TryAssassinate(other, SkillBackstab, ReuseTime); - + if (Assassinate_Dmg) { ndamage = Assassinate_Dmg; entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, ASSASSINATES, GetName()); @@ -797,26 +802,26 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) { //EndlessQuiver AA base1 = 100% Chance to avoid consumption arrow. int ChanceAvoidConsume = aabonuses.ConsumeProjectile + itembonuses.ConsumeProjectile + spellbonuses.ConsumeProjectile; - if (!ChanceAvoidConsume || (ChanceAvoidConsume < 100 && zone->random.Int(0,99) > ChanceAvoidConsume)){ + if (!ChanceAvoidConsume || (ChanceAvoidConsume < 100 && zone->random.Int(0,99) > ChanceAvoidConsume)){ DeleteItemInInventory(ammo_slot, 1, true); Log.Out(Logs::Detail, Logs::Combat, "Consumed one arrow from slot %d", ammo_slot); } else { Log.Out(Logs::Detail, Logs::Combat, "Endless Quiver prevented ammo consumption."); } - CheckIncreaseSkill(SkillArchery, GetTarget(), -15); + CheckIncreaseSkill(SkillArchery, GetTarget(), -15); CommonBreakInvisible(); } -void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const ItemInst* Ammo, uint16 weapon_damage, int16 chance_mod, int16 focus, int ReuseTime, +void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const ItemInst* Ammo, uint16 weapon_damage, int16 chance_mod, int16 focus, int ReuseTime, uint32 range_id, uint32 ammo_id, const Item_Struct *AmmoItem, int AmmoSlot, float speed) { - - if ((other == nullptr || - ((IsClient() && CastToClient()->dead) || - (other->IsClient() && other->CastToClient()->dead)) || - HasDied() || + + if ((other == nullptr || + ((IsClient() && CastToClient()->dead) || + (other->IsClient() && other->CastToClient()->dead)) || + HasDied() || (!IsAttackAllowed(other)) || - (other->GetInvul() || + (other->GetInvul() || other->GetSpecialAbility(IMMUNE_MELEE)))) { return; @@ -841,12 +846,12 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite LaunchProjectile = true; else{ /* - Item sync check on projectile landing. + Item sync check on projectile landing. Weapon damage is already calculated so this only affects procs! Ammo proc check will use database to find proc if you used up your last ammo. If you change range item mid projectile flight, you loose your chance to proc from bow (Deal with it!). */ - + if (!RangeWeapon && !Ammo && range_id && ammo_id){ ProjectileImpact = true; @@ -858,8 +863,8 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite _RangeWeapon = CastToClient()->m_inv[MainRange]; if (_RangeWeapon && _RangeWeapon->GetItem() && _RangeWeapon->GetItem()->ID == range_id) - RangeWeapon = _RangeWeapon; - + RangeWeapon = _RangeWeapon; + _Ammo = CastToClient()->m_inv[AmmoSlot]; if (_Ammo && _Ammo->GetItem() && _Ammo->GetItem()->ID == ammo_id) Ammo = _Ammo; @@ -985,7 +990,7 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite other->AddToHateList(this, hate, 0, false); other->Damage(this, TotalDmg, SPELL_UNKNOWN, SkillArchery); - + //Skill Proc Success if (TotalDmg > 0 && HasSkillProcSuccess() && other && !other->HasDied()){ if (ReuseTime) @@ -1047,10 +1052,10 @@ bool Mob::TryProjectileAttack(Mob* other, const Item_Struct *item, SkillUseTypes ProjectileAtk[slot].origin_x = GetX(); ProjectileAtk[slot].origin_y = GetY(); ProjectileAtk[slot].origin_z = GetZ(); - + if (RangeWeapon && RangeWeapon->GetItem()) ProjectileAtk[slot].ranged_id = RangeWeapon->GetItem()->ID; - + if (Ammo && Ammo->GetItem()) ProjectileAtk[slot].ammo_id = Ammo->GetItem()->ID; @@ -1085,7 +1090,7 @@ void Mob::ProjectileAttack() disable = false; Mob* target = entity_list.GetMobID(ProjectileAtk[i].target_id); - + if (target && target->IsMoving()){ //Only recalculate hit increment if target moving //Due to frequency that we need to check increment the targets position variables may not be updated even if moving. Do a simple check before calculating distance. if (ProjectileAtk[i].tlast_x != target->GetX() || ProjectileAtk[i].tlast_y != target->GetY()){ @@ -1109,7 +1114,7 @@ void Mob::ProjectileAttack() else CastToNPC()->DoRangedAttackDmg(target, false, ProjectileAtk[i].wpn_dmg,0, static_cast(ProjectileAtk[i].skill)); } - + else { if (ProjectileAtk[i].skill == SkillArchery) @@ -1120,7 +1125,7 @@ void Mob::ProjectileAttack() SpellOnTarget(ProjectileAtk[i].wpn_dmg, target, false, true, spells[ProjectileAtk[i].wpn_dmg].ResistDiff, true); } } - + ProjectileAtk[i].increment = 0; ProjectileAtk[i].target_id = 0; ProjectileAtk[i].wpn_dmg = 0; @@ -1180,7 +1185,7 @@ float Mob::GetRangeDistTargetSizeMod(Mob* other) mod = 42.0f + (5.8f * (tsize - 15.0f)); else mod = 75.0f; - + return (mod + 2.0f); //Add 2.0f as buffer to prevent any chance of failures, client enforce range check regardless. } @@ -1210,7 +1215,7 @@ void NPC::RangedAttack(Mob* other) float min_range = static_cast(RuleI(Combat, MinRangedAttackDist)); float max_range = 250; // needs to be longer than 200(most spells) - + if (sa_max_range) max_range = static_cast(sa_max_range); @@ -1242,12 +1247,12 @@ void NPC::RangedAttack(Mob* other) } void NPC::DoRangedAttackDmg(Mob* other, bool Launch, int16 damage_mod, int16 chance_mod, SkillUseTypes skill, float speed, const char *IDFile) { - - if ((other == nullptr || - (other->HasDied())) || - HasDied() || + + if ((other == nullptr || + (other->HasDied())) || + HasDied() || (!IsAttackAllowed(other)) || - (other->GetInvul() || + (other->GetInvul() || other->GetSpecialAbility(IMMUNE_MELEE))) { return; @@ -1268,7 +1273,7 @@ void NPC::DoRangedAttackDmg(Mob* other, bool Launch, int16 damage_mod, int16 cha ammo = GetAmmoIDfile(); ProjectileAnimation(other, 0,false,speed,0,0,0,ammo,skillInUse); - + if (RuleB(Combat, ProjectileDmgOnImpact)) { TryProjectileAttack(other, nullptr, skillInUse, damage_mod, nullptr, nullptr, 0, speed); @@ -1276,7 +1281,7 @@ void NPC::DoRangedAttackDmg(Mob* other, bool Launch, int16 damage_mod, int16 cha } } - if (!chance_mod) + if (!chance_mod) chance_mod = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 2); if (!other->CheckHitChance(this, skillInUse, MainRange, chance_mod)) @@ -1298,11 +1303,11 @@ void NPC::DoRangedAttackDmg(Mob* other, bool Launch, int16 damage_mod, int16 cha if (!damage_mod) damage_mod = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 3);//Damage modifier - TotalDmg += TotalDmg * damage_mod / 100; + TotalDmg += TotalDmg * damage_mod / 100; other->AvoidDamage(this, TotalDmg, false); other->MeleeMitigation(this, TotalDmg, MinDmg); - + if (TotalDmg > 0) CommonOutgoingHitSuccess(other, TotalDmg, skillInUse); else if (TotalDmg < -4) @@ -1432,18 +1437,18 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 //consume ammo DeleteItemInInventory(ammo_slot, 1, true); - CheckIncreaseSkill(SkillThrowing, GetTarget()); + CheckIncreaseSkill(SkillThrowing, GetTarget()); CommonBreakInvisible(); } void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item_Struct* AmmoItem, uint16 weapon_damage, int16 chance_mod,int16 focus, int ReuseTime, uint32 range_id, int AmmoSlot, float speed) { - if ((other == nullptr || - ((IsClient() && CastToClient()->dead) || - (other->IsClient() && other->CastToClient()->dead)) || - HasDied() || + if ((other == nullptr || + ((IsClient() && CastToClient()->dead) || + (other->IsClient() && other->CastToClient()->dead)) || + HasDied() || (!IsAttackAllowed(other)) || - (other->GetInvul() || + (other->GetInvul() || other->GetSpecialAbility(IMMUNE_MELEE)))) { return; @@ -1511,7 +1516,7 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite return; } } - else + else WDmg = weapon_damage; if (focus) //From FcBaseEffects @@ -1535,7 +1540,7 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite Log.Out(Logs::Detail, Logs::Combat, "Item DMG %d. Max Damage %d. Hit for damage %d", WDmg, MaxDmg, TotalDmg); if (!Assassinate_Dmg) other->AvoidDamage(this, TotalDmg, false); //CanRiposte=false - Can not riposte throw attacks. - + other->MeleeMitigation(this, TotalDmg, minDmg); if(TotalDmg > 0) CommonOutgoingHitSuccess(other, TotalDmg, SkillThrowing); @@ -1620,7 +1625,7 @@ void Mob::SendItemAnimation(Mob *to, const Item_Struct *item, SkillUseTypes skil safe_delete(outapp); } -void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, float angle, float tilt, float arc, const char *IDFile, SkillUseTypes skillInUse) { +void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, float angle, float tilt, float arc, const char *IDFile, SkillUseTypes skillInUse) { if (!to) return; @@ -2092,7 +2097,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) } ReuseTime = ReuseTime / HasteMod; - if(ReuseTime > 0 && !IsRiposte){ + if(ReuseTime > 0 && !IsRiposte){ p_timers.Start(pTimerCombatAbility, ReuseTime); } } @@ -2270,7 +2275,7 @@ float Mob::GetSpecialProcChances(uint16 hand) if (RuleB(Combat, AdjustSpecialProcPerMinute)) { ProcChance = (static_cast(weapon_speed) * - RuleR(Combat, AvgSpecialProcsPerMinute) / 60000.0f); + RuleR(Combat, AvgSpecialProcsPerMinute) / 60000.0f); ProcBonus += static_cast(mydex/35) + static_cast(itembonuses.HeroicDEX / 25); ProcChance += ProcChance * ProcBonus / 100.0f; } else { @@ -2363,7 +2368,7 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes int Hand = MainPrimary; if (hate == 0 && weapon_damage > 1) hate = weapon_damage; - if(weapon_damage > 0){ + if(weapon_damage > 0){ if (focus) //From FcBaseEffects weapon_damage += weapon_damage*focus/100; @@ -2376,7 +2381,7 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes int32 max_hit = (2 * weapon_damage*GetDamageTable(skillinuse)) / 100; if(GetLevel() >= 28 && IsWarriorClass() ) { - int ucDamageBonus = GetWeaponDamageBonus((const Item_Struct*) nullptr ); + int ucDamageBonus = GetWeaponDamageBonus((const Item_Struct*) nullptr ); min_hit += (int) ucDamageBonus; max_hit += (int) ucDamageBonus; hate += ucDamageBonus; @@ -2411,7 +2416,7 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes } else { other->AvoidDamage(this, damage, CanRiposte); other->MeleeMitigation(this, damage, min_hit); - if(damage > 0) + if(damage > 0) CommonOutgoingHitSuccess(other, damage, skillinuse); } @@ -2449,7 +2454,7 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes if (CanSkillProc && HasSkillProcs()) TrySkillProc(other, skillinuse, ReuseTime); - + if (CanSkillProc && (damage > 0) && HasSkillProcSuccess()) TrySkillProc(other, skillinuse, ReuseTime, true); } From 351e63ae729accccf7d831e3f12dff3d69031002 Mon Sep 17 00:00:00 2001 From: Uleat Date: Fri, 22 May 2015 19:15:51 -0400 Subject: [PATCH 047/129] Possible fix for some 'random' character select crashes --- changelog.txt | 3 +++ common/patches/rof.cpp | 3 ++- common/patches/rof2.cpp | 3 ++- common/patches/sod.cpp | 3 ++- common/patches/sof.cpp | 3 ++- common/patches/uf.cpp | 3 ++- 6 files changed, 13 insertions(+), 5 deletions(-) diff --git a/changelog.txt b/changelog.txt index 204d08d7b..6c5d695ab 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 05/22/2015 == +Uleat: Added null-term declaration for character names in ENCODE(OP_CharInfo) - where appropriate + == 05/20/2015 == demonstar55: Bard instrument mods should be more consistent with live. Zoning will keep instrument mod for long duration buffs (selo's) Still need to have procs/doom effects to inherit the instrument mods from their source buff/whatever diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index bfb47492a..89ad7db7f 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -2949,8 +2949,9 @@ namespace RoF eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; strcpy(eq_cse->Name, emu_cse->Name); - eq_ptr += strlen(eq_cse->Name); + eq_ptr += strlen(emu_cse->Name); eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; + eq_cse->Name[0] = '\0'; eq_cse->Class = emu_cse->Class; eq_cse->Race = emu_cse->Race; diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index da9fa311a..75c4c7836 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -3038,8 +3038,9 @@ namespace RoF2 eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; strcpy(eq_cse->Name, emu_cse->Name); - eq_ptr += strlen(eq_cse->Name); + eq_ptr += strlen(emu_cse->Name); eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; + eq_cse->Name[0] = '\0'; eq_cse->Class = emu_cse->Class; eq_cse->Race = emu_cse->Race; diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index a4765939d..8e730cfb9 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -1969,8 +1969,9 @@ namespace SoD eq_cse->Gender = emu_cse->Gender; strcpy(eq_cse->Name, emu_cse->Name); - eq_ptr += strlen(eq_cse->Name); + eq_ptr += strlen(emu_cse->Name); eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; + eq_cse->Name[0] = '\0'; eq_cse->Beard = emu_cse->Beard; eq_cse->HairColor = emu_cse->HairColor; diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 186515b3b..a677cede3 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -1628,8 +1628,9 @@ namespace SoF eq_cse->Gender = emu_cse->Gender; strcpy(eq_cse->Name, emu_cse->Name); - eq_ptr += strlen(eq_cse->Name); + eq_ptr += strlen(emu_cse->Name); eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; + eq_cse->Name[0] = '\0'; eq_cse->Beard = emu_cse->Beard; eq_cse->HairColor = emu_cse->HairColor; diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index 58131bd35..4177e4f81 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -2260,8 +2260,9 @@ namespace UF eq_cse->Gender = emu_cse->Gender; strcpy(eq_cse->Name, emu_cse->Name); - eq_ptr += strlen(eq_cse->Name); + eq_ptr += strlen(emu_cse->Name); eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; + eq_cse->Name[0] = '\0'; eq_cse->Beard = emu_cse->Beard; eq_cse->HairColor = emu_cse->HairColor; From 8aadc3632002696a9feada53d582e2e61b517ed7 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 23 May 2015 02:20:36 -0400 Subject: [PATCH 048/129] Rework buff duration formulas These are derived from the client SE_IllusionPresistence will also set the duration to 10k tics like live --- common/eq_packet_structs.h | 18 +-- common/patches/rof2_structs.h | 10 +- common/patches/rof_structs.h | 10 +- common/patches/sod_structs.h | 4 +- common/patches/sof_structs.h | 4 +- common/patches/titanium_structs.h | 4 +- common/patches/uf_structs.h | 8 +- common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + .../git/required/2015_05_23_BuffDurations.sql | 2 + zone/bonuses.cpp | 2 +- zone/effects.cpp | 24 ++-- zone/mob.h | 2 +- zone/spell_effects.cpp | 4 +- zone/spells.cpp | 129 +++++++++--------- zone/zonedb.cpp | 12 +- 16 files changed, 116 insertions(+), 120 deletions(-) create mode 100644 utils/sql/git/required/2015_05_23_BuffDurations.sql diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index f91107a14..e5ab88e57 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -553,7 +553,7 @@ struct SpellBuff_Struct /*002*/ uint8 bard_modifier; /*003*/ uint8 effect; //not real /*004*/ uint32 spellid; -/*008*/ uint32 duration; +/*008*/ int32 duration; /*012*/ uint32 counters; /*016*/ uint32 player_id; //'global' ID of the caster, for wearoff messages /*020*/ @@ -566,7 +566,7 @@ struct SpellBuffFade_Struct { /*006*/ uint8 effect; /*007*/ uint8 unknown7; /*008*/ uint32 spellid; -/*012*/ uint32 duration; +/*012*/ int32 duration; /*016*/ uint32 num_hits; /*020*/ uint32 unknown020; //prolly global player ID /*024*/ uint32 slotid; @@ -584,14 +584,8 @@ struct BuffRemoveRequest_Struct struct PetBuff_Struct { /*000*/ uint32 petid; -/*004*/ uint32 spellid[BUFF_COUNT]; -/*104*/ uint32 unknown700; -/*108*/ uint32 unknown701; -/*112*/ uint32 unknown702; -/*116*/ uint32 unknown703; -/*120*/ uint32 unknown704; -/*124*/ uint32 ticsremaining[BUFF_COUNT]; -/*224*/ uchar unknown705[20]; +/*004*/ uint32 spellid[BUFF_COUNT+5]; +/*124*/ int32 ticsremaining[BUFF_COUNT+5]; /*244*/ uint32 buffcount; }; @@ -4036,7 +4030,7 @@ struct MarkNPC_Struct struct InspectBuffs_Struct { /*000*/ uint32 spell_id[BUFF_COUNT]; -/*100*/ uint32 tics_remaining[BUFF_COUNT]; +/*100*/ int32 tics_remaining[BUFF_COUNT]; }; struct RaidGeneral_Struct { @@ -4737,7 +4731,7 @@ struct BuffIconEntry_Struct { uint32 buff_slot; uint32 spell_id; - uint32 tics_remaining; + int32 tics_remaining; uint32 num_hits; }; diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index 4aac9e7de..a572a3834 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -687,7 +687,7 @@ struct SpellBuff_Struct /*005*/ uint32 player_id; // 'global' ID of the caster, for wearoff messages /*009*/ uint32 unknown016; /*013*/ uint8 bard_modifier; -/*014*/ uint32 duration; +/*014*/ int32 duration; /*018*/ uint8 level; /*019*/ uint32 spellid; /*023*/ uint32 counters; @@ -703,7 +703,7 @@ struct SpellBuff_Struct_Old /*003*/ uint8 effect; // not real /*004*/ float unknown004; // Seen 1 for no buff /*008*/ uint32 spellid; -/*012*/ uint32 duration; +/*012*/ int32 duration; /*016*/ uint32 unknown016; /*020*/ uint32 player_id; // 'global' ID of the caster, for wearoff messages /*024*/ uint32 counters; @@ -720,7 +720,7 @@ struct SpellBuffFade_Struct_Live { /*007*/ uint8 unknown007; /*008*/ float unknown008; /*012*/ uint32 spellid; -/*016*/ uint32 duration; +/*016*/ int32 duration; /*020*/ uint32 playerId; // Global player ID? /*024*/ uint32 num_hits; /*028*/ uint8 unknown0028[64]; @@ -736,7 +736,7 @@ struct SpellBuffFade_Struct { /*006*/ uint8 effect; /*007*/ uint8 unknown7; /*008*/ uint32 spellid; -/*012*/ uint32 duration; +/*012*/ int32 duration; /*016*/ uint32 num_hits; /*020*/ uint32 unknown020; // Global player ID? /*024*/ uint32 playerId; // Player id who cast the buff @@ -2473,7 +2473,7 @@ struct GroupFollow_Struct { // Live Follow Struct struct InspectBuffs_Struct { /*000*/ uint32 spell_id[BUFF_COUNT]; -/*168*/ uint32 tics_remaining[BUFF_COUNT]; +/*168*/ int32 tics_remaining[BUFF_COUNT]; }; struct LFG_Struct { diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index 8fcfdb21d..7db21597c 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -676,7 +676,7 @@ struct SpellBuff_Struct /*005*/ uint32 player_id; // 'global' ID of the caster, for wearoff messages /*009*/ uint32 unknown016; /*013*/ uint8 bard_modifier; -/*014*/ uint32 duration; +/*014*/ int32 duration; /*018*/ uint8 level; /*019*/ uint32 spellid; /*023*/ uint32 counters; @@ -692,7 +692,7 @@ struct SpellBuff_Struct_Old /*003*/ uint8 effect; // not real /*004*/ float unknown004; // Seen 1 for no buff /*008*/ uint32 spellid; -/*012*/ uint32 duration; +/*012*/ int32 duration; /*016*/ uint32 unknown016; /*020*/ uint32 player_id; // 'global' ID of the caster, for wearoff messages /*024*/ uint32 counters; @@ -709,7 +709,7 @@ struct SpellBuffFade_Struct_Live { /*007*/ uint8 unknown007; /*008*/ float unknown008; /*012*/ uint32 spellid; -/*016*/ uint32 duration; +/*016*/ int32 duration; /*020*/ uint32 playerId; // Global player ID? /*024*/ uint32 num_hits; /*028*/ uint8 unknown0028[64]; @@ -725,7 +725,7 @@ struct SpellBuffFade_Struct { /*006*/ uint8 effect; /*007*/ uint8 unknown7; /*008*/ uint32 spellid; -/*012*/ uint32 duration; +/*012*/ int32 duration; /*016*/ uint32 num_hits; /*020*/ uint32 unknown020; // Global player ID? /*024*/ uint32 playerId; // Player id who cast the buff @@ -2501,7 +2501,7 @@ struct GroupFollow_Struct { // Live Follow Struct struct InspectBuffs_Struct { /*000*/ uint32 spell_id[BUFF_COUNT]; -/*168*/ uint32 tics_remaining[BUFF_COUNT]; +/*168*/ int32 tics_remaining[BUFF_COUNT]; }; struct LFG_Struct { diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 584318a71..2b7178daf 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -547,7 +547,7 @@ struct SpellBuff_Struct /*002*/ uint8 bard_modifier; /*003*/ uint8 effect; //not real /*004*/ uint32 spellid; -/*008*/ uint32 duration; +/*008*/ int32 duration; /*012*/ uint32 counters; /*016*/ uint32 unknown004; //Might need to be swapped with player_id /*020*/ uint32 player_id; //'global' ID of the caster, for wearoff messages @@ -564,7 +564,7 @@ struct SpellBuffFade_Struct { /*006*/ uint8 effect; /*007*/ uint8 unknown7; /*008*/ uint32 spellid; -/*012*/ uint32 duration; +/*012*/ int32 duration; /*016*/ uint32 unknown016; /*020*/ uint32 unknown020; //prolly global player ID /*024*/ uint32 playerId; // Player id who cast the buff diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index 23404e051..f7ad59011 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -526,7 +526,7 @@ struct SpellBuff_Struct /*002*/ uint8 bard_modifier; /*003*/ uint8 effect; //not real /*004*/ uint32 spellid; -/*008*/ uint32 duration; +/*008*/ int32 duration; /*012*/ uint32 counters; /*016*/ uint32 unknown004; //Might need to be swapped with player_id /*020*/ uint32 player_id; //'global' ID of the caster, for wearoff messages @@ -543,7 +543,7 @@ struct SpellBuffFade_Struct { /*006*/ uint8 effect; /*007*/ uint8 unknown7; /*008*/ uint32 spellid; -/*012*/ uint32 duration; +/*012*/ int32 duration; /*016*/ uint32 unknown016; /*020*/ uint32 unknown020; //prolly global player ID /*024*/ uint32 playerId; // Player id who cast the buff diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index 89a3634fd..bdca057d0 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -445,7 +445,7 @@ struct SpellBuff_Struct /*002*/ uint8 bard_modifier; /*003*/ uint8 effect; //not real /*004*/ uint32 spellid; -/*008*/ uint32 duration; +/*008*/ int32 duration; /*012*/ uint32 counters; /*016*/ uint32 player_id; //'global' ID of the caster, for wearoff messages }; @@ -457,7 +457,7 @@ struct SpellBuffFade_Struct { /*006*/ uint8 effect; /*007*/ uint8 unknown7; /*008*/ uint32 spellid; -/*012*/ uint32 duration; +/*012*/ int32 duration; /*016*/ uint32 unknown016; /*020*/ uint32 unknown020; //prolly global player ID /*024*/ uint32 slotid; diff --git a/common/patches/uf_structs.h b/common/patches/uf_structs.h index 66cc58d1d..79363d68a 100644 --- a/common/patches/uf_structs.h +++ b/common/patches/uf_structs.h @@ -551,7 +551,7 @@ struct SpellBuff_Struct /*003*/ uint8 effect; // not real /*004*/ uint32 unknown004; // Seen 1 for no buff /*008*/ uint32 spellid; -/*012*/ uint32 duration; +/*012*/ int32 duration; /*016*/ uint32 unknown016; /*020*/ uint32 player_id; // 'global' ID of the caster, for wearoff messages /*024*/ uint32 counters; @@ -568,7 +568,7 @@ struct SpellBuffFade_Struct_Underfoot { /*007*/ uint8 unknown7; /*008*/ float unknown008; /*012*/ uint32 spellid; -/*016*/ uint32 duration; +/*016*/ int32 duration; /*020*/ uint32 num_hits; /*024*/ uint32 playerId; // Global player ID? /*028*/ uint32 unknown020; @@ -585,7 +585,7 @@ struct SpellBuffFade_Struct { /*006*/ uint8 effect; /*007*/ uint8 unknown7; /*008*/ uint32 spellid; -/*012*/ uint32 duration; +/*012*/ int32 duration; /*016*/ uint32 unknown016; /*020*/ uint32 unknown020; // Global player ID? /*024*/ uint32 playerId; // Player id who cast the buff @@ -2188,7 +2188,7 @@ struct GroupFollow_Struct { // Underfoot Follow Struct struct InspectBuffs_Struct { /*000*/ uint32 spell_id[BUFF_COUNT]; /*100*/ uint32 filler100[5]; // BUFF_COUNT is really 30... -/*120*/ uint32 tics_remaining[BUFF_COUNT]; +/*120*/ int32 tics_remaining[BUFF_COUNT]; /*220*/ uint32 filler220[5]; // BUFF_COUNT is really 30... }; diff --git a/common/version.h b/common/version.h index 9ce7b285c..fd0f433f4 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 9078 +#define CURRENT_BINARY_DATABASE_VERSION 9079 #define COMPILE_DATE __DATE__ #define COMPILE_TIME __TIME__ #ifndef WIN32 diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 537f8009b..00d7b9609 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -332,6 +332,7 @@ 9076|2015_02_04_average_coin.sql|SHOW COLUMNS FROM `loottable` WHERE Field = 'avgcoin'|contains|smallint 9077|2015_02_12_zone_gravity.sql|SHOW COLUMNS FROM `zone` LIKE 'gravity'|empty| 9078|2015_05_20_BuffInstrumentMod.sql|SHOW COLUMNS FROM `character_buffs` LIKE 'instrument_mod'|empty| +9079|2015_05_23_BuffDurations.sql|SHOW COLUMNS FROM `character_buffs` LIKE 'ticsremaining'|contains|unsigned| # 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/2015_05_23_BuffDurations.sql b/utils/sql/git/required/2015_05_23_BuffDurations.sql new file mode 100644 index 000000000..5f03039a6 --- /dev/null +++ b/utils/sql/git/required/2015_05_23_BuffDurations.sql @@ -0,0 +1,2 @@ +ALTER TABLE `character_buffs` CHANGE COLUMN `ticsremaining` `ticsremaining` INT(11) SIGNED NOT NULL; +ALTER TABLE `merc_buffs` CHANGE COLUMN `TicsRemaining` `TicsRemaining` INT(11) SIGNED NOT NULL DEFAULT 0; diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index ab4afeab4..5dd7440f6 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1473,7 +1473,7 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon) } void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *new_bonus, uint16 casterId, - uint8 WornType, uint32 ticsremaining, int buffslot, int instrument_mod, + uint8 WornType, int32 ticsremaining, int buffslot, int instrument_mod, bool IsAISpellEffect, uint16 effect_id, int32 se_base, int32 se_limit, int32 se_max) { int i, effect_value, base2, max, effectid; diff --git a/zone/effects.cpp b/zone/effects.cpp index 8a42b8e09..0f9dcfca1 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -89,7 +89,7 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { if (IsClient() && GetClass() == WIZARD) ratio += RuleI(Spells, WizCritRatio); //Default is zero - + if (Critical){ value = value_BaseEffect*ratio/100; @@ -172,7 +172,7 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) { value += int(value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100)*ratio/100; value += int(value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100; value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100; - extra_dmg = target->GetFcDamageAmtIncoming(this, spell_id) + + extra_dmg = target->GetFcDamageAmtIncoming(this, spell_id) + int(GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100) + GetFocusEffect(focusFcDamageAmt, spell_id); @@ -219,11 +219,11 @@ int32 Mob::GetExtraSpellAmt(uint16 spell_id, int32 extra_spell_amt, int32 base_s total_cast_time = spells[spell_id].recovery_time + spells[spell_id].cast_time; if (total_cast_time > 0 && total_cast_time <= 2500) - extra_spell_amt = extra_spell_amt*25/100; - else if (total_cast_time > 2500 && total_cast_time < 7000) - extra_spell_amt = extra_spell_amt*(167*((total_cast_time - 1000)/1000)) / 1000; - else - extra_spell_amt = extra_spell_amt * total_cast_time / 7000; + extra_spell_amt = extra_spell_amt*25/100; + else if (total_cast_time > 2500 && total_cast_time < 7000) + extra_spell_amt = extra_spell_amt*(167*((total_cast_time - 1000)/1000)) / 1000; + else + extra_spell_amt = extra_spell_amt * total_cast_time / 7000; if(extra_spell_amt*2 < base_spell_dmg) return 0; @@ -281,7 +281,7 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { if (Critical) { entity_list.MessageClose_StringID(this, true, 100, MT_SpellCrits, OTHER_CRIT_HEAL, GetName(), itoa(value)); - + if (IsClient()) Message_StringID(MT_SpellCrits, YOU_CRIT_HEAL, itoa(value)); } @@ -413,6 +413,10 @@ int32 Client::GetActSpellCost(uint16 spell_id, int32 cost) int32 Mob::GetActSpellDuration(uint16 spell_id, int32 duration) { + if ((aabonuses.IllusionPersistence || spellbonuses.IllusionPersistence || itembonuses.IllusionPersistence) && + IsEffectInSpell(spell_id, SE_Illusion)) + return 10000; // ~16h + if (spells[spell_id].not_extendable) return duration; @@ -771,7 +775,7 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); } } else { - if (spells[spell_id].aemaxtargets && iCounter < spells[spell_id].aemaxtargets) + if (spells[spell_id].aemaxtargets && iCounter < spells[spell_id].aemaxtargets) caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); if (!spells[spell_id].aemaxtargets) caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); @@ -859,7 +863,7 @@ void EntityList::AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool aff if (!center->CheckLosFN(curmob)) continue; } else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... - // See notes in AESpell() above for more info. + // See notes in AESpell() above for more info. if (caster->IsAttackAllowed(curmob, true)) continue; if (caster->CheckAggro(curmob)) diff --git a/zone/mob.h b/zone/mob.h index 579ec75a8..bf7e2030a 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -200,7 +200,7 @@ public: bool IsBeneficialAllowed(Mob *target); virtual int GetCasterLevel(uint16 spell_id); void ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* newbon, uint16 casterID = 0, - uint8 WornType = 0, uint32 ticsremaining = 0, int buffslot = -1, int instrument_mod = 10, + uint8 WornType = 0, int32 ticsremaining = 0, int buffslot = -1, int instrument_mod = 10, bool IsAISpellEffect = false, uint16 effect_id = 0, int32 se_base = 0, int32 se_limit = 0, int32 se_max = 0); void NegateSpellsBonuses(uint16 spell_id); virtual float GetActSpellRange(uint16 spell_id, float range, bool IsBard = false); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 19e83c4df..59b154835 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -83,7 +83,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if(c_override) { int durat = CalcBuffDuration(caster, this, spell_id, caster_level); - if(durat > 0) + if(durat) // negatives are perma buffs { buffslot = AddBuff(caster, spell_id, durat, caster_level); if(buffslot == -1) // stacking failure @@ -3366,7 +3366,7 @@ void Mob::BuffProcess() if(buffs[buffs_i].spellid == SPELL_UNKNOWN) continue; - if(spells[buffs[buffs_i].spellid].buffdurationformula != DF_Permanent) + if(buffs[buffs_i].ticsremaining > 0) // perma buffs will either be -1 or -4 { if(!zone->BuffTimersSuspended() || !IsSuspendableSpell(buffs[buffs_i].spellid)) { diff --git a/zone/spells.cpp b/zone/spells.cpp index 525470303..d0f60ea47 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2630,80 +2630,75 @@ int Mob::CalcBuffDuration(Mob *caster, Mob *target, uint16 spell_id, int32 caste Log.Out(Logs::Detail, Logs::Spells, "Spell %d: Casting level %d, formula %d, base_duration %d: result %d", spell_id, castlevel, formula, duration, res); - return(res); + return res; } // the generic formula calculations int CalcBuffDuration_formula(int level, int formula, int duration) { - int i; // temp variable + int temp; - switch(formula) - { - case 0: // not a buff - return 0; - - case 1: - i = (int)ceil(level / 2.0f); - return i < duration ? (i < 1 ? 1 : i) : duration; - - case 2: - i = (int)ceil(level / 5.0f * 3); - return i < duration ? (i < 1 ? 1 : i) : duration; - - case 3: - i = level * 30; - return i < duration ? (i < 1 ? 1 : i) : duration; - - case 4: // only used by 'LowerElement' - return ((duration != 0) ? duration : 50); - - case 5: - i = duration; - // 0 value results in a 3 tick spell, else its between 1-3 ticks. - return i < 3 ? (i < 1 ? 3 : i) : 3; - - case 6: - i = (int)ceil(level / 2.0f); - return i < duration ? (i < 1 ? 1 : i) : duration; - - case 7: - i = level; - return (duration == 0) ? (i < 1 ? 1 : i) : duration; - - case 8: - i = level + 10; - return i < duration ? (i < 1 ? 1 : i) : duration; - - case 9: - i = level * 2 + 10; - return i < duration ? (i < 1 ? 1 : i) : duration; - - case 10: - i = level * 3 + 10; - return i < duration ? (i < 1 ? 1 : i) : duration; - - case 11: - return std::min((level + 3) * 30, duration); - - case 12: - case 13: - case 14: - case 15: // Don't know what the real formula for this should be. Used by Skinspikes potion. - return duration; - - case 50: // Permanent. Cancelled by casting/combat for perm invis, non-lev zones for lev, curing poison/curse counters, etc. - return 72000; // 5 days until better method to make permanent - - //case 51: // Permanent. Cancelled when out of range of aura. Placeholder until appropriate duration identified. - - case 3600: - return duration ? duration : 3600; - - default: - Log.Out(Logs::General, Logs::None, "CalcBuffDuration_formula: unknown formula %d", formula); + switch (formula) { + case 1: + temp = level > 3 ? level / 2 : 1; + break; + case 2: + temp = level > 3 ? level / 2 + 5 : 6; + break; + case 3: + temp = 30 * level; + break; + case 4: // only used by 'LowerElement' + temp = 50; + break; + case 5: + temp = 2; + break; + case 6: + temp = level / 2 + 2; + break; + case 7: + temp = level; + break; + case 8: + temp = level + 10; + break; + case 9: + temp = 2 * level + 10; + break; + case 10: + temp = 3 * level + 10; + break; + case 11: + temp = 30 * (level + 3); + break; + case 12: + temp = level > 7 ? level / 4 : 1; + break; + case 13: + temp = 4 * level + 10; + break; + case 14: + temp = 5 * (level + 2); + break; + case 15: + temp = 10 * (level + 10); + break; + case 50: // Permanent. Cancelled by casting/combat for perm invis, non-lev zones for lev, curing poison/curse + // counters, etc. + return -1; + case 51: // Permanent. Cancelled when out of range of aura. + return -4; + default: + // the client function has another bool parameter that if true returns -2 -- unsure + if (formula < 200) return 0; + temp = formula; + break; } + if (duration && duration < temp) + temp = duration; + return temp; } // helper function for AddBuff to determine stacking @@ -3066,7 +3061,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid if (duration == 0) { duration = CalcBuffDuration(caster, this, spell_id); - if (caster) + if (caster && duration > 0) // negatives are perma buffs duration = caster->GetActSpellDuration(spell_id, duration); } diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 8369652b7..0313b8064 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -2504,7 +2504,7 @@ void ZoneDatabase::SaveMercBuffs(Merc *merc) { "TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, " "CorruptionCounters, HitCount, MeleeRune, MagicRune, dot_rune, " "caston_x, Persistent, caston_y, caston_z, ExtraDIChance) " - "VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %i, %u, %i, %i, %i);", + "VALUES (%u, %u, %u, %u, %u, %d, %u, %u, %u, %u, %u, %u, %u, %i, %u, %i, %i, %i);", merc->GetMercID(), buffs[buffCount].spellid, buffs[buffCount].casterlevel, spells[buffs[buffCount].spellid].buffdurationformula, buffs[buffCount].ticsremaining, CalculatePoisonCounters(buffs[buffCount].spellid) > 0 ? buffs[buffCount].counters : 0, @@ -2982,7 +2982,7 @@ void ZoneDatabase::SaveBuffs(Client *client) { "caster_level, caster_name, ticsremaining, counters, numhits, melee_rune, " "magic_rune, persistent, dot_rune, caston_x, caston_y, caston_z, ExtraDIChance, " "instrument_mod) " - "VALUES('%u', '%u', '%u', '%u', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', " + "VALUES('%u', '%u', '%u', '%u', '%s', '%d', '%u', '%u', '%u', '%u', '%u', '%u', " "'%i', '%i', '%i', '%i', '%i')", client->CharacterID(), index, buffs[index].spellid, buffs[index].casterlevel, buffs[index].caster_name, buffs[index].ticsremaining, buffs[index].counters, buffs[index].numhits, buffs[index].melee_rune, @@ -3023,7 +3023,7 @@ void ZoneDatabase::LoadBuffs(Client *client) Client *caster = entity_list.GetClientByName(row[3]); uint32 caster_level = atoi(row[2]); - uint32 ticsremaining = atoul(row[4]); + int32 ticsremaining = atoi(row[4]); uint32 counters = atoul(row[5]); uint32 numhits = atoul(row[6]); uint32 melee_rune = atoul(row[7]); @@ -3128,12 +3128,12 @@ void ZoneDatabase::SavePetInfo(Client *client) query = StringFormat("INSERT INTO `character_pet_buffs` " "(`char_id`, `pet`, `slot`, `spell_id`, `caster_level`, " "`ticsremaining`, `counters`) " - "VALUES (%u, %u, %u, %u, %u, %u, %d)", + "VALUES (%u, %u, %u, %u, %u, %d, %d)", client->CharacterID(), pet, index, petinfo->Buffs[index].spellid, petinfo->Buffs[index].level, petinfo->Buffs[index].duration, petinfo->Buffs[index].counters); else - query += StringFormat(", (%u, %u, %u, %u, %u, %u, %d)", + query += StringFormat(", (%u, %u, %u, %u, %u, %d, %d)", client->CharacterID(), pet, index, petinfo->Buffs[index].spellid, petinfo->Buffs[index].level, petinfo->Buffs[index].duration, petinfo->Buffs[index].counters); @@ -3238,7 +3238,7 @@ void ZoneDatabase::LoadPetInfo(Client *client) { uint32 caster_level = atoi(row[3]); int caster_id = 0; // The castername field is currently unused - uint32 ticsremaining = atoul(row[5]); + int32 ticsremaining = atoi(row[5]); uint32 counters = atoul(row[6]); pi->Buffs[slot_id].spellid = spell_id; From 00721f4a9646d7b826299270b0267620f0b8b106 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 23 May 2015 17:14:08 -0400 Subject: [PATCH 049/129] Fix pet instrument mod issue --- common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + .../2015_05_23_PetBuffInstrumentMod.sql | 1 + zone/pets.cpp | 3 +- zone/zonedb.cpp | 135 +++++++++--------- 5 files changed, 75 insertions(+), 67 deletions(-) create mode 100644 utils/sql/git/required/2015_05_23_PetBuffInstrumentMod.sql diff --git a/common/version.h b/common/version.h index fd0f433f4..062afdecd 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 9079 +#define CURRENT_BINARY_DATABASE_VERSION 9080 #define COMPILE_DATE __DATE__ #define COMPILE_TIME __TIME__ #ifndef WIN32 diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 00d7b9609..954abe263 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -333,6 +333,7 @@ 9077|2015_02_12_zone_gravity.sql|SHOW COLUMNS FROM `zone` LIKE 'gravity'|empty| 9078|2015_05_20_BuffInstrumentMod.sql|SHOW COLUMNS FROM `character_buffs` LIKE 'instrument_mod'|empty| 9079|2015_05_23_BuffDurations.sql|SHOW COLUMNS FROM `character_buffs` LIKE 'ticsremaining'|contains|unsigned| +9080|2015_05_23_PetBuffInstrumentMod.sql|SHOW COLUMNS FROM `character_pet_buffs` LIKE 'instrument_mod'|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/2015_05_23_PetBuffInstrumentMod.sql b/utils/sql/git/required/2015_05_23_PetBuffInstrumentMod.sql new file mode 100644 index 000000000..40cc453dc --- /dev/null +++ b/utils/sql/git/required/2015_05_23_PetBuffInstrumentMod.sql @@ -0,0 +1 @@ +ALTER TABLE `character_pet_buffs` ADD COLUMN `instrument_mod` tinyint UNSIGNED DEFAULT 10 NOT NULL; diff --git a/zone/pets.cpp b/zone/pets.cpp index 5a6a0be9f..a504a05e9 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -426,7 +426,7 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, entity_list.AddNPC(npc, true, true); SetPetID(npc->GetID()); // We need to handle PetType 5 (petHatelist), add the current target to the hatelist of the pet - + if (record.petcontrol == petTargetLock) { @@ -588,6 +588,7 @@ void NPC::SetPetState(SpellBuff_Struct *pet_buffs, uint32 *items) { buffs[i].casterid = 0; buffs[i].counters = pet_buffs[i].counters; buffs[i].numhits = spells[pet_buffs[i].spellid].numhits; + buffs[i].instrument_mod = pet_buffs[i].bard_modifier; } else { buffs[i].spellid = SPELL_UNKNOWN; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 0313b8064..e81a66308 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -3127,16 +3127,16 @@ void ZoneDatabase::SavePetInfo(Client *client) if (query.length() == 0) query = StringFormat("INSERT INTO `character_pet_buffs` " "(`char_id`, `pet`, `slot`, `spell_id`, `caster_level`, " - "`ticsremaining`, `counters`) " - "VALUES (%u, %u, %u, %u, %u, %d, %d)", + "`ticsremaining`, `counters`, `instrument_mod`) " + "VALUES (%u, %u, %u, %u, %u, %d, %d, %u)", client->CharacterID(), pet, index, petinfo->Buffs[index].spellid, petinfo->Buffs[index].level, petinfo->Buffs[index].duration, - petinfo->Buffs[index].counters); + petinfo->Buffs[index].counters, petinfo->Buffs[index].bard_modifier); else - query += StringFormat(", (%u, %u, %u, %u, %u, %d, %d)", + query += StringFormat(", (%u, %u, %u, %u, %u, %d, %d, %u)", client->CharacterID(), pet, index, petinfo->Buffs[index].spellid, petinfo->Buffs[index].level, petinfo->Buffs[index].duration, - petinfo->Buffs[index].counters); + petinfo->Buffs[index].counters, petinfo->Buffs[index].bard_modifier); } database.QueryDatabase(query); query.clear(); @@ -3174,7 +3174,8 @@ void ZoneDatabase::UpdateItemRecastTimestamps(uint32 char_id, uint32 recast_type QueryDatabase(query); } -void ZoneDatabase::LoadPetInfo(Client *client) { +void ZoneDatabase::LoadPetInfo(Client *client) +{ // Load current pet and suspended pet PetInfo *petinfo = client->GetPetInfo(0); @@ -3183,17 +3184,18 @@ void ZoneDatabase::LoadPetInfo(Client *client) { memset(petinfo, 0, sizeof(PetInfo)); memset(suspended, 0, sizeof(PetInfo)); - std::string query = StringFormat("SELECT `pet`, `petname`, `petpower`, `spell_id`, " - "`hp`, `mana`, `size` FROM `character_pet_info` " - "WHERE `char_id` = %u", client->CharacterID()); - auto results = database.QueryDatabase(query); - if(!results.Success()) { + std::string query = StringFormat("SELECT `pet`, `petname`, `petpower`, `spell_id`, " + "`hp`, `mana`, `size` FROM `character_pet_info` " + "WHERE `char_id` = %u", + client->CharacterID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { return; } - PetInfo *pi; + PetInfo *pi; for (auto row = results.begin(); row != results.end(); ++row) { - uint16 pet = atoi(row[0]); + uint16 pet = atoi(row[0]); if (pet == 0) pi = petinfo; @@ -3202,7 +3204,7 @@ void ZoneDatabase::LoadPetInfo(Client *client) { else continue; - strncpy(pi->Name,row[1],64); + strncpy(pi->Name, row[1], 64); pi->petpower = atoi(row[2]); pi->SpellID = atoi(row[3]); pi->HP = atoul(row[4]); @@ -3210,56 +3212,60 @@ void ZoneDatabase::LoadPetInfo(Client *client) { pi->size = atof(row[6]); } - query = StringFormat("SELECT `pet`, `slot`, `spell_id`, `caster_level`, `castername`, " - "`ticsremaining`, `counters` FROM `character_pet_buffs` " - "WHERE `char_id` = %u", client->CharacterID()); - results = QueryDatabase(query); - if (!results.Success()) { - return; - } - - for (auto row = results.begin(); row != results.end(); ++row) { - uint16 pet = atoi(row[0]); - if (pet == 0) - pi = petinfo; - else if (pet == 1) - pi = suspended; - else - continue; - - uint32 slot_id = atoul(row[1]); - if(slot_id >= RuleI(Spells, MaxTotalSlotsPET)) - continue; - - uint32 spell_id = atoul(row[2]); - if(!IsValidSpell(spell_id)) - continue; - - uint32 caster_level = atoi(row[3]); - int caster_id = 0; - // The castername field is currently unused - int32 ticsremaining = atoi(row[5]); - uint32 counters = atoul(row[6]); - - pi->Buffs[slot_id].spellid = spell_id; - pi->Buffs[slot_id].level = caster_level; - pi->Buffs[slot_id].player_id = caster_id; - pi->Buffs[slot_id].slotid = 2; // Always 2 in buffs struct for real buffs - - pi->Buffs[slot_id].duration = ticsremaining; - pi->Buffs[slot_id].counters = counters; - } - - query = StringFormat("SELECT `pet`, `slot`, `item_id` " - "FROM `character_pet_inventory` " - "WHERE `char_id`=%u",client->CharacterID()); - results = database.QueryDatabase(query); - if (!results.Success()) { + query = StringFormat("SELECT `pet`, `slot`, `spell_id`, `caster_level`, `castername`, " + "`ticsremaining`, `counters`, `instrument_mod` FROM `character_pet_buffs` " + "WHERE `char_id` = %u", + client->CharacterID()); + results = QueryDatabase(query); + if (!results.Success()) { return; } - for(auto row = results.begin(); row != results.end(); ++row) { - uint16 pet = atoi(row[0]); + for (auto row = results.begin(); row != results.end(); ++row) { + uint16 pet = atoi(row[0]); + if (pet == 0) + pi = petinfo; + else if (pet == 1) + pi = suspended; + else + continue; + + uint32 slot_id = atoul(row[1]); + if (slot_id >= RuleI(Spells, MaxTotalSlotsPET)) + continue; + + uint32 spell_id = atoul(row[2]); + if (!IsValidSpell(spell_id)) + continue; + + uint32 caster_level = atoi(row[3]); + int caster_id = 0; + // The castername field is currently unused + int32 ticsremaining = atoi(row[5]); + uint32 counters = atoul(row[6]); + uint8 bard_mod = atoul(row[7]); + + pi->Buffs[slot_id].spellid = spell_id; + pi->Buffs[slot_id].level = caster_level; + pi->Buffs[slot_id].player_id = caster_id; + pi->Buffs[slot_id].slotid = 2; // Always 2 in buffs struct for real buffs + + pi->Buffs[slot_id].duration = ticsremaining; + pi->Buffs[slot_id].counters = counters; + pi->Buffs[slot_id].bard_modifier = bard_mod; + } + + query = StringFormat("SELECT `pet`, `slot`, `item_id` " + "FROM `character_pet_inventory` " + "WHERE `char_id`=%u", + client->CharacterID()); + results = database.QueryDatabase(query); + if (!results.Success()) { + return; + } + + for (auto row = results.begin(); row != results.end(); ++row) { + uint16 pet = atoi(row[0]); if (pet == 0) pi = petinfo; else if (pet == 1) @@ -3269,11 +3275,10 @@ void ZoneDatabase::LoadPetInfo(Client *client) { int slot = atoi(row[1]); if (slot < EmuConstants::EQUIPMENT_BEGIN || slot > EmuConstants::EQUIPMENT_END) - continue; - - pi->Items[slot] = atoul(row[2]); - } + continue; + pi->Items[slot] = atoul(row[2]); + } } bool ZoneDatabase::GetFactionData(FactionMods* fm, uint32 class_mod, uint32 race_mod, uint32 deity_mod, int32 faction_id) { From a46b1ac18b3243296b27105c454dd9eb3bfea77a Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 23 May 2015 17:36:05 -0400 Subject: [PATCH 050/129] Need to actually copy the bard mod too! --- zone/pets.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/pets.cpp b/zone/pets.cpp index a504a05e9..908b756d4 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -555,6 +555,7 @@ void NPC::GetPetState(SpellBuff_Struct *pet_buffs, uint32 *items, char *name) { pet_buffs[i].level = buffs[i].casterlevel; pet_buffs[i].effect = 10; pet_buffs[i].counters = buffs[i].counters; + pet_buffs[i].bard_modifier = buffs[i].instrument_mod; } else { pet_buffs[i].spellid = SPELL_UNKNOWN; From 70048eb6e111af1b13d4bf98e18a7a59b2521a46 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 23 May 2015 17:45:51 -0400 Subject: [PATCH 051/129] SE_IllusionPersistence affects self only --- zone/effects.cpp | 4 ---- zone/spell_effects.cpp | 5 +++-- zone/spells.cpp | 4 ++++ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 0f9dcfca1..493de9fa7 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -413,10 +413,6 @@ int32 Client::GetActSpellCost(uint16 spell_id, int32 cost) int32 Mob::GetActSpellDuration(uint16 spell_id, int32 duration) { - if ((aabonuses.IllusionPersistence || spellbonuses.IllusionPersistence || itembonuses.IllusionPersistence) && - IsEffectInSpell(spell_id, SE_Illusion)) - return 10000; // ~16h - if (spells[spell_id].not_extendable) return duration; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 59b154835..5fcee64cb 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1441,8 +1441,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) for(int x = EmuConstants::MATERIAL_BEGIN; x <= EmuConstants::MATERIAL_TINT_END; x++) SendWearChange(x); - if(caster && (caster->spellbonuses.IllusionPersistence || caster->aabonuses.IllusionPersistence - || caster->itembonuses.IllusionPersistence)) + if (caster == this && + (spellbonuses.IllusionPersistence || aabonuses.IllusionPersistence || + itembonuses.IllusionPersistence)) buffs[buffslot].persistant_buff = 1; else buffs[buffslot].persistant_buff = 0; diff --git a/zone/spells.cpp b/zone/spells.cpp index d0f60ea47..d8699d068 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2624,6 +2624,10 @@ int Mob::CalcBuffDuration(Mob *caster, Mob *target, uint16 spell_id, int32 caste castlevel = caster_level_override; int res = CalcBuffDuration_formula(castlevel, formula, duration); + if (caster == target && (target->aabonuses.IllusionPersistence || target->spellbonuses.IllusionPersistence || + target->itembonuses.IllusionPersistence) && + IsEffectInSpell(spell_id, SE_Illusion)) + res = 10000; // ~16h override res = mod_buff_duration(res, caster, target, spell_id); From 7041db7480b6acfe5056ea3dd679db28ed6c1171 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 23 May 2015 15:50:35 -0700 Subject: [PATCH 052/129] Adding dbstr_us.txt support to client files --- client_files/export/main.cpp | 36 ++++++++++++ client_files/import/main.cpp | 56 ++++++++++++++++++- .../sql/git/required/2015_05_23_dbstr_us.sql | 6 ++ 3 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 utils/sql/git/required/2015_05_23_dbstr_us.sql diff --git a/client_files/export/main.cpp b/client_files/export/main.cpp index 865fbd6d8..7ad7bf3e1 100644 --- a/client_files/export/main.cpp +++ b/client_files/export/main.cpp @@ -32,6 +32,7 @@ EQEmuLogSys Log; void ExportSpells(SharedDatabase *db); void ExportSkillCaps(SharedDatabase *db); void ExportBaseData(SharedDatabase *db); +void ExportDBStrings(SharedDatabase *db); int main(int argc, char **argv) { RegisterExecutablePlatform(ExePlatformClientExport); @@ -62,6 +63,7 @@ int main(int argc, char **argv) { ExportSpells(&database); ExportSkillCaps(&database); ExportBaseData(&database); + ExportDBStrings(&database); Log.CloseFileLogs(); @@ -200,3 +202,37 @@ void ExportBaseData(SharedDatabase *db) { fclose(f); } +void ExportDBStrings(SharedDatabase *db) { + Log.Out(Logs::General, Logs::Status, "Exporting DB Strings..."); + + FILE *f = fopen("export/dbstr_us.txt", "w"); + if(!f) { + Log.Out(Logs::General, Logs::Error, "Unable to open export/dbstr_us.txt to write, skipping."); + return; + } + + fprintf(f, "Major^Minor^String(New)\n"); + const std::string query = "SELECT * FROM db_str ORDER BY id, type"; + auto results = db->QueryDatabase(query); + if(results.Success()) { + for(auto row = results.begin(); row != results.end(); ++row) { + std::string line; + unsigned int fields = results.ColumnCount(); + for(unsigned int rowIndex = 0; rowIndex < fields; ++rowIndex) { + if(rowIndex != 0) + line.push_back('^'); + + if(row[rowIndex] != nullptr) { + line += row[rowIndex]; + } + } + + fprintf(f, "%s\n", line.c_str()); + } + } + else { + } + + fclose(f); +} + diff --git a/client_files/import/main.cpp b/client_files/import/main.cpp index 3a2f4154f..72ae6fd5d 100644 --- a/client_files/import/main.cpp +++ b/client_files/import/main.cpp @@ -30,6 +30,7 @@ EQEmuLogSys Log; void ImportSpells(SharedDatabase *db); void ImportSkillCaps(SharedDatabase *db); void ImportBaseData(SharedDatabase *db); +void ImportDBStrings(SharedDatabase *db); int main(int argc, char **argv) { RegisterExecutablePlatform(ExePlatformClientImport); @@ -59,6 +60,7 @@ int main(int argc, char **argv) { ImportSpells(&database); ImportSkillCaps(&database); ImportBaseData(&database); + ImportDBStrings(&database); Log.CloseFileLogs(); @@ -202,7 +204,6 @@ void ImportSkillCaps(SharedDatabase *db) { continue; } - int class_id, skill_id, level, cap; class_id = atoi(split[0].c_str()); skill_id = atoi(split[1].c_str()); @@ -262,3 +263,56 @@ void ImportBaseData(SharedDatabase *db) { fclose(f); } + +void ImportDBStrings(SharedDatabase *db) { + Log.Out(Logs::General, Logs::Status, "Importing DB Strings..."); + + FILE *f = fopen("import/dbstr_us.txt", "r"); + if(!f) { + Log.Out(Logs::General, Logs::Error, "Unable to open import/dbstr_us.txt to read, skipping."); + return; + } + + std::string delete_sql = "DELETE FROM db_str"; + db->QueryDatabase(delete_sql); + + char buffer[2048]; + bool first = true; + while(fgets(buffer, 2048, f)) { + if(first) { + first = false; + continue; + } + + for(int i = 0; i < 2048; ++i) { + if(buffer[i] == '\n') { + buffer[i] = 0; + break; + } + } + + auto split = SplitString(buffer, '^'); + + if(split.size() < 2) { + continue; + } + + std::string sql; + int id, type; + std::string value; + + id = atoi(split[0].c_str()); + type = atoi(split[1].c_str()); + + if(split.size() >= 3) { + value = ::EscapeString(split[2]); + } + + sql = StringFormat("INSERT INTO db_str(id, type, value) VALUES(%u, %u, '%s')", + id, type, value.c_str()); + + db->QueryDatabase(sql); + } + + fclose(f); +} diff --git a/utils/sql/git/required/2015_05_23_dbstr_us.sql b/utils/sql/git/required/2015_05_23_dbstr_us.sql new file mode 100644 index 000000000..d4e789b83 --- /dev/null +++ b/utils/sql/git/required/2015_05_23_dbstr_us.sql @@ -0,0 +1,6 @@ +CREATE TABLE `db_str` ( + `id` INT(10) NOT NULL, + `type` INT(10) NOT NULL, + `value` TEXT NOT NULL, + PRIMARY KEY (`id`, `type`) +);; From 7d61934ce640c4e0c68f71ef877be18affbfcc07 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 23 May 2015 15:52:42 -0700 Subject: [PATCH 053/129] Add db manifest for dbstr_us.txt stuff --- utils/sql/db_update_manifest.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 954abe263..34330f041 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -334,6 +334,7 @@ 9078|2015_05_20_BuffInstrumentMod.sql|SHOW COLUMNS FROM `character_buffs` LIKE 'instrument_mod'|empty| 9079|2015_05_23_BuffDurations.sql|SHOW COLUMNS FROM `character_buffs` LIKE 'ticsremaining'|contains|unsigned| 9080|2015_05_23_PetBuffInstrumentMod.sql|SHOW COLUMNS FROM `character_pet_buffs` LIKE 'instrument_mod'|empty| +9081|2015_05_23_dbstr_us.sql|SHOW TABLES LIKE 'db_str'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not From a882397eb632fcffde75941df2e5ce2c5d7fed46 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 23 May 2015 15:53:56 -0700 Subject: [PATCH 054/129] errant semi-colon, doesn't matter but still --- utils/sql/git/required/2015_05_23_dbstr_us.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/sql/git/required/2015_05_23_dbstr_us.sql b/utils/sql/git/required/2015_05_23_dbstr_us.sql index d4e789b83..1eba0afa6 100644 --- a/utils/sql/git/required/2015_05_23_dbstr_us.sql +++ b/utils/sql/git/required/2015_05_23_dbstr_us.sql @@ -3,4 +3,4 @@ CREATE TABLE `db_str` ( `type` INT(10) NOT NULL, `value` TEXT NOT NULL, PRIMARY KEY (`id`, `type`) -);; +); From ec8e7139ec8286e7b9616409b148962af3b11bd9 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 23 May 2015 15:54:33 -0700 Subject: [PATCH 055/129] errant else statements --- client_files/export/main.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/client_files/export/main.cpp b/client_files/export/main.cpp index 7ad7bf3e1..708d48456 100644 --- a/client_files/export/main.cpp +++ b/client_files/export/main.cpp @@ -196,7 +196,6 @@ void ExportBaseData(SharedDatabase *db) { fprintf(f, "%s\n", line.c_str()); } - } else { } fclose(f); @@ -230,8 +229,6 @@ void ExportDBStrings(SharedDatabase *db) { fprintf(f, "%s\n", line.c_str()); } } - else { - } fclose(f); } From 8922c72452b03c184654e8c88832f4e226ccd4bd Mon Sep 17 00:00:00 2001 From: Uleat Date: Sat, 23 May 2015 18:59:03 -0400 Subject: [PATCH 056/129] Added a name block memset to the server character select code and a few comments to the OP_CharInfo encodes --- common/patches/rof.cpp | 6 +++--- common/patches/rof2.cpp | 6 +++--- common/patches/sod.cpp | 6 +++--- common/patches/sof.cpp | 6 +++--- common/patches/uf.cpp | 6 +++--- world/worlddb.cpp | 1 + 6 files changed, 16 insertions(+), 15 deletions(-) diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 89ad7db7f..9f9f58ab2 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -2946,12 +2946,12 @@ namespace RoF for (int counter = 0; counter < character_count; ++counter) { emu_cse = (CharacterSelectEntry_Struct *)emu_ptr; - eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; + eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; // base address strcpy(eq_cse->Name, emu_cse->Name); eq_ptr += strlen(emu_cse->Name); - eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; - eq_cse->Name[0] = '\0'; + eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; // offset address (base + name length offset) + eq_cse->Name[0] = '\0'; // (offset)eq_cse->Name[0] = (base)eq_cse->Name[strlen(emu_cse->Name)] eq_cse->Class = emu_cse->Class; eq_cse->Race = emu_cse->Race; diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 75c4c7836..708c5a6a9 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -3035,12 +3035,12 @@ namespace RoF2 for (int counter = 0; counter < character_count; ++counter) { emu_cse = (CharacterSelectEntry_Struct *)emu_ptr; - eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; + eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; // base address strcpy(eq_cse->Name, emu_cse->Name); eq_ptr += strlen(emu_cse->Name); - eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; - eq_cse->Name[0] = '\0'; + eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; // offset address (base + name length offset) + eq_cse->Name[0] = '\0'; // (offset)eq_cse->Name[0] = (base)eq_cse->Name[strlen(emu_cse->Name)] eq_cse->Class = emu_cse->Class; eq_cse->Race = emu_cse->Race; diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 8e730cfb9..0a8073131 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -1962,7 +1962,7 @@ namespace SoD for (int counter = 0; counter < character_count; ++counter) { emu_cse = (CharacterSelectEntry_Struct *)emu_ptr; - eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; + eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; // base address eq_cse->Level = emu_cse->Level; eq_cse->HairStyle = emu_cse->HairStyle; @@ -1970,8 +1970,8 @@ namespace SoD strcpy(eq_cse->Name, emu_cse->Name); eq_ptr += strlen(emu_cse->Name); - eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; - eq_cse->Name[0] = '\0'; + eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; // offset address (base + name length offset) + eq_cse->Name[0] = '\0'; // (offset)eq_cse->Name[0] = (base)eq_cse->Name[strlen(emu_cse->Name)] eq_cse->Beard = emu_cse->Beard; eq_cse->HairColor = emu_cse->HairColor; diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index a677cede3..f0e8a2dbe 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -1621,7 +1621,7 @@ namespace SoF for (int counter = 0; counter < character_count; ++counter) { emu_cse = (CharacterSelectEntry_Struct *)emu_ptr; - eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; + eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; // base address eq_cse->Level = emu_cse->Level; eq_cse->HairStyle = emu_cse->HairStyle; @@ -1629,8 +1629,8 @@ namespace SoF strcpy(eq_cse->Name, emu_cse->Name); eq_ptr += strlen(emu_cse->Name); - eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; - eq_cse->Name[0] = '\0'; + eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; // offset address (base + name length offset) + eq_cse->Name[0] = '\0'; // (offset)eq_cse->Name[0] = (base)eq_cse->Name[strlen(emu_cse->Name)] eq_cse->Beard = emu_cse->Beard; eq_cse->HairColor = emu_cse->HairColor; diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index 4177e4f81..e963312e2 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -2253,7 +2253,7 @@ namespace UF for (int counter = 0; counter < character_count; ++counter) { emu_cse = (CharacterSelectEntry_Struct *)emu_ptr; - eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; + eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; // base address eq_cse->Level = emu_cse->Level; eq_cse->HairStyle = emu_cse->HairStyle; @@ -2261,8 +2261,8 @@ namespace UF strcpy(eq_cse->Name, emu_cse->Name); eq_ptr += strlen(emu_cse->Name); - eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; - eq_cse->Name[0] = '\0'; + eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; // offset address (base + name length offset) + eq_cse->Name[0] = '\0'; // (offset)eq_cse->Name[0] = (base)eq_cse->Name[strlen(emu_cse->Name)] eq_cse->Beard = emu_cse->Beard; eq_cse->HairColor = emu_cse->HairColor; diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 4b2a75ac6..b09758888 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -105,6 +105,7 @@ void WorldDatabase::GetCharSelectInfo(uint32 accountID, EQApplicationPacket **ou memset(&pp, 0, sizeof(PlayerProfile_Struct)); /* Fill CharacterSelectEntry_Struct */ + memset(cse->Name, 0, sizeof(cse->Name)); strcpy(cse->Name, row[1]); cse->Class = (uint8)atoi(row[4]); cse->Race = (uint32)atoi(row[3]); From 85bdcf413bdf4020763a8105a1d806fae208e335 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 24 May 2015 18:50:33 -0400 Subject: [PATCH 057/129] Bard songs go negative for some reason? Hot fix for now, I don't think they really need to be extended, but I need to investigate more. --- common/spdat.h | 33 +++++++++++++++++---------------- zone/spell_effects.cpp | 6 ++++-- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 487b5d00a..ff2976607 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -133,7 +133,7 @@ typedef enum { /* 42 */ ST_Directional = 0x2a, //ae around this target between two angles /* 43 */ ST_GroupClientAndPet = 0x2b, /* 44 */ ST_Beam = 0x2c, -/* 45 */ ST_Ring = 0x2d, +/* 45 */ ST_Ring = 0x2d, /* 46 */ ST_TargetsTarget = 0x2e, // uses the target of your target /* 47 */ ST_PetMaster = 0x2f, // uses the master as target /* 48 */ // UNKNOWN @@ -154,7 +154,7 @@ typedef enum { // full listing: https://forums.station.sony.com/eq/index.php?threads/enumerated-spa-list.206288/ // mirror: http://pastebin.com/MYeQqGwe #define SE_CurrentHP 0 // implemented - Heals and nukes, repeates every tic if in a buff -#define SE_ArmorClass 1 // implemented +#define SE_ArmorClass 1 // implemented #define SE_ATK 2 // implemented #define SE_MovementSpeed 3 // implemented - SoW, SoC, etc #define SE_STR 4 // implemented @@ -197,7 +197,7 @@ typedef enum { #define SE_Destroy 41 // implemented - Disintegrate, Banishment of Shadows #define SE_ShadowStep 42 // implemented #define SE_Berserk 43 // implemented (*not used in any known live spell) Makes client 'Berserk' giving crip blow chance. -#define SE_Lycanthropy 44 // implemented +#define SE_Lycanthropy 44 // implemented #define SE_Vampirism 45 // implemented (*not used in any known live spell) Stackable lifetap from melee. #define SE_ResistFire 46 // implemented #define SE_ResistCold 47 // implemented @@ -247,7 +247,7 @@ typedef enum { #define SE_SummonCorpse 91 // implemented #define SE_InstantHate 92 // implemented - add hate #define SE_StopRain 93 // implemented - Wake of Karana -#define SE_NegateIfCombat 94 // implemented +#define SE_NegateIfCombat 94 // implemented #define SE_Sacrifice 95 // implemented #define SE_Silence 96 // implemented #define SE_ManaPool 97 // implemented @@ -299,7 +299,7 @@ typedef enum { #define SE_LimitCastTimeMin 143 // implemented #define SE_LimitCastTimeMax 144 // implemented (*not used in any known live spell) #define SE_Teleport2 145 // implemented - Banishment of the Pantheon -//#define SE_ElectricityResist 146 // *not implemented (Lightning Rod: 23233) +//#define SE_ElectricityResist 146 // *not implemented (Lightning Rod: 23233) #define SE_PercentalHeal 147 // implemented #define SE_StackingCommand_Block 148 // implemented? #define SE_StackingCommand_Overwrite 149 // implemented? @@ -529,7 +529,7 @@ typedef enum { #define SE_CastOnFadeEffectAlways 373 // implemented - Triggers if fades after natural duration OR from rune/numhits fades. #define SE_ApplyEffect 374 // implemented #define SE_DotCritDmgIncrease 375 // implemented - Increase damage of DoT critical amount -//#define SE_Fling 376 // *not implemented - used in 2 test spells (12945 | Movement Test Spell 1) +//#define SE_Fling 376 // *not implemented - used in 2 test spells (12945 | Movement Test Spell 1) #define SE_CastOnFadeEffectNPC 377 // implemented - Triggers only if fades after natural duration (On live these are usually players spells that effect an NPC). #define SE_SpellEffectResistChance 378 // implemented - Increase chance to resist specific spell effect (base1=value, base2=spell effect id) #define SE_ShadowStepDirectional 379 // implemented - handled by client @@ -560,7 +560,7 @@ typedef enum { #define SE_LimitSpellSubclass 404 // *not implemented - Limits to specific types of spells (see CheckSpellCategory) [Categories NOT defined yet] #define SE_TwoHandBluntBlock 405 // implemented - chance to block attacks when using two hand blunt weapons (similiar to shield block) #define SE_CastonNumHitFade 406 // implemented - casts a spell when a buff fades due to its numhits being depleted -#define SE_CastonFocusEffect 407 // implemented - casts a spell if focus limits are met (ie triggers when a focus effects is applied) +#define SE_CastonFocusEffect 407 // implemented - casts a spell if focus limits are met (ie triggers when a focus effects is applied) #define SE_LimitHPPercent 408 // implemented - limited to a certain percent of your hp(ie heals up to 50%) #define SE_LimitManaPercent 409 // implemented - limited to a certain percent of your mana #define SE_LimitEndPercent 410 // implemented - limited to a certain percent of your end @@ -576,7 +576,7 @@ typedef enum { #define SE_FcLimitUse 420 // implemented - increases numhits count by percent (Note: not used in any known live spells) #define SE_FcIncreaseNumHits 421 // implemented[AA] - increases number of hits a buff has till fade. (focus) #define SE_LimitUseMin 422 // implemented - limit a focus to require a min amount of numhits value (used with above) -#define SE_LimitUseType 423 // implemented - limit a focus to require a certain numhits type +#define SE_LimitUseType 423 // implemented - limit a focus to require a certain numhits type #define SE_GravityEffect 424 // implemented - Pulls/pushes you toward/away the mob at a set pace //#define SE_Display 425 // *not implemented - Illusion: Flying Dragon(21626) //#define SE_IncreaseExtTargetWindow 426 // *not implmented[AA] - increases the capacity of your extended target window @@ -600,9 +600,9 @@ typedef enum { #define SE_ImprovedTaunt 444 // implemented - Locks Aggro On Caster and Decrease other Players Aggro by X% on NPC targets below level Y //#define SE_AddMercSlot 445 // *not implemented[AA] - [Hero's Barracks] Allows you to conscript additional mercs. #define SE_AStacker 446 // implementet - bufff stacking blocker (26219 | Qirik's Watch) -#define SE_BStacker 447 // implemented +#define SE_BStacker 447 // implemented #define SE_CStacker 448 // implemented -#define SE_DStacker 449 // implemented +#define SE_DStacker 449 // implemented #define SE_MitigateDotDamage 450 // implemented DOT spell mitigation rune with max value #define SE_MeleeThresholdGuard 451 // implemented Partial Melee Rune that only is lowered if melee hits are over X amount of damage #define SE_SpellThresholdGuard 452 // implemented Partial Spell Rune that only is lowered if spell hits are over X amount of damage @@ -618,6 +618,7 @@ typedef enum { #define DF_Permanent 50 +#define DF_Aura 51 // note this struct is historical, we don't actually need it to be // aligned to anything, but for maintaining it it is kept in the order that @@ -733,31 +734,31 @@ struct SPDat_Spell_Struct /* 197 */ bool not_extendable; /* 198- 199 */ /* 200 */ bool suspendable; // buff is suspended in suspended buff zones -/* 201 */ int viral_range; +/* 201 */ int viral_range; /* 202 */ /* 203 */ //int songcap; // individual song cap (how live currently does it, not implemented) /* 204 */ /* 205 */ bool no_block; -/* 206 */ +/* 206 */ /* 207 */ int spellgroup; /* 208 */ int rank; //increments AA effects with same name /* 209 */ int powerful_flag; // Need more investigation to figure out what to call this, for now we know -1 makes charm spells not break before their duration is complete, it does alot more though -/* 210 */ // bool DurationFrozen; ??? +/* 210 */ // bool DurationFrozen; ??? /* 211 */ int CastRestriction; //Various restriction categories for spells most seem targetable race related but have also seen others for instance only castable if target hp 20% or lower or only if target out of combat /* 212 */ bool AllowRest; /* 213 */ bool InCombat; //Allow spell if target is in combat /* 214 */ bool OutofCombat; //Allow spell if target is out of combat /* 215 - 217 */ -/* 218 */ int aemaxtargets; //Is used for various AE effects +/* 218 */ int aemaxtargets; //Is used for various AE effects /* 219 */ int maxtargets; //Is used for beam and ring spells for target # limits (not implemented) -/* 220 - 223 */ +/* 220 - 223 */ /* 224 */ bool persistdeath; // buff doesn't get stripped on death /* 225 - 226 */ /* 227 */ float min_dist; //spell power modified by distance from caster (Min Distance) /* 228 */ float min_dist_mod; //spell power modified by distance from caster (Modifier at Min Distance) /* 229 */ float max_dist; //spell power modified by distance from caster (Max Distance) /* 230 */ float max_dist_mod; //spell power modified by distance from caster (Modifier at Max Distance) -/* 231 */ float min_range; //Min casting range +/* 231 */ float min_range; //Min casting range /* 232 - 236 */ uint8 DamageShieldType; // This field does not exist in spells_us.txt }; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 5fcee64cb..a5b678e57 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3367,13 +3367,15 @@ void Mob::BuffProcess() if(buffs[buffs_i].spellid == SPELL_UNKNOWN) continue; - if(buffs[buffs_i].ticsremaining > 0) // perma buffs will either be -1 or -4 - { + // DF_Permanent uses -1 DF_Aura uses -4 but we need to check negatives for some spells for some reason? + if (spells[buffs[buffs_i].spellid].buffdurationformula != DF_Permanent && + spells[buffs[buffs_i].spellid].buffdurationformula != DF_Aura) { if(!zone->BuffTimersSuspended() || !IsSuspendableSpell(buffs[buffs_i].spellid)) { --buffs[buffs_i].ticsremaining; if (buffs[buffs_i].ticsremaining == 0) { + // Why do we need to let these go negative? Client uses negatives for perma buffs if (!IsShortDurationBuff(buffs[buffs_i].spellid) || IsFearSpell(buffs[buffs_i].spellid) || IsCharmSpell(buffs[buffs_i].spellid) || From 249d67a1c316c675268530fa607c8f02ff38ac06 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 25 May 2015 02:01:51 -0400 Subject: [PATCH 058/129] Bards get a bonus tic at some point Not sure what level but it's between 53 and 85 ... (although I remember reading something about around 60) I also didn't notice any of the odd effects the comments speak of ... I suspect they were fighting each other? --- zone/effects.cpp | 12 ++++-------- zone/spell_effects.cpp | 13 ------------- 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 493de9fa7..c814ea554 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -421,14 +421,10 @@ int32 Mob::GetActSpellDuration(uint16 spell_id, int32 duration) int tic_inc = 0; tic_inc = GetFocusEffect(focusSpellDurByTic, spell_id); - // Only need this for clients, since the change was for bard songs, I assume we should keep non bard songs getting +1 - // However if its bard or not and is mez, charm or fear, we need to add 1 so that client is in sync - if (IsClient() && !(IsShortDurationBuff(spell_id) && IsBardSong(spell_id)) || - IsFearSpell(spell_id) || - IsCharmSpell(spell_id) || - IsMezSpell(spell_id) || - IsBlindSpell(spell_id)) - tic_inc += 1; + // unsure on the exact details, but bard songs that don't cost mana at some point get an extra tick, 60 for now + // a level 53 bard reported getting 2 tics + if (IsShortDurationBuff(spell_id) && IsBardSong(spell_id) && spells[spell_id].mana == 0 && GetClass() == BARD && GetLevel() > 60) + tic_inc++; return (((duration * increase) / 100) + tic_inc); } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index a5b678e57..fc942f9fe 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3375,19 +3375,6 @@ void Mob::BuffProcess() --buffs[buffs_i].ticsremaining; if (buffs[buffs_i].ticsremaining == 0) { - // Why do we need to let these go negative? Client uses negatives for perma buffs - if (!IsShortDurationBuff(buffs[buffs_i].spellid) || - IsFearSpell(buffs[buffs_i].spellid) || - IsCharmSpell(buffs[buffs_i].spellid) || - IsMezSpell(buffs[buffs_i].spellid) || - IsBlindSpell(buffs[buffs_i].spellid)) - { - Log.Out(Logs::Detail, Logs::Spells, "Buff %d in slot %d has expired. Fading.", buffs[buffs_i].spellid, buffs_i); - BuffFadeBySlot(buffs_i); - } - } - else if (buffs[buffs_i].ticsremaining < 0) - { Log.Out(Logs::Detail, Logs::Spells, "Buff %d in slot %d has expired. Fading.", buffs[buffs_i].spellid, buffs_i); BuffFadeBySlot(buffs_i); } From 1bcb5c72a53a20b7491fba644e2aad6376d123eb Mon Sep 17 00:00:00 2001 From: hateborne Date: Mon, 25 May 2015 10:44:15 -0400 Subject: [PATCH 059/129] Making $npc->RemoveFromHateList actually work $npc->RemoveFromHateList is a mob function, not an NPC function. Casting to Mob to let it work. --- zone/perl_npc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index 0c9cec797..861759d12 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -823,7 +823,7 @@ XS(XS_NPC_RemoveFromHateList) if(ent == nullptr) Perl_croak(aTHX_ "ent is nullptr, avoiding crash."); - THIS->RemoveFromHateList(ent); + THIS->CastToMob()->RemoveFromHateList(ent); } XSRETURN_EMPTY; From 788959a5e287e07006419c3532668315c0a931d1 Mon Sep 17 00:00:00 2001 From: SecretsOTheP Date: Mon, 25 May 2015 12:35:53 -0400 Subject: [PATCH 060/129] Haynar's movement fixes. Changes Speed from float to int. EQ client deals with int step locs better than it does floats according to Haynar's testing. This also contains mob runspeed changes. I recommend you set runspeeds to start in the DB 1.25 for NPCs below 1.25 which will match player runspeeds almost equally. Existing DBs will need to be updated. General Cleanup of MobAI functions. Mobs now change their heading on AIMovement timers if their targets' heading has changed since that time. This prevents players from being able to land backstabs inbetween mob swings. Charmed/feared players now send the appropriate packet, there was a missing CastToClient() in spells that was missing. Mob runspeed can no longer be snared to 0%, instead, 1% of their base runspeed is the maximum. Roots apply as roots instead of a modifier under this code. There is going to be bugs with this code. It's better we push through it than revert it. Sanctuary has been running this for a good week and we've worked through the issues. Misc updates: Exported some variables to perl, including: EVENT_ITE_CLICK_CAST: EVENT_ITEM_CLICK: spell_id - returns the spell_id of the click effect. return value - cancels the cast. EVENT_DROP_ITEM: quantity - returns the # of items dropped in the packet. If the item has charges, charges are returned here instead. itemname - name of the item being dropped itemid - id of the item being droppped spell_id - spell_id associated with the item's click effect. slotid - the inventory slot id of the item being dropped. return value - cancels the item from being dropped. Added Perl function: CalcEXP. Calculates the experience you would gain for an NPC that cons a specific con value to you. Fixed a bug where you would receive the group experience bonus and group experience messages for simply being in a group, regardless of the player being in the same zone as you. --- zone/bonuses.cpp | 194 +++++++++++++++++---------- zone/bot.cpp | 28 ++-- zone/client.cpp | 6 +- zone/client.h | 2 +- zone/client_packet.cpp | 4 +- zone/command.cpp | 2 +- zone/effects.cpp | 51 ++++---- zone/embparser.cpp | 9 ++ zone/exp.cpp | 109 ++++++++++++++- zone/fearpath.cpp | 23 ---- zone/inventory.cpp | 2 +- zone/merc.cpp | 14 +- zone/mob.cpp | 282 +++++++++++++++++++++++++++++++-------- zone/mob.h | 30 ++++- zone/mob_ai.cpp | 291 +++++++++++++++++++---------------------- zone/npc.h | 1 - zone/perl_client.cpp | 30 +++++ zone/spell_effects.cpp | 14 +- zone/spells.cpp | 6 +- zone/tradeskills.cpp | 4 + zone/waypoints.cpp | 66 ++++------ zone/zone.cpp | 2 +- 22 files changed, 751 insertions(+), 419 deletions(-) diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 5dd7440f6..0c9c6d904 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -335,8 +335,8 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu } //FatherNitwit: New style haste, shields, and regens - if(newbon->haste < (int32)item->Haste) { - newbon->haste = item->Haste; + if((int32)item->Haste > 0) { + newbon->haste += item->Haste; } if(item->Regen > 0) newbon->HPRegen += item->Regen; @@ -390,10 +390,10 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu newbon->HitChance += item->Accuracy; } if(item->CombatEffects > 0) { - if((newbon->ProcChance + item->CombatEffects) > RuleI(Character, ItemCombatEffectsCap)) - newbon->ProcChance = RuleI(Character, ItemCombatEffectsCap); + if((newbon->MeleeDamage + item->CombatEffects) > RuleI(Character, ItemCombatEffectsCap)) + newbon->MeleeDamage = RuleI(Character, ItemCombatEffectsCap); else - newbon->ProcChance += item->CombatEffects; + newbon->MeleeDamage += item->CombatEffects; } if(item->DotShielding > 0) { if((newbon->DoTShielding + item->DotShielding) > RuleI(Character, ItemDoTShieldingCap)) @@ -428,7 +428,7 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu newbon->DSMitigation += item->DSMitigation; } if (item->Worn.Effect > 0 && item->Worn.Type == ET_WornEffect) {// latent effects - ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type); + ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type); } if (item->Focus.Effect>0 && (item->Focus.Type == ET_Focus)) { // focus effects @@ -559,7 +559,7 @@ void Client::AdditiveWornBonuses(const ItemInst *inst, StatBonuses* newbon, bool /* Powerful Non-live like option allows developers to add worn effects on items that can stack with other worn effects of the same spell effect type, instead of only taking the highest value. - Ie Cleave I = 40 pct cleave - So if you equip 3 cleave I items you will have a 120 pct cleave bonus. + Ie Cleave I = 40 pct cleave - So if you equip 3 cleave I items you will have a 120 pct cleave bonus. To enable use RuleI(Spells, AdditiveBonusWornType) Setting value = 2 Will force all live items to automatically be calculated additivily Setting value to anything else will indicate the item 'worntype' that if set to the same, will cause the bonuses to use this calculation @@ -579,7 +579,7 @@ void Client::AdditiveWornBonuses(const ItemInst *inst, StatBonuses* newbon, bool if(GetLevel() < item->ReqLevel) return; - + if (item->Worn.Effect > 0 && item->Worn.Type == RuleI(Spells, AdditiveBonusWornType)) ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type);// Non-live like - Addititive latent effects @@ -691,7 +691,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) continue; Log.Out(Logs::Detail, Logs::AA, "Applying Effect %d from AA %u in slot %d (base1: %d, base2: %d) on %s", effect, aaid, slot, base1, base2, this->GetCleanName()); - + uint8 focus = IsFocusEffect(0, 0, true,effect); if (focus) { @@ -1007,7 +1007,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) case SE_BlockBehind: newbon->BlockBehind += base1; break; - + case SE_StrikeThrough: case SE_StrikeThrough2: newbon->StrikeThrough += base1; @@ -1313,7 +1313,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) case SE_Vampirism: newbon->Vampirism += base1; - break; + break; case SE_FrenziedDevastation: newbon->FrenziedDevastation += base2; @@ -1416,7 +1416,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) } case SE_SkillProcSuccess:{ - + for(int e = 0; e < MAX_SKILL_PROCS; e++) { if(newbon->SkillProcSuccess[e] && newbon->SkillProcSuccess[e] == aaid) @@ -1449,7 +1449,7 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon) int buff_count = GetMaxTotalSlots(); for(i = 0; i < buff_count; i++) { if(buffs[i].spellid != SPELL_UNKNOWN){ - ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, 0, buffs[i].ticsremaining, i, buffs[i].instrument_mod); + ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, 0, buffs[i].ticsremaining,i); if (buffs[i].numhits > 0) Numhits(true); @@ -1472,9 +1472,8 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon) if (GetClass() == BARD) newbon->ManaRegen = 0; // Bards do not get mana regen from spells. } -void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *new_bonus, uint16 casterId, - uint8 WornType, int32 ticsremaining, int buffslot, int instrument_mod, - bool IsAISpellEffect, uint16 effect_id, int32 se_base, int32 se_limit, int32 se_max) +void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* new_bonus, uint16 casterId, uint8 WornType, uint32 ticsremaining, int buffslot, + bool IsAISpellEffect, uint16 effect_id, int32 se_base, int32 se_limit, int32 se_max) { int i, effect_value, base2, max, effectid; bool AdditiveWornBonus = false; @@ -1510,9 +1509,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne if (WornType && (RuleI(Spells, AdditiveBonusWornType) == WornType)) AdditiveWornBonus = true; - + effectid = spells[spell_id].effectid[i]; - effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, instrument_mod, caster, ticsremaining); + effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, caster, ticsremaining); base2 = spells[spell_id].base2[i]; max = spells[spell_id].max[i]; } @@ -1561,49 +1560,100 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_AttackSpeed: { - if ((effect_value - 100) > 0) { // Haste - if (new_bonus->haste < 0) break; // Slowed - Don't apply haste - if ((effect_value - 100) > new_bonus->haste) { - new_bonus->haste = effect_value - 100; + + if (AdditiveWornBonus) { + if ((effect_value - 100) > 0) { // Haste + if (new_bonus->haste < 0) break; // Slowed - Don't apply haste + if ((effect_value - 100) > new_bonus->haste) { + new_bonus->haste += effect_value - 100; + } + } + else if ((effect_value - 100) < 0) { // Slow + int real_slow_value = (100 - effect_value) * -1; + real_slow_value -= ((real_slow_value * GetSlowMitigation()/100)); + if (real_slow_value < new_bonus->haste) + new_bonus->haste += real_slow_value; } } - else if ((effect_value - 100) < 0) { // Slow - int real_slow_value = (100 - effect_value) * -1; - real_slow_value -= ((real_slow_value * GetSlowMitigation()/100)); - if (real_slow_value < new_bonus->haste) - new_bonus->haste = real_slow_value; + else + { + if ((effect_value - 100) > 0) { // Haste + if (new_bonus->haste < 0) break; // Slowed - Don't apply haste + if ((effect_value - 100) > new_bonus->haste) { + new_bonus->haste = effect_value - 100; + } + } + else if ((effect_value - 100) < 0) { // Slow + int real_slow_value = (100 - effect_value) * -1; + real_slow_value -= ((real_slow_value * GetSlowMitigation()/100)); + if (real_slow_value < new_bonus->haste) + new_bonus->haste = real_slow_value; + } } break; } case SE_AttackSpeed2: - { - if ((effect_value - 100) > 0) { // Haste V2 - Stacks with V1 but does not Overcap - if (new_bonus->hastetype2 < 0) break; //Slowed - Don't apply haste2 - if ((effect_value - 100) > new_bonus->hastetype2) { - new_bonus->hastetype2 = effect_value - 100; + { + if (AdditiveWornBonus) { + if ((effect_value - 100) > 0) { // Haste + if (new_bonus->hastetype2 < 0) break; // Slowed - Don't apply haste + if ((effect_value - 100) > new_bonus->hastetype2) { + new_bonus->hastetype2 += effect_value - 100; + } + } + else if ((effect_value - 100) < 0) { // Slow + int real_slow_value = (100 - effect_value) * -1; + real_slow_value -= ((real_slow_value * GetSlowMitigation()/100)); + if (real_slow_value < new_bonus->hastetype2) + new_bonus->hastetype2 += real_slow_value; } } - else if ((effect_value - 100) < 0) { // Slow - int real_slow_value = (100 - effect_value) * -1; - real_slow_value -= ((real_slow_value * GetSlowMitigation()/100)); - if (real_slow_value < new_bonus->hastetype2) - new_bonus->hastetype2 = real_slow_value; + else + { + if ((effect_value - 100) > 0) { // Haste + if (new_bonus->hastetype2 < 0) break; // Slowed - Don't apply haste + if ((effect_value - 100) > new_bonus->hastetype2) { + new_bonus->hastetype2 = effect_value - 100; + } + } + else if ((effect_value - 100) < 0) { // Slow + int real_slow_value = (100 - effect_value) * -1; + real_slow_value -= ((real_slow_value * GetSlowMitigation()/100)); + if (real_slow_value < new_bonus->hastetype2) + new_bonus->hastetype2 = real_slow_value; + } } break; } case SE_AttackSpeed3: { - if (effect_value < 0){ //Slow - effect_value -= ((effect_value * GetSlowMitigation()/100)); - if (effect_value < new_bonus->hastetype3) - new_bonus->hastetype3 = effect_value; - } + if (AdditiveWornBonus) { + if (effect_value < 0){ //Slow + effect_value -= ((effect_value * GetSlowMitigation()/100)); + if (effect_value < new_bonus->hastetype3) + new_bonus->hastetype3 += effect_value; + } - else if (effect_value > 0) { // Haste V3 - Stacks and Overcaps - if (effect_value > new_bonus->hastetype3) { - new_bonus->hastetype3 = effect_value; + else if (effect_value > 0) { // Haste V3 - Stacks and Overcaps + if (effect_value > new_bonus->hastetype3) { + new_bonus->hastetype3 += effect_value; + } + } + } + else + { + if (effect_value < 0){ //Slow + effect_value -= ((effect_value * GetSlowMitigation()/100)); + if (effect_value < new_bonus->hastetype3) + new_bonus->hastetype3 = effect_value; + } + + else if (effect_value > 0) { // Haste V3 - Stacks and Overcaps + if (effect_value > new_bonus->hastetype3) { + new_bonus->hastetype3 = effect_value; + } } } break; @@ -1618,13 +1668,21 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne if (effect_value < 0) //A few spells use negative values(Descriptions all indicate it should be a slow) effect_value = effect_value * -1; - - if (effect_value > 0 && effect_value > new_bonus->inhibitmelee) { - effect_value -= ((effect_value * GetSlowMitigation()/100)); - if (effect_value > new_bonus->inhibitmelee) - new_bonus->inhibitmelee = effect_value; + if (AdditiveWornBonus) { + if (effect_value > 0 && effect_value > new_bonus->inhibitmelee) { + effect_value -= ((effect_value * GetSlowMitigation()/100)); + if (effect_value > new_bonus->inhibitmelee) + new_bonus->inhibitmelee += effect_value; + } + } + else + { + if (effect_value > 0 && effect_value > new_bonus->inhibitmelee) { + effect_value -= ((effect_value * GetSlowMitigation()/100)); + if (effect_value > new_bonus->inhibitmelee) + new_bonus->inhibitmelee = effect_value; + } } - break; } @@ -1840,7 +1898,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne new_bonus->DamageShieldType = GetDamageShieldType(spell_id, max); else new_bonus->DamageShieldType = GetDamageShieldType(spell_id); - + break; } @@ -2021,7 +2079,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_Vampirism: new_bonus->Vampirism += effect_value; - break; + break; case SE_AllInstrumentMod: { @@ -2264,7 +2322,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_CriticalSpellChance: { new_bonus->CriticalSpellChance += effect_value; - + if (base2 > new_bonus->SpellCritDmgIncNoStack) new_bonus->SpellCritDmgIncNoStack = base2; break; @@ -2474,7 +2532,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_NegateAttacks: { - if (!new_bonus->NegateAttacks[0] || + if (!new_bonus->NegateAttacks[0] || ((new_bonus->NegateAttacks[0] && new_bonus->NegateAttacks[2]) && (new_bonus->NegateAttacks[2] < max))){ new_bonus->NegateAttacks[0] = 1; new_bonus->NegateAttacks[1] = buffslot; @@ -2494,7 +2552,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne break; } - + case SE_MeleeThresholdGuard: { if (new_bonus->MeleeThresholdGuard[0] < effect_value){ @@ -2861,17 +2919,17 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne new_bonus->NegateIfCombat = true; break; - case SE_Screech: + case SE_Screech: new_bonus->Screech = effect_value; break; case SE_AlterNPCLevel: if (IsNPC()){ - if (!new_bonus->AlterNPCLevel - || ((effect_value < 0) && (new_bonus->AlterNPCLevel > effect_value)) + if (!new_bonus->AlterNPCLevel + || ((effect_value < 0) && (new_bonus->AlterNPCLevel > effect_value)) || ((effect_value > 0) && (new_bonus->AlterNPCLevel < effect_value))) { - + int tmp_lv = GetOrigLevel() + effect_value; if (tmp_lv < 1) tmp_lv = 1; @@ -2909,7 +2967,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne new_bonus->BerserkSPA = true; break; - + case SE_Metabolism: new_bonus->Metabolism += effect_value; break; @@ -3010,7 +3068,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne } case SE_SkillProc:{ - + for(int e = 0; e < MAX_SKILL_PROCS; e++) { if(new_bonus->SkillProc[e] && new_bonus->SkillProc[e] == spell_id) @@ -3025,7 +3083,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne } case SE_SkillProcSuccess:{ - + for(int e = 0; e < MAX_SKILL_PROCS; e++) { if(new_bonus->SkillProcSuccess[e] && new_bonus->SkillProcSuccess[e] == spell_id) @@ -3041,9 +3099,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne //Special custom cases for loading effects on to NPC from 'npc_spels_effects' table if (IsAISpellEffect) { - + //Non-Focused Effect to modify incoming spell damage by resist type. - case SE_FcSpellVulnerability: + case SE_FcSpellVulnerability: ModVulnerability(base2, effect_value); break; } @@ -3109,7 +3167,7 @@ void NPC::CalcItemBonuses(StatBonuses *newbon) newbon->HitChance += cur->Accuracy; } if(cur->CombatEffects > 0) { - newbon->ProcChance += cur->CombatEffects; + newbon->MeleeDamage += cur->CombatEffects; } if (cur->Worn.Effect>0 && (cur->Worn.Type == ET_WornEffect)) { // latent effects ApplySpellsBonuses(cur->Worn.Effect, cur->Worn.Level, newbon, 0, cur->Worn.Type); @@ -4395,7 +4453,7 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) aabonuses.SlayUndead[0] = effect_value; aabonuses.SlayUndead[1] = effect_value; break; - + case SE_DoubleRangedAttack: spellbonuses.DoubleRangedAttack = effect_value; aabonuses.DoubleRangedAttack = effect_value; @@ -4415,7 +4473,7 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) aabonuses.ShieldEquipDmgMod[1] = effect_value; itembonuses.ShieldEquipDmgMod[0] = effect_value; itembonuses.ShieldEquipDmgMod[1] = effect_value; - break; + break; case SE_TriggerMeleeThreshold: spellbonuses.TriggerMeleeThreshold = false; diff --git a/zone/bot.cpp b/zone/bot.cpp index e2c506575..5ddbb1377 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -292,9 +292,7 @@ void Bot::ChangeBotArcherWeapons(bool isArcher) { void Bot::Sit() { if(IsMoving()) { moved = false; - // SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); - SendPosition(); - SetMoving(false); + SetCurrentSpeed(0); tar_ndx = 0; } @@ -3448,11 +3446,10 @@ void Bot::AI_Process() { if(IsMoving()) { SetHeading(0); SetRunAnimSpeed(0); + SetCurrentSpeed(GetRunSpeed()); if(moved) { - moved = false; - SendPosition(); - SetMoving(false); + SetCurrentSpeed(0); } } @@ -3495,11 +3492,10 @@ void Bot::AI_Process() { if(IsMoving()) { SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); SetRunAnimSpeed(0); - + SetCurrentSpeed(0); if(moved) { moved = false; - SendPosition(); - SetMoving(false); + SetCurrentSpeed(0); } } @@ -3517,11 +3513,10 @@ void Bot::AI_Process() { if(IsMoving()) { SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); SetRunAnimSpeed(0); - + SetCurrentSpeed(0); if(moved) { moved = false; - SendPosition(); - SetMoving(false); + SetCurrentSpeed(0); } } @@ -3740,7 +3735,7 @@ void Bot::AI_Process() { if(follow) { float dist = DistanceSquared(m_Position, follow->GetPosition()); - float speed = follow->GetRunspeed(); + int speed = follow->GetRunspeed(); if(dist < GetFollowDistance() + 1000) speed = follow->GetWalkspeed(); @@ -3757,9 +3752,8 @@ void Bot::AI_Process() { { if(moved) { - moved=false; - SendPosition(); - SetMoving(false); + moved = false; + SetCurrentSpeed(0); } } } @@ -3987,6 +3981,7 @@ void Bot::PetAIProcess() { botPet->SetHeading(botPet->GetTarget()->GetHeading()); if(moved) { moved=false; + SetCurrentSpeed(0); botPet->SendPosition(); botPet->SetMoving(false); } @@ -4020,6 +4015,7 @@ void Bot::PetAIProcess() { botPet->SetHeading(botPet->GetTarget()->GetHeading()); if(moved) { moved=false; + SetCurrentSpeed(0); botPet->SendPosition(); botPet->SetMoving(false); } diff --git a/zone/client.cpp b/zone/client.cpp index 0aa5510ff..7471a6a62 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -4991,7 +4991,7 @@ void Client::SetShadowStepExemption(bool v) if((cur_time - m_TimeSinceLastPositionCheck) > 1000) { float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck); - float runs = GetRunspeed(); + int runs = GetRunspeed(); if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor))) { printf("%s %i moving too fast! moved: %.2f in %ims, speed %.2f\n", __FILE__, __LINE__, @@ -5048,7 +5048,7 @@ void Client::SetKnockBackExemption(bool v) if((cur_time - m_TimeSinceLastPositionCheck) > 1000) { float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck); - float runs = GetRunspeed(); + int runs = GetRunspeed(); if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor))) { if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor))))) @@ -5105,7 +5105,7 @@ void Client::SetPortExemption(bool v) if((cur_time - m_TimeSinceLastPositionCheck) > 1000) { float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck); - float runs = GetRunspeed(); + int runs = GetRunspeed(); if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor))) { if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor))))) diff --git a/zone/client.h b/zone/client.h index 79dcf87ff..ac505ecdb 100644 --- a/zone/client.h +++ b/zone/client.h @@ -557,6 +557,7 @@ public: void SendCrystalCounts(); void AddEXP(uint32 in_add_exp, uint8 conlevel = 0xFF, bool resexp = false); + uint32 CalcEXP(uint8 conlevel = 0xFF); void SetEXP(uint32 set_exp, uint32 set_aaxp, bool resexp=false); void AddLevelBasedExp(uint8 exp_percentage, uint8 max_level=0); void SetLeadershipEXP(uint32 group_exp, uint32 raid_exp); @@ -1129,7 +1130,6 @@ public: inline bool IsDraggingCorpse() { return (DraggedCorpses.size() > 0); } void DragCorpses(); inline void ClearDraggedCorpses() { DraggedCorpses.clear(); } - inline void ResetPositionTimer() { position_timer_counter = 0; } void SendAltCurrencies(); void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount); void AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method = 0); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 3a2c6e2c9..3f40f3c00 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4266,7 +4266,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) if((cur_time - m_TimeSinceLastPositionCheck) > 0) { float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck); - float runs = GetRunspeed(); + int runs = GetRunspeed(); if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor))) { if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor))))) @@ -4334,7 +4334,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) if((cur_time - m_TimeSinceLastPositionCheck) > 2500) { float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck); - float runs = GetRunspeed(); + int runs = GetRunspeed(); if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor))) { if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor))))) diff --git a/zone/command.cpp b/zone/command.cpp index 3f3d4b6c1..a62ee4ce8 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -1484,7 +1484,7 @@ void command_npcstats(Client *c, const Seperator *sep) c->Message(0, "Current HP: %i Max HP: %i", c->GetTarget()->GetHP(), c->GetTarget()->GetMaxHP()); //c->Message(0, "Weapon Item Number: %s", c->GetTarget()->GetWeapNo()); c->Message(0, "Gender: %i Size: %f Bodytype: %d", c->GetTarget()->GetGender(), c->GetTarget()->GetSize(), c->GetTarget()->GetBodyType()); - c->Message(0, "Runspeed: %f Walkspeed: %f", c->GetTarget()->GetRunspeed(), c->GetTarget()->GetWalkspeed()); + c->Message(0, "Runspeed: %i Walkspeed: %i", c->GetTarget()->GetRunspeed(), c->GetTarget()->GetWalkspeed()); c->Message(0, "Spawn Group: %i Grid: %i", c->GetTarget()->CastToNPC()->GetSp2(), c->GetTarget()->CastToNPC()->GetGrid()); c->Message(0, "EmoteID: %i", c->GetTarget()->CastToNPC()->GetEmoteID()); c->GetTarget()->CastToNPC()->QueryLoot(c); diff --git a/zone/effects.cpp b/zone/effects.cpp index c814ea554..c7066da68 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -89,7 +89,7 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { if (IsClient() && GetClass() == WIZARD) ratio += RuleI(Spells, WizCritRatio); //Default is zero - + if (Critical){ value = value_BaseEffect*ratio/100; @@ -138,7 +138,7 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { value -= GetFocusEffect(focusFcDamageAmt, spell_id); - if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) + if(itembonuses.SpellDmg) value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); if (IsNPC() && CastToNPC()->GetSpellScale()) @@ -172,7 +172,7 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) { value += int(value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100)*ratio/100; value += int(value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100; value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100; - extra_dmg = target->GetFcDamageAmtIncoming(this, spell_id) + + extra_dmg = target->GetFcDamageAmtIncoming(this, spell_id) + int(GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100) + GetFocusEffect(focusFcDamageAmt, spell_id); @@ -200,6 +200,11 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) { extra_dmg /= duration; } + //Sanctuary Custom: Spelldmg per tick + if(itembonuses.SpellDmg) + value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg / 6, value); //per tick + + value -= extra_dmg; } @@ -211,23 +216,6 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) { int32 Mob::GetExtraSpellAmt(uint16 spell_id, int32 extra_spell_amt, int32 base_spell_dmg) { - int total_cast_time = 0; - - if (spells[spell_id].recast_time >= spells[spell_id].recovery_time) - total_cast_time = spells[spell_id].recast_time + spells[spell_id].cast_time; - else - total_cast_time = spells[spell_id].recovery_time + spells[spell_id].cast_time; - - if (total_cast_time > 0 && total_cast_time <= 2500) - extra_spell_amt = extra_spell_amt*25/100; - else if (total_cast_time > 2500 && total_cast_time < 7000) - extra_spell_amt = extra_spell_amt*(167*((total_cast_time - 1000)/1000)) / 1000; - else - extra_spell_amt = extra_spell_amt * total_cast_time / 7000; - - if(extra_spell_amt*2 < base_spell_dmg) - return 0; - return extra_spell_amt; } @@ -270,7 +258,7 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { value += GetFocusEffect(focusFcHealAmt, spell_id); value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); - if(itembonuses.HealAmt && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) + if(itembonuses.HealAmt) value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value) * modifier; value += value*target->GetHealRate(spell_id, this)/100; @@ -281,7 +269,7 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { if (Critical) { entity_list.MessageClose_StringID(this, true, 100, MT_SpellCrits, OTHER_CRIT_HEAL, GetName(), itoa(value)); - + if (IsClient()) Message_StringID(MT_SpellCrits, YOU_CRIT_HEAL, itoa(value)); } @@ -301,6 +289,9 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { if(chance && zone->random.Roll(chance)) value *= 2; + + if(itembonuses.HealAmt) + value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt / 6, value) * modifier; } if (IsNPC() && CastToNPC()->GetHealScale()) @@ -421,10 +412,14 @@ int32 Mob::GetActSpellDuration(uint16 spell_id, int32 duration) int tic_inc = 0; tic_inc = GetFocusEffect(focusSpellDurByTic, spell_id); - // unsure on the exact details, but bard songs that don't cost mana at some point get an extra tick, 60 for now - // a level 53 bard reported getting 2 tics - if (IsShortDurationBuff(spell_id) && IsBardSong(spell_id) && spells[spell_id].mana == 0 && GetClass() == BARD && GetLevel() > 60) - tic_inc++; + // Only need this for clients, since the change was for bard songs, I assume we should keep non bard songs getting +1 + // However if its bard or not and is mez, charm or fear, we need to add 1 so that client is in sync + if (IsClient() && !(IsShortDurationBuff(spell_id) && IsBardSong(spell_id)) || + IsFearSpell(spell_id) || + IsCharmSpell(spell_id) || + IsMezSpell(spell_id) || + IsBlindSpell(spell_id)) + tic_inc += 1; return (((duration * increase) / 100) + tic_inc); } @@ -767,7 +762,7 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); } } else { - if (spells[spell_id].aemaxtargets && iCounter < spells[spell_id].aemaxtargets) + if (spells[spell_id].aemaxtargets && iCounter < spells[spell_id].aemaxtargets) caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); if (!spells[spell_id].aemaxtargets) caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); @@ -855,7 +850,7 @@ void EntityList::AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool aff if (!center->CheckLosFN(curmob)) continue; } else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... - // See notes in AESpell() above for more info. + // See notes in AESpell() above for more info. if (caster->IsAttackAllowed(curmob, true)) continue; if (caster->CheckAggro(curmob)) diff --git a/zone/embparser.cpp b/zone/embparser.cpp index db017750a..326585806 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -1321,6 +1321,7 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID ExportVar(package_name.c_str(), "itemid", objid); ExportVar(package_name.c_str(), "itemname", iteminst->GetItem()->Name); ExportVar(package_name.c_str(), "slotid", extradata); + ExportVar(package_name.c_str(), "spell_id", iteminst->GetItem()->Click.Effect); break; } @@ -1399,6 +1400,14 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID ExportVar(package_name.c_str(), "killer_skill", sep.arg[3]); break; } + case EVENT_DROP_ITEM: { + ExportVar(package_name.c_str(), "quantity", iteminst->IsStackable() ? iteminst->GetCharges() : 1); + ExportVar(package_name.c_str(), "itemname", iteminst->GetItem()->Name); + ExportVar(package_name.c_str(), "itemid", iteminst->GetItem()->ID); + ExportVar(package_name.c_str(), "spell_id", iteminst->GetItem()->Click.Effect); + ExportVar(package_name.c_str(), "slotid", extradata); + break; + } default: { break; diff --git a/zone/exp.cpp b/zone/exp.cpp index 3a77f30dc..ab8506aca 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -59,6 +59,97 @@ static uint32 MaxBankedRaidLeadershipPoints(int Level) return 10; } +uint32 Client::CalcEXP(uint8 conlevel) { + + uint32 in_add_exp = EXP_FORMULA; + + + if((XPRate != 0)) + in_add_exp = static_cast(in_add_exp * (static_cast(XPRate) / 100.0f)); + + float totalmod = 1.0; + float zemmod = 1.0; + //get modifiers + if(RuleR(Character, ExpMultiplier) >= 0){ + totalmod *= RuleR(Character, ExpMultiplier); + } + + if(zone->newzone_data.zone_exp_multiplier >= 0){ + zemmod *= zone->newzone_data.zone_exp_multiplier; + } + + if(RuleB(Character,UseRaceClassExpBonuses)) + { + if(GetBaseRace() == HALFLING){ + totalmod *= 1.05; + } + + if(GetClass() == ROGUE || GetClass() == WARRIOR){ + totalmod *= 1.05; + } + } + + if(zone->IsHotzone()) + { + totalmod += RuleR(Zone, HotZoneBonus); + } + + in_add_exp = uint32(float(in_add_exp) * totalmod * zemmod); + + if(RuleB(Character,UseXPConScaling)) + { + if (conlevel != 0xFF) { + switch (conlevel) + { + case CON_GREEN: + in_add_exp = 0; + return 0; + case CON_LIGHTBLUE: + in_add_exp = in_add_exp * RuleI(Character, LightBlueModifier)/100; + break; + case CON_BLUE: + in_add_exp = in_add_exp * RuleI(Character, BlueModifier)/100; + break; + case CON_WHITE: + in_add_exp = in_add_exp * RuleI(Character, WhiteModifier)/100; + break; + case CON_YELLOW: + in_add_exp = in_add_exp * RuleI(Character, YellowModifier)/100; + break; + case CON_RED: + in_add_exp = in_add_exp * RuleI(Character, RedModifier)/100; + break; + } + } + } + + float aatotalmod = 1.0; + if(zone->newzone_data.zone_exp_multiplier >= 0){ + aatotalmod *= zone->newzone_data.zone_exp_multiplier; + } + + + + if(RuleB(Character,UseRaceClassExpBonuses)) + { + if(GetBaseRace() == HALFLING){ + aatotalmod *= 1.05; + } + + if(GetClass() == ROGUE || GetClass() == WARRIOR){ + aatotalmod *= 1.05; + } + } + + if(RuleB(Zone, LevelBasedEXPMods)){ + if(zone->level_exp_mod[GetLevel()].ExpMod){ + in_add_exp *= zone->level_exp_mod[GetLevel()].ExpMod; + } + } + + return in_add_exp; +} + void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) { this->EVENT_ITEM_ScriptStopReturn(); @@ -78,7 +169,7 @@ void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) { //figure out how much of this goes to AAs add_aaxp = add_exp * m_epp.perAA / 100; - //take that ammount away from regular exp + //take that amount away from regular exp add_exp -= add_aaxp; float totalmod = 1.0; @@ -247,12 +338,22 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { Message(13, "Error in Client::SetEXP. EXP not set."); return; // Must be invalid class/race } + uint32 i = 0; + uint32 membercount = 0; + if(GetGroup()) + { + for (i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (GetGroup()->members[i] != nullptr) { + membercount++; + } + } + } if ((set_exp + set_aaxp) > (m_pp.exp+m_pp.expAA)) { if (isrezzexp) this->Message_StringID(MT_Experience, REZ_REGAIN); else{ - if(this->IsGrouped()) + if(membercount > 1) this->Message_StringID(MT_Experience, GAIN_GROUPXP); else if(IsRaidGrouped()) Message_StringID(MT_Experience, GAIN_RAIDEXP); @@ -604,8 +705,8 @@ void Group::SplitExp(uint32 exp, Mob* other) { groupmod = 2.16; else groupmod = 1.0; - - groupexp += (uint32)((float)exp * groupmod * (RuleR(Character, GroupExpMultiplier))); + if(membercount > 1 && membercount < 6) + groupexp += (uint32)((float)exp * groupmod * (RuleR(Character, GroupExpMultiplier))); int conlevel = Mob::GetLevelCon(maxlevel, other->GetLevel()); if(conlevel == CON_GREEN) diff --git a/zone/fearpath.cpp b/zone/fearpath.cpp index 0b33a662e..0fd5ffa98 100644 --- a/zone/fearpath.cpp +++ b/zone/fearpath.cpp @@ -123,29 +123,6 @@ void Mob::ProcessFlee() } } -float Mob::GetFearSpeed() -{ - if (flee_mode) { - //we know ratio < FLEE_HP_RATIO - float speed = GetBaseRunspeed(); - float ratio = GetHPRatio(); - float multiplier = RuleR(Combat, FleeMultiplier); - - if (GetSnaredAmount() > 40) - multiplier = multiplier / 6.0f; - - speed = speed * ratio * multiplier / 100; - - //NPC will eventually stop. Snares speeds this up. - if (speed < 0.09) - speed = 0.0001f; - - return speed; - } - // fear and blind use their normal run speed - return GetRunspeed(); -} - void Mob::CalculateNewFearpoint() { if(RuleB(Pathing, Fear) && zone->pathing) diff --git a/zone/inventory.cpp b/zone/inventory.cpp index badc059e0..2ce30fe5e 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -606,7 +606,7 @@ void Client::DropItem(int16 slot_id) // Take control of item in client inventory ItemInst *inst = m_inv.PopItem(slot_id); if(inst) { - int i = parse->EventItem(EVENT_DROP_ITEM, this, inst, nullptr, "", 0); + int i = parse->EventItem(EVENT_DROP_ITEM, this, inst, nullptr, "", slot_id); if(i != 0) { safe_delete(inst); } diff --git a/zone/merc.cpp b/zone/merc.cpp index 5f06331fa..936dfcc68 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -1460,8 +1460,7 @@ void Merc::AI_Process() { if(moved) { moved = false; - SendPosition(); - SetMoving(false); + SetCurrentSpeed(0); } } @@ -1497,9 +1496,7 @@ void Merc::AI_Process() { SetRunAnimSpeed(0); if(moved) { - moved = false; - SendPosition(); - SetMoving(false); + SetCurrentSpeed(0); } } @@ -1710,7 +1707,7 @@ void Merc::AI_Process() { if(follow) { float dist = DistanceSquared(m_Position, follow->GetPosition()); - float speed = GetRunspeed(); + int speed = GetRunspeed(); if(dist < GetFollowDistance() + 1000) speed = GetWalkspeed(); @@ -1727,9 +1724,8 @@ void Merc::AI_Process() { { if(moved) { - moved=false; - SendPosition(); - SetMoving(false); + SetCurrentSpeed(0); + moved = false; } } } diff --git a/zone/mob.cpp b/zone/mob.cpp index a8bed6417..ddad36ff1 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #ifdef BOTS #include "bot.h" @@ -148,6 +149,28 @@ Mob::Mob(const char* in_name, size = in_size; base_size = size; runspeed = in_runspeed; + // neotokyo: sanity check + if (runspeed < 0 || runspeed > 20) + runspeed = 1.25f; + base_runspeed = (int)((float)runspeed * 40.0f); + // clients + if (runspeed == 0.7f) { + base_runspeed = 28; + walkspeed = 0.3f; + base_walkspeed = 12; + fearspeed = 0.625f; + base_fearspeed = 25; + // npcs + } else { + base_walkspeed = base_runspeed * 100 / 265; + walkspeed = ((float)base_walkspeed) * 0.025f; + base_fearspeed = base_runspeed * 100 / 127; + fearspeed = ((float)base_fearspeed) * 0.025f; + } + + + current_speed = base_runspeed; + m_PlayerState = 0; @@ -531,48 +554,32 @@ bool Mob::IsInvisible(Mob* other) const return(false); } -float Mob::_GetMovementSpeed(int mod) const -{ - // List of movement speed modifiers, including AAs & spells: - // http://everquest.allakhazam.com/db/item.html?item=1721;page=1;howmany=50#m10822246245352 - if (IsRooted()) - return 0.0f; +int Mob::_GetWalkSpeed() const { + + if (IsRooted() || IsStunned() || IsMezzed()) + return 0; + else if (IsPseudoRooted()) - return 0.00001f; + return 0; - float speed_mod = runspeed; + int aa_mod = 0; + int speed_mod = base_walkspeed; + int base_run = base_runspeed; + bool has_horse = false; + int runspeedcap = RuleI(Character,BaseRunSpeedCap); + runspeedcap += itembonuses.IncreaseRunSpeedCap + spellbonuses.IncreaseRunSpeedCap + aabonuses.IncreaseRunSpeedCap; + aa_mod += aabonuses.BaseMovementSpeed; - // These two cases ignore the cap, be wise in the DB for horses. if (IsClient()) { - if (CastToClient()->GetGMSpeed()) { - speed_mod = 3.125f; - if (mod != 0) - speed_mod += speed_mod * static_cast(mod) / 100.0f; + Mob *horse = entity_list.GetMob(CastToClient()->GetHorseId()); + if (horse) { + speed_mod = horse->GetBaseRunspeed(); return speed_mod; - } else { - Mob *horse = entity_list.GetMob(CastToClient()->GetHorseId()); - if (horse) { - speed_mod = horse->GetBaseRunspeed(); - if (mod != 0) - speed_mod += speed_mod * static_cast(mod) / 100.0f; - return speed_mod; - } } } - int aa_mod = 0; - int spell_mod = 0; - int runspeedcap = RuleI(Character,BaseRunSpeedCap); + int spell_mod = spellbonuses.movementspeed + itembonuses.movementspeed; int movemod = 0; - float frunspeedcap = 0.0f; - - runspeedcap += itembonuses.IncreaseRunSpeedCap + spellbonuses.IncreaseRunSpeedCap + aabonuses.IncreaseRunSpeedCap; - aa_mod += itembonuses.BaseMovementSpeed + spellbonuses.BaseMovementSpeed + aabonuses.BaseMovementSpeed; - spell_mod += spellbonuses.movementspeed + itembonuses.movementspeed; - - // hard cap - if (runspeedcap > 225) - runspeedcap = 225; if (spell_mod < 0) movemod += spell_mod; @@ -581,27 +588,189 @@ float Mob::_GetMovementSpeed(int mod) const else movemod = aa_mod; - // cap negative movemods from snares mostly - if (movemod < -85) + // hard cap + if (runspeedcap > 225) + runspeedcap = 225; + + if(movemod < -85) //cap it at moving very very slow movemod = -85; - if (movemod != 0) - speed_mod += speed_mod * static_cast(movemod) / 100.0f; + if (!has_horse && movemod != 0) + speed_mod += (base_run * movemod / 100); - // runspeed caps - frunspeedcap = static_cast(runspeedcap) / 100.0f; - if (IsClient() && speed_mod > frunspeedcap) - speed_mod = frunspeedcap; + if(speed_mod < 1) + return(1); - // apply final mod such as the -47 for walking - // use runspeed since it should stack with snares - // and if we get here, we know runspeed was the initial - // value before we applied movemod. - if (mod != 0) - speed_mod += runspeed * static_cast(mod) / 100.0f; + //runspeed cap. + if(IsClient()) + { + if(speed_mod > runspeedcap) + speed_mod = runspeedcap; + } + return speed_mod; +} - if (speed_mod <= 0.0f) - speed_mod = IsClient() ? 0.0001f : 0.0f; +int Mob::_GetRunSpeed() const { + if (IsRooted() || IsStunned() || IsMezzed()) + return 0; + + int aa_mod = 0; + int speed_mod = base_runspeed; + int base_walk = base_walkspeed; + bool has_horse = false; + if (IsClient()) + { + if(CastToClient()->GetGMSpeed()) + { + speed_mod = 325; + } + else + { + Mob* horse = entity_list.GetMob(CastToClient()->GetHorseId()); + if(horse) + { + speed_mod = horse->GetBaseRunspeed(); + base_walk = horse->GetBaseWalkspeed(); + has_horse = true; + } + } + } + + int runspeedcap = RuleI(Character,BaseRunSpeedCap); + runspeedcap += itembonuses.IncreaseRunSpeedCap + spellbonuses.IncreaseRunSpeedCap + aabonuses.IncreaseRunSpeedCap; + + aa_mod = itembonuses.IncreaseRunSpeedCap + spellbonuses.IncreaseRunSpeedCap + aabonuses.IncreaseRunSpeedCap; + int spell_mod = spellbonuses.movementspeed + itembonuses.movementspeed; + int movemod = 0; + + if(spell_mod < 0) + { + movemod += spell_mod; + } + else if(spell_mod > aa_mod) + { + movemod = spell_mod; + } + else + { + movemod = aa_mod; + } + + if(movemod < -85) //cap it at moving very very slow + movemod = -85; + + if (!has_horse && movemod != 0) + { + if (IsClient()) + { + speed_mod += (speed_mod * movemod / 100); + } else { + if (movemod < 0) { + speed_mod += (50 * movemod / 100); + // basically stoped + if(speed_mod < 1) + { + return(1); + } + // moving slowly + if (speed_mod < 8) + return(8); + } else { + speed_mod += GetBaseWalkspeed(); + if (movemod > 50) + speed_mod += 4; + if (movemod > 40) + speed_mod += 3; + } + } + } + + if(speed_mod < 1) + { + return(1); + } + //runspeed cap. + if(IsClient()) + { + if(speed_mod > runspeedcap) + speed_mod = runspeedcap; + } + return speed_mod; +} + +int Mob::_GetFearSpeed() const { + + if (IsRooted() || IsStunned() || IsMezzed()) + return 0; + + //float speed_mod = fearspeed; + int speed_mod = GetBaseFearSpeed(); + + // use a max of 1.75f in calcs. + int base_run = std::min(GetBaseRunspeed(), 70); + + int spell_mod = spellbonuses.movementspeed + itembonuses.movementspeed; + int movemod = 0; + + if(spell_mod < 0) + { + movemod += spell_mod; + } + + if(movemod < -85) //cap it at moving very very slow + movemod = -85; + + if (IsClient()) { + if (CastToClient()->IsRunning()) + speed_mod = GetBaseRunspeed(); + else + speed_mod = GetBaseWalkspeed(); + if (movemod < 0) + return GetBaseWalkspeed(); + speed_mod += (base_run * movemod / 100); + return speed_mod; + } else { + int hp_ratio = GetIntHPRatio(); + // very large snares 50% or higher + if (movemod < -49) + { + if (hp_ratio < 25) + { + return (1); + } + if (hp_ratio < 50) + return (8); + else + return (12); + } + if (hp_ratio < 5) { + speed_mod = base_walkspeed / 3; + } else if (hp_ratio < 15) { + speed_mod = base_walkspeed / 2; + } else if (hp_ratio < 25) { + speed_mod = base_walkspeed + 1; // add the +1 so they do the run animation + } else if (hp_ratio < 50) { + speed_mod *= 82; + speed_mod /= 100; + } + if (movemod > 0) { + speed_mod += GetBaseWalkspeed(); + if (movemod > 50) + speed_mod += 4; + if (movemod > 40) + speed_mod += 3; + return speed_mod; + } + else if (movemod < 0) { + speed_mod += (base_run * movemod / 100); + } + } + if (speed_mod < 1) + return (1); + if (speed_mod < 9) + return (8); + if (speed_mod < 13) + return (12); return speed_mod; } @@ -1201,7 +1370,6 @@ void Mob::SendPosition() PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)app->pBuffer; MakeSpawnUpdateNoDelta(spu); move_tic_count = 0; - tar_ndx = 20; entity_list.QueueClients(this, app, true); safe_delete(app); } @@ -1303,7 +1471,7 @@ void Mob::ShowStats(Client* client) if(n->respawn2 != 0) spawngroupid = n->respawn2->SpawnGroupID(); client->Message(0, " NPCID: %u SpawnGroupID: %u Grid: %i LootTable: %u FactionID: %i SpellsID: %u ", GetNPCTypeID(),spawngroupid, n->GetGrid(), n->GetLoottableID(), n->GetNPCFactionID(), n->GetNPCSpellsID()); - client->Message(0, " Accuracy: %i MerchantID: %i EmoteID: %i Runspeed: %f Walkspeed: %f", n->GetAccuracyRating(), n->MerchantType, n->GetEmoteID(), n->GetRunspeed(), n->GetWalkspeed()); + client->Message(0, " Accuracy: %i MerchantID: %i EmoteID: %i Runspeed: %u Walkspeed: %u", n->GetAccuracyRating(), n->MerchantType, n->GetEmoteID(), n->GetRunspeed(), n->GetWalkspeed()); n->QueryLoot(client); } if (IsAIControlled()) { @@ -5437,3 +5605,15 @@ void Mob::SendRemovePlayerState(PlayerState old_state) safe_delete(app); } +void Mob::SetCurrentSpeed(int in){ + if (current_speed != in) + { + current_speed = in; + tar_ndx = 20; + if (in == 0) { + SetRunAnimSpeed(0); + SetMoving(false); + SendPosition(); + } + } +} \ No newline at end of file diff --git a/zone/mob.h b/zone/mob.h index bf7e2030a..896b9c71b 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -364,6 +364,7 @@ public: inline Mob* GetTarget() const { return target; } virtual void SetTarget(Mob* mob); virtual inline float GetHPRatio() const { return max_hp == 0 ? 0 : ((float)cur_hp/max_hp*100); } + virtual inline float GetIntHPRatio() const { return max_hp == 0 ? 0 : (cur_hp/max_hp*100); } inline virtual int32 GetAC() const { return AC + itembonuses.AC + spellbonuses.AC; } inline virtual int32 GetATK() const { return ATK + itembonuses.ATK + spellbonuses.ATK; } inline virtual int32 GetATKBonus() const { return itembonuses.ATK + spellbonuses.ATK; } @@ -441,9 +442,12 @@ public: virtual void SetMoving(bool move) { moving = move; m_Delta = glm::vec4(); } virtual void GoToBind(uint8 bindnum = 0) { } virtual void Gate(); - float GetWalkspeed() const { return(_GetMovementSpeed(-47)); } - float GetRunspeed() const { return(_GetMovementSpeed(0)); } - float GetBaseRunspeed() const { return runspeed; } + int GetWalkspeed() const { return(_GetWalkSpeed()); } + int GetRunspeed() const { return(_GetRunSpeed()); } + void SetCurrentSpeed(int in); + int GetBaseRunspeed() const { return base_runspeed; } + int GetBaseWalkspeed() const { return base_walkspeed; } + int GetBaseFearSpeed() const { return base_fearspeed; } float GetMovespeed() const { return IsRunning() ? GetRunspeed() : GetWalkspeed(); } bool IsRunning() const { return m_is_running; } void SetRunning(bool val) { m_is_running = val; } @@ -801,7 +805,7 @@ public: //old fear function //void SetFeared(Mob *caster, uint32 duration, bool flee = false); - float GetFearSpeed(); + int GetFearSpeed() { return _GetFearSpeed(); } bool IsFeared() { return (spellbonuses.IsFeared || flee_mode); } // This returns true if the mob is feared or fleeing due to low HP inline void StartFleeing() { flee_mode = true; CalculateNewFearpoint(); } void ProcessFlee(); @@ -810,8 +814,8 @@ public: inline bool CheckAggro(Mob* other) {return hate_list.IsEntOnHateList(other);} float CalculateHeadingToTarget(float in_x, float in_y); - bool CalculateNewPosition(float x, float y, float z, float speed, bool checkZ = false); - virtual bool CalculateNewPosition2(float x, float y, float z, float speed, bool checkZ = true); + bool CalculateNewPosition(float x, float y, float z, int speed, bool checkZ = false, bool calcheading = true); + virtual bool CalculateNewPosition2(float x, float y, float z, int speed, bool checkZ = true, bool calcheading = true); float CalculateDistance(float x, float y, float z); float GetGroundZ(float new_x, float new_y, float z_offset=0.0); void SendTo(float new_x, float new_y, float new_z); @@ -882,6 +886,8 @@ public: Timer *GetSpecialAbilityTimer(int ability); void ClearSpecialAbilities(); void ProcessSpecialAbilities(const std::string &str); + bool IsMoved() { return moved; } + void SetMoved(bool moveflag) { moved = moveflag; } Shielders_Struct shielder[MAX_SHIELDERS]; Trade* trade; @@ -951,7 +957,10 @@ protected: void CommonDamage(Mob* other, int32 &damage, const uint16 spell_id, const SkillUseTypes attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic); static uint16 GetProcID(uint16 spell_id, uint8 effect_index); float _GetMovementSpeed(int mod) const; - virtual bool MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, bool checkZ); + int _GetWalkSpeed() const; + int _GetRunSpeed() const; + int _GetFearSpeed() const; + virtual bool MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, bool checkZ); virtual bool AI_EngagedCastCheck() { return(false); } virtual bool AI_PursueCastCheck() { return(false); } @@ -1048,6 +1057,13 @@ protected: float base_size; float size; float runspeed; + float walkspeed; + float fearspeed; + int base_runspeed; + int base_walkspeed; + int base_fearspeed; + int current_speed; + uint32 pLastChange; bool held; bool nocast; diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 355b6ba48..9909672de 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -339,9 +339,7 @@ bool NPC::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain //stop moving if were casting a spell and were not a bard... if(!IsBardSong(AIspells[i].spellid)) { - SetRunAnimSpeed(0); - SendPosition(); - SetMoving(false); + SetCurrentSpeed(0); } return CastSpell(AIspells[i].spellid, tar->GetID(), 1, AIspells[i].manacost == -2 ? 0 : -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, 0, &(AIspells[i].resist_adjust)); @@ -698,9 +696,7 @@ void Client::AI_SpellCast() { if(!IsBardSong(spell_to_cast)) { - SetRunAnimSpeed(0); - SendPosition(); - SetMoving(false); + SetCurrentSpeed(0); } CastSpell(spell_to_cast, tar->GetID(), slot_to_use); return; @@ -714,9 +710,7 @@ void Client::AI_SpellCast() { if(!IsBardSong(spell_to_cast)) { - SetRunAnimSpeed(0); - SendPosition(); - SetMoving(false); + SetCurrentSpeed(0); } CastSpell(spell_to_cast, tar->GetID(), slot_to_use); return; @@ -772,16 +766,13 @@ void Client::AI_Process() { if(GetTarget()) SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); - SetRunAnimSpeed(0); - SendPosition(); - SetMoving(false); - moved=false; + SetCurrentSpeed(0); } //continue on to attack code, ensuring that we execute the engaged code engaged = true; } else { if(AImovement_timer->Check()) { - animation = GetRunspeed() * 21; + //animation = GetFearSpeed() * 21; // Check if we have reached the last fear point if ((std::abs(GetX() - m_FearWalkTarget.x) < 0.1) && (std::abs(GetY() - m_FearWalkTarget.y) < 0.1)) { @@ -839,16 +830,13 @@ void Client::AI_Process() } if (AImovement_timer->Check()) { - SetRunAnimSpeed(0); + if(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w) + { + SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); + SendPosition(); + } + SetCurrentSpeed(0); } - if(IsMoving()) { - SetMoving(false); - moved=false; - SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); - SendPosition(); - tar_ndx =0; - } - if(GetTarget() && !IsStunned() && !IsMezzed() && !GetFeigned()) { if(attack_timer.Check()) { Attack(GetTarget(), MainPrimary); @@ -944,28 +932,27 @@ void Client::AI_Process() { if(!IsRooted()) { - animation = 21 * GetRunspeed(); - if(!RuleB(Pathing, Aggro) || !zone->pathing) - CalculateNewPosition2(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), GetRunspeed()); - else + if(AImovement_timer->Check()) { - bool WaypointChanged, NodeReached; - glm::vec3 Goal = UpdatePath(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), - GetRunspeed(), WaypointChanged, NodeReached); + if(!RuleB(Pathing, Aggro) || !zone->pathing) + CalculateNewPosition2(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), GetRunspeed()); + else + { + bool WaypointChanged, NodeReached; + glm::vec3 Goal = UpdatePath(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), + GetRunspeed(), WaypointChanged, NodeReached); - if(WaypointChanged) - tar_ndx = 20; + if(WaypointChanged) + tar_ndx = 20; - CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetRunspeed()); + CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetRunspeed()); + } } } else if(IsMoving()) { SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); - SetRunAnimSpeed(0); - SendPosition(); - SetMoving(false); - moved=false; + SetCurrentSpeed(0); } } AI_SpellCast(); @@ -998,21 +985,23 @@ void Client::AI_Process() return; float dist = DistanceSquared(m_Position, owner->GetPosition()); - if (dist >= 100) + if (dist >= 400) { - float speed = dist >= 225 ? GetRunspeed() : GetWalkspeed(); - animation = 21 * speed; - CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), speed); + if(AImovement_timer->Check()) + { + int speed = GetWalkspeed(); + if (dist >= 5625) + speed = GetRunspeed(); + + CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), speed); + } } else { - SetHeading(owner->GetHeading()); if(moved) { - moved=false; - SetMoving(false); - SendPosition(); - SetRunAnimSpeed(0); + SetCurrentSpeed(0); + moved = false; } } } @@ -1042,9 +1031,7 @@ void Mob::AI_Process() { { if(target) SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY())); - SetRunAnimSpeed(0); - SendPosition(); - SetMoving(false); + SetCurrentSpeed(0); moved=false; } //continue on to attack code, ensuring that we execute the engaged code @@ -1058,7 +1045,9 @@ void Mob::AI_Process() { CalculateNewFearpoint(); } if(!RuleB(Pathing, Fear) || !zone->pathing) + { CalculateNewPosition2(m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z, GetFearSpeed(), true); + } else { bool WaypointChanged, NodeReached; @@ -1156,15 +1145,21 @@ void Mob::AI_Process() { { if (AImovement_timer->Check()) { - SetRunAnimSpeed(0); + if(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w) + { + SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); + SendPosition(); + } + SetCurrentSpeed(0); } if(IsMoving()) { - SetMoving(false); - moved=false; - SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY())); - SendPosition(); - tar_ndx =0; + if(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w) + { + SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); + SendPosition(); + } + SetCurrentSpeed(0); } //casting checked above... @@ -1369,7 +1364,7 @@ void Mob::AI_Process() { CastToNPC()->DoClassAttacks(target); } AI_EngagedCastCheck(); - } //end is within combat range + } //end is within combat rangepet else { //we cannot reach our target... //underwater stuff only works with water maps in the zone! @@ -1425,10 +1420,7 @@ void Mob::AI_Process() { } else if(IsMoving()) { SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY())); - SetRunAnimSpeed(0); - SendPosition(); - SetMoving(false); - moved=false; + SetCurrentSpeed(0); } } @@ -1481,7 +1473,6 @@ void Mob::AI_Process() { } else if (AImovement_timer->Check() && !IsRooted()) { - SetRunAnimSpeed(0); if (IsPet()) { // we're a pet, do as we're told @@ -1500,18 +1491,18 @@ void Mob::AI_Process() { float dist = DistanceSquared(m_Position, owner->GetPosition()); if (dist >= 400) { - float speed = GetWalkspeed(); + int speed = GetWalkspeed(); if (dist >= 5625) speed = GetRunspeed(); + CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), speed); } else { if(moved) { - moved=false; - SetMoving(false); - SendPosition(); + SetCurrentSpeed(0); + moved = false; } } @@ -1557,19 +1548,15 @@ void Mob::AI_Process() { if (dist2 >= followdist) // Default follow distance is 100 { - float speed = GetWalkspeed(); + int speed = GetWalkspeed(); if (dist2 >= followdist + 150) speed = GetRunspeed(); CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), speed); } else { - if(moved) - { - SendPosition(); - moved=false; - SetMoving(false); - } + moved = false; + SetCurrentSpeed(0); } } } @@ -1666,40 +1653,92 @@ void NPC::AI_DoMovement() { if (gridno > 0 || cur_wp==-2) { if (movetimercompleted==true) { // time to pause at wp is over - AI_SetupNextWaypoint(); + + int32 spawn_id = this->GetSpawnPointID(); + LinkedListIterator iterator(zone->spawn2_list); + iterator.Reset(); + Spawn2 *found_spawn = nullptr; + + while(iterator.MoreElements()) + { + Spawn2* cur = iterator.GetData(); + iterator.Advance(); + if(cur->GetID() == spawn_id) + { + found_spawn = cur; + break; + } + } + + if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) { + CastToNPC()->Depop(true); //depop and resart spawn timer + if(found_spawn) + found_spawn->SetNPCPointerNull(); + } + else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) { + CastToNPC()->Depop(false);//depop without spawn timer + if(found_spawn) + found_spawn->SetNPCPointerNull(); + } + else { + movetimercompleted=false; + + Log.Out(Logs::Detail, Logs::Pathing, "We are departing waypoint %d.", cur_wp); + + //if we were under quest control (with no grid), we are done now.. + if(cur_wp == -2) { + Log.Out(Logs::Detail, Logs::Pathing, "Non-grid quest mob has reached its quest ordered waypoint. Leaving pathing mode."); + roamer = false; + cur_wp = 0; + } + + if(GetAppearance() != eaStanding) + SetAppearance(eaStanding, false); + + entity_list.OpenDoorsNear(CastToNPC()); + + if(!DistractedFromGrid) { + //kick off event_waypoint depart + char temp[16]; + sprintf(temp, "%d", cur_wp); + parse->EventNPC(EVENT_WAYPOINT_DEPART, CastToNPC(), nullptr, temp, 0); + + //setup our next waypoint, if we are still on our normal grid + //remember that the quest event above could have done anything it wanted with our grid + if(gridno > 0) { + CastToNPC()->CalculateNewWaypoint(); + } + } + else { + DistractedFromGrid = false; + } + } } // endif (movetimercompleted==true) else if (!(AIwalking_timer->Enabled())) { // currently moving - bool doMove = true; if (m_CurrentWayPoint.x == GetX() && m_CurrentWayPoint.y == GetY()) { // are we there yet? then stop Log.Out(Logs::Detail, Logs::AI, "We have reached waypoint %d (%.3f,%.3f,%.3f) on grid %d", cur_wp, GetX(), GetY(), GetZ(), GetGrid()); - if (cur_wp_pause != 0) { - SetWaypointPause(); + SetWaypointPause(); + if(GetAppearance() != eaStanding) SetAppearance(eaStanding, false); - SetMoving(false); - if (m_CurrentWayPoint.w >= 0.0) { - SetHeading(m_CurrentWayPoint.w); - } - SendPosition(); + SetMoving(false); + if (m_CurrentWayPoint.w >= 0.0) { + SetHeading(m_CurrentWayPoint.w); } + SendPosition(); //kick off event_waypoint arrive char temp[16]; sprintf(temp, "%d", cur_wp); parse->EventNPC(EVENT_WAYPOINT_ARRIVE, CastToNPC(), nullptr, temp, 0); - // start moving directly to next waypoint if we're at a 0 pause waypoint and we didn't get quest halted. - if (!AIwalking_timer->Enabled()) - AI_SetupNextWaypoint(); - else - doMove = false; // wipe feign memory since we reached our first waypoint if(cur_wp == 1) ClearFeignMemory(); } - if (doMove) - { // not at waypoint yet or at 0 pause WP, so keep moving + else + { // not at waypoint yet, so keep moving if(!RuleB(Pathing, AggroReturnToGrid) || !zone->pathing || (DistractedFromGrid == 0)) CalculateNewPosition2(m_CurrentWayPoint.x, m_CurrentWayPoint.y, m_CurrentWayPoint.z, walksp, true); else @@ -1727,7 +1766,8 @@ void NPC::AI_DoMovement() { SetGrid( 0 - GetGrid()); // revert to AI control Log.Out(Logs::Detail, Logs::Pathing, "Quest pathing is finished. Resuming on grid %d", GetGrid()); - SetAppearance(eaStanding, false); + if(GetAppearance() != eaStanding) + SetAppearance(eaStanding, false); CalculateNewWaypoint(); } @@ -1763,86 +1803,28 @@ void NPC::AI_DoMovement() { Log.Out(Logs::Detail, Logs::AI, "Reached guard point (%.3f,%.3f,%.3f)", m_GuardPoint.x, m_GuardPoint.y, m_GuardPoint.z); ClearFeignMemory(); moved=false; - SetMoving(false); if (GetTarget() == nullptr || DistanceSquared(m_Position, GetTarget()->GetPosition()) >= 5*5 ) { SetHeading(m_GuardPoint.w); } else { FaceTarget(GetTarget()); } - SendPosition(); + SetCurrentSpeed(0); SetAppearance(GetGuardPointAnim()); } } } } -void NPC::AI_SetupNextWaypoint() { - int32 spawn_id = this->GetSpawnPointID(); - LinkedListIterator iterator(zone->spawn2_list); - iterator.Reset(); - Spawn2 *found_spawn = nullptr; - - while (iterator.MoreElements()) - { - Spawn2* cur = iterator.GetData(); - iterator.Advance(); - if (cur->GetID() == spawn_id) - { - found_spawn = cur; - break; - } - } - - if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) { - CastToNPC()->Depop(true); //depop and restart spawn timer - if (found_spawn) - found_spawn->SetNPCPointerNull(); - } - else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) { - CastToNPC()->Depop(false);//depop without spawn timer - if (found_spawn) - found_spawn->SetNPCPointerNull(); - } - else { - movetimercompleted = false; - - Log.Out(Logs::Detail, Logs::Pathing, "We are departing waypoint %d.", cur_wp); - - //if we were under quest control (with no grid), we are done now.. - if (cur_wp == -2) { - Log.Out(Logs::Detail, Logs::Pathing, "Non-grid quest mob has reached its quest ordered waypoint. Leaving pathing mode."); - roamer = false; - cur_wp = 0; - } - - SetAppearance(eaStanding, false); - - entity_list.OpenDoorsNear(CastToNPC()); - - if (!DistractedFromGrid) { - //kick off event_waypoint depart - char temp[16]; - sprintf(temp, "%d", cur_wp); - parse->EventNPC(EVENT_WAYPOINT_DEPART, CastToNPC(), nullptr, temp, 0); - - //setup our next waypoint, if we are still on our normal grid - //remember that the quest event above could have done anything it wanted with our grid - if (GetGrid() > 0) { - CastToNPC()->CalculateNewWaypoint(); - } - } - else { - DistractedFromGrid = false; - } - } -} // Note: Mob that caused this may not get added to the hate list until after this function call completes void Mob::AI_Event_Engaged(Mob* attacker, bool iYellForHelp) { if (!IsAIControlled()) return; - SetAppearance(eaStanding); + if(GetAppearance() != eaStanding) + { + SetAppearance(eaStanding); + } if (iYellForHelp) { if(IsPet()) { @@ -1889,9 +1871,10 @@ void Mob::AI_Event_NoLongerEngaged() { pLastFightingDelayMoving += zone->random.Int(minLastFightingDelayMoving, maxLastFightingDelayMoving); // So mobs don't keep running as a ghost until AIwalking_timer fires // if they were moving prior to losing all hate - if(IsMoving()){ + // except if we're a pet, then we might run into some issues with pets backing off when they should immediately be moving + if(!IsPet()) + { SetRunAnimSpeed(0); - SetMoving(false); SendPosition(); } ClearRampage(); diff --git a/zone/npc.h b/zone/npc.h index f960c736e..95a857460 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -119,7 +119,6 @@ public: virtual void AI_Start(uint32 iMoveDelay = 0); virtual void AI_Stop(); void AI_DoMovement(); - void AI_SetupNextWaypoint(); bool AI_AddNPCSpells(uint32 iDBSpellsID); bool AI_AddNPCSpellsEffects(uint32 iDBSpellsEffectsID); virtual bool AI_EngagedCastCheck(); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index b9ba00622..eed8305f4 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -6188,6 +6188,35 @@ XS(XS_Client_GetTargetRingZ) XSRETURN(1); } +XS(XS_Client_CalcEXP); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_CalcEXP) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: CalcEXP(THIS, uint8 conlevel)"); + { + Client * THIS; + uint8 conlevel = 0xFF; + uint32 RETVAL; + if(items == 2) + conlevel = (uint16)SvUV(ST(1)); + dXSTARG; + + if (sv_derived_from(ST(0), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Client"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CalcEXP(conlevel); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + XS(XS_Client_QuestReward); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_QuestReward) { @@ -6482,6 +6511,7 @@ XS(boot_Client) newXSproto(strcpy(buf, "GetTargetRingY"), XS_Client_GetTargetRingY, file, "$$"); newXSproto(strcpy(buf, "GetTargetRingZ"), XS_Client_GetTargetRingZ, file, "$$"); newXSproto(strcpy(buf, "QuestReward"), XS_Client_QuestReward, file, "$$;$$$$$$$"); + newXSproto(strcpy(buf, "CalcEXP"), XS_Client_CalcEXP, file, "$"); XSRETURN_YES; } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index fc942f9fe..d0069683b 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -787,8 +787,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if (IsClient()) { - AI_Start(); - SendAppearancePacket(14, 100, true, true); + CastToClient()->AI_Start(); } else if(IsNPC()) { CastToNPC()->SetPetSpellID(0); //not a pet spell. } @@ -877,8 +876,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if(RuleB(Combat, EnableFearPathing)){ if(IsClient()) { - AI_Start(); - animation = static_cast(GetRunspeed() * 21.0f); //set our animation to match our speed about + CastToClient()->AI_Start(); } CalculateNewFearpoint(); @@ -3911,9 +3909,9 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) if(IsNPC()) { CastToNPC()->RestoreGuardSpotCharm(); - SendAppearancePacket(AT_Pet, 0, true, true); } + SendAppearancePacket(AT_Pet, 0, true, true); Mob* tempmob = GetOwner(); SetOwnerID(0); if(tempmob) @@ -3943,12 +3941,12 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) { InterruptSpell(); if (this->CastToClient()->IsLD()) - AI_Start(CLIENT_LD_TIMEOUT); + CastToClient()->AI_Start(CLIENT_LD_TIMEOUT); else { bool feared = FindType(SE_Fear); if(!feared) - AI_Stop(); + CastToClient()->AI_Stop(); } } break; @@ -3973,7 +3971,7 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) { bool charmed = FindType(SE_Charm); if(!charmed) - AI_Stop(); + CastToClient()->AI_Stop(); } if(curfp) { diff --git a/zone/spells.cpp b/zone/spells.cpp index d8699d068..f90335e31 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4754,14 +4754,12 @@ void Client::UnStun() { void NPC::Stun(int duration) { Mob::Stun(duration); - SetRunAnimSpeed(0); - SendPosition(); + SetCurrentSpeed(0); } void NPC::UnStun() { Mob::UnStun(); - SetRunAnimSpeed(static_cast(GetRunspeed())); - SendPosition(); + SetCurrentSpeed(GetRunspeed()); } void Mob::Mesmerize() diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 15cf45e20..6ed516da0 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -1561,6 +1561,8 @@ bool ZoneDatabase::EnableRecipe(uint32 recipe_id) if (!results.Success()) return results.RowsAffected() > 0; + + return false; } bool ZoneDatabase::DisableRecipe(uint32 recipe_id) @@ -1571,4 +1573,6 @@ bool ZoneDatabase::DisableRecipe(uint32 recipe_id) if (!results.Success()) return results.RowsAffected() > 0; + + return false; } diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index f75743a7d..a59e49966 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -394,7 +394,6 @@ void NPC::SetWaypointPause() if (cur_wp_pause == 0) { AIwalking_timer->Start(100); - AIwalking_timer->Trigger(); } else { @@ -437,9 +436,8 @@ void NPC::NextGuardPosition() { { if(moved) { - moved=false; - SetMoving(false); - SendPosition(); + moved = false; + SetCurrentSpeed(0); } } } @@ -490,10 +488,16 @@ float Mob::CalculateHeadingToTarget(float in_x, float in_y) { return (256*(360-angle)/360.0f); } -bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, bool checkZ) { +bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, bool checkZ) { if(GetID()==0) return true; + if(speed == 0) + { + SetCurrentSpeed(0); + return true; + } + if ((m_Position.x-x == 0) && (m_Position.y-y == 0)) {//spawn is at target coords if(m_Position.z-z != 0) { m_Position.z = z; @@ -515,9 +519,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b return true; } - bool send_update = false; int compare_steps = IsBoat() ? 1 : 20; - if(tar_ndx < compare_steps && m_TargetLocation.x==x && m_TargetLocation.y==y) { float new_x = m_Position.x + m_TargetV.x*tar_vector; @@ -589,7 +591,12 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b m_TargetV.x = x - nx; m_TargetV.y = y - ny; m_TargetV.z = z - nz; - + SetCurrentSpeed((int8)speed); + pRunAnimSpeed = speed; + if(IsClient()) + { + animation = speed; + } //pRunAnimSpeed = (int8)(speed*NPC_RUNANIM_RATIO); //speed *= NPC_SPEED_MULTIPLIER; @@ -599,10 +606,10 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b // 2: get unit vector // -------------------------------------------------------------------------- float mag = sqrtf (m_TargetV.x*m_TargetV.x + m_TargetV.y*m_TargetV.y + m_TargetV.z*m_TargetV.z); - tar_vector = speed / mag; + tar_vector = (float)speed / mag; // mob move fix - int numsteps = (int) ( mag * 20 / speed) + 1; + int numsteps = (int) ( mag * 16.0f / (float)speed); // mob move fix @@ -612,9 +619,9 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b if (numsteps>1) { tar_vector=1.0f ; - m_TargetV.x = m_TargetV.x/numsteps; - m_TargetV.y = m_TargetV.y/numsteps; - m_TargetV.z = m_TargetV.z/numsteps; + m_TargetV.x = 1.25f * m_TargetV.x/(float)numsteps; + m_TargetV.y = 1.25f * m_TargetV.y/(float)numsteps; + m_TargetV.z = 1.25f *m_TargetV.z/(float)numsteps; float new_x = m_Position.x + m_TargetV.x; float new_y = m_Position.y + m_TargetV.y; @@ -640,14 +647,13 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b m_Position.y = y; m_Position.z = z; - tar_ndx = 20; Log.Out(Logs::Detail, Logs::AI, "Only a single step to get there... jumping."); } } else { - tar_vector/=20.0f; + tar_vector/=16.0f; float new_x = m_Position.x + m_TargetV.x*tar_vector; float new_y = m_Position.y + m_TargetV.y*tar_vector; @@ -703,32 +709,20 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b m_Delta = glm::vec4(m_Position.x - nx, m_Position.y - ny, m_Position.z - nz, 0.0f); if (IsClient()) - { SendPosUpdate(1); - CastToClient()->ResetPositionTimer(); - } else - { - // force an update now - move_tic_count = RuleI(Zone, NPCPositonUpdateTicCount); SendPosUpdate(); - SetAppearance(eaStanding, false); - } - pLastChange = Timer::GetCurrentTime(); + SetAppearance(eaStanding, false); + pLastChange = Timer::GetCurrentTime(); return true; } -bool Mob::CalculateNewPosition2(float x, float y, float z, float speed, bool checkZ) { - if(IsNPC() || IsClient() || IsPet()) { - pRunAnimSpeed = (int8)(speed*NPC_RUNANIM_RATIO); - speed *= NPC_SPEED_MULTIPLIER; - } - +bool Mob::CalculateNewPosition2(float x, float y, float z, int speed, bool checkZ, bool calcHeading) { return MakeNewPositionAndSendUpdate(x, y, z, speed, checkZ); } -bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool checkZ) { +bool Mob::CalculateNewPosition(float x, float y, float z, int speed, bool checkZ, bool calcHeading) { if(GetID()==0) return true; @@ -737,14 +731,12 @@ bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool chec float nz = m_Position.z; // if NPC is rooted - if (speed == 0.0) { + if (speed == 0) { SetHeading(CalculateHeadingToTarget(x, y)); if(moved){ - SendPosition(); - SetMoving(false); + SetCurrentSpeed(0); moved=false; } - SetRunAnimSpeed(0); Log.Out(Logs::Detail, Logs::AI, "Rooted while calculating new position to (%.3f, %.3f, %.3f)", x, y, z); return true; } @@ -756,8 +748,8 @@ bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool chec if (m_TargetV.x == 0 && m_TargetV.y == 0) return false; - pRunAnimSpeed = (uint8)(speed*NPC_RUNANIM_RATIO); - speed *= NPC_SPEED_MULTIPLIER; + SetCurrentSpeed((int8)(speed)); //*NPC_RUNANIM_RATIO); + //speed *= NPC_SPEED_MULTIPLIER; Log.Out(Logs::Detail, Logs::AI, "Calculating new position to (%.3f, %.3f, %.3f) vector (%.3f, %.3f, %.3f) rate %.3f RAS %d", x, y, z, m_TargetV.x, m_TargetV.y, m_TargetV.z, speed, pRunAnimSpeed); diff --git a/zone/zone.cpp b/zone/zone.cpp index 56181695d..3bf2173c9 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -2209,9 +2209,9 @@ void Zone::ReloadWorld(uint32 Option){ entity_list.ClearAreas(); parse->ReloadQuests(); } else if(Option == 1) { - zone->Repop(0); entity_list.ClearAreas(); parse->ReloadQuests(); + zone->Repop(0); } } From 5c4389effb571a4cf2bfb0a73d0c849e2a52e3eb Mon Sep 17 00:00:00 2001 From: SecretsOTheP Date: Mon, 25 May 2015 12:39:36 -0400 Subject: [PATCH 061/129] Revert custom changes that came with my code load --- zone/bonuses.cpp | 192 +++++++++++++++++------------------------------ zone/effects.cpp | 51 +++++++------ 2 files changed, 95 insertions(+), 148 deletions(-) diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 0c9c6d904..5dd7440f6 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -335,8 +335,8 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu } //FatherNitwit: New style haste, shields, and regens - if((int32)item->Haste > 0) { - newbon->haste += item->Haste; + if(newbon->haste < (int32)item->Haste) { + newbon->haste = item->Haste; } if(item->Regen > 0) newbon->HPRegen += item->Regen; @@ -390,10 +390,10 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu newbon->HitChance += item->Accuracy; } if(item->CombatEffects > 0) { - if((newbon->MeleeDamage + item->CombatEffects) > RuleI(Character, ItemCombatEffectsCap)) - newbon->MeleeDamage = RuleI(Character, ItemCombatEffectsCap); + if((newbon->ProcChance + item->CombatEffects) > RuleI(Character, ItemCombatEffectsCap)) + newbon->ProcChance = RuleI(Character, ItemCombatEffectsCap); else - newbon->MeleeDamage += item->CombatEffects; + newbon->ProcChance += item->CombatEffects; } if(item->DotShielding > 0) { if((newbon->DoTShielding + item->DotShielding) > RuleI(Character, ItemDoTShieldingCap)) @@ -428,7 +428,7 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu newbon->DSMitigation += item->DSMitigation; } if (item->Worn.Effect > 0 && item->Worn.Type == ET_WornEffect) {// latent effects - ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type); + ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type); } if (item->Focus.Effect>0 && (item->Focus.Type == ET_Focus)) { // focus effects @@ -559,7 +559,7 @@ void Client::AdditiveWornBonuses(const ItemInst *inst, StatBonuses* newbon, bool /* Powerful Non-live like option allows developers to add worn effects on items that can stack with other worn effects of the same spell effect type, instead of only taking the highest value. - Ie Cleave I = 40 pct cleave - So if you equip 3 cleave I items you will have a 120 pct cleave bonus. + Ie Cleave I = 40 pct cleave - So if you equip 3 cleave I items you will have a 120 pct cleave bonus. To enable use RuleI(Spells, AdditiveBonusWornType) Setting value = 2 Will force all live items to automatically be calculated additivily Setting value to anything else will indicate the item 'worntype' that if set to the same, will cause the bonuses to use this calculation @@ -579,7 +579,7 @@ void Client::AdditiveWornBonuses(const ItemInst *inst, StatBonuses* newbon, bool if(GetLevel() < item->ReqLevel) return; - + if (item->Worn.Effect > 0 && item->Worn.Type == RuleI(Spells, AdditiveBonusWornType)) ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type);// Non-live like - Addititive latent effects @@ -691,7 +691,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) continue; Log.Out(Logs::Detail, Logs::AA, "Applying Effect %d from AA %u in slot %d (base1: %d, base2: %d) on %s", effect, aaid, slot, base1, base2, this->GetCleanName()); - + uint8 focus = IsFocusEffect(0, 0, true,effect); if (focus) { @@ -1007,7 +1007,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) case SE_BlockBehind: newbon->BlockBehind += base1; break; - + case SE_StrikeThrough: case SE_StrikeThrough2: newbon->StrikeThrough += base1; @@ -1313,7 +1313,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) case SE_Vampirism: newbon->Vampirism += base1; - break; + break; case SE_FrenziedDevastation: newbon->FrenziedDevastation += base2; @@ -1416,7 +1416,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) } case SE_SkillProcSuccess:{ - + for(int e = 0; e < MAX_SKILL_PROCS; e++) { if(newbon->SkillProcSuccess[e] && newbon->SkillProcSuccess[e] == aaid) @@ -1449,7 +1449,7 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon) int buff_count = GetMaxTotalSlots(); for(i = 0; i < buff_count; i++) { if(buffs[i].spellid != SPELL_UNKNOWN){ - ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, 0, buffs[i].ticsremaining,i); + ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, 0, buffs[i].ticsremaining, i, buffs[i].instrument_mod); if (buffs[i].numhits > 0) Numhits(true); @@ -1472,8 +1472,9 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon) if (GetClass() == BARD) newbon->ManaRegen = 0; // Bards do not get mana regen from spells. } -void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* new_bonus, uint16 casterId, uint8 WornType, uint32 ticsremaining, int buffslot, - bool IsAISpellEffect, uint16 effect_id, int32 se_base, int32 se_limit, int32 se_max) +void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *new_bonus, uint16 casterId, + uint8 WornType, int32 ticsremaining, int buffslot, int instrument_mod, + bool IsAISpellEffect, uint16 effect_id, int32 se_base, int32 se_limit, int32 se_max) { int i, effect_value, base2, max, effectid; bool AdditiveWornBonus = false; @@ -1509,9 +1510,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne if (WornType && (RuleI(Spells, AdditiveBonusWornType) == WornType)) AdditiveWornBonus = true; - + effectid = spells[spell_id].effectid[i]; - effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, caster, ticsremaining); + effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, instrument_mod, caster, ticsremaining); base2 = spells[spell_id].base2[i]; max = spells[spell_id].max[i]; } @@ -1560,100 +1561,49 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_AttackSpeed: { - - if (AdditiveWornBonus) { - if ((effect_value - 100) > 0) { // Haste - if (new_bonus->haste < 0) break; // Slowed - Don't apply haste - if ((effect_value - 100) > new_bonus->haste) { - new_bonus->haste += effect_value - 100; - } - } - else if ((effect_value - 100) < 0) { // Slow - int real_slow_value = (100 - effect_value) * -1; - real_slow_value -= ((real_slow_value * GetSlowMitigation()/100)); - if (real_slow_value < new_bonus->haste) - new_bonus->haste += real_slow_value; + if ((effect_value - 100) > 0) { // Haste + if (new_bonus->haste < 0) break; // Slowed - Don't apply haste + if ((effect_value - 100) > new_bonus->haste) { + new_bonus->haste = effect_value - 100; } } - else - { - if ((effect_value - 100) > 0) { // Haste - if (new_bonus->haste < 0) break; // Slowed - Don't apply haste - if ((effect_value - 100) > new_bonus->haste) { - new_bonus->haste = effect_value - 100; - } - } - else if ((effect_value - 100) < 0) { // Slow - int real_slow_value = (100 - effect_value) * -1; - real_slow_value -= ((real_slow_value * GetSlowMitigation()/100)); - if (real_slow_value < new_bonus->haste) - new_bonus->haste = real_slow_value; - } + else if ((effect_value - 100) < 0) { // Slow + int real_slow_value = (100 - effect_value) * -1; + real_slow_value -= ((real_slow_value * GetSlowMitigation()/100)); + if (real_slow_value < new_bonus->haste) + new_bonus->haste = real_slow_value; } break; } case SE_AttackSpeed2: - { - if (AdditiveWornBonus) { - if ((effect_value - 100) > 0) { // Haste - if (new_bonus->hastetype2 < 0) break; // Slowed - Don't apply haste - if ((effect_value - 100) > new_bonus->hastetype2) { - new_bonus->hastetype2 += effect_value - 100; - } - } - else if ((effect_value - 100) < 0) { // Slow - int real_slow_value = (100 - effect_value) * -1; - real_slow_value -= ((real_slow_value * GetSlowMitigation()/100)); - if (real_slow_value < new_bonus->hastetype2) - new_bonus->hastetype2 += real_slow_value; + { + if ((effect_value - 100) > 0) { // Haste V2 - Stacks with V1 but does not Overcap + if (new_bonus->hastetype2 < 0) break; //Slowed - Don't apply haste2 + if ((effect_value - 100) > new_bonus->hastetype2) { + new_bonus->hastetype2 = effect_value - 100; } } - else - { - if ((effect_value - 100) > 0) { // Haste - if (new_bonus->hastetype2 < 0) break; // Slowed - Don't apply haste - if ((effect_value - 100) > new_bonus->hastetype2) { - new_bonus->hastetype2 = effect_value - 100; - } - } - else if ((effect_value - 100) < 0) { // Slow - int real_slow_value = (100 - effect_value) * -1; - real_slow_value -= ((real_slow_value * GetSlowMitigation()/100)); - if (real_slow_value < new_bonus->hastetype2) - new_bonus->hastetype2 = real_slow_value; - } + else if ((effect_value - 100) < 0) { // Slow + int real_slow_value = (100 - effect_value) * -1; + real_slow_value -= ((real_slow_value * GetSlowMitigation()/100)); + if (real_slow_value < new_bonus->hastetype2) + new_bonus->hastetype2 = real_slow_value; } break; } case SE_AttackSpeed3: { - if (AdditiveWornBonus) { - if (effect_value < 0){ //Slow - effect_value -= ((effect_value * GetSlowMitigation()/100)); - if (effect_value < new_bonus->hastetype3) - new_bonus->hastetype3 += effect_value; - } - - else if (effect_value > 0) { // Haste V3 - Stacks and Overcaps - if (effect_value > new_bonus->hastetype3) { - new_bonus->hastetype3 += effect_value; - } - } + if (effect_value < 0){ //Slow + effect_value -= ((effect_value * GetSlowMitigation()/100)); + if (effect_value < new_bonus->hastetype3) + new_bonus->hastetype3 = effect_value; } - else - { - if (effect_value < 0){ //Slow - effect_value -= ((effect_value * GetSlowMitigation()/100)); - if (effect_value < new_bonus->hastetype3) - new_bonus->hastetype3 = effect_value; - } - else if (effect_value > 0) { // Haste V3 - Stacks and Overcaps - if (effect_value > new_bonus->hastetype3) { - new_bonus->hastetype3 = effect_value; - } + else if (effect_value > 0) { // Haste V3 - Stacks and Overcaps + if (effect_value > new_bonus->hastetype3) { + new_bonus->hastetype3 = effect_value; } } break; @@ -1668,21 +1618,13 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne if (effect_value < 0) //A few spells use negative values(Descriptions all indicate it should be a slow) effect_value = effect_value * -1; - if (AdditiveWornBonus) { - if (effect_value > 0 && effect_value > new_bonus->inhibitmelee) { - effect_value -= ((effect_value * GetSlowMitigation()/100)); - if (effect_value > new_bonus->inhibitmelee) - new_bonus->inhibitmelee += effect_value; - } - } - else - { - if (effect_value > 0 && effect_value > new_bonus->inhibitmelee) { - effect_value -= ((effect_value * GetSlowMitigation()/100)); - if (effect_value > new_bonus->inhibitmelee) - new_bonus->inhibitmelee = effect_value; - } + + if (effect_value > 0 && effect_value > new_bonus->inhibitmelee) { + effect_value -= ((effect_value * GetSlowMitigation()/100)); + if (effect_value > new_bonus->inhibitmelee) + new_bonus->inhibitmelee = effect_value; } + break; } @@ -1898,7 +1840,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne new_bonus->DamageShieldType = GetDamageShieldType(spell_id, max); else new_bonus->DamageShieldType = GetDamageShieldType(spell_id); - + break; } @@ -2079,7 +2021,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_Vampirism: new_bonus->Vampirism += effect_value; - break; + break; case SE_AllInstrumentMod: { @@ -2322,7 +2264,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_CriticalSpellChance: { new_bonus->CriticalSpellChance += effect_value; - + if (base2 > new_bonus->SpellCritDmgIncNoStack) new_bonus->SpellCritDmgIncNoStack = base2; break; @@ -2532,7 +2474,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_NegateAttacks: { - if (!new_bonus->NegateAttacks[0] || + if (!new_bonus->NegateAttacks[0] || ((new_bonus->NegateAttacks[0] && new_bonus->NegateAttacks[2]) && (new_bonus->NegateAttacks[2] < max))){ new_bonus->NegateAttacks[0] = 1; new_bonus->NegateAttacks[1] = buffslot; @@ -2552,7 +2494,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne break; } - + case SE_MeleeThresholdGuard: { if (new_bonus->MeleeThresholdGuard[0] < effect_value){ @@ -2919,17 +2861,17 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne new_bonus->NegateIfCombat = true; break; - case SE_Screech: + case SE_Screech: new_bonus->Screech = effect_value; break; case SE_AlterNPCLevel: if (IsNPC()){ - if (!new_bonus->AlterNPCLevel - || ((effect_value < 0) && (new_bonus->AlterNPCLevel > effect_value)) + if (!new_bonus->AlterNPCLevel + || ((effect_value < 0) && (new_bonus->AlterNPCLevel > effect_value)) || ((effect_value > 0) && (new_bonus->AlterNPCLevel < effect_value))) { - + int tmp_lv = GetOrigLevel() + effect_value; if (tmp_lv < 1) tmp_lv = 1; @@ -2967,7 +2909,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne new_bonus->BerserkSPA = true; break; - + case SE_Metabolism: new_bonus->Metabolism += effect_value; break; @@ -3068,7 +3010,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne } case SE_SkillProc:{ - + for(int e = 0; e < MAX_SKILL_PROCS; e++) { if(new_bonus->SkillProc[e] && new_bonus->SkillProc[e] == spell_id) @@ -3083,7 +3025,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne } case SE_SkillProcSuccess:{ - + for(int e = 0; e < MAX_SKILL_PROCS; e++) { if(new_bonus->SkillProcSuccess[e] && new_bonus->SkillProcSuccess[e] == spell_id) @@ -3099,9 +3041,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne //Special custom cases for loading effects on to NPC from 'npc_spels_effects' table if (IsAISpellEffect) { - + //Non-Focused Effect to modify incoming spell damage by resist type. - case SE_FcSpellVulnerability: + case SE_FcSpellVulnerability: ModVulnerability(base2, effect_value); break; } @@ -3167,7 +3109,7 @@ void NPC::CalcItemBonuses(StatBonuses *newbon) newbon->HitChance += cur->Accuracy; } if(cur->CombatEffects > 0) { - newbon->MeleeDamage += cur->CombatEffects; + newbon->ProcChance += cur->CombatEffects; } if (cur->Worn.Effect>0 && (cur->Worn.Type == ET_WornEffect)) { // latent effects ApplySpellsBonuses(cur->Worn.Effect, cur->Worn.Level, newbon, 0, cur->Worn.Type); @@ -4453,7 +4395,7 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) aabonuses.SlayUndead[0] = effect_value; aabonuses.SlayUndead[1] = effect_value; break; - + case SE_DoubleRangedAttack: spellbonuses.DoubleRangedAttack = effect_value; aabonuses.DoubleRangedAttack = effect_value; @@ -4473,7 +4415,7 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) aabonuses.ShieldEquipDmgMod[1] = effect_value; itembonuses.ShieldEquipDmgMod[0] = effect_value; itembonuses.ShieldEquipDmgMod[1] = effect_value; - break; + break; case SE_TriggerMeleeThreshold: spellbonuses.TriggerMeleeThreshold = false; diff --git a/zone/effects.cpp b/zone/effects.cpp index c7066da68..c814ea554 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -89,7 +89,7 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { if (IsClient() && GetClass() == WIZARD) ratio += RuleI(Spells, WizCritRatio); //Default is zero - + if (Critical){ value = value_BaseEffect*ratio/100; @@ -138,7 +138,7 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { value -= GetFocusEffect(focusFcDamageAmt, spell_id); - if(itembonuses.SpellDmg) + if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); if (IsNPC() && CastToNPC()->GetSpellScale()) @@ -172,7 +172,7 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) { value += int(value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100)*ratio/100; value += int(value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100; value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100; - extra_dmg = target->GetFcDamageAmtIncoming(this, spell_id) + + extra_dmg = target->GetFcDamageAmtIncoming(this, spell_id) + int(GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100) + GetFocusEffect(focusFcDamageAmt, spell_id); @@ -200,11 +200,6 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) { extra_dmg /= duration; } - //Sanctuary Custom: Spelldmg per tick - if(itembonuses.SpellDmg) - value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg / 6, value); //per tick - - value -= extra_dmg; } @@ -216,6 +211,23 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) { int32 Mob::GetExtraSpellAmt(uint16 spell_id, int32 extra_spell_amt, int32 base_spell_dmg) { + int total_cast_time = 0; + + if (spells[spell_id].recast_time >= spells[spell_id].recovery_time) + total_cast_time = spells[spell_id].recast_time + spells[spell_id].cast_time; + else + total_cast_time = spells[spell_id].recovery_time + spells[spell_id].cast_time; + + if (total_cast_time > 0 && total_cast_time <= 2500) + extra_spell_amt = extra_spell_amt*25/100; + else if (total_cast_time > 2500 && total_cast_time < 7000) + extra_spell_amt = extra_spell_amt*(167*((total_cast_time - 1000)/1000)) / 1000; + else + extra_spell_amt = extra_spell_amt * total_cast_time / 7000; + + if(extra_spell_amt*2 < base_spell_dmg) + return 0; + return extra_spell_amt; } @@ -258,7 +270,7 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { value += GetFocusEffect(focusFcHealAmt, spell_id); value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); - if(itembonuses.HealAmt) + if(itembonuses.HealAmt && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value) * modifier; value += value*target->GetHealRate(spell_id, this)/100; @@ -269,7 +281,7 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { if (Critical) { entity_list.MessageClose_StringID(this, true, 100, MT_SpellCrits, OTHER_CRIT_HEAL, GetName(), itoa(value)); - + if (IsClient()) Message_StringID(MT_SpellCrits, YOU_CRIT_HEAL, itoa(value)); } @@ -289,9 +301,6 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { if(chance && zone->random.Roll(chance)) value *= 2; - - if(itembonuses.HealAmt) - value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt / 6, value) * modifier; } if (IsNPC() && CastToNPC()->GetHealScale()) @@ -412,14 +421,10 @@ int32 Mob::GetActSpellDuration(uint16 spell_id, int32 duration) int tic_inc = 0; tic_inc = GetFocusEffect(focusSpellDurByTic, spell_id); - // Only need this for clients, since the change was for bard songs, I assume we should keep non bard songs getting +1 - // However if its bard or not and is mez, charm or fear, we need to add 1 so that client is in sync - if (IsClient() && !(IsShortDurationBuff(spell_id) && IsBardSong(spell_id)) || - IsFearSpell(spell_id) || - IsCharmSpell(spell_id) || - IsMezSpell(spell_id) || - IsBlindSpell(spell_id)) - tic_inc += 1; + // unsure on the exact details, but bard songs that don't cost mana at some point get an extra tick, 60 for now + // a level 53 bard reported getting 2 tics + if (IsShortDurationBuff(spell_id) && IsBardSong(spell_id) && spells[spell_id].mana == 0 && GetClass() == BARD && GetLevel() > 60) + tic_inc++; return (((duration * increase) / 100) + tic_inc); } @@ -762,7 +767,7 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); } } else { - if (spells[spell_id].aemaxtargets && iCounter < spells[spell_id].aemaxtargets) + if (spells[spell_id].aemaxtargets && iCounter < spells[spell_id].aemaxtargets) caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); if (!spells[spell_id].aemaxtargets) caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); @@ -850,7 +855,7 @@ void EntityList::AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool aff if (!center->CheckLosFN(curmob)) continue; } else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... - // See notes in AESpell() above for more info. + // See notes in AESpell() above for more info. if (caster->IsAttackAllowed(curmob, true)) continue; if (caster->CheckAggro(curmob)) From 41ca23eb7cab1b2e2c099c936ae5077d93af9c79 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 25 May 2015 13:20:26 -0400 Subject: [PATCH 062/129] Revert "Making $npc->RemoveFromHateList actually work" --- zone/perl_npc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index 861759d12..0c9cec797 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -823,7 +823,7 @@ XS(XS_NPC_RemoveFromHateList) if(ent == nullptr) Perl_croak(aTHX_ "ent is nullptr, avoiding crash."); - THIS->CastToMob()->RemoveFromHateList(ent); + THIS->RemoveFromHateList(ent); } XSRETURN_EMPTY; From ee136881c8a66e025dfdbc2e2e137c514d3b58a2 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 25 May 2015 23:48:11 -0500 Subject: [PATCH 063/129] Implemented disjointed zone based time, this can be triggered via quest methods Added parameter to LUA and Perl method settime(hour, minute, [update_world = true]) - If update_world is false, the zone will then unsubscribe itself from regular worldserver time synchronizations Added DB ver 9082 with update to add npc_types texture columns if table does not currently have them --- changelog.txt | 7 +++++ common/eqtime.cpp | 6 ++-- common/eqtime.h | 6 ++-- common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + world/zoneserver.cpp | 2 +- zone/client_packet.cpp | 2 +- zone/command.cpp | 6 ++-- zone/embparser.cpp | 2 +- zone/embparser_api.cpp | 23 +++++++++++---- zone/lua_general.cpp | 11 +++++-- zone/questmgr.cpp | 5 ++-- zone/questmgr.h | 2 +- zone/spawn2.cpp | 10 +++---- zone/worldserver.cpp | 48 +++++++++++++++++-------------- zone/zone.cpp | 49 +++++++++++++++++++++++++------- zone/zone.h | 11 +++++-- 17 files changed, 128 insertions(+), 65 deletions(-) diff --git a/changelog.txt b/changelog.txt index 6c5d695ab..7d965260c 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,12 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- + +== 05/25/2015 == +Akkadius: Implemented disjointed zone based time, this can be triggered via quest methods +Akkadius: Added parameter to LUA and Perl method settime(hour, minute, [update_world = true]) + - If update_world is false, the zone will then unsubscribe itself from regular worldserver time synchronizations +Akkadius: Added DB ver 9082 with update to add npc_types texture columns if table does not currently have them + == 05/22/2015 == Uleat: Added null-term declaration for character names in ENCODE(OP_CharInfo) - where appropriate diff --git a/common/eqtime.cpp b/common/eqtime.cpp index 79301a28f..cb3afb37d 100644 --- a/common/eqtime.cpp +++ b/common/eqtime.cpp @@ -55,7 +55,7 @@ EQTime::EQTime() //Set default time zone timezone=0; //Start EQTimer - setEQTimeOfDay(start, time(0)); + SetCurrentEQTimeOfDay(start, time(0)); } EQTime::~EQTime() @@ -67,7 +67,7 @@ EQTime::~EQTime() //Input: Current Time (as a time_t), a pointer to the TimeOfDay_Struct that will be written to. //Output: 0=Error, 1=Sucess -int EQTime::getEQTimeOfDay( time_t timeConvert, struct TimeOfDay_Struct *eqTimeOfDay ) +int EQTime::GetCurrentEQTimeOfDay( time_t timeConvert, struct TimeOfDay_Struct *eqTimeOfDay ) { /* check to see if we have a reference time to go by. */ if( eqTime.start_realtime == 0 ) @@ -124,7 +124,7 @@ int EQTime::getEQTimeOfDay( time_t timeConvert, struct TimeOfDay_Struct *eqTimeO } //setEQTimeOfDay -int EQTime::setEQTimeOfDay(TimeOfDay_Struct start_eq, time_t start_real) +int EQTime::SetCurrentEQTimeOfDay(TimeOfDay_Struct start_eq, time_t start_real) { if(start_real==0) return 0; diff --git a/common/eqtime.h b/common/eqtime.h index aa608f307..aeda9f0f6 100644 --- a/common/eqtime.h +++ b/common/eqtime.h @@ -21,8 +21,8 @@ public: ~EQTime(); //Get functions - int getEQTimeOfDay( TimeOfDay_Struct *eqTimeOfDay ) { return(getEQTimeOfDay(time(nullptr), eqTimeOfDay)); } - int getEQTimeOfDay( time_t timeConvert, TimeOfDay_Struct *eqTimeOfDay ); + int GetCurrentEQTimeOfDay( TimeOfDay_Struct *eqTimeOfDay ) { return(GetCurrentEQTimeOfDay(time(nullptr), eqTimeOfDay)); } + int GetCurrentEQTimeOfDay( time_t timeConvert, TimeOfDay_Struct *eqTimeOfDay ); TimeOfDay_Struct getStartEQTime() { return eqTime.start_eqtime; } time_t getStartRealTime() { return eqTime.start_realtime; } uint32 getEQTimeZone() { return timezone; } @@ -30,7 +30,7 @@ public: uint32 getEQTimeZoneMin() { return timezone%60; } //Set functions - int setEQTimeOfDay(TimeOfDay_Struct start_eq, time_t start_real); + int SetCurrentEQTimeOfDay(TimeOfDay_Struct start_eq, time_t start_real); void setEQTimeZone(int32 in_timezone) { timezone=in_timezone; } //Time math/logic functions diff --git a/common/version.h b/common/version.h index 062afdecd..78f7a67b3 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 9080 +#define CURRENT_BINARY_DATABASE_VERSION 9082 #define COMPILE_DATE __DATE__ #define COMPILE_TIME __TIME__ #ifndef WIN32 diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 34330f041..5ca25a029 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -335,6 +335,7 @@ 9079|2015_05_23_BuffDurations.sql|SHOW COLUMNS FROM `character_buffs` LIKE 'ticsremaining'|contains|unsigned| 9080|2015_05_23_PetBuffInstrumentMod.sql|SHOW COLUMNS FROM `character_pet_buffs` LIKE 'instrument_mod'|empty| 9081|2015_05_23_dbstr_us.sql|SHOW TABLES LIKE 'db_str'|empty| +9082|2015_05_25_npc_types_texture_fields.sql|SHOW COLUMNS FROM `npc_types` LIKE 'armtexture'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index b40f3dc04..17cac7f62 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -964,7 +964,7 @@ bool ZoneServer::Process() { case ServerOP_SetWorldTime: { Log.Out(Logs::Detail, Logs::World_Server,"Received SetWorldTime"); eqTimeOfDay* newtime = (eqTimeOfDay*) pack->pBuffer; - zoneserver_list.worldclock.setEQTimeOfDay(newtime->start_eqtime, newtime->start_realtime); + zoneserver_list.worldclock.SetCurrentEQTimeOfDay(newtime->start_eqtime, newtime->start_realtime); Log.Out(Logs::Detail, Logs::World_Server,"New time = %d-%d-%d %d:%d (%d)\n", newtime->start_eqtime.year, newtime->start_eqtime.month, (int)newtime->start_eqtime.day, (int)newtime->start_eqtime.hour, (int)newtime->start_eqtime.minute, (int)newtime->start_realtime); zoneserver_list.worldclock.saveFile(WorldConfig::get()->EQTimeFile.c_str()); zoneserver_list.SendTimeSync(); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 3f40f3c00..9f527476c 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1703,7 +1703,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) /* Time of Day packet */ outapp = new EQApplicationPacket(OP_TimeOfDay, sizeof(TimeOfDay_Struct)); TimeOfDay_Struct* tod = (TimeOfDay_Struct*)outapp->pBuffer; - zone->zone_time.getEQTimeOfDay(time(0), tod); + zone->zone_time.GetCurrentEQTimeOfDay(time(0), tod); outapp->priority = 6; FastQueuePacket(&outapp); diff --git a/zone/command.cpp b/zone/command.cpp index a62ee4ce8..a286f92bd 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -1369,7 +1369,7 @@ void command_date(Client *c, const Seperator *sep) else { int h=0, m=0; TimeOfDay_Struct eqTime; - zone->zone_time.getEQTimeOfDay( time(0), &eqTime); + zone->zone_time.GetCurrentEQTimeOfDay( time(0), &eqTime); if(!sep->IsNumber(4)) h=eqTime.hour; else @@ -1402,7 +1402,7 @@ void command_timezone(Client *c, const Seperator *sep) // Update all clients with new TZ. EQApplicationPacket* outapp = new EQApplicationPacket(OP_TimeOfDay, sizeof(TimeOfDay_Struct)); TimeOfDay_Struct* tod = (TimeOfDay_Struct*)outapp->pBuffer; - zone->zone_time.getEQTimeOfDay(time(0), tod); + zone->zone_time.GetCurrentEQTimeOfDay(time(0), tod); entity_list.QueueClients(c, outapp); safe_delete(outapp); } @@ -4393,7 +4393,7 @@ void command_time(Client *c, const Seperator *sep) else { c->Message(13, "To set the Time: #time HH [MM]"); TimeOfDay_Struct eqTime; - zone->zone_time.getEQTimeOfDay( time(0), &eqTime); + zone->zone_time.GetCurrentEQTimeOfDay( time(0), &eqTime); sprintf(timeMessage,"%02d:%s%d %s (Timezone: %ih %im)", ((eqTime.hour - 1) % 12) == 0 ? 12 : ((eqTime.hour - 1) % 12), (eqTime.minute < 10) ? "0" : "", diff --git a/zone/embparser.cpp b/zone/embparser.cpp index 326585806..d571dce24 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -1072,7 +1072,7 @@ void PerlembParser::ExportZoneVariables(std::string &package_name) { ExportVar(package_name.c_str(), "instanceid", zone->GetInstanceID()); ExportVar(package_name.c_str(), "instanceversion", zone->GetInstanceVersion()); TimeOfDay_Struct eqTime; - zone->zone_time.getEQTimeOfDay( time(0), &eqTime); + zone->zone_time.GetCurrentEQTimeOfDay( time(0), &eqTime); ExportVar(package_name.c_str(), "zonehour", eqTime.hour - 1); ExportVar(package_name.c_str(), "zonemin", eqTime.minute); ExportVar(package_name.c_str(), "zonetime", (eqTime.hour - 1) * 100 + eqTime.minute); diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 800ed9f83..6db7777ac 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -1182,13 +1182,26 @@ XS(XS__settime); XS(XS__settime) { dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: settime(new_hour, new_min)"); + if (items < 2) + Perl_croak(aTHX_ "Usage: settime(new_hour, new_min, [update_world = true])"); - int new_hour = (int)SvIV(ST(0)); - int new_min = (int)SvIV(ST(1)); + if (items == 2){ + int new_hour = (int)SvIV(ST(0)); + int new_min = (int)SvIV(ST(1)); + quest_manager.settime(new_hour, new_min, true); + } + else if (items == 3){ + int new_hour = (int)SvIV(ST(0)); + int new_min = (int)SvIV(ST(1)); - quest_manager.settime(new_hour, new_min); + int update_world = (int)SvIV(ST(2)); + if (update_world == 1){ + quest_manager.settime(new_hour, new_min, true); + } + else{ + quest_manager.settime(new_hour, new_min, false); + } + } XSRETURN_EMPTY; } diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 59a5d3740..6b290dc03 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -386,7 +386,11 @@ void lua_create_guild(const char *name, const char *leader) { } void lua_set_time(int hour, int min) { - quest_manager.settime(hour, min); + quest_manager.settime(hour, min, true); +} + +void lua_set_time(int hour, int min, bool update_world) { + quest_manager.settime(hour, min, update_world); } void lua_signal(int npc_id, int signal_id) { @@ -979,7 +983,7 @@ int lua_get_zone_weather() { luabind::adl::object lua_get_zone_time(lua_State *L) { TimeOfDay_Struct eqTime; - zone->zone_time.getEQTimeOfDay(time(0), &eqTime); + zone->zone_time.GetCurrentEQTimeOfDay(time(0), &eqTime); luabind::adl::object ret = luabind::newtable(L); ret["zone_hour"] = eqTime.hour - 1; @@ -1467,7 +1471,8 @@ luabind::scope lua_register_general() { luabind::def("set_sky", &lua_set_sky), luabind::def("set_guild", &lua_set_guild), luabind::def("create_guild", &lua_create_guild), - luabind::def("set_time", &lua_set_time), + luabind::def("set_time", (void(*)(int, int))&lua_set_time), + luabind::def("set_time", (void(*)(int, int, bool))&lua_set_time), luabind::def("signal", (void(*)(int,int))&lua_signal), luabind::def("signal", (void(*)(int,int,int))&lua_signal), luabind::def("set_global", &lua_set_global), diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index b9025956e..6b5c8978d 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1292,9 +1292,10 @@ void QuestManager::CreateGuild(const char *guild_name, const char *leader) { } } -void QuestManager::settime(uint8 new_hour, uint8 new_min) { +void QuestManager::settime(uint8 new_hour, uint8 new_min, bool update_world /*= true*/) +{ if (zone) - zone->SetTime(new_hour + 1, new_min); + zone->SetTime(new_hour + 1, new_min, update_world); } void QuestManager::itemlink(int item_id) { diff --git a/zone/questmgr.h b/zone/questmgr.h index b278e2c2a..879407dc6 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -126,7 +126,7 @@ public: void setsky(uint8 new_sky); void setguild(uint32 new_guild_id, uint8 new_rank); void CreateGuild(const char *guild_name, const char *leader); - void settime(uint8 new_hour, uint8 new_min); + void settime(uint8 new_hour, uint8 new_min, bool update_world = true); void itemlink(int item_id); void signal(int npc_id, int wait_ms = 0); void signalwith(int npc_id, int signal_id, int wait_ms = 0); diff --git a/zone/spawn2.cpp b/zone/spawn2.cpp index c0eafea80..c42356ea9 100644 --- a/zone/spawn2.cpp +++ b/zone/spawn2.cpp @@ -624,7 +624,7 @@ void SpawnConditionManager::Process() { //get our current time TimeOfDay_Struct tod; - zone->zone_time.getEQTimeOfDay(&tod); + zone->zone_time.GetCurrentEQTimeOfDay(&tod); //see if time is past our nearest event. if(EQTime::IsTimeBefore(&next_event, &tod)) @@ -673,7 +673,7 @@ void SpawnConditionManager::ExecEvent(SpawnEvent &event, bool send_update) { } TimeOfDay_Struct tod; - zone->zone_time.getEQTimeOfDay(&tod); + zone->zone_time.GetCurrentEQTimeOfDay(&tod); if(event.strict && (event.next.hour != tod.hour || event.next.day != tod.day || event.next.month != tod.month || event.next.year != tod.year)) { Log.Out(Logs::Detail, Logs::Spawns, "Event %d: Unable to execute. Condition is strict, and event time has already passed.", event.id); @@ -871,7 +871,7 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in //better solution, and I just dont care thats much. //get our current time TimeOfDay_Struct tod; - zone->zone_time.getEQTimeOfDay(&tod); + zone->zone_time.GetCurrentEQTimeOfDay(&tod); for(auto cur = spawn_events.begin(); cur != spawn_events.end(); ++cur) { SpawnEvent &cevent = *cur; @@ -1096,7 +1096,7 @@ void SpawnConditionManager::ToggleEvent(uint32 event_id, bool enabled, bool stri if(reset_base) { Log.Out(Logs::Detail, Logs::Spawns, "Spawn event %d located in this zone. State set. Trigger time reset (period %d).", event_id, cevent.period); //start with the time now - zone->zone_time.getEQTimeOfDay(&cevent.next); + zone->zone_time.GetCurrentEQTimeOfDay(&cevent.next); //advance the next time by our period EQTime::AddMinutes(cevent.period, &cevent.next); } else { @@ -1141,7 +1141,7 @@ void SpawnConditionManager::ToggleEvent(uint32 event_id, bool enabled, bool stri if(reset_base) { Log.Out(Logs::Detail, Logs::Spawns, "Spawn event %d is in zone %s. State set. Trigger time reset (period %d). Notifying world.", event_id, zone_short_name.c_str(), e.period); //start with the time now - zone->zone_time.getEQTimeOfDay(&e.next); + zone->zone_time.GetCurrentEQTimeOfDay(&e.next); //advance the next time by our period EQTime::AddMinutes(e.period, &e.next); } else { diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index aa3e32d43..517106aca 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -742,32 +742,36 @@ void WorldServer::Process() { break; } case ServerOP_SyncWorldTime: { - if(zone!=0) { + if (zone != 0 && !zone->is_zone_time_localized) { Log.Out(Logs::Moderate, Logs::Zone_Server, "%s Received Message SyncWorldTime", __FUNCTION__); - eqTimeOfDay* newtime = (eqTimeOfDay*) pack->pBuffer; - zone->zone_time.setEQTimeOfDay(newtime->start_eqtime, newtime->start_realtime); + + eqTimeOfDay* newtime = (eqTimeOfDay*)pack->pBuffer; + zone->zone_time.SetCurrentEQTimeOfDay(newtime->start_eqtime, newtime->start_realtime); EQApplicationPacket* outapp = new EQApplicationPacket(OP_TimeOfDay, sizeof(TimeOfDay_Struct)); - TimeOfDay_Struct* tod = (TimeOfDay_Struct*)outapp->pBuffer; - zone->zone_time.getEQTimeOfDay(time(0), tod); + TimeOfDay_Struct* time_of_day = (TimeOfDay_Struct*)outapp->pBuffer; + zone->zone_time.GetCurrentEQTimeOfDay(time(0), time_of_day); entity_list.QueueClients(0, outapp, false); safe_delete(outapp); - //TEST - char timeMessage[255]; - time_t timeCurrent = time(nullptr); - TimeOfDay_Struct eqTime; - zone->zone_time.getEQTimeOfDay( timeCurrent, &eqTime); - //if ( eqTime.hour >= 0 && eqTime.minute >= 0 ) - //{ - sprintf(timeMessage,"EQTime [%02d:%s%d %s]", - ((eqTime.hour - 1) % 12) == 0 ? 12 : ((eqTime.hour - 1) % 12), - (eqTime.minute < 10) ? "0" : "", - eqTime.minute, - (eqTime.hour >= 13) ? "pm" : "am" - ); - Log.Out(Logs::General, Logs::Zone_Server, "Time Broadcast Packet: %s", timeMessage); - zone->GotCurTime(true); - //} - //Test + + /* Buffer garbage to generate debug message */ + char time_message[255]; + time_t current_time = time(nullptr); + TimeOfDay_Struct eq_time; + zone->zone_time.GetCurrentEQTimeOfDay(current_time, &eq_time); + + sprintf(time_message, "EQTime [%02d:%s%d %s]", + ((eq_time.hour - 1) % 12) == 0 ? 12 : ((eq_time.hour - 1) % 12), + (eq_time.minute < 10) ? "0" : "", + eq_time.minute, + (eq_time.hour >= 13) ? "pm" : "am" + ); + + Log.Out(Logs::General, Logs::Zone_Server, "Time Broadcast Packet: %s", time_message); + zone->SetZoneHasCurrentTime(true); + + } + if (zone->is_zone_time_localized){ + Log.Out(Logs::General, Logs::Zone_Server, "Received request to sync time from world, but our time is localized currently"); } break; } diff --git a/zone/zone.cpp b/zone/zone.cpp index 3bf2173c9..e58bea24a 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -697,7 +697,7 @@ void Zone::Shutdown(bool quite) Log.Out(Logs::General, Logs::Status, "Zone Shutdown: %s (%i)", zone->GetShortName(), zone->GetZoneID()); petition_list.ClearPetitions(); - zone->GotCurTime(false); + zone->SetZoneHasCurrentTime(false); if (!quite) Log.Out(Logs::General, Logs::Normal, "Zone shutdown: going to sleep"); ZoneLoaded = false; @@ -760,6 +760,8 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) qGlobals = nullptr; default_ruleset = 0; + is_zone_time_localized = false; + loglevelvar = 0; merchantvar = 0; tradevar = 0; @@ -805,7 +807,7 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) totalBS = 0; aas = nullptr; totalAAs = 0; - gottime = false; + zone_has_current_time = false; Instance_Shutdown_Timer = nullptr; bool is_perma = false; @@ -1484,7 +1486,7 @@ void Zone::Repop(uint32 delay) { void Zone::GetTimeSync() { - if (worldserver.Connected() && !gottime) { + if (worldserver.Connected() && !zone_has_current_time) { ServerPacket* pack = new ServerPacket(ServerOP_GetWorldTime, 0); worldserver.SendPacket(pack); safe_delete(pack); @@ -1508,17 +1510,42 @@ void Zone::SetDate(uint16 year, uint8 month, uint8 day, uint8 hour, uint8 minute } } -void Zone::SetTime(uint8 hour, uint8 minute) +void Zone::SetTime(uint8 hour, uint8 minute, bool update_world /*= true*/) { if (worldserver.Connected()) { ServerPacket* pack = new ServerPacket(ServerOP_SetWorldTime, sizeof(eqTimeOfDay)); - eqTimeOfDay* eqtod = (eqTimeOfDay*)pack->pBuffer; - zone_time.getEQTimeOfDay(time(0), &eqtod->start_eqtime); - eqtod->start_eqtime.minute=minute; - eqtod->start_eqtime.hour=hour; - eqtod->start_realtime=time(0); - printf("Setting master time on world server to: %d:%d (%d)\n", hour, minute, (int)eqtod->start_realtime); - worldserver.SendPacket(pack); + eqTimeOfDay* eq_time_of_day = (eqTimeOfDay*)pack->pBuffer; + + zone_time.GetCurrentEQTimeOfDay(time(0), &eq_time_of_day->start_eqtime); + + eq_time_of_day->start_eqtime.minute = minute; + eq_time_of_day->start_eqtime.hour = hour; + eq_time_of_day->start_realtime = time(0); + + /* By Default we update worlds time, but we can optionally no update world which updates the rest of the zone servers */ + if (update_world){ + Log.Out(Logs::General, Logs::Zone_Server, "Setting master time on world server to: %d:%d (%d)\n", hour, minute, (int)eq_time_of_day->start_realtime); + worldserver.SendPacket(pack); + + /* Set Time Localization Flag */ + zone->is_zone_time_localized = false; + } + /* When we don't update world, we are localizing ourselves, we become disjointed from normal syncs and set time locally */ + else{ + + Log.Out(Logs::General, Logs::Zone_Server, "Setting zone localized time..."); + + zone->zone_time.SetCurrentEQTimeOfDay(eq_time_of_day->start_eqtime, eq_time_of_day->start_realtime); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TimeOfDay, sizeof(TimeOfDay_Struct)); + TimeOfDay_Struct* time_of_day = (TimeOfDay_Struct*)outapp->pBuffer; + zone->zone_time.GetCurrentEQTimeOfDay(time(0), time_of_day); + entity_list.QueueClients(0, outapp, false); + safe_delete(outapp); + + /* Set Time Localization Flag */ + zone->is_zone_time_localized = true; + } + safe_delete(pack); } } diff --git a/zone/zone.h b/zone/zone.h index 80e0473f2..7b1b855a7 100644 --- a/zone/zone.h +++ b/zone/zone.h @@ -43,6 +43,7 @@ struct ZonePoint int32 target_zone_instance; uint32 client_version_mask; }; + struct ZoneClientAuth_Struct { uint32 ip; // client's IP address uint32 wid; // client's WorldID# @@ -85,6 +86,10 @@ public: Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name); ~Zone(); + + /* When zone has its own version of time */ + bool is_zone_time_localized; + bool Init(bool iStaticZone); bool LoadZoneCFG(const char* filename, uint16 instance_id, bool DontLoadDefault = false); bool SaveZoneCFG(); @@ -153,7 +158,7 @@ public: inline bool InstantGrids() { return(!initgrids_timer.Enabled()); } void SetStaticZone(bool sz) { staticzone = sz; } inline bool IsStaticZone() { return staticzone; } - inline void GotCurTime(bool time) { gottime = time; } + inline void SetZoneHasCurrentTime(bool time) { zone_has_current_time = time; } void SpawnConditionChanged(const SpawnCondition &c, int16 old_value); @@ -206,7 +211,7 @@ public: EQTime zone_time; void GetTimeSync(); void SetDate(uint16 year, uint8 month, uint8 day, uint8 hour, uint8 minute); - void SetTime(uint8 hour, uint8 minute); + void SetTime(uint8 hour, uint8 minute, bool update_world = true); void weatherSend(); bool CanBind() const { return(can_bind); } @@ -319,7 +324,7 @@ private: bool staticzone; - bool gottime; + bool zone_has_current_time; uint32 pQueuedMerchantsWorkID; uint32 pQueuedTempMerchantsWorkID; From d2a1fb7acfeadc391e9d4bde0b9158ea03d9fa29 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 25 May 2015 23:49:11 -0500 Subject: [PATCH 064/129] Add file 2015_05_25_npc_types_texture_fields.sql --- .../git/required/2015_05_25_npc_types_texture_fields.sql | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 utils/sql/git/required/2015_05_25_npc_types_texture_fields.sql diff --git a/utils/sql/git/required/2015_05_25_npc_types_texture_fields.sql b/utils/sql/git/required/2015_05_25_npc_types_texture_fields.sql new file mode 100644 index 000000000..acd8d26b6 --- /dev/null +++ b/utils/sql/git/required/2015_05_25_npc_types_texture_fields.sql @@ -0,0 +1,6 @@ +ALTER TABLE npc_types +ADD COLUMN `armtexture` tinyint(2) NOT NULL DEFAULT '0' AFTER `raid_target`, +ADD COLUMN `bracertexture` tinyint(2) NOT NULL DEFAULT '0' AFTER `armtexture`, +ADD COLUMN `handtexture` tinyint(2) NOT NULL DEFAULT '0' AFTER `bracertexture`, +ADD COLUMN `legtexture` tinyint(2) NOT NULL DEFAULT '0' AFTER `handtexture`, +ADD COLUMN `feettexture` tinyint(2) NOT NULL DEFAULT '0' AFTER `legtexture`; \ No newline at end of file From b6091c19606405a0b5177e9cd9ddcb8cc9afe3a5 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 25 May 2015 23:51:23 -0500 Subject: [PATCH 065/129] Update changelog descriptor cause prob not clear enough [skip ci] --- changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.txt b/changelog.txt index 7d965260c..cdb2fd5f8 100644 --- a/changelog.txt +++ b/changelog.txt @@ -5,6 +5,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) Akkadius: Implemented disjointed zone based time, this can be triggered via quest methods Akkadius: Added parameter to LUA and Perl method settime(hour, minute, [update_world = true]) - If update_world is false, the zone will then unsubscribe itself from regular worldserver time synchronizations + - Basically this localizes the zones time and keeps it from syncing with world updates Akkadius: Added DB ver 9082 with update to add npc_types texture columns if table does not currently have them == 05/22/2015 == From 5c194c708741663932f344598c2708413bc94a00 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 25 May 2015 23:57:48 -0500 Subject: [PATCH 066/129] Some syntax adjustments to eqtime.cpp [skip ci] --- common/eqtime.cpp | 79 +++++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/common/eqtime.cpp b/common/eqtime.cpp index cb3afb37d..e504964b8 100644 --- a/common/eqtime.cpp +++ b/common/eqtime.cpp @@ -43,17 +43,17 @@ EQTime::EQTime(TimeOfDay_Struct start_eq, time_t start_real) EQTime::EQTime() { - timezone=0; + timezone = 0; memset(&eqTime, 0, sizeof(eqTime)); //Defaults for time TimeOfDay_Struct start; - start.day=1; - start.hour=9; - start.minute=0; - start.month=1; - start.year=3100; + start.day = 1; + start.hour = 9; + start.minute = 0; + start.month = 1; + start.year = 3100; //Set default time zone - timezone=0; + timezone = 0; //Start EQTimer SetCurrentEQTimeOfDay(start, time(0)); } @@ -67,10 +67,10 @@ EQTime::~EQTime() //Input: Current Time (as a time_t), a pointer to the TimeOfDay_Struct that will be written to. //Output: 0=Error, 1=Sucess -int EQTime::GetCurrentEQTimeOfDay( time_t timeConvert, struct TimeOfDay_Struct *eqTimeOfDay ) +int EQTime::GetCurrentEQTimeOfDay(time_t timeConvert, struct TimeOfDay_Struct *eqTimeOfDay) { /* check to see if we have a reference time to go by. */ - if( eqTime.start_realtime == 0 ) + if (eqTime.start_realtime == 0) return 0; unsigned long diff = timeConvert - eqTime.start_realtime; @@ -83,7 +83,7 @@ int EQTime::GetCurrentEQTimeOfDay( time_t timeConvert, struct TimeOfDay_Struct * int32 ntz = timezone; /* The minutes range from 0 - 59 */ - diff += eqTime.start_eqtime.minute + (ntz%60); + diff += eqTime.start_eqtime.minute + (ntz % 60); eqTimeOfDay->minute = diff % 60; diff /= 60; ntz /= 60; @@ -97,24 +97,24 @@ int EQTime::GetCurrentEQTimeOfDay( time_t timeConvert, struct TimeOfDay_Struct * // // Modify it so that it works from // 0-23 for our calculations - diff += ( eqTime.start_eqtime.hour - 1) + (ntz%24); - eqTimeOfDay->hour = (diff%24) + 1; + diff += (eqTime.start_eqtime.hour - 1) + (ntz % 24); + eqTimeOfDay->hour = (diff % 24) + 1; diff /= 24; ntz /= 24; // The days range from 1-28 // Modify it so that it works from // 0-27 for our calculations - diff += ( eqTime.start_eqtime.day - 1 ) + (ntz%28); - eqTimeOfDay->day = (diff%28) + 1; + diff += (eqTime.start_eqtime.day - 1) + (ntz % 28); + eqTimeOfDay->day = (diff % 28) + 1; diff /= 28; ntz /= 28; // The months range from 1-12 // Modify it so that it works from // 0-11 for our calculations - diff += ( eqTime.start_eqtime.month - 1 ) + (ntz%12); - eqTimeOfDay->month = (diff%12) + 1; + diff += (eqTime.start_eqtime.month - 1) + (ntz % 12); + eqTimeOfDay->month = (diff % 12) + 1; diff /= 12; ntz /= 12; @@ -126,10 +126,10 @@ int EQTime::GetCurrentEQTimeOfDay( time_t timeConvert, struct TimeOfDay_Struct * //setEQTimeOfDay int EQTime::SetCurrentEQTimeOfDay(TimeOfDay_Struct start_eq, time_t start_real) { - if(start_real==0) + if (start_real == 0) return 0; - eqTime.start_eqtime=start_eq; - eqTime.start_realtime=start_real; + eqTime.start_eqtime = start_eq; + eqTime.start_realtime = start_real; return 1; } @@ -139,7 +139,7 @@ bool EQTime::saveFile(const char *filename) { std::ofstream of; of.open(filename); - if(!of) + if (!of) { Log.Out(Logs::General, Logs::Error, "EQTime::saveFile failed: Unable to open file '%s'", filename); return false; @@ -200,24 +200,24 @@ bool EQTime::loadFile(const char *filename) bool EQTime::IsTimeBefore(TimeOfDay_Struct *base, TimeOfDay_Struct *test) { - if(base->year > test->year) + if (base->year > test->year) return(true); - if(base->year < test->year) + if (base->year < test->year) return(false); //same years - if(base->month > test->month) + if (base->month > test->month) return(true); - if(base->month < test->month) + if (base->month < test->month) return(false); //same month - if(base->day > test->day) + if (base->day > test->day) return(true); - if(base->day < test->day) + if (base->day < test->day) return(false); //same day - if(base->hour > test->hour) + if (base->hour > test->hour) return(true); - if(base->hour < test->hour) + if (base->hour < test->hour) return(false); //same hour... return(base->minute > test->minute); @@ -230,7 +230,7 @@ void EQTime::AddMinutes(uint32 minutes, TimeOfDay_Struct *to) { //minutes start at 0, everything else starts at 1 cur = to->minute; cur += minutes; - if(cur < 60) { + if (cur < 60) { to->minute = cur; return; } @@ -238,29 +238,29 @@ void EQTime::AddMinutes(uint32 minutes, TimeOfDay_Struct *to) { //carry hours cur /= 60; cur += to->hour; - if(cur <= 24) { + if (cur <= 24) { to->hour = cur; return; } - to->hour = ((cur-1) % 24) + 1; + to->hour = ((cur - 1) % 24) + 1; //carry days - cur = (cur-1) / 24; + cur = (cur - 1) / 24; cur += to->day; - if(cur <= 28) { + if (cur <= 28) { to->day = cur; return; } - to->day = ((cur-1) % 28) + 1; + to->day = ((cur - 1) % 28) + 1; //carry months - cur = (cur-1) / 28; + cur = (cur - 1) / 28; cur += to->month; - if(cur <= 12) { + if (cur <= 12) { to->month = cur; return; } - to->month = ((cur-1) % 12) + 1; + to->month = ((cur - 1) % 12) + 1; //carry years - to->year += (cur-1) / 12; + to->year += (cur - 1) / 12; } void EQTime::ToString(TimeOfDay_Struct *t, std::string &str) { @@ -269,5 +269,4 @@ void EQTime::ToString(TimeOfDay_Struct *t, std::string &str) { t->month, t->day, t->year, t->hour, t->minute); buf[127] = '\0'; str = buf; -} - +} \ No newline at end of file From e7902342ddba8f86d50dc98caeed0a01f9f63f49 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 26 May 2015 00:57:39 -0400 Subject: [PATCH 067/129] EQ seems to round the ticks weird ... A few examples in the comments ... --- zone/effects.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index c814ea554..c1d037670 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -425,8 +425,15 @@ int32 Mob::GetActSpellDuration(uint16 spell_id, int32 duration) // a level 53 bard reported getting 2 tics if (IsShortDurationBuff(spell_id) && IsBardSong(spell_id) && spells[spell_id].mana == 0 && GetClass() == BARD && GetLevel() > 60) tic_inc++; + float focused = ((duration * increase) / 100.0f) + tic_inc; + int ifocused = static_cast(focused); - return (((duration * increase) / 100) + tic_inc); + // 7.6 is rounded to 7, 8.6 is rounded to 9 + // 6 is 6, etc + if (FCMP(focused, ifocused) || ifocused % 2) // equal or odd + return ifocused; + else // even and not equal round to odd + return ifocused + 1; } int32 Client::GetActSpellCasttime(uint16 spell_id, int32 casttime) From 2b495cea5a67f585d1f902e4db0fbb4f7dcbce76 Mon Sep 17 00:00:00 2001 From: SecretsOTheP Date: Tue, 26 May 2015 01:19:49 -0400 Subject: [PATCH 068/129] bot fixes for compiling --- zone/bot.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 5ddbb1377..8de38f6a4 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3446,7 +3446,7 @@ void Bot::AI_Process() { if(IsMoving()) { SetHeading(0); SetRunAnimSpeed(0); - SetCurrentSpeed(GetRunSpeed()); + SetCurrentSpeed(GetRunspeed()); if(moved) { SetCurrentSpeed(0); @@ -3512,7 +3512,6 @@ void Bot::AI_Process() { if(atCombatRange) { if(IsMoving()) { SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); - SetRunAnimSpeed(0); SetCurrentSpeed(0); if(moved) { moved = false; From 76d7fe158695c938b194967d7ba566bd6e154268 Mon Sep 17 00:00:00 2001 From: SecretsOTheP Date: Tue, 26 May 2015 02:27:48 -0400 Subject: [PATCH 069/129] Fixes for mobs on pause waypoints dancing around. Fixes for runspeed <= 0 as reported by demonstar55 --- zone/bot.cpp | 56 +++------------------------------------------- zone/mob.cpp | 10 ++++----- zone/mob_ai.cpp | 16 +++++++------ zone/waypoints.cpp | 2 +- 4 files changed, 18 insertions(+), 66 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 8de38f6a4..10e00e3f2 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -373,7 +373,7 @@ NPCType Bot::FillNPCTypeStruct(uint32 botSpellsID, std::string botName, std::str BotNPCType.d_melee_texture2 = 0; BotNPCType.qglobal = false; BotNPCType.attack_speed = 0; - BotNPCType.runspeed = 1.25; + BotNPCType.runspeed = 0.7f; BotNPCType.bodytype = 1; BotNPCType.findable = 0; BotNPCType.hp_regen = 1; @@ -410,7 +410,7 @@ NPCType Bot::CreateDefaultNPCTypeStructForBot(std::string botName, std::string b Result.drakkin_details = 0; Result.drakkin_heritage = 0; Result.drakkin_tattoo = 0; - Result.runspeed = 1.25; + Result.runspeed = 0.7f; Result.bodytype = 1; Result.findable = 0; Result.hp_regen = 1; @@ -3739,8 +3739,6 @@ void Bot::AI_Process() { if(dist < GetFollowDistance() + 1000) speed = follow->GetWalkspeed(); - SetRunAnimSpeed(0); - if(dist > GetFollowDistance()) { CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), speed); if(rest_timer.Enabled()) @@ -3832,12 +3830,10 @@ void Bot::PetAIProcess() { botPet->GetAIMovementTimer()->Check(); if(botPet->IsMoving()) { - botPet->SetRunAnimSpeed(0); botPet->SetHeading(botPet->GetTarget()->GetHeading()); if(moved) { moved=false; - botPet->SendPosition(); - botPet->SetMoving(false); + botPet->SetRunAnimSpeed(0); } } @@ -10542,52 +10538,6 @@ bool Bot::CanHeal() { } bool Bot::CalculateNewPosition2(float x, float y, float z, float speed, bool checkZ) { - // 2.5625 is the inverse of 0.3902439. The only difference is in implementation. - // NOTE: You can not change just one of the constants below. They are the same number, just expressed inversly of each other. - // const float clientOverServerRatio = 2.5625f; - const float serverOverClientRatio = 0.3902439f; - - // Use this block if using 2.5625 as the ratio. - // const int clientAnimationMovementRateTypeMultiple = 8; - - // WildcardX: These are valid rates and observations based on painstaking testing of the client response to these values - // - // - // 0 * 8 = 0 : No Movement - // 1 * 8 = 8 : Death Walk - // 2 * 8 = 16 : Slow Walk - // 3 * 8 = 24 : Normal Walk - // 4 * 8 = 32 : Jog - // 5 * 8 = 40 : Normal Run - // 6 * 8 = 48 : Faster Run - // 7 * 8 = 56 : Even Faster Run - // 8 * 8 = 64 : Fastest Yet Run (Bard Song Speed?) - // 9 * 8 = 72 : Faster Fastest Yet Run - // 10 * 8 = 80 : .... you get the idea, this is pretty fast - // 11 * 8 = 88 : .... warp speed anyone? - // 12 * 8 = 96 : .... transwarp drive was invented by gnomes in Norrath - // 13 * 8 = 104 : ... who needs warp drives when you can just displace through time and space? - // - // - // You get the idea here with these... These seem to be "benchmark values" of animation movement and how fast - // the client thinks the Mob is moving so it can make it all look seemless between updates from the server. - // This chart is scalable by the client so you can pass an animation rate of 50 and get a "faster run" but not quite a "even faster run" - - // Convert the Bot movement rate to a value the client understands based on the chart above - // Use this block if using 2.5625 as the ratio. - // speed *= clientMovementRateTypeMultiple; - - - // This sets the movement animation rate with the client - // Use this block if using 2.5625 as the ratio. - // pRunAnimSpeed = speed; - pRunAnimSpeed = ((serverOverClientRatio * 10.0f) * speed) * 10.0f; - - // Now convert our "speed" from the value necessary for the client to animate the correct movement type rate to the server side speed - // Use this block if using 2.5625 as the ratio. - // speed *= serverOverClientRatio; - speed = pRunAnimSpeed / serverOverClientRatio; - return MakeNewPositionAndSendUpdate(x, y, z, speed, checkZ); } diff --git a/zone/mob.cpp b/zone/mob.cpp index ddad36ff1..f7348a869 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -599,7 +599,7 @@ int Mob::_GetWalkSpeed() const { speed_mod += (base_run * movemod / 100); if(speed_mod < 1) - return(1); + return(0); //runspeed cap. if(IsClient()) @@ -670,7 +670,7 @@ int Mob::_GetRunSpeed() const { // basically stoped if(speed_mod < 1) { - return(1); + return(0); } // moving slowly if (speed_mod < 8) @@ -687,7 +687,7 @@ int Mob::_GetRunSpeed() const { if(speed_mod < 1) { - return(1); + return(0); } //runspeed cap. if(IsClient()) @@ -736,7 +736,7 @@ int Mob::_GetFearSpeed() const { { if (hp_ratio < 25) { - return (1); + return (0); } if (hp_ratio < 50) return (8); @@ -766,7 +766,7 @@ int Mob::_GetFearSpeed() const { } } if (speed_mod < 1) - return (1); + return (0); if (speed_mod < 9) return (8); if (speed_mod < 13) diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 9909672de..b3aaad1dc 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1719,14 +1719,16 @@ void NPC::AI_DoMovement() { if (m_CurrentWayPoint.x == GetX() && m_CurrentWayPoint.y == GetY()) { // are we there yet? then stop Log.Out(Logs::Detail, Logs::AI, "We have reached waypoint %d (%.3f,%.3f,%.3f) on grid %d", cur_wp, GetX(), GetY(), GetZ(), GetGrid()); - SetWaypointPause(); - if(GetAppearance() != eaStanding) - SetAppearance(eaStanding, false); - SetMoving(false); - if (m_CurrentWayPoint.w >= 0.0) { - SetHeading(m_CurrentWayPoint.w); + if (cur_wp_pause != 0) { + SetWaypointPause(); + if(GetAppearance() != eaStanding) + SetAppearance(eaStanding, false); + SetMoving(false); + if (m_CurrentWayPoint.w >= 0.0) { + SetHeading(m_CurrentWayPoint.w); + } + SendPosition(); } - SendPosition(); //kick off event_waypoint arrive char temp[16]; diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index a59e49966..c4f37864d 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -492,7 +492,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo if(GetID()==0) return true; - if(speed == 0) + if(speed <= 0) { SetCurrentSpeed(0); return true; From 92c756c8207fc069973956257e83336862394e7d Mon Sep 17 00:00:00 2001 From: Uleat Date: Tue, 26 May 2015 15:51:18 -0400 Subject: [PATCH 070/129] Fix for character select screen client crashes (fix #418) --- changelog.txt | 2 ++ common/eq_stream.cpp | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/changelog.txt b/changelog.txt index cdb2fd5f8..3af28ecf1 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 05/26/2015 == +Uleat: Fix for packet size adjustment after initial declaration (issue #418) - should clear up some random client crashes and the inability to progress to charsel screen on affected accounts == 05/25/2015 == Akkadius: Implemented disjointed zone based time, this can be triggered via quest methods diff --git a/common/eq_stream.cpp b/common/eq_stream.cpp index be1ae50d4..7fa53ae02 100644 --- a/common/eq_stream.cpp +++ b/common/eq_stream.cpp @@ -571,12 +571,25 @@ void EQStream::SendPacket(uint16 opcode, EQApplicationPacket *p) } } + + // The (p->size++) and (length--) modification code inside of the (opcode & 0x00FF == 0) checks are a temporary solution + // to a discongruency in packet size due to an alteration of packet size after the initial declaration. This led to client + // crashes where the original packet size + protocol bytes mod MaxLen() produced a 0 result. + // + // These changes are local-only and there may be other calling methods affected by the EQ##Packet::serialize() issue. + if ((opcode & 0x00FF) == 0) { // this is final..but, had scope issues during early testing at this point + Log.Out(Logs::Detail, Logs::Netcode, _L "Adjusting application packet length for high byte opcode 0x%04X (%d to %d)" __L, opcode, p->size++, p->size); + } + // Convert the EQApplicationPacket to 1 or more EQProtocolPackets if (p->size>(MaxLen-8)) { // proto-op(2), seq(2), app-op(2) ... data ... crc(2) Log.Out(Logs::Detail, Logs::Netcode, _L "Making oversized packet, len %d" __L, p->size); unsigned char *tmpbuff=new unsigned char[p->size+3]; length=p->serialize(opcode, tmpbuff); + if ((opcode & 0x00FF) == 0) { // temp solution until all serialize() calls can be evaluated + Log.Out(Logs::Detail, Logs::Netcode, _L "Adjusting protocol packet length for high byte opcode 0x%04X (%d to %d)" __L, opcode, length--, length); + } EQProtocolPacket *out=new EQProtocolPacket(OP_Fragment,nullptr,MaxLen-4); *(uint32 *)(out->pBuffer+2)=htonl(p->Size()); @@ -601,6 +614,9 @@ void EQStream::SendPacket(uint16 opcode, EQApplicationPacket *p) unsigned char *tmpbuff=new unsigned char[p->Size()+3]; length=p->serialize(opcode, tmpbuff+2) + 2; + if ((opcode & 0x00FF) == 0) { // temp solution until all serialize() calls can be evaluated + Log.Out(Logs::Detail, Logs::Netcode, _L "Adjusting protocol packet length for high byte opcode 0x%04X (%d to %d)" __L, opcode, length--, length); + } EQProtocolPacket *out=new EQProtocolPacket(OP_Packet,tmpbuff,length); From dbd615572ce2beb60ef1e4ba972ef233093e7230 Mon Sep 17 00:00:00 2001 From: Uleat Date: Tue, 26 May 2015 21:19:48 -0400 Subject: [PATCH 071/129] Revert "Fix for character select screen client crashes (fix #418)" This reverts commit 92c756c8207fc069973956257e83336862394e7d. --- changelog.txt | 2 -- common/eq_stream.cpp | 16 ---------------- 2 files changed, 18 deletions(-) diff --git a/changelog.txt b/changelog.txt index 3af28ecf1..cdb2fd5f8 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,7 +1,5 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- -== 05/26/2015 == -Uleat: Fix for packet size adjustment after initial declaration (issue #418) - should clear up some random client crashes and the inability to progress to charsel screen on affected accounts == 05/25/2015 == Akkadius: Implemented disjointed zone based time, this can be triggered via quest methods diff --git a/common/eq_stream.cpp b/common/eq_stream.cpp index 7fa53ae02..be1ae50d4 100644 --- a/common/eq_stream.cpp +++ b/common/eq_stream.cpp @@ -571,25 +571,12 @@ void EQStream::SendPacket(uint16 opcode, EQApplicationPacket *p) } } - - // The (p->size++) and (length--) modification code inside of the (opcode & 0x00FF == 0) checks are a temporary solution - // to a discongruency in packet size due to an alteration of packet size after the initial declaration. This led to client - // crashes where the original packet size + protocol bytes mod MaxLen() produced a 0 result. - // - // These changes are local-only and there may be other calling methods affected by the EQ##Packet::serialize() issue. - if ((opcode & 0x00FF) == 0) { // this is final..but, had scope issues during early testing at this point - Log.Out(Logs::Detail, Logs::Netcode, _L "Adjusting application packet length for high byte opcode 0x%04X (%d to %d)" __L, opcode, p->size++, p->size); - } - // Convert the EQApplicationPacket to 1 or more EQProtocolPackets if (p->size>(MaxLen-8)) { // proto-op(2), seq(2), app-op(2) ... data ... crc(2) Log.Out(Logs::Detail, Logs::Netcode, _L "Making oversized packet, len %d" __L, p->size); unsigned char *tmpbuff=new unsigned char[p->size+3]; length=p->serialize(opcode, tmpbuff); - if ((opcode & 0x00FF) == 0) { // temp solution until all serialize() calls can be evaluated - Log.Out(Logs::Detail, Logs::Netcode, _L "Adjusting protocol packet length for high byte opcode 0x%04X (%d to %d)" __L, opcode, length--, length); - } EQProtocolPacket *out=new EQProtocolPacket(OP_Fragment,nullptr,MaxLen-4); *(uint32 *)(out->pBuffer+2)=htonl(p->Size()); @@ -614,9 +601,6 @@ void EQStream::SendPacket(uint16 opcode, EQApplicationPacket *p) unsigned char *tmpbuff=new unsigned char[p->Size()+3]; length=p->serialize(opcode, tmpbuff+2) + 2; - if ((opcode & 0x00FF) == 0) { // temp solution until all serialize() calls can be evaluated - Log.Out(Logs::Detail, Logs::Netcode, _L "Adjusting protocol packet length for high byte opcode 0x%04X (%d to %d)" __L, opcode, length--, length); - } EQProtocolPacket *out=new EQProtocolPacket(OP_Packet,tmpbuff,length); From 24917257e6ae7007a71385b2d89dcadd51be8243 Mon Sep 17 00:00:00 2001 From: Uleat Date: Wed, 27 May 2015 22:24:00 -0400 Subject: [PATCH 072/129] Application packet size fix for high byte opcodes --- common/eq_stream.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/common/eq_stream.cpp b/common/eq_stream.cpp index be1ae50d4..c48cba0fd 100644 --- a/common/eq_stream.cpp +++ b/common/eq_stream.cpp @@ -573,16 +573,18 @@ void EQStream::SendPacket(uint16 opcode, EQApplicationPacket *p) // Convert the EQApplicationPacket to 1 or more EQProtocolPackets if (p->size>(MaxLen-8)) { // proto-op(2), seq(2), app-op(2) ... data ... crc(2) - Log.Out(Logs::Detail, Logs::Netcode, _L "Making oversized packet, len %d" __L, p->size); + Log.Out(Logs::Detail, Logs::Netcode, _L "Making oversized packet, len %d" __L, p->Size()); unsigned char *tmpbuff=new unsigned char[p->size+3]; length=p->serialize(opcode, tmpbuff); + if (length != p->Size()) + Log.Out(Logs::Detail, Logs::Netcode, _L "Packet adjustment, len %d to %d" __L, p->Size(), length); EQProtocolPacket *out=new EQProtocolPacket(OP_Fragment,nullptr,MaxLen-4); - *(uint32 *)(out->pBuffer+2)=htonl(p->Size()); + *(uint32 *)(out->pBuffer+2)=htonl(length); used=MaxLen-10; memcpy(out->pBuffer+6,tmpbuff,used); - Log.Out(Logs::Detail, Logs::Netcode, _L "First fragment: used %d/%d. Put size %d in the packet" __L, used, p->size, p->Size()); + Log.Out(Logs::Detail, Logs::Netcode, _L "First fragment: used %d/%d. Payload size %d in the packet" __L, used, length, p->size); SequencedPush(out); @@ -593,7 +595,7 @@ void EQStream::SendPacket(uint16 opcode, EQApplicationPacket *p) out->size=chunksize+2; SequencedPush(out); used+=chunksize; - Log.Out(Logs::Detail, Logs::Netcode, _L "Subsequent fragment: len %d, used %d/%d." __L, chunksize, used, p->size); + Log.Out(Logs::Detail, Logs::Netcode, _L "Subsequent fragment: len %d, used %d/%d." __L, chunksize, used, length); } delete p; delete[] tmpbuff; From dbbae0e735ad229b5a8b614f2db7f373cfecea5c Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 28 May 2015 13:26:55 -0400 Subject: [PATCH 073/129] Crash for no zonemap --- zone/attack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 49b5df187..c5dfcc6d7 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3717,7 +3717,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons // update NPC stuff auto new_pos = glm::vec3(m_Position.x + (a->force * std::sin(a->meleepush_xy) + m_Delta.x), m_Position.y + (a->force * std::cos(a->meleepush_xy) + m_Delta.y), m_Position.z); - if (zone->zonemap->CheckLoS(glm::vec3(m_Position), new_pos)) { // If we have LoS on the new loc it should be reachable. + if (zone->zonemap && zone->zonemap->CheckLoS(glm::vec3(m_Position), new_pos)) { // If we have LoS on the new loc it should be reachable. if (IsNPC()) { // Is this adequate? Teleport(new_pos); From 22ef16947cbb015732d1e48cedd9800884430cf4 Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Thu, 28 May 2015 11:24:17 -0700 Subject: [PATCH 074/129] Bots will no longer crash when disbanding on death or normal disbanding.. HP values on bots will no longer roll over when checking STAMINA --- zone/bot.cpp | 26 +++++++++++++++++--------- zone/client_packet.cpp | 5 +++++ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 10e00e3f2..a3e07707c 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -751,9 +751,13 @@ void Bot::GenerateBaseStats() { this->DR = DiseaseResist; this->PR = PoisonResist; this->CR = ColdResist; + this->PhR = 0; this->Corrup = CorruptionResist; SetBotSpellID(BotSpellID); this->size = BotSize; + + this->pAggroRange = 0; + this->pAssistRange = 0; } void Bot::GenerateAppearance() { @@ -1374,20 +1378,20 @@ int32 Bot::GenerateBaseHitPoints() // Calc Base Hit Points int new_base_hp = 0; uint32 lm = GetClassLevelFactor(); - uint32 Post255; - uint32 NormalSTA = GetSTA(); - + int32 Post255; + int32 NormalSTA = GetSTA(); + if(GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= ClientVersion::SoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) { float SoDPost255; - + if(((NormalSTA - 255) / 2) > 0) SoDPost255 = ((NormalSTA - 255) / 2); else SoDPost255 = 0; - + int hp_factor = GetClassHPFactor(); - + if(level < 41) { new_base_hp = (5 + (GetLevel() * hp_factor / 12) + ((NormalSTA - SoDPost255) * GetLevel() * hp_factor / 3600)); @@ -1415,7 +1419,7 @@ int32 Bot::GenerateBaseHitPoints() new_base_hp = (5)+(GetLevel()*lm/10) + (((NormalSTA-Post255)*GetLevel()*lm/3000)) + ((Post255*1)*lm/6000); } this->base_hp = new_base_hp; - + return new_base_hp; } @@ -5913,7 +5917,12 @@ bool Bot::Death(Mob *killerMob, int32 damage, uint16 spell_id, SkillUseTypes att g->members[j] = nullptr; } } - + + //Make sure group still exists if it doesnt they were already updated in RemoveBotFromGroup + g = GetGroup(); + if (!g) + break; + // update the client group EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct)); GroupJoin_Struct* gu = (GroupJoin_Struct*)outapp->pBuffer; @@ -10196,7 +10205,6 @@ int32 Bot::CalcMaxHP() { bot_hp += GenerateBaseHitPoints() + itembonuses.HP; nd += aabonuses.MaxHP; //Natural Durability, Physical Enhancement, Planar Durability - bot_hp = (float)bot_hp * (float)nd / (float)10000; //this is to fix the HP-above-495k issue bot_hp += spellbonuses.HP + aabonuses.HP; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 9f527476c..01046d7fb 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -6362,7 +6362,12 @@ void Client::Handle_OP_GroupDisband(const EQApplicationPacket *app) } } } + + group = GetGroup(); + if (!group) //We must recheck this here.. incase the final bot disbanded the party..otherwise we crash + return; #endif + if (group->GroupCount() < 3) { group->DisbandGroup(); From 95243fd6ce67405ac992291d7ec1501b3788425a Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Thu, 28 May 2015 11:45:07 -0700 Subject: [PATCH 075/129] Modified ZippZipp's bot name fix from the forums. Limited bot name length to fix a crash. Added Filter check too if you use the Name Filter. --- zone/bot.cpp | 42 +++++++++++++++++------------------------- zone/bot.h | 2 +- zone/questmgr.cpp | 10 +++++----- 3 files changed, 23 insertions(+), 31 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index a3e07707c..650d6e191 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2327,30 +2327,22 @@ bool Bot::IsValidName() { return Result; } -bool Bot::IsBotNameAvailable(std::string* errorMessage) { +bool Bot::IsBotNameAvailable(char *botName, std::string* errorMessage) { + if (botName == "" || strlen(botName) > 15 || !database.CheckNameFilter(botName) || !database.CheckUsedName(botName)) { + return false; //Check if Botname is Empty / Check if Botname larger than 15 char / Valid to Player standards / Not used by a player! + } - if(!this->GetCleanName()) - return false; - - std::string query = StringFormat("SELECT COUNT(id) FROM vwBotCharacterMobs " - "WHERE name LIKE '%s'", this->GetCleanName()); - auto results = database.QueryDatabase(query); + std::string query = StringFormat("SELECT id FROM vwBotCharacterMobs WHERE name LIKE '%s'", botName); + auto results = database.QueryDatabase(query); if(!results.Success()) { - *errorMessage = std::string(results.ErrorMessage()); + *errorMessage = std::string(results.ErrorMessage()); return false; - } - - uint32 existingNameCount = 0; - - for (auto row = results.begin(); row != results.end(); ++row) { - existingNameCount = atoi(row[0]); - break; - } - - if(existingNameCount != 0) + } + if (results.RowCount()) { //Name already in use! return false; - - return true; + } + + return true; //We made it with a valid name! } bool Bot::Save() { @@ -11330,6 +11322,11 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(!strcasecmp(sep->arg[5], "female")) gender = 1; + if(!IsBotNameAvailable(sep->arg[2],&TempErrorMessage)) { + c->Message(0, "The name %s is already being used or is invalid. Please choose a different name.", sep->arg[2]); + return; + } + NPCType DefaultNPCTypeStruct = CreateDefaultNPCTypeStructForBot(std::string(sep->arg[2]), std::string(), c->GetLevel(), atoi(sep->arg[4]), atoi(sep->arg[3]), gender); Bot* NewBot = new Bot(DefaultNPCTypeStruct, c); @@ -11344,11 +11341,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { return; } - if(!NewBot->IsBotNameAvailable(&TempErrorMessage)) { - c->Message(0, "The name %s is already being used. Please choose a different name.", NewBot->GetCleanName()); - return; - } - if(!TempErrorMessage.empty()) { c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); return; diff --git a/zone/bot.h b/zone/bot.h index 871d96df4..33644c8ea 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -154,7 +154,7 @@ public: // Class Methods bool IsValidRaceClassCombo(); bool IsValidName(); - bool IsBotNameAvailable(std::string* errorMessage); + static bool IsBotNameAvailable(char *botName, std::string* errorMessage); bool DeleteBot(std::string* errorMessage); void Spawn(Client* botCharacterOwner, std::string* errorMessage); virtual void SetLevel(uint8 in_level, bool command = false); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 6b5c8978d..825e3b7b1 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -2071,6 +2071,11 @@ bool QuestManager::createBot(const char *name, const char *lastname, uint8 level return false; } + if(Bot::IsBotNameAvailable((char*)name,&TempErrorMessage)) { + initiator->Message(0, "The name %s is already being used or is invalid. Please choose a different name.", (char*)name); + return false; + } + NPCType DefaultNPCTypeStruct = Bot::CreateDefaultNPCTypeStructForBot(name, lastname, level, race, botclass, gender); Bot* NewBot = new Bot(DefaultNPCTypeStruct, initiator); @@ -2086,11 +2091,6 @@ bool QuestManager::createBot(const char *name, const char *lastname, uint8 level return false; } - if(!NewBot->IsBotNameAvailable(&TempErrorMessage)) { - initiator->Message(0, "The name %s is already being used. Please choose a different name.", NewBot->GetCleanName()); - return false; - } - if(!TempErrorMessage.empty()) { initiator->Message(13, "Database Error: %s", TempErrorMessage.c_str()); return false; From 235d6b6c4822d7cb61983259cfd31c269463b278 Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Thu, 28 May 2015 13:56:39 -0700 Subject: [PATCH 076/129] Bots will not benefit from heroic stats, focus effects like a client --- zone/bot.cpp | 550 ++++++++++++++++++++++++++++++++++----------------- zone/bot.h | 4 +- 2 files changed, 376 insertions(+), 178 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 650d6e191..a8e645ff9 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -5836,10 +5836,10 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli } } if(inst) { + client->DeleteItemInInventory(i, 0, UpdateClient); if(!botCanWear[i]) { client->PushItemOnCursor(*inst, true); } - client->DeleteItemInInventory(i, 0, UpdateClient); } } @@ -9564,8 +9564,9 @@ bool Bot::DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, uint16 s } void Bot::CalcBonuses() { + memset(&itembonuses, 0, sizeof(StatBonuses)); GenerateBaseStats(); - CalcItemBonuses(); + CalcItemBonuses(&itembonuses); CalcSpellBonuses(&spellbonuses); GenerateAABonuses(&aabonuses); SetAttackTimer(); @@ -10814,191 +10815,386 @@ void Bot::ProcessBotInspectionRequest(Bot* inspectedBot, Client* client) { } } -void Bot::CalcItemBonuses() +void Bot::CalcItemBonuses(StatBonuses* newbon) { - memset(&itembonuses, 0, sizeof(StatBonuses)); const Item_Struct* itemtmp = 0; for (int i = EmuConstants::EQUIPMENT_BEGIN; i <= EmuConstants::EQUIPMENT_END; ++i) { const ItemInst* item = GetBotItem(i); if(item) { - for(int j = AUG_BEGIN; j < EmuConstants::ITEM_COMMON_SIZE; ++j) { - const ItemInst* aug = item->GetAugment(j); - if(aug) { - itemtmp = aug->GetItem(); - if(itemtmp->AC != 0) - itembonuses.AC += itemtmp->AC; - if(itemtmp->HP != 0) - itembonuses.HP += itemtmp->HP; - if(itemtmp->Mana != 0) - itembonuses.Mana += itemtmp->Mana; - if(itemtmp->Endur != 0) - itembonuses.Endurance += itemtmp->Endur; - if(itemtmp->AStr != 0) - itembonuses.STR += itemtmp->AStr; - if(itemtmp->ASta != 0) - itembonuses.STA += itemtmp->ASta; - if(itemtmp->ADex != 0) - itembonuses.DEX += itemtmp->ADex; - if(itemtmp->AAgi != 0) - itembonuses.AGI += itemtmp->AAgi; - if(itemtmp->AInt != 0) - itembonuses.INT += itemtmp->AInt; - if(itemtmp->AWis != 0) - itembonuses.WIS += itemtmp->AWis; - if(itemtmp->ACha != 0) - itembonuses.CHA += itemtmp->ACha; - if(itemtmp->MR != 0) - itembonuses.MR += itemtmp->MR; - if(itemtmp->FR != 0) - itembonuses.FR += itemtmp->FR; - if(itemtmp->CR != 0) - itembonuses.CR += itemtmp->CR; - if(itemtmp->PR != 0) - itembonuses.PR += itemtmp->PR; - if(itemtmp->DR != 0) - itembonuses.DR += itemtmp->DR; - if(itemtmp->SVCorruption != 0) - itembonuses.Corrup += itemtmp->SVCorruption; - if(itemtmp->Regen != 0) - itembonuses.HPRegen += itemtmp->Regen; - if(itemtmp->ManaRegen != 0) - itembonuses.ManaRegen += itemtmp->ManaRegen; - if(itemtmp->Attack != 0) - itembonuses.ATK += itemtmp->Attack; - if(itemtmp->DamageShield != 0) - itembonuses.DamageShield += itemtmp->DamageShield; - if(itemtmp->SpellShield != 0) - itembonuses.SpellDamageShield += itemtmp->SpellShield; - if(itemtmp->Shielding != 0) - itembonuses.MeleeMitigation += itemtmp->Shielding; - if(itemtmp->StunResist != 0) - itembonuses.StunResist += itemtmp->StunResist; - if(itemtmp->StrikeThrough != 0) - itembonuses.StrikeThrough += itemtmp->StrikeThrough; - if(itemtmp->Avoidance != 0) - itembonuses.AvoidMeleeChance += itemtmp->Avoidance; - if(itemtmp->Accuracy != 0) - itembonuses.HitChance += itemtmp->Accuracy; - if(itemtmp->CombatEffects != 0) - itembonuses.ProcChance += itemtmp->CombatEffects; - if(itemtmp->Haste != 0) - if(itembonuses.haste < itemtmp->Haste) - itembonuses.haste = itemtmp->Haste; - if(GetClass() == BARD && itemtmp->BardValue != 0) { - if(itemtmp->BardType == ItemTypeBrassInstrument) - itembonuses.brassMod += itemtmp->BardValue; - else if(itemtmp->BardType == ItemTypePercussionInstrument) - itembonuses.percussionMod += itemtmp->BardValue; - else if(itemtmp->BardType == ItemTypeSinging) - itembonuses.singingMod += itemtmp->BardValue; - else if(itemtmp->BardType == ItemTypeStringedInstrument) - itembonuses.stringedMod += itemtmp->BardValue; - else if(itemtmp->BardType == ItemTypeWindInstrument) - itembonuses.windMod += itemtmp->BardValue; - else if(itemtmp->BardType == ItemTypeAllInstrumentTypes) { - itembonuses.brassMod += itemtmp->BardValue; - itembonuses.percussionMod += itemtmp->BardValue; - itembonuses.singingMod += itemtmp->BardValue; - itembonuses.stringedMod += itemtmp->BardValue; - itembonuses.windMod += itemtmp->BardValue; - } - } - if ((itemtmp->Worn.Effect != 0) && (itemtmp->Worn.Type == ET_WornEffect)) { // latent effects - ApplySpellsBonuses(itemtmp->Worn.Effect, itemtmp->Worn.Level, &itembonuses,0,itemtmp->Worn.Type); - } - } - } - itemtmp = item->GetItem(); - if(itemtmp->AC != 0) - itembonuses.AC += itemtmp->AC; - if(itemtmp->HP != 0) - itembonuses.HP += itemtmp->HP; - if(itemtmp->Mana != 0) - itembonuses.Mana += itemtmp->Mana; - if(itemtmp->Endur != 0) - itembonuses.Endurance += itemtmp->Endur; - if(itemtmp->AStr != 0) - itembonuses.STR += itemtmp->AStr; - if(itemtmp->ASta != 0) - itembonuses.STA += itemtmp->ASta; - if(itemtmp->ADex != 0) - itembonuses.DEX += itemtmp->ADex; - if(itemtmp->AAgi != 0) - itembonuses.AGI += itemtmp->AAgi; - if(itemtmp->AInt != 0) - itembonuses.INT += itemtmp->AInt; - if(itemtmp->AWis != 0) - itembonuses.WIS += itemtmp->AWis; - if(itemtmp->ACha != 0) - itembonuses.CHA += itemtmp->ACha; - if(itemtmp->MR != 0) - itembonuses.MR += itemtmp->MR; - if(itemtmp->FR != 0) - itembonuses.FR += itemtmp->FR; - if(itemtmp->CR != 0) - itembonuses.CR += itemtmp->CR; - if(itemtmp->PR != 0) - itembonuses.PR += itemtmp->PR; - if(itemtmp->DR != 0) - itembonuses.DR += itemtmp->DR; - if(itemtmp->SVCorruption != 0) - itembonuses.Corrup += itemtmp->SVCorruption; - if(itemtmp->Regen != 0) - itembonuses.HPRegen += itemtmp->Regen; - if(itemtmp->ManaRegen != 0) - itembonuses.ManaRegen += itemtmp->ManaRegen; - if(itemtmp->Attack != 0) - itembonuses.ATK += itemtmp->Attack; - if(itemtmp->DamageShield != 0) - itembonuses.DamageShield += itemtmp->DamageShield; - if(itemtmp->SpellShield != 0) - itembonuses.SpellDamageShield += itemtmp->SpellShield; - if(itemtmp->Shielding != 0) - itembonuses.MeleeMitigation += itemtmp->Shielding; - if(itemtmp->StunResist != 0) - itembonuses.StunResist += itemtmp->StunResist; - if(itemtmp->StrikeThrough != 0) - itembonuses.StrikeThrough += itemtmp->StrikeThrough; - if(itemtmp->Avoidance != 0) - itembonuses.AvoidMeleeChance += itemtmp->Avoidance; - if(itemtmp->Accuracy != 0) - itembonuses.HitChance += itemtmp->Accuracy; - if(itemtmp->CombatEffects != 0) - itembonuses.ProcChance += itemtmp->CombatEffects; - if(itemtmp->Haste != 0) - if(itembonuses.haste < itemtmp->Haste) - itembonuses.haste = itemtmp->Haste; - if(GetClass() == BARD && itemtmp->BardValue != 0) { - if(itemtmp->BardType == ItemTypeBrassInstrument) - itembonuses.brassMod += itemtmp->BardValue; - else if(itemtmp->BardType == ItemTypePercussionInstrument) - itembonuses.percussionMod += itemtmp->BardValue; - else if(itemtmp->BardType == ItemTypeSinging) - itembonuses.singingMod += itemtmp->BardValue; - else if(itemtmp->BardType == ItemTypeStringedInstrument) - itembonuses.stringedMod += itemtmp->BardValue; - else if(itemtmp->BardType == ItemTypeWindInstrument) - itembonuses.windMod += itemtmp->BardValue; - else if(itemtmp->BardType == ItemTypeAllInstrumentTypes) { - itembonuses.brassMod += itemtmp->BardValue; - itembonuses.percussionMod += itemtmp->BardValue; - itembonuses.singingMod += itemtmp->BardValue; - itembonuses.stringedMod += itemtmp->BardValue; - itembonuses.windMod += itemtmp->BardValue; - } - } - if ((itemtmp->Worn.Effect != 0) && (itemtmp->Worn.Type == ET_WornEffect)) { // latent effects - ApplySpellsBonuses(itemtmp->Worn.Effect, itemtmp->Worn.Level, &itembonuses,0,itemtmp->Worn.Type); - } + AddItemBonuses(item, newbon); + } + } +} + +void Bot::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug, bool isTribute) { + if(!inst || !inst->IsType(ItemClassCommon)) + { + return; + } + + if(inst->GetAugmentType()==0 && isAug == true) + { + return; + } + + const Item_Struct *item = inst->GetItem(); + + if(!isTribute && !inst->IsEquipable(GetBaseRace(),GetClass())) + { + if(item->ItemType != ItemTypeFood && item->ItemType != ItemTypeDrink) + return; + } + + if(GetLevel() < item->ReqLevel) + { + return; + } + + if(GetLevel() >= item->RecLevel) + { + newbon->AC += item->AC; + newbon->HP += item->HP; + newbon->Mana += item->Mana; + newbon->Endurance += item->Endur; + newbon->ATK += item->Attack; + newbon->STR += (item->AStr + item->HeroicStr); + newbon->STA += (item->ASta + item->HeroicSta); + newbon->DEX += (item->ADex + item->HeroicDex); + newbon->AGI += (item->AAgi + item->HeroicAgi); + newbon->INT += (item->AInt + item->HeroicInt); + newbon->WIS += (item->AWis + item->HeroicWis); + newbon->CHA += (item->ACha + item->HeroicCha); + + newbon->MR += (item->MR + item->HeroicMR); + newbon->FR += (item->FR + item->HeroicFR); + newbon->CR += (item->CR + item->HeroicCR); + newbon->PR += (item->PR + item->HeroicPR); + newbon->DR += (item->DR + item->HeroicDR); + newbon->Corrup += (item->SVCorruption + item->HeroicSVCorrup); + + newbon->STRCapMod += item->HeroicStr; + newbon->STACapMod += item->HeroicSta; + newbon->DEXCapMod += item->HeroicDex; + newbon->AGICapMod += item->HeroicAgi; + newbon->INTCapMod += item->HeroicInt; + newbon->WISCapMod += item->HeroicWis; + newbon->CHACapMod += item->HeroicCha; + newbon->MRCapMod += item->HeroicMR; + newbon->CRCapMod += item->HeroicFR; + newbon->FRCapMod += item->HeroicCR; + newbon->PRCapMod += item->HeroicPR; + newbon->DRCapMod += item->HeroicDR; + newbon->CorrupCapMod += item->HeroicSVCorrup; + + newbon->HeroicSTR += item->HeroicStr; + newbon->HeroicSTA += item->HeroicSta; + newbon->HeroicDEX += item->HeroicDex; + newbon->HeroicAGI += item->HeroicAgi; + newbon->HeroicINT += item->HeroicInt; + newbon->HeroicWIS += item->HeroicWis; + newbon->HeroicCHA += item->HeroicCha; + newbon->HeroicMR += item->HeroicMR; + newbon->HeroicFR += item->HeroicFR; + newbon->HeroicCR += item->HeroicCR; + newbon->HeroicPR += item->HeroicPR; + newbon->HeroicDR += item->HeroicDR; + newbon->HeroicCorrup += item->HeroicSVCorrup; + + } + else + { + int lvl = GetLevel(); + int reclvl = item->RecLevel; + + newbon->AC += CalcRecommendedLevelBonus( lvl, reclvl, item->AC ); + newbon->HP += CalcRecommendedLevelBonus( lvl, reclvl, item->HP ); + newbon->Mana += CalcRecommendedLevelBonus( lvl, reclvl, item->Mana ); + newbon->Endurance += CalcRecommendedLevelBonus( lvl, reclvl, item->Endur ); + newbon->ATK += CalcRecommendedLevelBonus( lvl, reclvl, item->Attack ); + newbon->STR += CalcRecommendedLevelBonus( lvl, reclvl, (item->AStr + item->HeroicStr) ); + newbon->STA += CalcRecommendedLevelBonus( lvl, reclvl, (item->ASta + item->HeroicSta) ); + newbon->DEX += CalcRecommendedLevelBonus( lvl, reclvl, (item->ADex + item->HeroicDex) ); + newbon->AGI += CalcRecommendedLevelBonus( lvl, reclvl, (item->AAgi + item->HeroicAgi) ); + newbon->INT += CalcRecommendedLevelBonus( lvl, reclvl, (item->AInt + item->HeroicInt) ); + newbon->WIS += CalcRecommendedLevelBonus( lvl, reclvl, (item->AWis + item->HeroicWis) ); + newbon->CHA += CalcRecommendedLevelBonus( lvl, reclvl, (item->ACha + item->HeroicCha) ); + + newbon->MR += CalcRecommendedLevelBonus( lvl, reclvl, (item->MR + item->HeroicMR) ); + newbon->FR += CalcRecommendedLevelBonus( lvl, reclvl, (item->FR + item->HeroicFR) ); + newbon->CR += CalcRecommendedLevelBonus( lvl, reclvl, (item->CR + item->HeroicCR) ); + newbon->PR += CalcRecommendedLevelBonus( lvl, reclvl, (item->PR + item->HeroicPR) ); + newbon->DR += CalcRecommendedLevelBonus( lvl, reclvl, (item->DR + item->HeroicDR) ); + newbon->Corrup += CalcRecommendedLevelBonus( lvl, reclvl, (item->SVCorruption + item->HeroicSVCorrup) ); + + newbon->STRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicStr ); + newbon->STACapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicSta ); + newbon->DEXCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicDex ); + newbon->AGICapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicAgi ); + newbon->INTCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicInt ); + newbon->WISCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicWis ); + newbon->CHACapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicCha ); + newbon->MRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicMR ); + newbon->CRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicFR ); + newbon->FRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicCR ); + newbon->PRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicPR ); + newbon->DRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicDR ); + newbon->CorrupCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicSVCorrup ); + + newbon->HeroicSTR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicStr ); + newbon->HeroicSTA += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicSta ); + newbon->HeroicDEX += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicDex ); + newbon->HeroicAGI += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicAgi ); + newbon->HeroicINT += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicInt ); + newbon->HeroicWIS += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicWis ); + newbon->HeroicCHA += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicCha ); + newbon->HeroicMR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicMR ); + newbon->HeroicFR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicFR ); + newbon->HeroicCR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicCR ); + newbon->HeroicPR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicPR ); + newbon->HeroicDR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicDR ); + newbon->HeroicCorrup += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicSVCorrup ); + } + + //FatherNitwit: New style haste, shields, and regens + if(newbon->haste < (int32)item->Haste) { + newbon->haste = item->Haste; + } + if(item->Regen > 0) + newbon->HPRegen += item->Regen; + + if(item->ManaRegen > 0) + newbon->ManaRegen += item->ManaRegen; + + if(item->EnduranceRegen > 0) + newbon->EnduranceRegen += item->EnduranceRegen; + + if(item->DamageShield > 0) { + if((newbon->DamageShield + item->DamageShield) > RuleI(Character, ItemDamageShieldCap)) + newbon->DamageShield = RuleI(Character, ItemDamageShieldCap); + else + newbon->DamageShield += item->DamageShield; + } + if(item->SpellShield > 0) { + if((newbon->SpellShield + item->SpellShield) > RuleI(Character, ItemSpellShieldingCap)) + newbon->SpellShield = RuleI(Character, ItemSpellShieldingCap); + else + newbon->SpellShield += item->SpellShield; + } + if(item->Shielding > 0) { + if((newbon->MeleeMitigation + item->Shielding) > RuleI(Character, ItemShieldingCap)) + newbon->MeleeMitigation = RuleI(Character, ItemShieldingCap); + else + newbon->MeleeMitigation += item->Shielding; + } + if(item->StunResist > 0) { + if((newbon->StunResist + item->StunResist) > RuleI(Character, ItemStunResistCap)) + newbon->StunResist = RuleI(Character, ItemStunResistCap); + else + newbon->StunResist += item->StunResist; + } + if(item->StrikeThrough > 0) { + if((newbon->StrikeThrough + item->StrikeThrough) > RuleI(Character, ItemStrikethroughCap)) + newbon->StrikeThrough = RuleI(Character, ItemStrikethroughCap); + else + newbon->StrikeThrough += item->StrikeThrough; + } + if(item->Avoidance > 0) { + if((newbon->AvoidMeleeChance + item->Avoidance) > RuleI(Character, ItemAvoidanceCap)) + newbon->AvoidMeleeChance = RuleI(Character, ItemAvoidanceCap); + else + newbon->AvoidMeleeChance += item->Avoidance; + } + if(item->Accuracy > 0) { + if((newbon->HitChance + item->Accuracy) > RuleI(Character, ItemAccuracyCap)) + newbon->HitChance = RuleI(Character, ItemAccuracyCap); + else + newbon->HitChance += item->Accuracy; + } + if(item->CombatEffects > 0) { + if((newbon->ProcChance + item->CombatEffects) > RuleI(Character, ItemCombatEffectsCap)) + newbon->ProcChance = RuleI(Character, ItemCombatEffectsCap); + else + newbon->ProcChance += item->CombatEffects; + } + if(item->DotShielding > 0) { + if((newbon->DoTShielding + item->DotShielding) > RuleI(Character, ItemDoTShieldingCap)) + newbon->DoTShielding = RuleI(Character, ItemDoTShieldingCap); + else + newbon->DoTShielding += item->DotShielding; + } + + if(item->HealAmt > 0) { + if((newbon->HealAmt + item->HealAmt) > RuleI(Character, ItemHealAmtCap)) + newbon->HealAmt = RuleI(Character, ItemHealAmtCap); + else + newbon->HealAmt += item->HealAmt; + } + if(item->SpellDmg > 0) { + if((newbon->SpellDmg + item->SpellDmg) > RuleI(Character, ItemSpellDmgCap)) + newbon->SpellDmg = RuleI(Character, ItemSpellDmgCap); + else + newbon->SpellDmg += item->SpellDmg; + } + if(item->Clairvoyance > 0) { + if((newbon->Clairvoyance + item->Clairvoyance) > RuleI(Character, ItemClairvoyanceCap)) + newbon->Clairvoyance = RuleI(Character, ItemClairvoyanceCap); + else + newbon->Clairvoyance += item->Clairvoyance; + } + + if(item->DSMitigation > 0) { + if((newbon->DSMitigation + item->DSMitigation) > RuleI(Character, ItemDSMitigationCap)) + newbon->DSMitigation = RuleI(Character, ItemDSMitigationCap); + else + newbon->DSMitigation += item->DSMitigation; + } + if (item->Worn.Effect > 0 && item->Worn.Type == ET_WornEffect) {// latent effects + ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type); + } + + if (item->Focus.Effect>0 && (item->Focus.Type == ET_Focus)) { // focus effects + ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, newbon, 0); + } + + switch(item->BardType) + { + case 51: /* All (e.g. Singing Short Sword) */ + { + if(item->BardValue > newbon->singingMod) + newbon->singingMod = item->BardValue; + if(item->BardValue > newbon->brassMod) + newbon->brassMod = item->BardValue; + if(item->BardValue > newbon->stringedMod) + newbon->stringedMod = item->BardValue; + if(item->BardValue > newbon->percussionMod) + newbon->percussionMod = item->BardValue; + if(item->BardValue > newbon->windMod) + newbon->windMod = item->BardValue; + break; + } + case 50: /* Singing */ + { + if(item->BardValue > newbon->singingMod) + newbon->singingMod = item->BardValue; + break; + } + case 23: /* Wind */ + { + if(item->BardValue > newbon->windMod) + newbon->windMod = item->BardValue; + break; + } + case 24: /* stringed */ + { + if(item->BardValue > newbon->stringedMod) + newbon->stringedMod = item->BardValue; + break; + } + case 25: /* brass */ + { + if(item->BardValue > newbon->brassMod) + newbon->brassMod = item->BardValue; + break; + } + case 26: /* Percussion */ + { + if(item->BardValue > newbon->percussionMod) + newbon->percussionMod = item->BardValue; + break; } } - if(itembonuses.HPRegen > CalcHPRegenCap()) - itembonuses.HPRegen = CalcHPRegenCap(); + if (item->SkillModValue != 0 && item->SkillModType <= HIGHEST_SKILL){ + if ((item->SkillModValue > 0 && newbon->skillmod[item->SkillModType] < item->SkillModValue) || + (item->SkillModValue < 0 && newbon->skillmod[item->SkillModType] > item->SkillModValue)) + { + newbon->skillmod[item->SkillModType] = item->SkillModValue; + } + } - if(itembonuses.ManaRegen > CalcManaRegenCap()) - itembonuses.ManaRegen = CalcManaRegenCap(); + // Add Item Faction Mods + //if (item->FactionMod1) + //{ + // if (item->FactionAmt1 > 0 && item->FactionAmt1 > GetItemFactionBonus(item->FactionMod1)) + // { + // AddItemFactionBonus(item->FactionMod1, item->FactionAmt1); + // } + // else if (item->FactionAmt1 < 0 && item->FactionAmt1 < GetItemFactionBonus(item->FactionMod1)) + // { + // AddItemFactionBonus(item->FactionMod1, item->FactionAmt1); + // } + //} + //if (item->FactionMod2) + //{ + // if (item->FactionAmt2 > 0 && item->FactionAmt2 > GetItemFactionBonus(item->FactionMod2)) + // { + // AddItemFactionBonus(item->FactionMod2, item->FactionAmt2); + // } + // else if (item->FactionAmt2 < 0 && item->FactionAmt2 < GetItemFactionBonus(item->FactionMod2)) + // { + // AddItemFactionBonus(item->FactionMod2, item->FactionAmt2); + // } + //} + //if (item->FactionMod3) + //{ + // if (item->FactionAmt3 > 0 && item->FactionAmt3 > GetItemFactionBonus(item->FactionMod3)) + // { + // AddItemFactionBonus(item->FactionMod3, item->FactionAmt3); + // } + // else if (item->FactionAmt3 < 0 && item->FactionAmt3 < GetItemFactionBonus(item->FactionMod3)) + // { + // AddItemFactionBonus(item->FactionMod3, item->FactionAmt3); + // } + //} + //if (item->FactionMod4) + //{ + // if (item->FactionAmt4 > 0 && item->FactionAmt4 > GetItemFactionBonus(item->FactionMod4)) + // { + // AddItemFactionBonus(item->FactionMod4, item->FactionAmt4); + // } + // else if (item->FactionAmt4 < 0 && item->FactionAmt4 < GetItemFactionBonus(item->FactionMod4)) + // { + // AddItemFactionBonus(item->FactionMod4, item->FactionAmt4); + // } + //} + + if (item->ExtraDmgSkill != 0 && item->ExtraDmgSkill <= HIGHEST_SKILL) { + if((newbon->SkillDamageAmount[item->ExtraDmgSkill] + item->ExtraDmgAmt) > RuleI(Character, ItemExtraDmgCap)) + newbon->SkillDamageAmount[item->ExtraDmgSkill] = RuleI(Character, ItemExtraDmgCap); + else + newbon->SkillDamageAmount[item->ExtraDmgSkill] += item->ExtraDmgAmt; + } + + if (!isAug) + { + int i; + for (i = 0; i < EmuConstants::ITEM_COMMON_SIZE; i++) { + AddItemBonuses(inst->GetAugment(i),newbon,true); + } + } + +} + +int Bot::CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat) +{ + if( (reclevel > 0) && (level < reclevel) ) + { + int32 statmod = (level * 10000 / reclevel) * basestat; + + if( statmod < 0 ) + { + statmod -= 5000; + return (statmod/10000); + } + else + { + statmod += 5000; + return (statmod/10000); + } + } + + return 0; } // This method is intended to call all necessary methods to do all bot stat calculations, including spell buffs, equipment, AA bonsues, etc. diff --git a/zone/bot.h b/zone/bot.h index 33644c8ea..4cf04aa3a 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -190,7 +190,9 @@ public: bool CanDoSpecialAttack(Mob *other); virtual int32 CheckAggroAmount(uint16 spellid); virtual void CalcBonuses(); - void CalcItemBonuses(); + void CalcItemBonuses(StatBonuses* newbon); + void AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug = false, bool isTribute = false); + int CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat); virtual void MakePet(uint16 spell_id, const char* pettype, const char *petname = nullptr); virtual FACTION_VALUE GetReverseFactionCon(Mob* iOther); inline virtual bool IsPet() { return false; } From d7b9d7c990274324dcece76698ac09532c47cf37 Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Thu, 28 May 2015 14:08:48 -0700 Subject: [PATCH 077/129] Forgot the Regen caps oops! --- zone/bot.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/zone/bot.cpp b/zone/bot.cpp index a8e645ff9..ebc5f47d3 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -10825,6 +10825,16 @@ void Bot::CalcItemBonuses(StatBonuses* newbon) AddItemBonuses(item, newbon); } } + + // Caps + if(newbon->HPRegen > CalcHPRegenCap()) + newbon->HPRegen = CalcHPRegenCap(); + + if(newbon->ManaRegen > CalcManaRegenCap()) + newbon->ManaRegen = CalcManaRegenCap(); + + if(newbon->EnduranceRegen > CalcEnduranceRegenCap()) + newbon->EnduranceRegen = CalcEnduranceRegenCap(); } void Bot::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug, bool isTribute) { From 5917052a6d5097363d4f714d8b63cc1e090d7c4c Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 28 May 2015 18:00:25 -0400 Subject: [PATCH 078/129] I guess short duration buffs needed the extra tick --- zone/spell_effects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index d0069683b..9d0fd7858 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3372,7 +3372,7 @@ void Mob::BuffProcess() { --buffs[buffs_i].ticsremaining; - if (buffs[buffs_i].ticsremaining == 0) { + if ((buffs[buffs_i].ticsremaining == 0 && !IsShortDurationBuff(buffs[buffs_i].spellid)) || buffs[buffs_i].ticsremaining < 0) { Log.Out(Logs::Detail, Logs::Spells, "Buff %d in slot %d has expired. Fading.", buffs[buffs_i].spellid, buffs_i); BuffFadeBySlot(buffs_i); } From be210950d7018e8a9b93d3d874315390fe6e3d99 Mon Sep 17 00:00:00 2001 From: regneq Date: Thu, 28 May 2015 15:05:45 -0700 Subject: [PATCH 079/129] readded previous commit smoother pathing. --- zone/client.h | 1 + zone/mob_ai.cpp | 144 +++++++++++++++++++++++---------------------- zone/npc.h | 1 + zone/waypoints.cpp | 11 +++- 4 files changed, 85 insertions(+), 72 deletions(-) diff --git a/zone/client.h b/zone/client.h index ac505ecdb..73bde1a69 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1130,6 +1130,7 @@ public: inline bool IsDraggingCorpse() { return (DraggedCorpses.size() > 0); } void DragCorpses(); inline void ClearDraggedCorpses() { DraggedCorpses.clear(); } + inline void ResetPositionTimer() { position_timer_counter = 0; } void SendAltCurrencies(); void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount); void AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method = 0); diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index b3aaad1dc..650ba350c 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1653,76 +1653,17 @@ void NPC::AI_DoMovement() { if (gridno > 0 || cur_wp==-2) { if (movetimercompleted==true) { // time to pause at wp is over - - int32 spawn_id = this->GetSpawnPointID(); - LinkedListIterator iterator(zone->spawn2_list); - iterator.Reset(); - Spawn2 *found_spawn = nullptr; - - while(iterator.MoreElements()) - { - Spawn2* cur = iterator.GetData(); - iterator.Advance(); - if(cur->GetID() == spawn_id) - { - found_spawn = cur; - break; - } - } - - if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) { - CastToNPC()->Depop(true); //depop and resart spawn timer - if(found_spawn) - found_spawn->SetNPCPointerNull(); - } - else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) { - CastToNPC()->Depop(false);//depop without spawn timer - if(found_spawn) - found_spawn->SetNPCPointerNull(); - } - else { - movetimercompleted=false; - - Log.Out(Logs::Detail, Logs::Pathing, "We are departing waypoint %d.", cur_wp); - - //if we were under quest control (with no grid), we are done now.. - if(cur_wp == -2) { - Log.Out(Logs::Detail, Logs::Pathing, "Non-grid quest mob has reached its quest ordered waypoint. Leaving pathing mode."); - roamer = false; - cur_wp = 0; - } - - if(GetAppearance() != eaStanding) - SetAppearance(eaStanding, false); - - entity_list.OpenDoorsNear(CastToNPC()); - - if(!DistractedFromGrid) { - //kick off event_waypoint depart - char temp[16]; - sprintf(temp, "%d", cur_wp); - parse->EventNPC(EVENT_WAYPOINT_DEPART, CastToNPC(), nullptr, temp, 0); - - //setup our next waypoint, if we are still on our normal grid - //remember that the quest event above could have done anything it wanted with our grid - if(gridno > 0) { - CastToNPC()->CalculateNewWaypoint(); - } - } - else { - DistractedFromGrid = false; - } - } + AI_SetupNextWaypoint(); } // endif (movetimercompleted==true) else if (!(AIwalking_timer->Enabled())) { // currently moving + bool doMove = true; if (m_CurrentWayPoint.x == GetX() && m_CurrentWayPoint.y == GetY()) { // are we there yet? then stop Log.Out(Logs::Detail, Logs::AI, "We have reached waypoint %d (%.3f,%.3f,%.3f) on grid %d", cur_wp, GetX(), GetY(), GetZ(), GetGrid()); if (cur_wp_pause != 0) { SetWaypointPause(); - if(GetAppearance() != eaStanding) - SetAppearance(eaStanding, false); + SetAppearance(eaStanding, false); SetMoving(false); if (m_CurrentWayPoint.w >= 0.0) { SetHeading(m_CurrentWayPoint.w); @@ -1734,13 +1675,17 @@ void NPC::AI_DoMovement() { char temp[16]; sprintf(temp, "%d", cur_wp); parse->EventNPC(EVENT_WAYPOINT_ARRIVE, CastToNPC(), nullptr, temp, 0); - + // start moving directly to next waypoint if we're at a 0 pause waypoint and we didn't get quest halted. + if (!AIwalking_timer->Enabled()) + AI_SetupNextWaypoint(); + else + doMove = false; // wipe feign memory since we reached our first waypoint if(cur_wp == 1) ClearFeignMemory(); } - else - { // not at waypoint yet, so keep moving + if (doMove) + { // not at waypoint yet or at 0 pause WP, so keep moving if(!RuleB(Pathing, AggroReturnToGrid) || !zone->pathing || (DistractedFromGrid == 0)) CalculateNewPosition2(m_CurrentWayPoint.x, m_CurrentWayPoint.y, m_CurrentWayPoint.z, walksp, true); else @@ -1768,8 +1713,7 @@ void NPC::AI_DoMovement() { SetGrid( 0 - GetGrid()); // revert to AI control Log.Out(Logs::Detail, Logs::Pathing, "Quest pathing is finished. Resuming on grid %d", GetGrid()); - if(GetAppearance() != eaStanding) - SetAppearance(eaStanding, false); + SetAppearance(eaStanding, false); CalculateNewWaypoint(); } @@ -1818,15 +1762,73 @@ void NPC::AI_DoMovement() { } } +void NPC::AI_SetupNextWaypoint() { + int32 spawn_id = this->GetSpawnPointID(); + LinkedListIterator iterator(zone->spawn2_list); + iterator.Reset(); + Spawn2 *found_spawn = nullptr; + + while (iterator.MoreElements()) + { + Spawn2* cur = iterator.GetData(); + iterator.Advance(); + if (cur->GetID() == spawn_id) + { + found_spawn = cur; + break; + } + } + + if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) { + CastToNPC()->Depop(true); //depop and restart spawn timer + if (found_spawn) + found_spawn->SetNPCPointerNull(); + } + else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) { + CastToNPC()->Depop(false);//depop without spawn timer + if (found_spawn) + found_spawn->SetNPCPointerNull(); + } + else { + movetimercompleted = false; + + Log.Out(Logs::Detail, Logs::Pathing, "We are departing waypoint %d.", cur_wp); + + //if we were under quest control (with no grid), we are done now.. + if (cur_wp == -2) { + Log.Out(Logs::Detail, Logs::Pathing, "Non-grid quest mob has reached its quest ordered waypoint. Leaving pathing mode."); + roamer = false; + cur_wp = 0; + } + + SetAppearance(eaStanding, false); + + entity_list.OpenDoorsNear(CastToNPC()); + + if (!DistractedFromGrid) { + //kick off event_waypoint depart + char temp[16]; + sprintf(temp, "%d", cur_wp); + parse->EventNPC(EVENT_WAYPOINT_DEPART, CastToNPC(), nullptr, temp, 0); + + //setup our next waypoint, if we are still on our normal grid + //remember that the quest event above could have done anything it wanted with our grid + if (GetGrid() > 0) { + CastToNPC()->CalculateNewWaypoint(); + } + } + else { + DistractedFromGrid = false; + } + } +} + // Note: Mob that caused this may not get added to the hate list until after this function call completes void Mob::AI_Event_Engaged(Mob* attacker, bool iYellForHelp) { if (!IsAIControlled()) return; - if(GetAppearance() != eaStanding) - { - SetAppearance(eaStanding); - } + SetAppearance(eaStanding); if (iYellForHelp) { if(IsPet()) { diff --git a/zone/npc.h b/zone/npc.h index 95a857460..f960c736e 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -119,6 +119,7 @@ public: virtual void AI_Start(uint32 iMoveDelay = 0); virtual void AI_Stop(); void AI_DoMovement(); + void AI_SetupNextWaypoint(); bool AI_AddNPCSpells(uint32 iDBSpellsID); bool AI_AddNPCSpellsEffects(uint32 iDBSpellsEffectsID); virtual bool AI_EngagedCastCheck(); diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index c4f37864d..7570165a3 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -394,6 +394,7 @@ void NPC::SetWaypointPause() if (cur_wp_pause == 0) { AIwalking_timer->Start(100); + AIwalking_timer->Trigger(); } else { @@ -519,6 +520,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo return true; } + bool send_update = false; int compare_steps = IsBoat() ? 1 : 20; if(tar_ndx < compare_steps && m_TargetLocation.x==x && m_TargetLocation.y==y) { @@ -709,11 +711,18 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo m_Delta = glm::vec4(m_Position.x - nx, m_Position.y - ny, m_Position.z - nz, 0.0f); if (IsClient()) + { SendPosUpdate(1); + CastToClient()->ResetPositionTimer(); + } else + { + // force an update now + move_tic_count = RuleI(Zone, NPCPositonUpdateTicCount); SendPosUpdate(); + SetAppearance(eaStanding, false); + } - SetAppearance(eaStanding, false); pLastChange = Timer::GetCurrentTime(); return true; } From 36de3879f8970d31f1e3be474f5ffa75d7aa3369 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 29 May 2015 00:40:34 -0400 Subject: [PATCH 080/129] There is a variety to focus messages I'm not 100% sure these are classic, but Tit+ at least. I was able to verify these messages -- crap ton more though. --- zone/spell_effects.cpp | 20 ++++++++++++++++++-- zone/string_ids.h | 3 +++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 9d0fd7858..26ebe83c6 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -5394,8 +5394,24 @@ int16 Client::GetFocusEffect(focusType type, uint16 spell_id) { if(UsedItem && rand_effectiveness && focus_max_real != 0) realTotal = CalcFocusEffect(type, UsedFocusID, spell_id); - if (realTotal != 0 && UsedItem) - Message_StringID(MT_Spells, BEGINS_TO_GLOW, UsedItem->Name); + if (realTotal != 0 && UsedItem) { + // there are a crap ton more of these, I was able to verify these ones though + uint32 string_id = BEGINS_TO_GLOW; + switch (type) { + case focusSpellHaste: + string_id = SHIMMERS_BRIEFLY; + break; + case focusManaCost: + string_id = FLICKERS_PALE_LIGHT; + break; + case focusSpellDuration: + string_id = SPARKLES; + break; + default: + break; + } + Message_StringID(MT_Spells, string_id, UsedItem->Name); + } } //Check if spell focus effect exists for the client. diff --git a/zone/string_ids.h b/zone/string_ids.h index 2db4e34e8..139dc1005 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -233,6 +233,8 @@ #define CORPSE_DECAY_NOW 1227 //This corpse is waiting to expire. #define CORPSE_ITEM_LOST 1228 //Your items will no longer stay with you when you respawn on death. You will now need to return to your corpse for your items. #define CORPSE_EXP_LOST 1229 //You will now lose experience when you die. +#define FLICKERS_PALE_LIGHT 1230 //Your %1 flickers with a pale light. +#define SPARKLES 1236 //Your %1 sparkles. #define SURNAME_REJECTED 1374 //Your new surname was rejected. Please try a different name. #define DUEL_DECLINE 1383 //%1 has declined your challenge to duel to the death. #define DUEL_ACCEPTED 1384 //%1 has already accepted a duel with someone else. @@ -375,6 +377,7 @@ #define GROUP_INVITEE_SELF 12270 //12270 You cannot invite yourself. #define NOT_IN_CONTROL 12368 //You do not have control of yourself right now. #define ALREADY_CASTING 12442 //You are already casting a spell! +#define SHIMMERS_BRIEFLY 12444 //Your %1 shimmers briefly. #define SENSE_CORPSE_NOT_NAME 12446 //You don't sense any corpses of that name. #define SENSE_CORPSE_NONE 12447 //You don't sense any corpses. #define SCREECH_BUFF_BLOCK 12448 //Your immunity buff protected you from the spell %1! From 070183789bdb5124ea13da1b280f3eab6bffd8ce Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 29 May 2015 01:58:38 -0400 Subject: [PATCH 081/129] More focus messages --- zone/spell_effects.cpp | 8 +++++++- zone/string_ids.h | 7 +++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 26ebe83c6..ce1d444fb 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -5396,7 +5396,7 @@ int16 Client::GetFocusEffect(focusType type, uint16 spell_id) { if (realTotal != 0 && UsedItem) { // there are a crap ton more of these, I was able to verify these ones though - uint32 string_id = BEGINS_TO_GLOW; + uint32 string_id = BEGINS_TO_GLOW; // this is really just clicky message ... switch (type) { case focusSpellHaste: string_id = SHIMMERS_BRIEFLY; @@ -5407,6 +5407,12 @@ int16 Client::GetFocusEffect(focusType type, uint16 spell_id) { case focusSpellDuration: string_id = SPARKLES; break; + case focusImprovedDamage: + string_id = ALIVE_WITH_POWER; + break; + case focusRange: + string_id = PULSES_WITH_LIGHT; + break; default: break; } diff --git a/zone/string_ids.h b/zone/string_ids.h index 139dc1005..fb52985c0 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -234,7 +234,14 @@ #define CORPSE_ITEM_LOST 1228 //Your items will no longer stay with you when you respawn on death. You will now need to return to your corpse for your items. #define CORPSE_EXP_LOST 1229 //You will now lose experience when you die. #define FLICKERS_PALE_LIGHT 1230 //Your %1 flickers with a pale light. +#define PULSES_WITH_LIGHT 1231 //Your %1 pulses with light as your vision sharpens. +#define FEEDS_WITH_POWER 1232 //Your %1 feeds you with power. +#define POWER_DRAIN_INTO 1233 //You feel your power drain into your %1. +#define SEEMS_DRAINED 1234 //Your %1 seems drained of power. +#define ALIVE_WITH_POWER 1235 //Your %1 feels alive with power. #define SPARKLES 1236 //Your %1 sparkles. +#define GROWS_DIM 1237 //Your %1 grows dim. +#define BEGINS_TO_SHINE 1238 //Your %1 begins to shine. #define SURNAME_REJECTED 1374 //Your new surname was rejected. Please try a different name. #define DUEL_DECLINE 1383 //%1 has declined your challenge to duel to the death. #define DUEL_ACCEPTED 1384 //%1 has already accepted a duel with someone else. From 8646791d1d7e248806710d3bf2719dbe31007788 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 29 May 2015 03:26:35 -0400 Subject: [PATCH 082/129] Proc buffs like the shissar rogue poisons have a level override This corrects the level in those cases. Probably should propagate the level overrides a bit more, but this fixes the main issues right now. --- zone/attack.cpp | 2 +- zone/client_packet.cpp | 2 +- zone/common.h | 1 + zone/mob.cpp | 24 ++++++++++++++---------- zone/mob.h | 10 +++++----- zone/spell_effects.cpp | 35 ++++++++++++++++++----------------- zone/spells.cpp | 18 +++++++++++------- 7 files changed, 51 insertions(+), 41 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index c5dfcc6d7..666cb19f8 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -4143,7 +4143,7 @@ void Mob::TrySpellProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on, outapp->priority = 3; entity_list.QueueCloseClients(this, outapp, false, 200, 0, true); safe_delete(outapp); - ExecWeaponProc(nullptr, SpellProcs[i].spellID, on); + ExecWeaponProc(nullptr, SpellProcs[i].spellID, on, SpellProcs[i].level_override); CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0, SpellProcs[i].base_spellID); } else { diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 01046d7fb..ccb27a188 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -704,7 +704,7 @@ void Client::CompleteConnect() case SE_AddMeleeProc: case SE_WeaponProc: { - AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); + AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid, buffs[j1].casterlevel); break; } case SE_DefensiveProc: diff --git a/zone/common.h b/zone/common.h index b2e4d5ed5..13a65f7c5 100644 --- a/zone/common.h +++ b/zone/common.h @@ -468,6 +468,7 @@ typedef struct uint16 spellID; uint16 chance; uint16 base_spellID; + int level_override; } tProc; struct Shielders_Struct { diff --git a/zone/mob.cpp b/zone/mob.cpp index f7348a869..7f6cb9fbf 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -263,15 +263,19 @@ Mob::Mob(const char* in_name, PermaProcs[j].spellID = SPELL_UNKNOWN; PermaProcs[j].chance = 0; PermaProcs[j].base_spellID = SPELL_UNKNOWN; + PermaProcs[j].level_override = -1; SpellProcs[j].spellID = SPELL_UNKNOWN; SpellProcs[j].chance = 0; SpellProcs[j].base_spellID = SPELL_UNKNOWN; + SpellProcs[j].level_override = -1; DefensiveProcs[j].spellID = SPELL_UNKNOWN; DefensiveProcs[j].chance = 0; DefensiveProcs[j].base_spellID = SPELL_UNKNOWN; + DefensiveProcs[j].level_override = -1; RangedProcs[j].spellID = SPELL_UNKNOWN; RangedProcs[j].chance = 0; RangedProcs[j].base_spellID = SPELL_UNKNOWN; + RangedProcs[j].level_override = -1; } for (i = 0; i < _MaterialCount; i++) @@ -3160,7 +3164,7 @@ int32 Mob::GetActSpellCasttime(uint16 spell_id, int32 casttime) { return casttime; } -void Mob::ExecWeaponProc(const ItemInst *inst, uint16 spell_id, Mob *on) { +void Mob::ExecWeaponProc(const ItemInst *inst, uint16 spell_id, Mob *on, int level_override) { // Changed proc targets to look up based on the spells goodEffect flag. // This should work for the majority of weapons. if(spell_id == SPELL_UNKNOWN || on->GetSpecialAbility(NO_HARM_FROM_CLIENT)) { @@ -3199,14 +3203,14 @@ void Mob::ExecWeaponProc(const ItemInst *inst, uint16 spell_id, Mob *on) { twinproc = true; if (IsBeneficialSpell(spell_id)) { - SpellFinished(spell_id, this, 10, 0, -1, spells[spell_id].ResistDiff, true); + SpellFinished(spell_id, this, 10, 0, -1, spells[spell_id].ResistDiff, true, level_override); if(twinproc) - SpellOnTarget(spell_id, this, false, false, 0, true); + SpellOnTarget(spell_id, this, false, false, 0, true, level_override); } else if(!(on->IsClient() && on->CastToClient()->dead)) { //dont proc on dead clients - SpellFinished(spell_id, on, 10, 0, -1, spells[spell_id].ResistDiff, true); + SpellFinished(spell_id, on, 10, 0, -1, spells[spell_id].ResistDiff, true, level_override); if(twinproc) - SpellOnTarget(spell_id, on, false, false, 0, true); + SpellOnTarget(spell_id, on, false, false, 0, true, level_override); } return; } @@ -5605,15 +5609,15 @@ void Mob::SendRemovePlayerState(PlayerState old_state) safe_delete(app); } -void Mob::SetCurrentSpeed(int in){ +void Mob::SetCurrentSpeed(int in){ if (current_speed != in) - { - current_speed = in; + { + current_speed = in; tar_ndx = 20; if (in == 0) { SetRunAnimSpeed(0); SetMoving(false); SendPosition(); } - } -} \ No newline at end of file + } +} diff --git a/zone/mob.h b/zone/mob.h index 896b9c71b..944b80d97 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -227,10 +227,10 @@ public: void CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot, uint16 mana_used, uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0); bool SpellFinished(uint16 spell_id, Mob *target, uint16 slot = 10, uint16 mana_used = 0, - uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false); + uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1); virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect = false, - bool use_resist_adjust = false, int16 resist_adjust = 0, bool isproc = false); - virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100); + bool use_resist_adjust = false, int16 resist_adjust = 0, bool isproc = false, int level_override = -1); + virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100, int level_override = -1); virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction); virtual bool CheckFizzle(uint16 spell_id); @@ -536,7 +536,7 @@ public: bool HasDefensiveProcs() const; bool HasSkillProcs() const; bool HasSkillProcSuccess() const; - bool AddProcToWeapon(uint16 spell_id, bool bPerma = false, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN); + bool AddProcToWeapon(uint16 spell_id, bool bPerma = false, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN, int level_override = -1); bool RemoveProcFromWeapon(uint16 spell_id, bool bAll = false); bool HasProcs() const; bool IsCombatProc(uint16 spell_id); @@ -1077,7 +1077,7 @@ protected: void TryWeaponProc(const ItemInst* inst, const Item_Struct* weapon, Mob *on, uint16 hand = MainPrimary); void TrySpellProc(const ItemInst* inst, const Item_Struct* weapon, Mob *on, uint16 hand = MainPrimary); void TryWeaponProc(const ItemInst* weapon, Mob *on, uint16 hand = MainPrimary); - void ExecWeaponProc(const ItemInst* weapon, uint16 spell_id, Mob *on); + void ExecWeaponProc(const ItemInst* weapon, uint16 spell_id, Mob *on, int level_override = -1); virtual float GetProcChances(float ProcBonus, uint16 hand = MainPrimary); virtual float GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 hand = MainPrimary, Mob *on = nullptr); virtual float GetSpecialProcChances(uint16 hand); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index ce1d444fb..28f5518fa 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -44,7 +44,7 @@ extern WorldServer worldserver; // the spell can still fail here, if the buff can't stack // in this case false will be returned, true otherwise -bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) +bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_override) { int caster_level, buffslot, effect, effect_value, i; ItemInst *SummonedItem=nullptr; @@ -59,26 +59,27 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) const SPDat_Spell_Struct &spell = spells[spell_id]; bool c_override = false; - if(caster && caster->IsClient() && GetCastedSpellInvSlot() > 0) - { - const ItemInst* inst = caster->CastToClient()->GetInv().GetItem(GetCastedSpellInvSlot()); - if(inst) - { - if(inst->GetItem()->Click.Level > 0) - { + if (caster && caster->IsClient() && GetCastedSpellInvSlot() > 0) { + const ItemInst *inst = caster->CastToClient()->GetInv().GetItem(GetCastedSpellInvSlot()); + if (inst) { + if (inst->GetItem()->Click.Level > 0) { caster_level = inst->GetItem()->Click.Level; c_override = true; - } - else - { + } else { caster_level = caster ? caster->GetCasterLevel(spell_id) : GetCasterLevel(spell_id); } - } - else + } else if (level_override > 0) { + caster_level = level_override; + c_override = true; + } else { caster_level = caster ? caster->GetCasterLevel(spell_id) : GetCasterLevel(spell_id); - } - else + } + } else if (level_override > 0) { + caster_level = level_override; + c_override = true; + } else { caster_level = caster ? caster->GetCasterLevel(spell_id) : GetCasterLevel(spell_id); + } if(c_override) { @@ -1786,9 +1787,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) #endif if(spells[spell_id].base2[i] == 0) - AddProcToWeapon(procid, false, 100, spell_id); + AddProcToWeapon(procid, false, 100, spell_id, level_override); else - AddProcToWeapon(procid, false, spells[spell_id].base2[i]+100, spell_id); + AddProcToWeapon(procid, false, spells[spell_id].base2[i]+100, spell_id, level_override); break; } diff --git a/zone/spells.cpp b/zone/spells.cpp index f90335e31..7286ba0aa 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1897,7 +1897,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce // we can't interrupt in this, or anything called from this! // if you need to abort the casting, return false bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 mana_used, - uint32 inventory_slot, int16 resist_adjust, bool isproc) + uint32 inventory_slot, int16 resist_adjust, bool isproc, int level_override) { //EQApplicationPacket *outapp = nullptr; Mob *ae_center = nullptr; @@ -2060,14 +2060,14 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 return(false); } if (isproc) { - SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, true); + SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, true, level_override); } else { if (spells[spell_id].targettype == ST_TargetOptional){ if (!TrySpellProjectile(spell_target, spell_id)) return false; } - else if(!SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, false)) { + else if(!SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, false, level_override)) { if(IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id)) { // Prevent mana usage/timers being set for beneficial buffs if(casting_spell_type == 1) @@ -3292,7 +3292,8 @@ int Mob::CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite) // and if you don't want effects just return false. interrupting here will // break stuff // -bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_resist_adjust, int16 resist_adjust, bool isproc) +bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_resist_adjust, int16 resist_adjust, + bool isproc, int level_override) { // well we can't cast a spell on target without a target @@ -3324,7 +3325,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r if(!IsValidSpell(spell_id)) return false; - uint16 caster_level = GetCasterLevel(spell_id); + uint16 caster_level = level_override > 0 ? level_override : GetCasterLevel(spell_id); Log.Out(Logs::Detail, Logs::Spells, "Casting spell %d on %s with effective caster level %d", spell_id, spelltar->GetName(), caster_level); @@ -3739,7 +3740,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r } // cause the effects to the target - if(!spelltar->SpellEffect(this, spell_id, spell_effectiveness)) + if(!spelltar->SpellEffect(this, spell_id, spell_effectiveness, level_override)) { // if SpellEffect returned false there's a problem applying the // spell. It's most likely a buff that can't stack. @@ -5098,7 +5099,7 @@ bool Mob::IsCombatProc(uint16 spell_id) { return false; } -bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 base_spell_id) { +bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 base_spell_id, int level_override) { if(spell_id == SPELL_UNKNOWN) return(false); @@ -5109,6 +5110,7 @@ bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 b PermaProcs[i].spellID = spell_id; PermaProcs[i].chance = iChance; PermaProcs[i].base_spellID = base_spell_id; + PermaProcs[i].level_override = level_override; Log.Out(Logs::Detail, Logs::Spells, "Added permanent proc spell %d with chance %d to slot %d", spell_id, iChance, i); return true; @@ -5121,6 +5123,7 @@ bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 b SpellProcs[i].spellID = spell_id; SpellProcs[i].chance = iChance; SpellProcs[i].base_spellID = base_spell_id;; + SpellProcs[i].level_override = level_override; Log.Out(Logs::Detail, Logs::Spells, "Added spell-granted proc spell %d with chance %d to slot %d", spell_id, iChance, i); return true; } @@ -5136,6 +5139,7 @@ bool Mob::RemoveProcFromWeapon(uint16 spell_id, bool bAll) { SpellProcs[i].spellID = SPELL_UNKNOWN; SpellProcs[i].chance = 0; SpellProcs[i].base_spellID = SPELL_UNKNOWN; + SpellProcs[i].level_override = -1; Log.Out(Logs::Detail, Logs::Spells, "Removed proc %d from slot %d", spell_id, i); } } From a41fd122bce10ab6c4af7abb42863f4d76a17ff0 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 29 May 2015 13:18:04 -0400 Subject: [PATCH 083/129] Add PetType petNone so IsCharmed stops lying --- zone/common.h | 3 ++- zone/mob.cpp | 8 +++++--- zone/spell_effects.cpp | 2 ++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/zone/common.h b/zone/common.h index 13a65f7c5..d9e3637a8 100644 --- a/zone/common.h +++ b/zone/common.h @@ -521,7 +521,8 @@ typedef enum { petOther, petCharmed, petNPCFollow, - petTargetLock //remain active as long something is on the hatelist. Don't listen to any commands + petTargetLock, //remain active as long something is on the hatelist. Don't listen to any commands + petNone = 0xFF // not a pet } PetType; typedef enum { diff --git a/zone/mob.cpp b/zone/mob.cpp index 7f6cb9fbf..f26a3fb77 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -338,7 +338,7 @@ Mob::Mob(const char* in_name, pLastChange = 0; SetPetID(0); SetOwnerID(0); - typeofpet = petCharmed; //default to charmed... + typeofpet = petNone; // default to not a pet petpower = 0; held = false; nocast = false; @@ -2296,8 +2296,10 @@ void Mob::SetOwnerID(uint16 NewOwnerID) { if (NewOwnerID == GetID() && NewOwnerID != 0) // ok, no charming yourself now =p return; ownerid = NewOwnerID; - if (ownerid == 0 && this->IsNPC() && this->GetPetType() != petCharmed) - this->Depop(); + // if we're setting the owner ID to 0 and they're not either charmed or not-a-pet then + // they're a normal pet and should be despawned + if (ownerid == 0 && IsNPC() && GetPetType() != petCharmed && GetPetType() != petNone) + Depop(); } // used in checking for behind (backstab) and checking in front (melee LoS) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 28f5518fa..e3b4c4a3b 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -773,6 +773,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove caster->SetPet(this); SetOwnerID(caster->GetID()); SetPetOrder(SPO_Follow); + SetPetType(petCharmed); if(caster->IsClient()){ EQApplicationPacket *app = new EQApplicationPacket(OP_Charm, sizeof(Charm_Struct)); @@ -3915,6 +3916,7 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) SendAppearancePacket(AT_Pet, 0, true, true); Mob* tempmob = GetOwner(); SetOwnerID(0); + SetPetType(petNone); if(tempmob) { tempmob->SetPet(0); From 0348c0817d1b7bb0514b056035d9f0c823abc6a4 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 29 May 2015 14:39:09 -0400 Subject: [PATCH 084/129] Make ResistSpell aware of the level_override nerf --- zone/mob.h | 3 ++- zone/spells.cpp | 29 +++++++++++++++-------------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/zone/mob.h b/zone/mob.h index 944b80d97..d1563347d 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -211,7 +211,8 @@ public: virtual int32 GetActSpellDuration(uint16 spell_id, int32 duration); virtual int32 GetActSpellCasttime(uint16 spell_id, int32 casttime); float ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override = false, - int resist_override = 0, bool CharismaCheck = false, bool CharmTick = false, bool IsRoot = false); + int resist_override = 0, bool CharismaCheck = false, bool CharmTick = false, bool IsRoot = false, + int level_override = -1); int ResistPhysical(int level_diff, uint8 caster_level); uint16 GetSpecializeSkillValue(uint16 spell_id) const; void SendSpellBarDisable(); diff --git a/zone/spells.cpp b/zone/spells.cpp index 7286ba0aa..615239a9e 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3665,9 +3665,9 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r if(IsResistableSpell(spell_id)) { if (IsCharmSpell(spell_id) || IsMezSpell(spell_id) || IsFearSpell(spell_id)) - spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust,true); + spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust, true, false, false, level_override); else - spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust); + spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust, false, false, false, level_override); if(spell_effectiveness < 100) { @@ -4208,7 +4208,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) // pvp_resist_base // pvp_resist_calc // pvp_resist_cap -float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override, int resist_override, bool CharismaCheck, bool CharmTick, bool IsRoot) +float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override, int resist_override, bool CharismaCheck, bool CharmTick, bool IsRoot, int level_override) { if(!caster) @@ -4351,18 +4351,19 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use int level_mod = 0; //Adjust our resist chance based on level modifiers - int temp_level_diff = GetLevel() - caster->GetLevel(); + uint8 caster_level = level_override > 0 ? level_override : caster->GetLevel(); + int temp_level_diff = GetLevel() - caster_level; //Physical Resists are calclated using their own formula derived from extensive parsing. if (resist_type == RESIST_PHYSICAL) { - level_mod = ResistPhysical(temp_level_diff, caster->GetLevel()); + level_mod = ResistPhysical(temp_level_diff, caster_level); } else { if(IsNPC() && GetLevel() >= RuleI(Casting,ResistFalloff)) { - int a = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel(); + int a = (RuleI(Casting,ResistFalloff)-1) - caster_level; if(a > 0) { temp_level_diff = a; @@ -4389,7 +4390,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use level_mod = -level_mod; } - if(IsNPC() && (caster->GetLevel() - GetLevel()) < -20) + if(IsNPC() && (caster_level - GetLevel()) < -20) { level_mod = 1000; } @@ -4400,7 +4401,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use int level_diff; if(GetLevel() >= RuleI(Casting,ResistFalloff)) { - level_diff = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel(); + level_diff = (RuleI(Casting,ResistFalloff)-1) - caster_level; if(level_diff < 0) { level_diff = 0; @@ -4408,7 +4409,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use } else { - level_diff = GetLevel() - caster->GetLevel(); + level_diff = GetLevel() - caster_level; } level_mod += (2 * level_diff); } @@ -4519,14 +4520,14 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use if(IsNPC()) { - if(GetLevel() > caster->GetLevel() && GetLevel() >= 17 && caster->GetLevel() <= 50) + if(GetLevel() > caster_level && GetLevel() >= 17 && caster_level <= 50) { partial_modifier += 5; } - if(GetLevel() >= 30 && caster->GetLevel() < 50) + if(GetLevel() >= 30 && caster_level < 50) { - partial_modifier += (caster->GetLevel() - 25); + partial_modifier += (caster_level - 25); } if(GetLevel() < 15) @@ -4537,9 +4538,9 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use if(caster->IsNPC()) { - if((GetLevel() - caster->GetLevel()) >= 20) + if((GetLevel() - caster_level) >= 20) { - partial_modifier += (GetLevel() - caster->GetLevel()) * 1.5; + partial_modifier += (GetLevel() - caster_level) * 1.5; } } From b0d85e35587d73370ea4e641b947e4cb49bbbe90 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 29 May 2015 14:55:32 -0400 Subject: [PATCH 085/129] More focus messages thanks to Google --- zone/spell_effects.cpp | 6 ++++++ zone/string_ids.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index e3b4c4a3b..b11b21403 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -5416,6 +5416,12 @@ int16 Client::GetFocusEffect(focusType type, uint16 spell_id) { case focusRange: string_id = PULSES_WITH_LIGHT; break; + case focusSpellHateMod: + string_id = GLOWS_BLUE; + break; + case focusImprovedHeal: + string_id = FEEDS_WITH_POWER; + break; default: break; } diff --git a/zone/string_ids.h b/zone/string_ids.h index fb52985c0..19d7436d0 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -350,6 +350,8 @@ #define YOU_HEAL 9068 //You have healed %1 for %2 points of damage. #define YOUR_HIT_DOT 9072 //%1 has taken %2 damage from your %3. #define HIT_NON_MELEE 9073 //%1 hit %2 for %3 points of non-melee damage. +#define GLOWS_BLUE 9074 //Your %1 glows blue. +#define GLOWS_RED 9075 //Your %1 glows red. #define SHAKE_OFF_STUN 9077 #define STRIKETHROUGH_STRING 9078 //You strike through your opponent's defenses! #define SPELL_REFLECT 9082 //%1's spell has been reflected by %2. From 96264cb688a5e60bb830e697adfef8be529d9c48 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 29 May 2015 15:26:32 -0400 Subject: [PATCH 086/129] Send the BEGIN_TO_GLOW message after OP_BeginCast when casting a clicky --- zone/spells.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/zone/spells.cpp b/zone/spells.cpp index 615239a9e..1af93bba5 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -489,6 +489,12 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot, safe_delete(outapp); outapp = nullptr; + if (IsClient() && slot == USE_ITEM_SPELL_SLOT &&item_slot != 0xFFFFFFFF) { + auto item = CastToClient()->GetInv().GetItem(item_slot); + if (item && item->GetItem()) + Message_StringID(MT_Spells, BEGINS_TO_GLOW, item->GetItem()->Name); + } + if (!DoCastingChecks()) { InterruptSpell(); return false; From d9cab4820ab2161a1d5faf536ad76e1d22c56766 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 29 May 2015 21:16:30 -0400 Subject: [PATCH 087/129] More focus messages --- zone/spell_effects.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index b11b21403..3336026c7 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -5399,6 +5399,7 @@ int16 Client::GetFocusEffect(focusType type, uint16 spell_id) { if (realTotal != 0 && UsedItem) { // there are a crap ton more of these, I was able to verify these ones though + // the RNG effective ones appear to have a different message for failing to focus uint32 string_id = BEGINS_TO_GLOW; // this is really just clicky message ... switch (type) { case focusSpellHaste: @@ -5422,6 +5423,9 @@ int16 Client::GetFocusEffect(focusType type, uint16 spell_id) { case focusImprovedHeal: string_id = FEEDS_WITH_POWER; break; + case focusReagentCost: + string_id = BEGINS_TO_SHINE; + break; default: break; } From 38cdea7d7e3e556190e4f3c838090250f4d459f5 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 30 May 2015 02:57:03 -0400 Subject: [PATCH 088/129] Furious Bash focus message --- zone/special_attacks.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 0f0a110cd..a3557d021 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -120,7 +120,10 @@ void Mob::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, hate += item->GetItem()->AC; } const Item_Struct *itm = item->GetItem(); - hate = hate * (100 + GetFuriousBash(itm->Focus.Effect)) / 100; + auto fbash = GetFuriousBash(itm->Focus.Effect); + hate = hate * (100 + fbash) / 100; + if (fbash) + Message_StringID(MT_Spells, GLOWS_RED, itm->Name); } } } From bfb40f6c5f79727ebf7426a27c1cf337e7c4a441 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 30 May 2015 03:08:02 -0400 Subject: [PATCH 089/129] Add failure messages for RNG focus --- zone/spell_effects.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 3336026c7..8b71c7d92 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -5397,7 +5397,7 @@ int16 Client::GetFocusEffect(focusType type, uint16 spell_id) { if(UsedItem && rand_effectiveness && focus_max_real != 0) realTotal = CalcFocusEffect(type, UsedFocusID, spell_id); - if (realTotal != 0 && UsedItem) { + if ((rand_effectiveness && UsedItem) || (realTotal != 0 && UsedItem)) { // there are a crap ton more of these, I was able to verify these ones though // the RNG effective ones appear to have a different message for failing to focus uint32 string_id = BEGINS_TO_GLOW; // this is really just clicky message ... @@ -5405,25 +5405,31 @@ int16 Client::GetFocusEffect(focusType type, uint16 spell_id) { case focusSpellHaste: string_id = SHIMMERS_BRIEFLY; break; - case focusManaCost: + case focusManaCost: // this might be GROWS_DIM for fail string_id = FLICKERS_PALE_LIGHT; break; case focusSpellDuration: string_id = SPARKLES; break; case focusImprovedDamage: - string_id = ALIVE_WITH_POWER; + if (realTotal) + string_id = ALIVE_WITH_POWER; + else + string_id = SEEMS_DRAINED; break; case focusRange: string_id = PULSES_WITH_LIGHT; break; - case focusSpellHateMod: + case focusSpellHateMod: // GLOWS_RED for increasing hate string_id = GLOWS_BLUE; break; case focusImprovedHeal: - string_id = FEEDS_WITH_POWER; + if (realTotal) + string_id = FEEDS_WITH_POWER; + else + string_id = POWER_DRAIN_INTO; break; - case focusReagentCost: + case focusReagentCost: // this might be GROWS_DIM for fail as well ... string_id = BEGINS_TO_SHINE; break; default: From 03c006bef52914075121d055c765494d993b8c24 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 30 May 2015 15:43:16 -0400 Subject: [PATCH 090/129] Implement ST_AEClientV1 This should at least be as correct as ST_AEBard is, unsure of the differences --- zone/spells.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 1af93bba5..868180dff 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -370,6 +370,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot, // and a target wasn't provided, then it's us; unless TGB is on and this // is a TGB compatible spell. if((IsGroupSpell(spell_id) || + spell.targettype == ST_AEClientV1 || spell.targettype == ST_Self || spell.targettype == ST_AECaster || spell.targettype == ST_Ring || @@ -393,7 +394,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot, // we checked for spells not requiring targets above if(target_id == 0) { - Log.Out(Logs::Detail, Logs::Spells, "Spell Error: no target. spell=%d\n", GetName(), spell_id); + Log.Out(Logs::Detail, Logs::Spells, "Spell Error: no target. spell=%d", spell_id); if(IsClient()) { //clients produce messages... npcs should not for this case Message_StringID(13, SPELL_NEED_TAR); @@ -1618,6 +1619,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce case ST_AEBard: case ST_AECaster: + case ST_AEClientV1: { spell_target = nullptr; ae_center = this; From 7011395d4c7df74e95ca0d6b394f91699e750207 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 31 May 2015 00:09:59 -0400 Subject: [PATCH 091/129] Pet target in UF+ I think older clients might have something like this that sets some spawn data, but these are the only clients that display something --- common/emu_oplist.h | 1 + utils/patches/patch_RoF.conf | 1 + utils/patches/patch_RoF2.conf | 1 + utils/patches/patch_UF.conf | 1 + zone/pets.cpp | 16 ++++++++++++++++ zone/pets.h | 1 + 6 files changed, 21 insertions(+) diff --git a/common/emu_oplist.h b/common/emu_oplist.h index c51e4b600..e4793826e 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -348,6 +348,7 @@ N(OP_OpenTributeMaster), N(OP_PDeletePetition), N(OP_PetBuffWindow), N(OP_PetCommands), +N(OP_PetHoTT), N(OP_Petition), N(OP_PetitionBug), N(OP_PetitionCheckIn), diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index 02d7c9bb8..6e3f0ab1a 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -194,6 +194,7 @@ OP_Consent=0x400e OP_ConsentDeny=0x34c1 OP_AutoFire=0x314e OP_PetCommands=0x0093 +OP_PetHoTT=0x0df4 OP_DeleteSpell=0x305c OP_Surname=0x1a87 OP_ClearSurname=0x17b6 diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index 312f725fe..1ebf05442 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -193,6 +193,7 @@ OP_Consent=0x1fd1 OP_ConsentDeny=0x7a45 OP_AutoFire=0x241e OP_PetCommands=0x0159 +OP_PetHoTT=0x794a OP_DeleteSpell=0x3358 OP_Surname=0x0423 OP_ClearSurname=0x3fb0 diff --git a/utils/patches/patch_UF.conf b/utils/patches/patch_UF.conf index cbe57e399..6f94c92db 100644 --- a/utils/patches/patch_UF.conf +++ b/utils/patches/patch_UF.conf @@ -197,6 +197,7 @@ OP_Consent=0x6bb9 # C OP_ConsentDeny=0x4cd1 # C OP_AutoFire=0x5db5 # C OP_PetCommands=0x7706 # C +OP_PetHoTT=0x2528 OP_DeleteSpell=0x0698 # C OP_Surname=0x44ae # C OP_ClearSurname=0x6705 # C diff --git a/zone/pets.cpp b/zone/pets.cpp index 908b756d4..e096328b9 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -457,6 +457,22 @@ 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()->GetClientVersionBit() & 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 f0c71fbe7..32ca00eac 100644 --- a/zone/pets.h +++ b/zone/pets.h @@ -39,6 +39,7 @@ 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); }; From 46dd1511af6916afee044e27d11fc0adfb979da6 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 1 Jun 2015 14:15:45 -0400 Subject: [PATCH 092/129] Fix DoBuffTic crash --- zone/spell_effects.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 8b71c7d92..b331113bd 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3707,6 +3707,8 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) // do we need to do anyting here? } } + if (!IsValidSpell(buff.spellid)) // if we faded we're no longer valid! + break; } } From aacd288ad7709687e5e3ca4f89ed1038fb69a929 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 1 Jun 2015 15:47:04 -0400 Subject: [PATCH 093/129] Update comment [skip ci] --- common/spdat.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/spdat.h b/common/spdat.h index ff2976607..bf2d23f30 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -151,7 +151,7 @@ typedef enum { } DmgShieldType; //Spell Effect IDs -// full listing: https://forums.station.sony.com/eq/index.php?threads/enumerated-spa-list.206288/ +// https://forums.daybreakgames.com/eq/index.php?threads/enumerated-spa-list.206288/ // mirror: http://pastebin.com/MYeQqGwe #define SE_CurrentHP 0 // implemented - Heals and nukes, repeates every tic if in a buff #define SE_ArmorClass 1 // implemented From c3c6d1897965eb2e794f0fd19d9a6d5ee9307a7b Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 1 Jun 2015 16:02:55 -0400 Subject: [PATCH 094/129] Fix RoF+ AA clientver bug --- common/patches/rof.cpp | 2 +- common/patches/rof2.cpp | 2 +- zone/aa.cpp | 8 +++----- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 9f9f58ab2..fae650eb5 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -2852,7 +2852,7 @@ namespace RoF // Check clientver field to verify this AA should be sent for SoF // clientver 1 is for all clients and 5 is for Live - if (emu->clientver <= 5) + if (emu->clientver <= 7) { OUT(id); eq->unknown004 = 1; diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 708c5a6a9..42e97b9eb 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -2941,7 +2941,7 @@ namespace RoF2 // Check clientver field to verify this AA should be sent for SoF // clientver 1 is for all clients and 5 is for Live - if (emu->clientver <= 5) + if (emu->clientver <= 8) { OUT(id); eq->unknown004 = 1; diff --git a/zone/aa.cpp b/zone/aa.cpp index b0208326a..b6e220d60 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -1309,11 +1309,9 @@ void Client::SendAA(uint32 id, int seq) { SendAA_Struct* saa_next = nullptr; saa_next = zone->FindAA(saa->sof_next_id); - // hard-coding values like this is dangerous and makes adding/updating clients a nightmare... - if (saa_next && - (((GetClientVersionBit() == 4) && (saa_next->clientver > 4)) - || ((GetClientVersionBit() == 8) && (saa_next->clientver > 5)) - || ((GetClientVersionBit() == 16) && (saa_next->clientver > 6)))){ + // this check should work as long as we continue to just add the clients and just increase + // each number .... + if (saa_next && static_cast(GetClientVersion()) < saa_next->clientver - 1) { saa->next_id=0xFFFFFFFF; } } From 9a5ff58213b80ec08f2595b152d7c40b8d2f522b Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Mon, 1 Jun 2015 22:20:41 -0400 Subject: [PATCH 095/129] Added GetGlobal() support for all Mobs. - Uses memory (no database hits) - Allows entity-based quest global checks --- zone/lua_mob.cpp | 6 ++++++ zone/lua_mob.h | 1 + zone/mob.cpp | 33 +++++++++++++++++++++++++++++++++ zone/mob.h | 1 + zone/perl_mob.cpp | 32 ++++++++++++++++++++++++++++++++ 5 files changed, 73 insertions(+) diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index 036af2072..60a42760c 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -1631,6 +1631,11 @@ void Lua_Mob::TempName(const char *newname) { self->TempName(newname); } +std::string Lua_Mob::GetGlobal(const char *varname) { + Lua_Safe_Call_String(); + return self->GetGlobal(varname); +} + void Lua_Mob::SetGlobal(const char *varname, const char *newvalue, int options, const char *duration) { Lua_Safe_Call_Void(); self->SetGlobal(varname, newvalue, options, duration); @@ -2120,6 +2125,7 @@ luabind::scope lua_register_mob() { .def("SendSpellEffect", (void(Lua_Mob::*)(uint32,uint32,uint32,bool,uint32,bool,Lua_Client))&Lua_Mob::SendSpellEffect) .def("TempName", (void(Lua_Mob::*)(void))&Lua_Mob::TempName) .def("TempName", (void(Lua_Mob::*)(const char*))&Lua_Mob::TempName) + .def("GetGlobal", (std::string(Lua_Mob::*)(const char*))&Lua_Mob::GetGlobal) .def("SetGlobal", (void(Lua_Mob::*)(const char*,const char*,int,const char*))&Lua_Mob::SetGlobal) .def("SetGlobal", (void(Lua_Mob::*)(const char*,const char*,int,const char*,Lua_Mob))&Lua_Mob::SetGlobal) .def("TarGlobal", (void(Lua_Mob::*)(const char*,const char*,const char*,int,int,int))&Lua_Mob::TarGlobal) diff --git a/zone/lua_mob.h b/zone/lua_mob.h index 3caf62839..54c388ed4 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -307,6 +307,7 @@ public: uint32 unk020, bool perm_effect, Lua_Client c); void TempName(); void TempName(const char *newname); + std::string GetGlobal(const char *varname); void SetGlobal(const char *varname, const char *newvalue, int options, const char *duration); void SetGlobal(const char *varname, const char *newvalue, int options, const char *duration, Lua_Mob other); void TarGlobal(const char *varname, const char *value, const char *duration, int npc_id, int char_id, int zone_id); diff --git a/zone/mob.cpp b/zone/mob.cpp index f26a3fb77..8a224db0b 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4202,6 +4202,39 @@ int32 Mob::GetItemStat(uint32 itemid, const char *identifier) return stat; } +std::string Mob::GetGlobal(const char *varname) { + int qgCharid = 0; + int qgNpcid = 0; + + if (this->IsNPC()) + qgNpcid = this->GetNPCTypeID(); + + if (this->IsClient()) + qgCharid = this->CastToClient()->CharacterID(); + + QGlobalCache *qglobals = nullptr; + std::list globalMap; + + if (this->IsClient()) + qglobals = this->CastToClient()->GetQGlobals(); + + if (this->IsNPC()) + qglobals = this->CastToNPC()->GetQGlobals(); + + if(qglobals) + QGlobalCache::Combine(globalMap, qglobals->GetBucket(), qgNpcid, qgCharid, zone->GetZoneID()); + + std::list::iterator iter = globalMap.begin(); + while(iter != globalMap.end()) { + if ((*iter).name.compare(varname) == 0) + return (*iter).value; + + ++iter; + } + + return "Undefined"; +} + void Mob::SetGlobal(const char *varname, const char *newvalue, int options, const char *duration, Mob *other) { int qgZoneid = zone->GetZoneID(); diff --git a/zone/mob.h b/zone/mob.h index d1563347d..74a53346e 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -911,6 +911,7 @@ public: inline virtual bool IsBlockedBuff(int16 SpellID) { return false; } inline virtual bool IsBlockedPetBuff(int16 SpellID) { return false; } + std::string GetGlobal(const char *varname); void SetGlobal(const char *varname, const char *newvalue, int options, const char *duration, Mob *other = nullptr); void TarGlobal(const char *varname, const char *value, const char *duration, int npcid, int charid, int zoneid); void DelGlobal(const char *varname); diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index f5ab99cd6..f5ef80a4c 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -7321,6 +7321,37 @@ XS(XS_Mob_GetItemStat) XSRETURN(1); } +XS(XS_Mob_GetGlobal); +XS(XS_Mob_GetGlobal) +{ + dXSARGS; + if (items < 2) + Perl_croak(aTHX_ "Usage: GetGlobal(THIS, varname)"); + { + Mob* THIS; + Const_char* varname = (Const_char*)SvPV_nolen(ST(1)); + std::string ret_val = "Undefined"; + Const_char* RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Mob *, tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Mob"); + if (THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + if (THIS->GetGlobal(varname) != "Undefined") + ret_val = THIS->GetGlobal(varname); + + RETVAL = ret_val.c_str(); + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + XS(XS_Mob_SetGlobal); XS(XS_Mob_SetGlobal) { @@ -8623,6 +8654,7 @@ XS(boot_Mob) newXSproto(strcpy(buf, "SpellEffect"), XS_Mob_SpellEffect, file, "$$;$$$$$$"); newXSproto(strcpy(buf, "TempName"), XS_Mob_TempName, file, "$:$"); newXSproto(strcpy(buf, "GetItemStat"), XS_Mob_GetItemStat, file, "$$$"); + newXSproto(strcpy(buf, "GetGlobal"), XS_Mob_GetGlobal, file, "$$"); newXSproto(strcpy(buf, "SetGlobal"), XS_Mob_SetGlobal, file, "$$$$$;$"); newXSproto(strcpy(buf, "TarGlobal"), XS_Mob_TarGlobal, file, "$$$$$$$"); newXSproto(strcpy(buf, "DelGlobal"), XS_Mob_DelGlobal, file, "$$"); From 2c6fd44811cc76bb8201d03c96eecfe6381c6a23 Mon Sep 17 00:00:00 2001 From: Russell Kinasz Date: Tue, 2 Jun 2015 12:25:09 -0700 Subject: [PATCH 096/129] Implemented encounter timers - no spawn required --- zone/encounter.cpp | 53 +++++++++++++++++++++++++++ zone/encounter.h | 62 ++++++++++++++++++++++++++++++++ zone/entity.cpp | 47 ++++++++++++++++++++++++ zone/entity.h | 8 +++++ zone/lua_general.cpp | 22 ++++++++---- zone/lua_parser.cpp | 25 +++++++++---- zone/lua_parser.h | 6 ++-- zone/lua_parser_events.cpp | 11 ++++++ zone/lua_parser_events.h | 8 +++++ zone/net.cpp | 1 + zone/quest_interface.h | 2 +- zone/quest_parser_collection.cpp | 6 ++-- zone/quest_parser_collection.h | 3 +- zone/questmgr.cpp | 2 ++ 14 files changed, 237 insertions(+), 19 deletions(-) create mode 100644 zone/encounter.cpp create mode 100644 zone/encounter.h diff --git a/zone/encounter.cpp b/zone/encounter.cpp new file mode 100644 index 000000000..1f77de43a --- /dev/null +++ b/zone/encounter.cpp @@ -0,0 +1,53 @@ +/* EQEMu: Everquest Server Emulator +Copyright (C) 2001-2003 EQEMu Development Team (http://eqemulator.net) + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY except by those people which sell it, which +are required to give you total support for your newly bought product; +without even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef _WINDOWS +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif + +#include "../common/races.h" +#include "encounter.h" +#include "entity.h" +#include "mob.h" + +class Zone; + +Encounter::Encounter(const char* enc_name) + :Mob + ( + nullptr, nullptr, 0, 0, 0, INVISIBLE_MAN, 0, BT_NoTarget, 0, 0, 0, 0, 0, glm::vec4(0,0,0,0), 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ) +{ + encounter_name[0] = 0; + strn0cpy(encounter_name, enc_name, 64); + remove_me = false; +} + +Encounter::~Encounter() +{ + +} + +bool Encounter::Process() { + if (remove_me) return false; + return true; +} diff --git a/zone/encounter.h b/zone/encounter.h new file mode 100644 index 000000000..a2977d7e0 --- /dev/null +++ b/zone/encounter.h @@ -0,0 +1,62 @@ +/* EQEMu: Everquest Server Emulator +Copyright (C) 2001-2003 EQEMu Development Team (http://eqemulator.net) + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY except by those people which sell it, which +are required to give you total support for your newly bought product; +without even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef ENCOUNTER_H +#define ENCOUNTER_H + +#include "mob.h" +#include "../common/types.h" +#include "../common/timer.h" + +class Group; +class Raid; +struct ExtraAttackOptions; + +class Encounter : public Mob +{ +public: + Encounter(const char* enc_name); + ~Encounter(); + + //abstract virtual function implementations required by base abstract class + virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, SkillUseTypes attack_skill) { return true; } + virtual void Damage(Mob* from, int32 damage, uint16 spell_id, SkillUseTypes attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false) { return; } + virtual bool Attack(Mob* other, int Hand = MainPrimary, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false, + ExtraAttackOptions *opts = nullptr) { + return false; + } + virtual bool HasRaid() { return false; } + virtual bool HasGroup() { return false; } + virtual Raid* GetRaid() { return 0; } + virtual Group* GetGroup() { return 0; } + + bool IsEncounter() const { return true; } + const char* GetEncounterName() const { return encounter_name; } + + bool Process(); + virtual void Depop(bool not_used = true) { remove_me = true; } + + +protected: + bool remove_me; + char encounter_name[64]; + +private: +}; + +#endif diff --git a/zone/entity.cpp b/zone/entity.cpp index bf1f6b720..baa4f4354 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -173,6 +173,11 @@ Beacon *Entity::CastToBeacon() return static_cast(this); } +Encounter *Entity::CastToEncounter() +{ + return static_cast(this); +} + const Client *Entity::CastToClient() const { if (this == 0x00) { @@ -263,6 +268,11 @@ const Beacon* Entity::CastToBeacon() const return static_cast(this); } +const Encounter* Entity::CastToEncounter() const +{ + return static_cast(this); +} + #ifdef BOTS Bot *Entity::CastToBot() { @@ -533,6 +543,21 @@ void EntityList::BeaconProcess() } } +void EntityList::EncounterProcess() +{ + auto it = encounter_list.begin(); + while (it != encounter_list.end()) { + if (!it->second->Process()) { + safe_delete(it->second); + free_ids.push(it->first); + it = encounter_list.erase(it); + } + else { + ++it; + } + } +} + void EntityList::AddGroup(Group *group) { if (group == nullptr) //this seems to be happening somehow... @@ -708,6 +733,12 @@ void EntityList::AddBeacon(Beacon *beacon) beacon_list.insert(std::pair(beacon->GetID(), beacon)); } +void EntityList::AddEncounter(Encounter *encounter) +{ + encounter->SetID(GetFreeID()); + encounter_list.insert(std::pair(encounter->GetID(), encounter)); +} + void EntityList::AddToSpawnQueue(uint16 entityid, NewSpawn_Struct **ns) { uint32 count; @@ -935,6 +966,11 @@ Entity *EntityList::GetEntityBeacon(uint16 id) return beacon_list.count(id) ? beacon_list.at(id) : nullptr; } +Entity *EntityList::GetEntityEncounter(uint16 id) +{ + return encounter_list.count(id) ? encounter_list.at(id) : nullptr; +} + Entity *EntityList::GetID(uint16 get_id) { Entity *ent = 0; @@ -950,6 +986,8 @@ Entity *EntityList::GetID(uint16 get_id) return ent; else if ((ent=entity_list.GetEntityBeacon(get_id)) != 0) return ent; + else if ((ent = entity_list.GetEntityEncounter(get_id)) != 0) + return ent; else return 0; } @@ -3424,6 +3462,15 @@ bool EntityList::IsMobInZone(Mob *who) } ++it; } + + auto enc_it = encounter_list.begin(); + while (enc_it != encounter_list.end()) { + if (enc_it->second == who) { + return true; + } + ++enc_it; + } + return false; } diff --git a/zone/entity.h b/zone/entity.h index 8af87f649..446c5d505 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -30,6 +30,7 @@ #include "position.h" #include "zonedump.h" +class Encounter; class Beacon; class Client; class Corpse; @@ -77,6 +78,7 @@ public: virtual bool IsDoor() const { return false; } virtual bool IsTrap() const { return false; } virtual bool IsBeacon() const { return false; } + virtual bool IsEncounter() const { return false; } virtual bool Process() { return false; } virtual bool Save() { return true; } @@ -91,6 +93,7 @@ public: Doors *CastToDoors(); Trap *CastToTrap(); Beacon *CastToBeacon(); + Encounter *CastToEncounter(); const Client *CastToClient() const; const NPC *CastToNPC() const; @@ -101,6 +104,7 @@ public: const Doors *CastToDoors() const; const Trap *CastToTrap() const; const Beacon *CastToBeacon() const; + const Encounter *CastToEncounter() const; inline const uint16& GetID() const { return id; } inline const time_t& GetSpawnTimeStamp() const { return spawn_timestamp; } @@ -203,6 +207,7 @@ public: void MobProcess(); void TrapProcess(); void BeaconProcess(); + void EncounterProcess(); void ProcessMove(Client *c, const glm::vec3& location); void ProcessMove(NPC *n, float x, float y, float z); void AddArea(int id, int type, float min_x, float max_x, float min_y, float max_y, float min_z, float max_z); @@ -228,6 +233,7 @@ public: void AddDoor(Doors* door); void AddTrap(Trap* trap); void AddBeacon(Beacon *beacon); + void AddEncounter(Encounter *encounter); void AddProximity(NPC *proximity_for); void Clear(); bool RemoveMob(uint16 delete_id); @@ -266,6 +272,7 @@ public: Entity *GetEntityCorpse(uint16 id); Entity *GetEntityTrap(uint16 id); Entity *GetEntityBeacon(uint16 id); + Entity *GetEntityEncounter(uint16 id); Entity *GetEntityMob(const char *name); Entity *GetEntityCorpse(const char *name); @@ -448,6 +455,7 @@ private: std::unordered_map door_list; std::unordered_map trap_list; std::unordered_map beacon_list; + std::unordered_map encounter_list; std::list proximity_list; std::list group_list; std::list raid_list; diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 6b290dc03..271bc0a97 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -34,6 +34,8 @@ struct lua_registered_event { extern std::map> lua_encounter_events_registered; extern std::map lua_encounters_loaded; +extern std::map lua_encounters; + extern void MapOpcodes(); extern void ClearMappedOpcode(EmuOpcode op); @@ -42,19 +44,23 @@ void unregister_event(std::string package_name, std::string name, int evt); void load_encounter(std::string name) { if(lua_encounters_loaded.count(name) > 0) return; - + Encounter *enc = new Encounter(name.c_str()); + entity_list.AddEncounter(enc); + lua_encounters[name] = enc; lua_encounters_loaded[name] = true; - parse->EventEncounter(EVENT_ENCOUNTER_LOAD, name, 0); + parse->EventEncounter(EVENT_ENCOUNTER_LOAD, name, "", 0); } void load_encounter_with_data(std::string name, std::string info_str) { if(lua_encounters_loaded.count(name) > 0) return; - + Encounter *enc = new Encounter(name.c_str()); + entity_list.AddEncounter(enc); + lua_encounters[name] = enc; lua_encounters_loaded[name] = true; std::vector info_ptrs; info_ptrs.push_back(&info_str); - parse->EventEncounter(EVENT_ENCOUNTER_LOAD, name, 0, &info_ptrs); + parse->EventEncounter(EVENT_ENCOUNTER_LOAD, name, "", 0, &info_ptrs); } void unload_encounter(std::string name) { @@ -80,8 +86,10 @@ void unload_encounter(std::string name) { } } + lua_encounters[name]->Depop(); + lua_encounters.erase(name); lua_encounters_loaded.erase(name); - parse->EventEncounter(EVENT_ENCOUNTER_UNLOAD, name, 0); + parse->EventEncounter(EVENT_ENCOUNTER_UNLOAD, name, "", 0); } void unload_encounter_with_data(std::string name, std::string info_str) { @@ -109,10 +117,12 @@ void unload_encounter_with_data(std::string name, std::string info_str) { } } + lua_encounters[name]->Depop(); + lua_encounters.erase(name); lua_encounters_loaded.erase(name); std::vector info_ptrs; info_ptrs.push_back(&info_str); - parse->EventEncounter(EVENT_ENCOUNTER_UNLOAD, name, 0, &info_ptrs); + parse->EventEncounter(EVENT_ENCOUNTER_UNLOAD, name, "", 0, &info_ptrs); } void register_event(std::string package_name, std::string name, int evt, luabind::adl::object func) { diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index 7f6412b0f..383691864 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -128,6 +128,7 @@ struct lua_registered_event { std::map> lua_encounter_events_registered; std::map lua_encounters_loaded; +std::map lua_encounters; LuaParser::LuaParser() { for(int i = 0; i < _LargestEventID; ++i) { @@ -135,6 +136,7 @@ LuaParser::LuaParser() { PlayerArgumentDispatch[i] = handle_player_null; ItemArgumentDispatch[i] = handle_item_null; SpellArgumentDispatch[i] = handle_spell_null; + EncounterArgumentDispatch[i] = handle_encounter_null; } NPCArgumentDispatch[EVENT_SAY] = handle_npc_event_say; @@ -213,6 +215,8 @@ LuaParser::LuaParser() { SpellArgumentDispatch[EVENT_SPELL_FADE] = handle_spell_fade; SpellArgumentDispatch[EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE] = handle_translocate_finish; + EncounterArgumentDispatch[EVENT_TIMER] = handle_encounter_timer; + L = nullptr; } @@ -575,7 +579,7 @@ int LuaParser::_EventSpell(std::string package_name, QuestEventID evt, NPC* npc, return 0; } -int LuaParser::EventEncounter(QuestEventID evt, std::string encounter_name, uint32 extra_data, std::vector *extra_pointers) { +int LuaParser::EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, std::vector *extra_pointers) { evt = ConvertLuaEvent(evt); if(evt >= _LargestEventID) { return 0; @@ -587,10 +591,10 @@ int LuaParser::EventEncounter(QuestEventID evt, std::string encounter_name, uint return 0; } - return _EventEncounter(package_name, evt, encounter_name, extra_data, extra_pointers); + return _EventEncounter(package_name, evt, encounter_name, data, extra_data, extra_pointers); } -int LuaParser::_EventEncounter(std::string package_name, QuestEventID evt, std::string encounter_name, uint32 extra_data, +int LuaParser::_EventEncounter(std::string package_name, QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, std::vector *extra_pointers) { const char *sub_name = LuaEvents[evt]; @@ -604,13 +608,17 @@ int LuaParser::_EventEncounter(std::string package_name, QuestEventID evt, std:: lua_pushstring(L, encounter_name.c_str()); lua_setfield(L, -2, "name"); - if(extra_pointers) { + /*if(extra_pointers) { std::string *str = EQEmu::any_cast(extra_pointers->at(0)); lua_pushstring(L, str->c_str()); lua_setfield(L, -2, "data"); - } + }*/ - quest_manager.StartQuest(nullptr, nullptr, nullptr, encounter_name); + auto arg_function = EncounterArgumentDispatch[evt]; + arg_function(this, L, data, extra_data, extra_pointers); + + Encounter *enc = lua_encounters[encounter_name]; + quest_manager.StartQuest(enc, nullptr, nullptr, encounter_name); if(lua_pcall(L, 1, 1, 0)) { std::string error = lua_tostring(L, -1); AddError(error); @@ -786,6 +794,11 @@ void LuaParser::ReloadQuests() { lua_encounter_events_registered.clear(); lua_encounters_loaded.clear(); + for (auto encounter : lua_encounters) { + encounter.second->Depop(); + } + lua_encounters.clear(); + if(L) { lua_close(L); } diff --git a/zone/lua_parser.h b/zone/lua_parser.h index 13cebe0fc..63d9facfe 100644 --- a/zone/lua_parser.h +++ b/zone/lua_parser.h @@ -39,7 +39,7 @@ public: std::vector *extra_pointers); virtual int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, std::vector *extra_pointers); - virtual int EventEncounter(QuestEventID evt, std::string encounter_name, uint32 extra_data, + virtual int EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, std::vector *extra_pointers); virtual bool HasQuestSub(uint32 npc_id, QuestEventID evt); @@ -82,7 +82,7 @@ private: uint32 extra_data, std::vector *extra_pointers, luabind::adl::object *l_func = nullptr); int _EventSpell(std::string package_name, QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, std::vector *extra_pointers, luabind::adl::object *l_func = nullptr); - int _EventEncounter(std::string package_name, QuestEventID evt, std::string encounter_name, uint32 extra_data, + int _EventEncounter(std::string package_name, QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, std::vector *extra_pointers); void LoadScript(std::string filename, std::string package_name); @@ -99,6 +99,8 @@ private: PlayerArgumentHandler PlayerArgumentDispatch[_LargestEventID]; ItemArgumentHandler ItemArgumentDispatch[_LargestEventID]; SpellArgumentHandler SpellArgumentDispatch[_LargestEventID]; + EncounterArgumentHandler EncounterArgumentDispatch[_LargestEventID]; + }; #endif diff --git a/zone/lua_parser_events.cpp b/zone/lua_parser_events.cpp index 2780d0212..adccf433b 100644 --- a/zone/lua_parser_events.cpp +++ b/zone/lua_parser_events.cpp @@ -704,4 +704,15 @@ void handle_spell_null(QuestInterface *parse, lua_State* L, NPC* npc, Client* cl std::vector *extra_pointers) { } +void handle_encounter_timer(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, + std::vector *extra_pointers) { + lua_pushstring(L, data.c_str()); + lua_setfield(L, -2, "timer"); +} + +void handle_encounter_null(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, + std::vector *extra_pointers) { + +} + #endif diff --git a/zone/lua_parser_events.h b/zone/lua_parser_events.h index 1965a9189..818bcf5be 100644 --- a/zone/lua_parser_events.h +++ b/zone/lua_parser_events.h @@ -6,6 +6,7 @@ typedef void(*NPCArgumentHandler)(QuestInterface*, lua_State*, NPC*, Mob*, std:: typedef void(*PlayerArgumentHandler)(QuestInterface*, lua_State*, Client*, std::string, uint32, std::vector*); typedef void(*ItemArgumentHandler)(QuestInterface*, lua_State*, Client*, ItemInst*, Mob*, std::string, uint32, std::vector*); typedef void(*SpellArgumentHandler)(QuestInterface*, lua_State*, NPC*, Client*, uint32, uint32, std::vector*); +typedef void(*EncounterArgumentHandler)(QuestInterface*, lua_State*, std::string, uint32, std::vector*); //NPC void handle_npc_event_say(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, @@ -127,5 +128,12 @@ void handle_translocate_finish(QuestInterface *parse, lua_State* L, NPC* npc, Cl void handle_spell_null(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, uint32 extra_data, std::vector *extra_pointers); + +//Encounter +void handle_encounter_timer(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, + std::vector *extra_pointers); +void handle_encounter_null(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, + std::vector *extra_pointers); + #endif #endif diff --git a/zone/net.cpp b/zone/net.cpp index 92f568af0..ef6b46836 100644 --- a/zone/net.cpp +++ b/zone/net.cpp @@ -423,6 +423,7 @@ int main(int argc, char** argv) { entity_list.Process(); entity_list.MobProcess(); entity_list.BeaconProcess(); + entity_list.EncounterProcess(); if (zone) { if(!zone->Process()) { diff --git a/zone/quest_interface.h b/zone/quest_interface.h index 68c79923f..c1cf3dc73 100644 --- a/zone/quest_interface.h +++ b/zone/quest_interface.h @@ -41,7 +41,7 @@ public: std::vector *extra_pointers) { return 0; } virtual int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, std::vector *extra_pointers) { return 0; } - virtual int EventEncounter(QuestEventID evt, std::string encounter_name, uint32 extra_data, + virtual int EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, std::vector *extra_pointers) { return 0; } virtual bool HasQuestSub(uint32 npcid, QuestEventID evt) { return false; } diff --git a/zone/quest_parser_collection.cpp b/zone/quest_parser_collection.cpp index 37e518c24..845769532 100644 --- a/zone/quest_parser_collection.cpp +++ b/zone/quest_parser_collection.cpp @@ -434,14 +434,14 @@ int QuestParserCollection::EventSpell(QuestEventID evt, NPC* npc, Client *client return 0; } -int QuestParserCollection::EventEncounter(QuestEventID evt, std::string encounter_name, uint32 extra_data, +int QuestParserCollection::EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, std::vector *extra_pointers) { auto iter = _encounter_quest_status.find(encounter_name); if(iter != _encounter_quest_status.end()) { //loaded or failed to load if(iter->second != QuestFailedToLoad) { std::map::iterator qiter = _interfaces.find(iter->second); - return qiter->second->EventEncounter(evt, encounter_name, extra_data, extra_pointers); + return qiter->second->EventEncounter(evt, encounter_name, data, extra_data, extra_pointers); } } else { std::string filename; @@ -449,7 +449,7 @@ int QuestParserCollection::EventEncounter(QuestEventID evt, std::string encounte if(qi) { _encounter_quest_status[encounter_name] = qi->GetIdentifier(); qi->LoadEncounterScript(filename, encounter_name); - return qi->EventEncounter(evt, encounter_name, extra_data, extra_pointers); + return qi->EventEncounter(evt, encounter_name, data, extra_data, extra_pointers); } else { _encounter_quest_status[encounter_name] = QuestFailedToLoad; } diff --git a/zone/quest_parser_collection.h b/zone/quest_parser_collection.h index 62cb034dc..3ebce378c 100644 --- a/zone/quest_parser_collection.h +++ b/zone/quest_parser_collection.h @@ -21,6 +21,7 @@ #include "../common/types.h" +#include "encounter.h" #include "beacon.h" #include "client.h" #include "corpse.h" @@ -71,7 +72,7 @@ public: std::vector *extra_pointers = nullptr); int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, std::vector *extra_pointers = nullptr); - int EventEncounter(QuestEventID evt, std::string encounter_name, uint32 extra_data, + int EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, std::vector *extra_pointers = nullptr); void GetErrors(std::list &err); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 825e3b7b1..67a1f06da 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -84,6 +84,8 @@ void QuestManager::Process() { if(entity_list.IsMobInZone(cur->mob)) { if(cur->mob->IsNPC()) { parse->EventNPC(EVENT_TIMER, cur->mob->CastToNPC(), nullptr, cur->name, 0); + } else if (cur->mob->IsEncounter()) { + parse->EventEncounter(EVENT_TIMER, cur->mob->CastToEncounter()->GetEncounterName(), cur->name, 0, nullptr); } else { //this is inheriently unsafe if we ever make it so more than npc/client start timers parse->EventPlayer(EVENT_TIMER, cur->mob->CastToClient(), cur->name, 0); From 6ff06ded43a5971287ea6c305bb13ca8aac3fbe7 Mon Sep 17 00:00:00 2001 From: Russell Kinasz Date: Tue, 2 Jun 2015 15:21:27 -0700 Subject: [PATCH 097/129] Fix for extra_pointers in Encounter methods --- zone/lua_parser.cpp | 2 ++ zone/lua_parser_events.cpp | 20 +++++++++++++++++++- zone/lua_parser_events.h | 4 ++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index 383691864..2442838a3 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -216,6 +216,8 @@ LuaParser::LuaParser() { SpellArgumentDispatch[EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE] = handle_translocate_finish; EncounterArgumentDispatch[EVENT_TIMER] = handle_encounter_timer; + EncounterArgumentDispatch[EVENT_ENCOUNTER_LOAD] = handle_encounter_load; + EncounterArgumentDispatch[EVENT_ENCOUNTER_UNLOAD] = handle_encounter_unload; L = nullptr; } diff --git a/zone/lua_parser_events.cpp b/zone/lua_parser_events.cpp index adccf433b..5428304a6 100644 --- a/zone/lua_parser_events.cpp +++ b/zone/lua_parser_events.cpp @@ -710,8 +710,26 @@ void handle_encounter_timer(QuestInterface *parse, lua_State* L, std::string dat lua_setfield(L, -2, "timer"); } +void handle_encounter_load(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, + std::vector *extra_pointers) { + if (extra_pointers) { + std::string *str = EQEmu::any_cast(extra_pointers->at(0)); + lua_pushstring(L, str->c_str()); + lua_setfield(L, -2, "data"); + } +} + +void handle_encounter_unload(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, + std::vector *extra_pointers) { + if (extra_pointers) { + std::string *str = EQEmu::any_cast(extra_pointers->at(0)); + lua_pushstring(L, str->c_str()); + lua_setfield(L, -2, "data"); + } +} + void handle_encounter_null(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { } diff --git a/zone/lua_parser_events.h b/zone/lua_parser_events.h index 818bcf5be..46609fb06 100644 --- a/zone/lua_parser_events.h +++ b/zone/lua_parser_events.h @@ -132,6 +132,10 @@ void handle_spell_null(QuestInterface *parse, lua_State* L, NPC* npc, Client* cl //Encounter void handle_encounter_timer(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, std::vector *extra_pointers); +void handle_encounter_load(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, + std::vector *extra_pointers); +void handle_encounter_unload(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, + std::vector *extra_pointers); void handle_encounter_null(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, std::vector *extra_pointers); From c351a9b54f74b2b1b1464052ca64fb1c19067d0f Mon Sep 17 00:00:00 2001 From: Russell Kinasz Date: Tue, 2 Jun 2015 15:27:57 -0700 Subject: [PATCH 098/129] Removed unnecessary commented code. --- zone/lua_parser.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index 2442838a3..4b5a1faad 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -610,12 +610,6 @@ int LuaParser::_EventEncounter(std::string package_name, QuestEventID evt, std:: lua_pushstring(L, encounter_name.c_str()); lua_setfield(L, -2, "name"); - /*if(extra_pointers) { - std::string *str = EQEmu::any_cast(extra_pointers->at(0)); - lua_pushstring(L, str->c_str()); - lua_setfield(L, -2, "data"); - }*/ - auto arg_function = EncounterArgumentDispatch[evt]; arg_function(this, L, data, extra_data, extra_pointers); From 328b7bb93c3100526110ec408336d17eb5b943ee Mon Sep 17 00:00:00 2001 From: Russell Kinasz Date: Tue, 2 Jun 2015 16:32:42 -0700 Subject: [PATCH 099/129] Add encounters header to lua_general.cpp --- zone/lua_general.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 271bc0a97..139951724 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -18,6 +18,7 @@ #include "qglobals.h" #include "../common/timer.h" #include "../common/eqemu_logsys.h" +#include "encounter.h" struct Events { }; struct Factions { }; From dbd07106d7078557b61cb565e4ea76946744d3ab Mon Sep 17 00:00:00 2001 From: Russell Kinasz Date: Tue, 2 Jun 2015 17:17:40 -0700 Subject: [PATCH 100/129] Updated zone cmakelists.txt --- zone/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 44a6d02c8..2510dcddb 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -20,6 +20,7 @@ SET(zone_sources embparser_api.cpp embperl.cpp embxs.cpp + encounter.cpp entity.cpp exp.cpp fearpath.cpp @@ -137,6 +138,7 @@ SET(zone_headers embparser.h embperl.h embxs.h + encounter.h entity.h errmsg.h event_codes.h From 7f30950fdbbe3b33a0630804abcf8861e0425685 Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Wed, 3 Jun 2015 10:08:55 -0700 Subject: [PATCH 101/129] More group related bot crashes fixed. Bots can now be targeted in the group window and kicked from party. They are also no longer raid_targets when conned. --- zone/bot.cpp | 14 ++++++-------- zone/client_packet.cpp | 10 ++++++++-- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index ebc5f47d3..e76389b24 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -758,6 +758,7 @@ void Bot::GenerateBaseStats() { this->pAggroRange = 0; this->pAssistRange = 0; + this->raid_target = false; } void Bot::GenerateAppearance() { @@ -5292,9 +5293,6 @@ bool Bot::RemoveBotFromGroup(Bot* bot, Group* group) { if(group->DelMember(bot)) database.SetGroupID(bot->GetCleanName(), 0, bot->GetBotID()); - - if(group->GroupCount() <= 1 && ZoneLoaded) - group->DisbandGroup(); } else { for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { @@ -5896,6 +5894,11 @@ bool Bot::Death(Mob *killerMob, int32 damage, uint16 spell_id, SkillUseTypes att // delete from group data RemoveBotFromGroup(this, g); + + //Make sure group still exists if it doesnt they were already updated in RemoveBotFromGroup + g = GetGroup(); + if (!g) + break; // if group members exist below this one, move // them all up one slot in the group list @@ -5910,11 +5913,6 @@ bool Bot::Death(Mob *killerMob, int32 damage, uint16 spell_id, SkillUseTypes att } } - //Make sure group still exists if it doesnt they were already updated in RemoveBotFromGroup - g = GetGroup(); - if (!g) - break; - // update the client group EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct)); GroupJoin_Struct* gu = (GroupJoin_Struct*)outapp->pBuffer; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index ccb27a188..b9dfb1c69 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -6354,10 +6354,16 @@ void Client::Handle_OP_GroupDisband(const EQApplicationPacket *app) Bot::ProcessBotGroupDisband(this, std::string()); } else { - Mob* tempMember = entity_list.GetMob(gd->name2); + Mob* tempMember = entity_list.GetMob(gd->name1); //Name1 is the target you are disbanding if (tempMember) { if (tempMember->IsBot()) - Bot::ProcessBotGroupDisband(this, std::string(tempMember->GetCleanName())); + tempMember->CastToBot()->RemoveBotFromGroup(tempMember->CastToBot(), group); + if (LFP) + { + // If we are looking for players, update to show we are on our own now. + UpdateLFP(); + } + return; //No need to continue from here we were removing a bot from party } } } From 64caf298fbe7db7d2d99217677918a57ddbcd694 Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Wed, 3 Jun 2015 10:17:28 -0700 Subject: [PATCH 102/129] Only return if the target was a Bot member --- zone/client_packet.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index b9dfb1c69..8a7b8a133 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -6355,9 +6355,8 @@ void Client::Handle_OP_GroupDisband(const EQApplicationPacket *app) } else { Mob* tempMember = entity_list.GetMob(gd->name1); //Name1 is the target you are disbanding - if (tempMember) { - if (tempMember->IsBot()) - tempMember->CastToBot()->RemoveBotFromGroup(tempMember->CastToBot(), group); + if (tempMember && tempMember->IsBot()) { + tempMember->CastToBot()->RemoveBotFromGroup(tempMember->CastToBot(), group); if (LFP) { // If we are looking for players, update to show we are on our own now. From 00d258a952cb17c679036eae374611c29328b0a1 Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Wed, 3 Jun 2015 14:18:51 -0700 Subject: [PATCH 103/129] Bot pets will now scale with focus effects. Pets will not scale passed 3x their normal size and stay within 10 levels of their base level unless changed with the rule PetPowerLevelCap --- common/ruletypes.h | 1 + zone/bot.h | 1 + zone/pets.cpp | 18 +++++++++++++++--- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index a581e527d..cf77dddf6 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -151,6 +151,7 @@ RULE_CATEGORY_END() RULE_CATEGORY( Pets ) RULE_REAL( Pets, AttackCommandRange, 150 ) RULE_BOOL( Pets, UnTargetableSwarmPet, false ) +RULE_REAL( Pets, PetPowerLevelCap, 10 ) // Max number of levels your pet can go up with pet power RULE_CATEGORY_END() RULE_CATEGORY( GM ) diff --git a/zone/bot.h b/zone/bot.h index 4cf04aa3a..25e0d1c52 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -60,6 +60,7 @@ enum SpellTypeIndex { }; class Bot : public NPC { + friend class Mob; public: // Class enums enum BotfocusType { //focus types diff --git a/zone/pets.cpp b/zone/pets.cpp index e096328b9..f5f741f44 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -28,6 +28,10 @@ #include "pets.h" #include "zonedb.h" +#ifdef BOTS +#include "bot.h" +#endif + #ifndef WIN32 #include #include "../common/unix.h" @@ -231,6 +235,10 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, act_power = CastToClient()->GetFocusEffect(focusPetPower, spell_id);//Client only act_power = CastToClient()->mod_pet_power(act_power, spell_id); } +#ifdef BOTS + else if (this->IsBot()) + act_power = CastToBot()->GetBotFocusEffect(Bot::BotfocusPetPower, spell_id); +#endif } else if (petpower > 0) act_power = petpower; @@ -260,7 +268,11 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, memcpy(npc_type, base, sizeof(NPCType)); // If pet power is set to -1 in the DB, use stat scaling - if (this->IsClient() && record.petpower == -1) + if ((this->IsClient() +#ifdef BOTS + || this->IsBot() +#endif + ) && record.petpower == -1) { float scale_power = (float)act_power / 100.0f; if(scale_power > 0) @@ -268,10 +280,10 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, npc_type->max_hp *= (1 + scale_power); npc_type->cur_hp = npc_type->max_hp; npc_type->AC *= (1 + scale_power); - npc_type->level += 1 + ((int)act_power / 25); // gains an additional level for every 25 pet power + npc_type->level += 1 + ((int)act_power / 25) > npc_type->level + RuleR(Pets, PetPowerLevelCap) ? RuleR(Pets, PetPowerLevelCap) : 1 + ((int)act_power / 25); // gains an additional level for every 25 pet power npc_type->min_dmg = (npc_type->min_dmg * (1 + (scale_power / 2))); npc_type->max_dmg = (npc_type->max_dmg * (1 + (scale_power / 2))); - npc_type->size *= (1 + (scale_power / 2)); + npc_type->size = npc_type->size * (1 + (scale_power / 2)) > npc_type->size * 3 ? npc_type->size * 3 : (1 + (scale_power / 2)); } record.petpower = act_power; } From a5d9faf8ea69458847cdb83c450319b45f1ddd5c Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 4 Jun 2015 01:02:48 -0400 Subject: [PATCH 104/129] Allow bard DOTs to get random extra tick This extra tick business needs to be figured out more ... bard invul and crescendo songs DO NOT get this extra tick, but DOTs do --- zone/effects.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index c1d037670..55e56cdac 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -423,7 +423,9 @@ int32 Mob::GetActSpellDuration(uint16 spell_id, int32 duration) // unsure on the exact details, but bard songs that don't cost mana at some point get an extra tick, 60 for now // a level 53 bard reported getting 2 tics - if (IsShortDurationBuff(spell_id) && IsBardSong(spell_id) && spells[spell_id].mana == 0 && GetClass() == BARD && GetLevel() > 60) + // bard DOTs do get this extra tick, but beneficial long bard songs don't? (invul, crescendo) + if ((IsShortDurationBuff(spell_id) || IsDetrimentalSpell(spell_id)) && IsBardSong(spell_id) && + spells[spell_id].mana == 0 && GetClass() == BARD && GetLevel() > 60) tic_inc++; float focused = ((duration * increase) / 100.0f) + tic_inc; int ifocused = static_cast(focused); From 0b17dc73f1bcf2f074a47fbedd45685e07af6986 Mon Sep 17 00:00:00 2001 From: Russell Kinasz Date: Fri, 5 Jun 2015 12:23:42 -0700 Subject: [PATCH 105/129] Update to encounter timers so they can actually work from hooked events --- zone/CMakeLists.txt | 6 ++++-- zone/lua_encounter.cpp | 14 ++++++++++++++ zone/lua_encounter.h | 30 ++++++++++++++++++++++++++++++ zone/lua_general.cpp | 6 ++++++ zone/lua_parser.cpp | 9 ++++++--- zone/lua_parser_events.cpp | 15 +++++++++++---- zone/lua_parser_events.h | 10 +++++----- 7 files changed, 76 insertions(+), 14 deletions(-) create mode 100644 zone/lua_encounter.cpp create mode 100644 zone/lua_encounter.h diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 2510dcddb..5573e6f4f 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -20,7 +20,7 @@ SET(zone_sources embparser_api.cpp embperl.cpp embxs.cpp - encounter.cpp + encounter.cpp entity.cpp exp.cpp fearpath.cpp @@ -36,6 +36,7 @@ SET(zone_sources lua_corpse.cpp lua_client.cpp lua_door.cpp + lua_encounter.cpp lua_entity.cpp lua_entity_list.cpp lua_general.cpp @@ -138,7 +139,7 @@ SET(zone_headers embparser.h embperl.h embxs.h - encounter.h + encounter.h entity.h errmsg.h event_codes.h @@ -150,6 +151,7 @@ SET(zone_headers lua_bit.h lua_client.h lua_corpse.h + lua_encounter.h lua_entity.h lua_entity_list.h lua_general.h diff --git a/zone/lua_encounter.cpp b/zone/lua_encounter.cpp new file mode 100644 index 000000000..a01c846fb --- /dev/null +++ b/zone/lua_encounter.cpp @@ -0,0 +1,14 @@ +#ifdef LUA_EQEMU + +#include "lua.hpp" +#include +#include "lua_encounter.h" +#include "encounter.h" + + +luabind::scope lua_register_encounter() { + return luabind::class_("Encounter") + .def(luabind::constructor<>()); +} + +#endif \ No newline at end of file diff --git a/zone/lua_encounter.h b/zone/lua_encounter.h new file mode 100644 index 000000000..d4ce63bb9 --- /dev/null +++ b/zone/lua_encounter.h @@ -0,0 +1,30 @@ +#ifndef EQEMU_LUA_ENCOUNTER_H +#define EQEMU_LUA_ENCOUNTER_H +#ifdef LUA_EQEMU + +#include "lua_ptr.h" + +class Encounter; + +namespace luabind { + struct scope; + class object; +} + +luabind::scope lua_register_encounter(); + +class Lua_Encounter : public Lua_Ptr +{ + typedef Encounter NativeType; +public: + Lua_Encounter() { SetLuaPtrData(nullptr); } + Lua_Encounter(Encounter *d) { SetLuaPtrData(reinterpret_cast(d)); } + virtual ~Lua_Encounter() { } + + operator Encounter*() { + return reinterpret_cast(GetLuaPtrData()); + } + +}; +#endif +#endif \ No newline at end of file diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 139951724..777396daa 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -19,6 +19,7 @@ #include "../common/timer.h" #include "../common/eqemu_logsys.h" #include "encounter.h" +#include "lua_encounter.h" struct Events { }; struct Factions { }; @@ -296,6 +297,10 @@ void lua_set_timer(const char *timer, int time_ms, Lua_Mob mob) { quest_manager.settimerMS(timer, time_ms, mob); } +void lua_set_timer(const char *timer, int time_ms, Lua_Encounter enc) { + quest_manager.settimerMS(timer, time_ms, enc); +} + void lua_stop_timer(const char *timer) { quest_manager.stoptimer(timer); } @@ -1457,6 +1462,7 @@ luabind::scope lua_register_general() { luabind::def("set_timer", (void(*)(const char*, int))&lua_set_timer), luabind::def("set_timer", (void(*)(const char*, int, Lua_ItemInst))&lua_set_timer), luabind::def("set_timer", (void(*)(const char*, int, Lua_Mob))&lua_set_timer), + luabind::def("set_timer", (void(*)(const char*, int, Lua_Encounter))&lua_set_timer), luabind::def("stop_timer", (void(*)(const char*))&lua_stop_timer), luabind::def("stop_timer", (void(*)(const char*, Lua_ItemInst))&lua_stop_timer), luabind::def("stop_timer", (void(*)(const char*, Lua_Mob))&lua_stop_timer), diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index 4b5a1faad..24f25948e 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -34,6 +34,7 @@ #include "questmgr.h" #include "zone.h" #include "lua_parser.h" +#include "lua_encounter.h" const char *LuaEvents[_LargestEventID] = { "event_say", @@ -610,10 +611,11 @@ int LuaParser::_EventEncounter(std::string package_name, QuestEventID evt, std:: lua_pushstring(L, encounter_name.c_str()); lua_setfield(L, -2, "name"); - auto arg_function = EncounterArgumentDispatch[evt]; - arg_function(this, L, data, extra_data, extra_pointers); - Encounter *enc = lua_encounters[encounter_name]; + + auto arg_function = EncounterArgumentDispatch[evt]; + arg_function(this, L, enc, data, extra_data, extra_pointers); + quest_manager.StartQuest(enc, nullptr, nullptr, encounter_name); if(lua_pcall(L, 1, 1, 0)) { std::string error = lua_tostring(L, -1); @@ -977,6 +979,7 @@ void LuaParser::MapFunctions(lua_State *L) { lua_register_client_version(), lua_register_appearance(), lua_register_entity(), + lua_register_encounter(), lua_register_mob(), lua_register_special_abilities(), lua_register_npc(), diff --git a/zone/lua_parser_events.cpp b/zone/lua_parser_events.cpp index 5428304a6..840bcb40d 100644 --- a/zone/lua_parser_events.cpp +++ b/zone/lua_parser_events.cpp @@ -22,6 +22,7 @@ #include "lua_door.h" #include "lua_object.h" #include "lua_packet.h" +#include "lua_encounter.h" #include "zone.h" #include "lua_parser_events.h" @@ -704,14 +705,20 @@ void handle_spell_null(QuestInterface *parse, lua_State* L, NPC* npc, Client* cl std::vector *extra_pointers) { } -void handle_encounter_timer(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, +void handle_encounter_timer(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data, std::vector *extra_pointers) { lua_pushstring(L, data.c_str()); lua_setfield(L, -2, "timer"); } -void handle_encounter_load(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, +void handle_encounter_load(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data, std::vector *extra_pointers) { + if (encounter) { + Lua_Encounter l_enc(encounter); + luabind::adl::object l_enc_o = luabind::adl::object(L, l_enc); + l_enc_o.push(L); + lua_setfield(L, -2, "encounter"); + } if (extra_pointers) { std::string *str = EQEmu::any_cast(extra_pointers->at(0)); lua_pushstring(L, str->c_str()); @@ -719,7 +726,7 @@ void handle_encounter_load(QuestInterface *parse, lua_State* L, std::string data } } -void handle_encounter_unload(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, +void handle_encounter_unload(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data, std::vector *extra_pointers) { if (extra_pointers) { std::string *str = EQEmu::any_cast(extra_pointers->at(0)); @@ -728,7 +735,7 @@ void handle_encounter_unload(QuestInterface *parse, lua_State* L, std::string da } } -void handle_encounter_null(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, +void handle_encounter_null(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data, std::vector *extra_pointers) { } diff --git a/zone/lua_parser_events.h b/zone/lua_parser_events.h index 46609fb06..0a2ab5ad9 100644 --- a/zone/lua_parser_events.h +++ b/zone/lua_parser_events.h @@ -6,7 +6,7 @@ typedef void(*NPCArgumentHandler)(QuestInterface*, lua_State*, NPC*, Mob*, std:: typedef void(*PlayerArgumentHandler)(QuestInterface*, lua_State*, Client*, std::string, uint32, std::vector*); typedef void(*ItemArgumentHandler)(QuestInterface*, lua_State*, Client*, ItemInst*, Mob*, std::string, uint32, std::vector*); typedef void(*SpellArgumentHandler)(QuestInterface*, lua_State*, NPC*, Client*, uint32, uint32, std::vector*); -typedef void(*EncounterArgumentHandler)(QuestInterface*, lua_State*, std::string, uint32, std::vector*); +typedef void(*EncounterArgumentHandler)(QuestInterface*, lua_State*, Encounter* encounter, std::string, uint32, std::vector*); //NPC void handle_npc_event_say(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, @@ -130,13 +130,13 @@ void handle_spell_null(QuestInterface *parse, lua_State* L, NPC* npc, Client* cl //Encounter -void handle_encounter_timer(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, +void handle_encounter_timer(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data, std::vector *extra_pointers); -void handle_encounter_load(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, +void handle_encounter_load(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data, std::vector *extra_pointers); -void handle_encounter_unload(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, +void handle_encounter_unload(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data, std::vector *extra_pointers); -void handle_encounter_null(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, +void handle_encounter_null(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data, std::vector *extra_pointers); #endif From 6cb1861c91836b2a9cf41685e65a84ee4c68662b Mon Sep 17 00:00:00 2001 From: Russell Kinasz Date: Fri, 5 Jun 2015 12:32:58 -0700 Subject: [PATCH 106/129] Update to encounter timers so they can actually work from hooked events --- zone/lua_general.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 777396daa..bf65fea7c 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -313,6 +313,10 @@ void lua_stop_timer(const char *timer, Lua_Mob mob) { quest_manager.stoptimer(timer, mob); } +void lua_stop_timer(const char *timer, Lua_Encounter enc) { + quest_manager.stoptimer(timer, enc); +} + void lua_stop_all_timers() { quest_manager.stopalltimers(); } @@ -325,6 +329,10 @@ void lua_stop_all_timers(Lua_Mob mob) { quest_manager.stopalltimers(mob); } +void lua_stop_all_timers(Lua_Encounter enc) { + quest_manager.stopalltimers(enc); +} + void lua_depop() { quest_manager.depop(0); } @@ -1466,9 +1474,11 @@ luabind::scope lua_register_general() { luabind::def("stop_timer", (void(*)(const char*))&lua_stop_timer), luabind::def("stop_timer", (void(*)(const char*, Lua_ItemInst))&lua_stop_timer), luabind::def("stop_timer", (void(*)(const char*, Lua_Mob))&lua_stop_timer), + luabind::def("stop_timer", (void(*)(const char*, Lua_Encounter))&lua_stop_timer), luabind::def("stop_all_timers", (void(*)(void))&lua_stop_all_timers), luabind::def("stop_all_timers", (void(*)(Lua_ItemInst))&lua_stop_all_timers), luabind::def("stop_all_timers", (void(*)(Lua_Mob))&lua_stop_all_timers), + luabind::def("stop_all_timers", (void(*)(Lua_Encounter))&lua_stop_all_timers), luabind::def("depop", (void(*)(void))&lua_depop), luabind::def("depop", (void(*)(int))&lua_depop), luabind::def("depop_with_timer", (void(*)(void))&lua_depop_with_timer), From b45f0f9dbc4205ca567a37daccba83baaa4fba9c Mon Sep 17 00:00:00 2001 From: Russell Kinasz Date: Fri, 5 Jun 2015 12:57:53 -0700 Subject: [PATCH 107/129] Lua_Encounter doesn't need to expose constructor --- zone/lua_encounter.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/zone/lua_encounter.cpp b/zone/lua_encounter.cpp index a01c846fb..08a4f95d3 100644 --- a/zone/lua_encounter.cpp +++ b/zone/lua_encounter.cpp @@ -7,8 +7,7 @@ luabind::scope lua_register_encounter() { - return luabind::class_("Encounter") - .def(luabind::constructor<>()); + return luabind::class_("Encounter"); } #endif \ No newline at end of file From a9b98ed0577a7916d09435ac7939a85eec798ec6 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 5 Jun 2015 19:07:01 -0400 Subject: [PATCH 108/129] Add 64-bit ntoh/hton functions for Linux BSD macros aren't tested, but should work. These should already be defined on Windows. --- common/types.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/common/types.h b/common/types.h index 9064b67e0..2e1b5d3f3 100644 --- a/common/types.h +++ b/common/types.h @@ -83,4 +83,20 @@ typedef const char Const_char; //for perl XS #define DLLFUNC extern "C" #endif +// htonll and ntohll already defined on windows +#ifndef WIN32 +# if defined(__linux__) +# include +# elif defined(__FreeBSD__) || defined(__NetBSD__) +# include +# elif defined (__OpenBSD__) +# include +# define be16toh(x) betoh16(x) +# define be32toh(x) betoh32(x) +# define be64toh(x) betoh64(x) +# endif +# define htonll(x) htobe64(x) +# define ntohll(x) be64toh(x) +#endif + #endif From 03bc245318ad9b5ed9d1c8c60463320dc7c13abf Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 5 Jun 2015 21:30:35 -0400 Subject: [PATCH 109/129] Fix fleeing when zones have map files --- zone/pathing.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/zone/pathing.cpp b/zone/pathing.cpp index 8224f3692..32a55d73a 100644 --- a/zone/pathing.cpp +++ b/zone/pathing.cpp @@ -16,7 +16,7 @@ #define snprintf _snprintf #endif -//#define PATHDEBUG +//#define PATHDEBUG extern Zone *zone; @@ -205,10 +205,10 @@ glm::vec3 PathManager::GetPathNodeCoordinates(int NodeNumber, bool BestZ) } std::deque PathManager::FindRoute(int startID, int endID) -{ +{ Log.Out(Logs::Detail, Logs::None, "FindRoute from node %i to %i", startID, endID); - memset(ClosedListFlag, 0, sizeof(int) * Head.PathNodeCount); + memset(ClosedListFlag, 0, sizeof(int) * Head.PathNodeCount); std::deque OpenList, ClosedList; @@ -665,6 +665,9 @@ glm::vec3 Mob::UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &Wa bool SameDestination = (To == PathingDestination); + if (Speed <= 0) // our speed is 0, we cant move so lets return the dest + return To; // this will also avoid the teleports cleanly + int NextNode; if(To == From) From 42a5ddcf77ecf81560b2775c906516328951c2f7 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 6 Jun 2015 17:46:53 -0400 Subject: [PATCH 110/129] Cut down on some HP update spam This increases the timer that mobs will send out updates (It could probably be increased more) This will also reset the timer every time SendHPUpdate is called to prevent sending like 3+ completely useless updates at once Also skip sending the update to the client if we're sending an OP_Damage with the damage since the client will apply this number --- zone/attack.cpp | 2 +- zone/bot.cpp | 22 +++++++++++----------- zone/client.cpp | 4 ++-- zone/client.h | 2 ++ zone/client_process.cpp | 4 +++- zone/mob.cpp | 20 ++++++++++---------- zone/mob.h | 3 ++- zone/npc.cpp | 5 +++-- zone/npc.h | 1 + 9 files changed, 35 insertions(+), 28 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 666cb19f8..a229f197e 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3693,7 +3693,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons //send an HP update if we are hurt if(GetHP() < GetMaxHP()) - SendHPUpdate(); + SendHPUpdate(!iBuffTic); // the OP_Damage actually updates the client in these cases, so we skill them } //end `if damage was done` //send damage packet... diff --git a/zone/bot.cpp b/zone/bot.cpp index e76389b24..8b3741443 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -755,7 +755,7 @@ void Bot::GenerateBaseStats() { this->Corrup = CorruptionResist; SetBotSpellID(BotSpellID); this->size = BotSize; - + this->pAggroRange = 0; this->pAssistRange = 0; this->raid_target = false; @@ -1381,18 +1381,18 @@ int32 Bot::GenerateBaseHitPoints() uint32 lm = GetClassLevelFactor(); int32 Post255; int32 NormalSTA = GetSTA(); - + if(GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= ClientVersion::SoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) { float SoDPost255; - + if(((NormalSTA - 255) / 2) > 0) SoDPost255 = ((NormalSTA - 255) / 2); else SoDPost255 = 0; - + int hp_factor = GetClassHPFactor(); - + if(level < 41) { new_base_hp = (5 + (GetLevel() * hp_factor / 12) + ((NormalSTA - SoDPost255) * GetLevel() * hp_factor / 3600)); @@ -1420,7 +1420,7 @@ int32 Bot::GenerateBaseHitPoints() new_base_hp = (5)+(GetLevel()*lm/10) + (((NormalSTA-Post255)*GetLevel()*lm/3000)) + ((Post255*1)*lm/6000); } this->base_hp = new_base_hp; - + return new_base_hp; } @@ -2342,7 +2342,7 @@ bool Bot::IsBotNameAvailable(char *botName, std::string* errorMessage) { if (results.RowCount()) { //Name already in use! return false; } - + return true; //We made it with a valid name! } @@ -2901,7 +2901,7 @@ bool Bot::Process() SetEndurance(GetEndurance() + CalcEnduranceRegen() + RestRegenEndurance); } - if (sendhpupdate_timer.Check()) { + if (sendhpupdate_timer.Check(false)) { SendHPUpdate(); if(HasPet()) @@ -5894,7 +5894,7 @@ bool Bot::Death(Mob *killerMob, int32 damage, uint16 spell_id, SkillUseTypes att // delete from group data RemoveBotFromGroup(this, g); - + //Make sure group still exists if it doesnt they were already updated in RemoveBotFromGroup g = GetGroup(); if (!g) @@ -5912,7 +5912,7 @@ bool Bot::Death(Mob *killerMob, int32 damage, uint16 spell_id, SkillUseTypes att g->members[j] = nullptr; } } - + // update the client group EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct)); GroupJoin_Struct* gu = (GroupJoin_Struct*)outapp->pBuffer; @@ -10823,7 +10823,7 @@ void Bot::CalcItemBonuses(StatBonuses* newbon) AddItemBonuses(item, newbon); } } - + // Caps if(newbon->HPRegen > CalcHPRegenCap()) newbon->HPRegen = CalcHPRegenCap(); diff --git a/zone/client.cpp b/zone/client.cpp index 7471a6a62..bdcbbbf68 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -116,7 +116,7 @@ Client::Client(EQStreamInterface* ieqs) ), //these must be listed in the order they appear in client.h position_timer(250), - hpupdate_timer(1800), + hpupdate_timer(2000), camp_timer(29000), process_timer(100), stamina_timer(40000), @@ -8631,4 +8631,4 @@ void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold, QueuePacket(outapp, false, Client::CLIENT_CONNECTED); safe_delete(outapp); -} \ No newline at end of file +} diff --git a/zone/client.h b/zone/client.h index 73bde1a69..2015a52ba 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1257,6 +1257,8 @@ public: int32 GetMeleeDamage(Mob* other, bool GetMinDamage = false); void QuestReward(Mob* target, uint32 copper = 0, uint32 silver = 0, uint32 gold = 0, uint32 platinum = 0, uint32 itemid = 0, uint32 exp = 0, bool faction = false); + + void ResetHPUpdateTimer() { hpupdate_timer.Start(); } protected: friend class Mob; void CalcItemBonuses(StatBonuses* newbon); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 42681340b..be2e1321f 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -129,7 +129,9 @@ bool Client::Process() { if(IsTracking() && (GetClientVersion() >= ClientVersion::SoD) && TrackingTimer.Check()) DoTracking(); - if(hpupdate_timer.Check()) + // SendHPUpdate calls hpupdate_timer.Start so it can delay this timer, so lets not reset with the check + // since the function will anyways + if(hpupdate_timer.Check(false)) SendHPUpdate(); if(mana_timer.Check()) diff --git a/zone/mob.cpp b/zone/mob.cpp index 8a224db0b..1591cc057 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1266,7 +1266,7 @@ void Mob::CreateHPPacket(EQApplicationPacket* app) } // sends hp update of this mob to people who might care -void Mob::SendHPUpdate() +void Mob::SendHPUpdate(bool skip_self) { EQApplicationPacket hp_app; Group *group; @@ -1355,8 +1355,7 @@ void Mob::SendHPUpdate() } // send to self - we need the actual hps here - if(IsClient()) - { + if(IsClient() && !skip_self) { EQApplicationPacket* hp_app2 = new EQApplicationPacket(OP_HPUpdate,sizeof(SpawnHPUpdate_Struct)); SpawnHPUpdate_Struct* ds = (SpawnHPUpdate_Struct*)hp_app2->pBuffer; ds->cur_hp = CastToClient()->GetHP() - itembonuses.HP; @@ -1365,6 +1364,7 @@ void Mob::SendHPUpdate() CastToClient()->QueuePacket(hp_app2); safe_delete(hp_app2); } + ResetHPUpdateTimer(); // delay the timer } // this one just warps the mob to the current location @@ -4205,25 +4205,25 @@ int32 Mob::GetItemStat(uint32 itemid, const char *identifier) std::string Mob::GetGlobal(const char *varname) { int qgCharid = 0; int qgNpcid = 0; - + if (this->IsNPC()) qgNpcid = this->GetNPCTypeID(); - + if (this->IsClient()) qgCharid = this->CastToClient()->CharacterID(); - + QGlobalCache *qglobals = nullptr; std::list globalMap; - + if (this->IsClient()) qglobals = this->CastToClient()->GetQGlobals(); - + if (this->IsNPC()) qglobals = this->CastToNPC()->GetQGlobals(); if(qglobals) QGlobalCache::Combine(globalMap, qglobals->GetBucket(), qgNpcid, qgCharid, zone->GetZoneID()); - + std::list::iterator iter = globalMap.begin(); while(iter != globalMap.end()) { if ((*iter).name.compare(varname) == 0) @@ -4231,7 +4231,7 @@ std::string Mob::GetGlobal(const char *varname) { ++iter; } - + return "Undefined"; } diff --git a/zone/mob.h b/zone/mob.h index 74a53346e..ded9c04dc 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -508,7 +508,8 @@ public: static void CreateSpawnPacket(EQApplicationPacket* app, NewSpawn_Struct* ns); virtual void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho); void CreateHPPacket(EQApplicationPacket* app); - void SendHPUpdate(); + void SendHPUpdate(bool skip_self = false); + virtual void ResetHPUpdateTimer() {}; // does nothing //Util static uint32 RandomTimer(int min, int max); diff --git a/zone/npc.cpp b/zone/npc.cpp index 0ece7ab6c..d4a33b6d0 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -115,7 +115,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, const glm::vec4& position, int if knightattack_timer(1000), assist_timer(AIassistcheck_delay), qglobal_purge_timer(30000), - sendhpupdate_timer(1000), + sendhpupdate_timer(2000), enraged_timer(1000), taunt_timer(TauntReuseTime * 1000), m_SpawnPoint(position), @@ -650,7 +650,8 @@ bool NPC::Process() } } - if (sendhpupdate_timer.Check() && (IsTargeted() || (IsPet() && GetOwner() && GetOwner()->IsClient()))) { + // we might actually want to reset in this check ... won't until issues arise at least :P + if (sendhpupdate_timer.Check(false) && (IsTargeted() || (IsPet() && GetOwner() && GetOwner()->IsClient()))) { if(!IsFullHP || cur_hp Date: Sun, 7 Jun 2015 16:53:38 -0700 Subject: [PATCH 111/129] Full group was being excluded from group exp bonus --- zone/exp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/exp.cpp b/zone/exp.cpp index ab8506aca..826b12e67 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -705,7 +705,7 @@ void Group::SplitExp(uint32 exp, Mob* other) { groupmod = 2.16; else groupmod = 1.0; - if(membercount > 1 && membercount < 6) + if(membercount > 1 && membercount <= 6) groupexp += (uint32)((float)exp * groupmod * (RuleR(Character, GroupExpMultiplier))); int conlevel = Mob::GetLevelCon(maxlevel, other->GetLevel()); From c2e436521411c136330845b50383a99eb4aaf1df Mon Sep 17 00:00:00 2001 From: Uleat Date: Sun, 7 Jun 2015 22:07:40 -0400 Subject: [PATCH 112/129] Implemented rule-based disenchanted bag use --- changelog.txt | 3 + common/ruletypes.h | 1 + .../2014_03_17_EnforceAugmentRules.sql | 2 +- .../2015_06_07_TransformSummonedBagsRule.sql | 1 + zone/client.h | 1 + zone/client_process.cpp | 6 +- zone/inventory.cpp | 181 ++++++++++++++++++ 7 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 utils/sql/git/optional/2015_06_07_TransformSummonedBagsRule.sql diff --git a/changelog.txt b/changelog.txt index cdb2fd5f8..bc96a8ef1 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 06/07/2015 == +Uleat: Implemented optional rule for using disenchanted bags. Action triggers at the same point that temporary items are removed. +Optional SQL: utils/sql/git/optional/2015_06_07_TransformSummonedBagsRule.sql == 05/25/2015 == Akkadius: Implemented disjointed zone based time, this can be triggered via quest methods diff --git a/common/ruletypes.h b/common/ruletypes.h index cf77dddf6..d0f198164 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -611,6 +611,7 @@ RULE_BOOL ( Inventory, EnforceAugmentUsability, true) // Forces augmented item u RULE_BOOL ( Inventory, EnforceAugmentWear, true) // Forces augment wear slot validation RULE_BOOL ( Inventory, DeleteTransformationMold, true) //False if you want mold to last forever RULE_BOOL ( Inventory, AllowAnyWeaponTransformation, false) //Weapons can use any weapon transformation +RULE_BOOL ( Inventory, TransformSummonedBags, false) //Transforms summoned bags into disenchanted ones instead of deleting RULE_CATEGORY_END() RULE_CATEGORY( Client ) diff --git a/utils/sql/git/optional/2014_03_17_EnforceAugmentRules.sql b/utils/sql/git/optional/2014_03_17_EnforceAugmentRules.sql index e89d4a437..25ec3734d 100644 --- a/utils/sql/git/optional/2014_03_17_EnforceAugmentRules.sql +++ b/utils/sql/git/optional/2014_03_17_EnforceAugmentRules.sql @@ -1,3 +1,3 @@ INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Inventory:EnforceAugmentRestriction', 'false', 'Forces augment slot restrictions.'); INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Inventory:EnforceAugmentUsability', 'false', 'Forces augmented item usability.'); -INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Inventory:EnforceAugmentWear', 'false', 'Forces augment wear slot validation.'); \ No newline at end of file +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Inventory:EnforceAugmentWear', 'false', 'Forces augment wear slot validation.'); diff --git a/utils/sql/git/optional/2015_06_07_TransformSummonedBagsRule.sql b/utils/sql/git/optional/2015_06_07_TransformSummonedBagsRule.sql new file mode 100644 index 000000000..d0af46a24 --- /dev/null +++ b/utils/sql/git/optional/2015_06_07_TransformSummonedBagsRule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Inventory:TransformSummonedBags', 'true', 'Transforms summoned bags into disenchanted ones instead of deleting.'); diff --git a/zone/client.h b/zone/client.h index 2015a52ba..de093990c 100644 --- a/zone/client.h +++ b/zone/client.h @@ -906,6 +906,7 @@ public: bool DecreaseByID(uint32 type, uint8 amt); uint8 SlotConvert2(uint8 slot); //Maybe not needed. void Escape(); //AA Escape + void DisenchantSummonedBags(bool client_update = true); void RemoveNoRent(bool client_update = true); void RemoveDuplicateLore(bool client_update = true); void MoveSlotNotAllowed(bool client_update = true); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index be2e1321f..4a16e7698 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -838,7 +838,11 @@ void Client::BulkSendInventoryItems() { } bool deletenorent = database.NoRentExpired(GetName()); - if(deletenorent){ RemoveNoRent(false); } //client was offline for more than 30 minutes, delete no rent items + if (deletenorent) { //client was offline for more than 30 minutes, delete no rent items + if (RuleB(Inventory, TransformSummonedBags)) + DisenchantSummonedBags(false); + RemoveNoRent(false); + } RemoveDuplicateLore(false); MoveSlotNotAllowed(false); diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 2ce30fe5e..e1904d59f 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -2158,6 +2158,187 @@ bool Client::DecreaseByID(uint32 type, uint8 amt) { return true; } +static bool IsSummonedBagID(uint32 item_id) +{ + switch (item_id) { + case 17147: // "Spiritual Prismatic Pack" + case 17303: // "Spirit Pouch" + case 17304: // "Dimensional Pocket" + case 17305: // "Dimensional Hole" + case 17306: // "Glowing Backpack" + case 17307: // "Quiver of Marr" + case 17308: // "Bandoleer of Luclin" + case 17309: // "Pouch of Quellious" + case 17310: // "Phantom Satchel" + case 17510: // "Glowing Chest" + case 17900: // "Grandmaster's Satchel" + case 57260: // "Glowing Backpack" + case 57261: // "Pouch of Quellious" + case 57262: // "Phantom Satchel" + case 60224: // "Faded-Glyph Tablet" + case 95199: // "Beginner Artisan Satchel" + case 95200: // "Apprentice Artisan Satchel" + case 95201: // "Freshman Artisan Satchel" + case 95202: // "Journeyman Artisan Satchel" + case 95203: // "Expert Artisan Satchel" + case 95204: // "Master Artisan Satchel" + //case 96960: // "Artisan Satchel" - no 12-slot disenchanted bags + return true; + default: + return false; + } +} + +static uint32 GetDisenchantedBagID(uint8 bag_slots) +{ + switch (bag_slots) { + case 4: + return 77772; // "Small Disenchanted Backpack" + case 6: + return 77774; // "Disenchanted Backpack" + case 8: + return 77776; // "Large Disenchanted Backpack" + case 10: + return 77778; // "Huge Disenchanted Backpack" + default: + return 0; // no suitable conversions + } +} + +static bool CopyBagContents(ItemInst* new_bag, const ItemInst* old_bag) +{ + if (!new_bag || !old_bag) { return false; } + if (new_bag->GetItem()->BagSlots < old_bag->GetItem()->BagSlots) { return false; } + + // pre-check for size comparisons + for (auto bag_slot = 0; bag_slot < old_bag->GetItem()->BagSlots; ++bag_slot) { + if (!old_bag->GetItem(bag_slot)) { continue; } + if (old_bag->GetItem(bag_slot)->GetItem()->Size > new_bag->GetItem()->BagSize) { + Log.Out(Logs::General, Logs::Inventory, "Copy Bag Contents: Failure due to %s is larger than size capacity of %s (%i > %i)", + old_bag->GetItem(bag_slot)->GetItem()->Name, new_bag->GetItem()->Name, old_bag->GetItem(bag_slot)->GetItem()->Size, new_bag->GetItem()->BagSize); + return false; + } + } + + for (auto bag_slot = 0; bag_slot < old_bag->GetItem()->BagSlots; ++bag_slot) { + if (!old_bag->GetItem(bag_slot)) { continue; } + new_bag->PutItem(bag_slot, *(old_bag->GetItem(bag_slot))); + } + + return true; +} + +void Client::DisenchantSummonedBags(bool client_update) +{ + for (auto slot_id = EmuConstants::GENERAL_BEGIN; slot_id <= EmuConstants::GENERAL_END; ++slot_id) { + auto inst = m_inv[slot_id]; + if (!inst) { continue; } + if (!IsSummonedBagID(inst->GetItem()->ID)) { continue; } + if (inst->GetItem()->ItemClass != ItemClassContainer) { continue; } + if (inst->GetTotalItemCount() == 1) { continue; } + + auto new_id = GetDisenchantedBagID(inst->GetItem()->BagSlots); + if (!new_id) { continue; } + auto new_item = database.GetItem(new_id); + if (!new_item) { continue; } + auto new_inst = database.CreateBaseItem(new_item); + if (!new_inst) { continue; } + + if (CopyBagContents(new_inst, inst)) { + Log.Out(Logs::General, Logs::Inventory, "Disenchant Summoned Bags: Replacing %s with %s in slot %i", inst->GetItem()->Name, new_inst->GetItem()->Name, slot_id); + PutItemInInventory(slot_id, *new_inst, client_update); + } + safe_delete(new_inst); + } + + for (auto slot_id = EmuConstants::BANK_BEGIN; slot_id <= EmuConstants::BANK_END; ++slot_id) { + auto inst = m_inv[slot_id]; + if (!inst) { continue; } + if (!IsSummonedBagID(inst->GetItem()->ID)) { continue; } + if (inst->GetItem()->ItemClass != ItemClassContainer) { continue; } + if (inst->GetTotalItemCount() == 1) { continue; } + + auto new_id = GetDisenchantedBagID(inst->GetItem()->BagSlots); + if (!new_id) { continue; } + auto new_item = database.GetItem(new_id); + if (!new_item) { continue; } + auto new_inst = database.CreateBaseItem(new_item); + if (!new_inst) { continue; } + + if (CopyBagContents(new_inst, inst)) { + Log.Out(Logs::General, Logs::Inventory, "Disenchant Summoned Bags: Replacing %s with %s in slot %i", inst->GetItem()->Name, new_inst->GetItem()->Name, slot_id); + PutItemInInventory(slot_id, *new_inst, client_update); + } + safe_delete(new_inst); + } + + for (auto slot_id = EmuConstants::SHARED_BANK_BEGIN; slot_id <= EmuConstants::SHARED_BANK_END; ++slot_id) { + auto inst = m_inv[slot_id]; + if (!inst) { continue; } + if (!IsSummonedBagID(inst->GetItem()->ID)) { continue; } + if (inst->GetItem()->ItemClass != ItemClassContainer) { continue; } + if (inst->GetTotalItemCount() == 1) { continue; } + + auto new_id = GetDisenchantedBagID(inst->GetItem()->BagSlots); + if (!new_id) { continue; } + auto new_item = database.GetItem(new_id); + if (!new_item) { continue; } + auto new_inst = database.CreateBaseItem(new_item); + if (!new_inst) { continue; } + + if (CopyBagContents(new_inst, inst)) { + Log.Out(Logs::General, Logs::Inventory, "Disenchant Summoned Bags: Replacing %s with %s in slot %i", inst->GetItem()->Name, new_inst->GetItem()->Name, slot_id); + PutItemInInventory(slot_id, *new_inst, client_update); + } + safe_delete(new_inst); + } + + while (!m_inv.CursorEmpty()) { + auto inst = m_inv[MainCursor]; + if (!inst) { break; } + if (!IsSummonedBagID(inst->GetItem()->ID)) { break; } + if (inst->GetItem()->ItemClass != ItemClassContainer) { break; } + if (inst->GetTotalItemCount() == 1) { break; } + + auto new_id = GetDisenchantedBagID(inst->GetItem()->BagSlots); + if (!new_id) { break; } + auto new_item = database.GetItem(new_id); + if (!new_item) { break; } + auto new_inst = database.CreateBaseItem(new_item); + if (!new_inst) { break; } + + if (CopyBagContents(new_inst, inst)) { + Log.Out(Logs::General, Logs::Inventory, "Disenchant Summoned Bags: Replacing %s with %s in slot %i", inst->GetItem()->Name, new_inst->GetItem()->Name, MainCursor); + std::list local; + local.push_front(new_inst); + m_inv.PopItem(MainCursor); + safe_delete(inst); + + while (!m_inv.CursorEmpty()) { + auto limbo_inst = m_inv.PopItem(MainCursor); + if (limbo_inst == nullptr) { continue; } + local.push_back(limbo_inst); + } + + for (auto iter = local.begin(); iter != local.end(); ++iter) { + auto cur_inst = *iter; + if (cur_inst == nullptr) { continue; } + m_inv.PushCursor(*cur_inst); + safe_delete(cur_inst); + } + local.clear(); + + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); + database.SaveCursor(this->CharacterID(), s, e); + } + else { + safe_delete(new_inst); // deletes disenchanted bag if not used + } + + break; + } +} + void Client::RemoveNoRent(bool client_update) { for (auto slot_id = EmuConstants::EQUIPMENT_BEGIN; slot_id <= EmuConstants::EQUIPMENT_END; ++slot_id) { From f198ab714f6a5f0093455ec320643e4ea7b094c0 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 7 Jun 2015 22:31:21 -0400 Subject: [PATCH 113/129] Make inspect buffs LAA optional for target buffs --- common/ruletypes.h | 1 + .../optional/2015_06_07_SpellsTargetBuffsRule.sql | 1 + zone/client_packet.cpp | 2 +- zone/entity.cpp | 12 +++++++----- 4 files changed, 10 insertions(+), 6 deletions(-) create mode 100644 utils/sql/git/optional/2015_06_07_SpellsTargetBuffsRule.sql diff --git a/common/ruletypes.h b/common/ruletypes.h index d0f198164..9a36b77f4 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -345,6 +345,7 @@ RULE_BOOL ( Spells, SwarmPetTargetLock, false) // Use old method of swarm pets t RULE_BOOL ( Spells, NPC_UseFocusFromSpells, true) // Allow npcs to use most spell derived focus effects. RULE_BOOL ( Spells, NPC_UseFocusFromItems, false) // Allow npcs to use most item derived focus effects. RULE_BOOL ( Spells, UseAdditiveFocusFromWornSlot, false) // Allows an additive focus effect to be calculated from worn slot. +RULE_BOOL ( Spells, AlwaysSendTargetsBuffs, false) // ignore LAA level if true RULE_CATEGORY_END() RULE_CATEGORY( Combat ) diff --git a/utils/sql/git/optional/2015_06_07_SpellsTargetBuffsRule.sql b/utils/sql/git/optional/2015_06_07_SpellsTargetBuffsRule.sql new file mode 100644 index 000000000..1dcc9d922 --- /dev/null +++ b/utils/sql/git/optional/2015_06_07_SpellsTargetBuffsRule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AlwaysSendTargetBuffs', 'false', 'Allows the server to send the targets buffs ignoring the LAA.'); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 8a7b8a133..e4ce874fc 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -12897,7 +12897,7 @@ void Client::Handle_OP_TargetCommand(const EQApplicationPacket *app) inspect_buffs = group->GetLeadershipAA(groupAAInspectBuffs); } } - if (nt == this || inspect_buffs || (nt->IsClient() && !nt->CastToClient()->GetPVP()) || + if (GetGM() || RuleB(Spells, AlwaysSendTargetsBuffs) || nt == this || inspect_buffs || (nt->IsClient() && !nt->CastToClient()->GetPVP()) || (nt->IsPet() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP()) || (nt->IsMerc() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP())) nt->SendBuffsToClient(this); diff --git a/zone/entity.cpp b/zone/entity.cpp index baa4f4354..c7493b907 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -79,7 +79,7 @@ Client *Entity::CastToClient() } #ifdef _EQDEBUG if (!IsClient()) { - Log.Out(Logs::General, Logs::Error, "CastToClient error (not client)"); + Log.Out(Logs::General, Logs::Error, "CastToClient error (not client)"); return 0; } #endif @@ -268,7 +268,7 @@ const Beacon* Entity::CastToBeacon() const return static_cast(this); } -const Encounter* Entity::CastToEncounter() const +const Encounter* Entity::CastToEncounter() const { return static_cast(this); } @@ -565,7 +565,7 @@ void EntityList::AddGroup(Group *group) uint32 gid = worldserver.NextGroupID(); if (gid == 0) { - Log.Out(Logs::General, Logs::Error, + Log.Out(Logs::General, Logs::Error, "Unable to get new group ID from world server. group is going to be broken."); return; } @@ -594,7 +594,7 @@ void EntityList::AddRaid(Raid *raid) uint32 gid = worldserver.NextGroupID(); if (gid == 0) { - Log.Out(Logs::General, Logs::Error, + Log.Out(Logs::General, Logs::Error, "Unable to get new group ID from world server. group is going to be broken."); return; } @@ -1428,7 +1428,9 @@ void EntityList::QueueClientsByTarget(Mob *sender, const EQApplicationPacket *ap if (c != sender) { if (Target == sender) { if (inspect_buffs) { // if inspect_buffs is true we're sending a mob's buffs to those with the LAA - if (c->IsRaidGrouped()) { + if (c->GetGM() || RuleB(Spells, AlwaysSendTargetsBuffs)) { + Send = true; + } else if (c->IsRaidGrouped()) { Raid *raid = c->GetRaid(); if (!raid) continue; From 4bb2bb14389d7218c91d2d3855793cfa992b9779 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 7 Jun 2015 23:41:54 -0400 Subject: [PATCH 114/129] AA packet work mostly and small fix to expendable AAs --- common/eq_packet_structs.h | 13 +++--------- common/patches/rof.cpp | 13 ++++++------ common/patches/rof2.cpp | 13 ++++++------ common/patches/rof2_structs.h | 33 ++++++++++++------------------ common/patches/rof_structs.h | 34 ++++++++++++------------------- common/patches/sod.cpp | 2 ++ common/patches/sod_structs.h | 22 ++++++++++---------- common/patches/sof.cpp | 2 ++ common/patches/sof_structs.h | 20 ++++++++---------- common/patches/titanium.cpp | 4 ++-- common/patches/titanium_structs.h | 9 ++------ common/patches/uf.cpp | 8 +++++--- common/patches/uf_structs.h | 21 ++++++++++--------- common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + zone/aa.cpp | 17 +++++++++++----- zone/client.cpp | 8 ++++---- zone/client_packet.cpp | 9 ++++---- zone/zonedb.cpp | 8 ++++---- zone/zonedb.h | 2 +- 20 files changed, 115 insertions(+), 126 deletions(-) diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index e5ab88e57..8e9f97138 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -726,6 +726,7 @@ struct AA_Array { uint32 AA; uint32 value; + uint32 charges; }; @@ -4276,14 +4277,6 @@ struct AA_Action { /*12*/ uint32 exp_value; }; - -struct AA_Skills { //this should be removed and changed to AA_Array -/*00*/ uint32 aa_skill; // Total AAs Spent -/*04*/ uint32 aa_value; -/*08*/ uint32 unknown08; -/*12*/ -}; - struct AAExpUpdate_Struct { /*00*/ uint32 unknown00; //seems to be a value from AA_Action.ability /*04*/ uint32 aapoints_unspent; @@ -4301,12 +4294,12 @@ struct AltAdvStats_Struct { }; struct PlayerAA_Struct { // Is this still used? - AA_Skills aa_list[MAX_PP_AA_ARRAY]; + AA_Array aa_list[MAX_PP_AA_ARRAY]; }; struct AATable_Struct { /*00*/ int32 aa_spent; // Total AAs Spent -/*04*/ AA_Skills aa_list[MAX_PP_AA_ARRAY]; +/*04*/ AA_Array aa_list[MAX_PP_AA_ARRAY]; }; struct Weather_Struct { diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index fae650eb5..f4f01e300 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -2120,7 +2120,7 @@ namespace RoF { outapp->WriteUInt32(emu->aa_array[r].AA); outapp->WriteUInt32(emu->aa_array[r].value); - outapp->WriteUInt32(0); + outapp->WriteUInt32(emu->aa_array[r].charges); } // Fill the other 60 AAs with zeroes @@ -2818,9 +2818,9 @@ namespace RoF for (uint32 i = 0; i < MAX_PP_AA_ARRAY; ++i) { - eq->aa_list[i].aa_skill = emu->aa_list[i].aa_skill; - eq->aa_list[i].aa_value = emu->aa_list[i].aa_value; - eq->aa_list[i].unknown08 = emu->aa_list[i].unknown08; + eq->aa_list[i].AA = emu->aa_list[i].AA; + eq->aa_list[i].value = emu->aa_list[i].value; + eq->aa_list[i].charges = emu->aa_list[i].charges; } FINISH_ENCODE(); @@ -2868,9 +2868,9 @@ namespace RoF OUT(cost); OUT(seq); OUT(current_level); - eq->unknown037 = 1; // Introduced during HoT + eq->prereq_skill_count = 1; // min 1 OUT(prereq_skill); - eq->unknown045 = 1; // New Mar 21 2012 - Seen 1 + eq->prereq_minpoints_count = 1; // min 1 OUT(prereq_minpoints); eq->type = emu->sof_type; OUT(spellid); @@ -2886,6 +2886,7 @@ namespace RoF OUT(cost2); eq->aa_expansion = emu->aa_expansion; eq->special_category = emu->special_category; + eq->expendable_charges = emu->special_category == 7 ? 1 : 0; // temp hack, this can actually be any number OUT(total_abilities); unsigned int r; for (r = 0; r < emu->total_abilities; r++) { diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 42e97b9eb..096467edf 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -2200,7 +2200,7 @@ namespace RoF2 { outapp->WriteUInt32(emu->aa_array[r].AA); outapp->WriteUInt32(emu->aa_array[r].value); - outapp->WriteUInt32(0); + outapp->WriteUInt32(emu->aa_array[r].charges); } // Fill the other 60 AAs with zeroes @@ -2907,9 +2907,9 @@ namespace RoF2 for (uint32 i = 0; i < MAX_PP_AA_ARRAY; ++i) { - eq->aa_list[i].aa_skill = emu->aa_list[i].aa_skill; - eq->aa_list[i].aa_value = emu->aa_list[i].aa_value; - eq->aa_list[i].unknown08 = emu->aa_list[i].unknown08; + eq->aa_list[i].AA = emu->aa_list[i].AA; + eq->aa_list[i].value = emu->aa_list[i].value; + eq->aa_list[i].charges = emu->aa_list[i].charges; } FINISH_ENCODE(); @@ -2957,9 +2957,9 @@ namespace RoF2 OUT(cost); OUT(seq); OUT(current_level); - eq->unknown037 = 1; // Introduced during HoT + eq->prereq_skill_count = 1; // min 1 OUT(prereq_skill); - eq->unknown045 = 1; // New Mar 21 2012 - Seen 1 + eq->prereq_minpoints_count = 1; // min 1 OUT(prereq_minpoints); eq->type = emu->sof_type; OUT(spellid); @@ -2976,6 +2976,7 @@ namespace RoF2 eq->aa_expansion = emu->aa_expansion; eq->special_category = emu->special_category; OUT(total_abilities); + eq->expendable_charges = emu->special_category == 7 ? 1 : 0; // temp hack, this can actually be any number unsigned int r; for (r = 0; r < emu->total_abilities; r++) { OUT(abilities[r].skill_id); diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index a572a3834..812e9c464 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -877,7 +877,7 @@ struct AA_Array { uint32 AA; uint32 value; - uint32 unknown08; // Looks like AA_Array is now 12 bytes in Live + uint32 charges; // expendable charges }; struct Disciplines_Struct { @@ -4253,9 +4253,9 @@ struct SendAA_Struct { /*0025*/ uint32 cost; /*0029*/ uint32 seq; /*0033*/ uint32 current_level; //1s, MQ2 calls this AARankRequired -/*0037*/ uint32 unknown037; // Introduced during HoT +/*0037*/ uint32 prereq_skill_count; // mutliple prereqs at least 1, even no prereqs /*0041*/ uint32 prereq_skill; //is < 0, abs() is category # -/*0045*/ uint32 unknown045; // New Mar 21 2012 - Seen 1 +/*0045*/ uint32 prereq_minpoints_count; // mutliple prereqs at least 1, even no prereqs /*0049*/ uint32 prereq_minpoints; //min points in the prereq /*0053*/ uint32 type; /*0057*/ uint32 spellid; @@ -4268,10 +4268,16 @@ struct SendAA_Struct { /*0081*/ uint32 last_id; /*0085*/ uint32 next_id; /*0089*/ uint32 cost2; -/*0093*/ uint8 unknown80[7]; +/*0093*/ uint8 unknown93; +/*0094*/ uint8 grant_only; // VetAAs, progression, etc +/*0095*/ uint8 unknown95; // 1 for skill cap increase AAs, Mystical Attuning, and RNG attack inc, doesn't seem to matter though +/*0096*/ uint32 expendable_charges; // max charges of the AA /*0100*/ uint32 aa_expansion; /*0104*/ uint32 special_category; -/*0108*/ uint32 unknown0096; +/*0108*/ uint8 shroud; +/*0109*/ uint8 unknown109; +/*0110*/ uint8 layonhands; // 1 for lay on hands -- doesn't seem to matter? +/*0111*/ uint8 unknown111; /*0112*/ uint32 total_abilities; /*0116*/ AA_Ability abilities[0]; }; @@ -4288,12 +4294,6 @@ struct AA_Action { /*16*/ }; -struct AA_Skills { //this should be removed and changed to AA_Array -/*00*/ uint32 aa_skill; // Total AAs Spent -/*04*/ uint32 aa_value; -/*08*/ uint32 unknown08; -/*12*/ -}; struct AAExpUpdate_Struct { /*00*/ uint32 unknown00; //seems to be a value from AA_Action.ability @@ -4313,14 +4313,7 @@ struct AltAdvStats_Struct { }; struct PlayerAA_Struct { // Is this still used? - AA_Skills aa_list[MAX_PP_AA_ARRAY]; -}; - -struct AA_Values { -/*00*/ uint32 aa_skill; -/*04*/ uint32 aa_value; -/*08*/ uint32 unknown08; -/*12*/ + AA_Array aa_list[MAX_PP_AA_ARRAY]; }; struct AATable_Struct { @@ -4330,7 +4323,7 @@ struct AATable_Struct { /*12*/ uint32 aa_spent_archetype; // Seen 40 /*16*/ uint32 aa_spent_class; // Seen 103 /*20*/ uint32 aa_spent_special; // Seen 0 -/*24*/ AA_Values aa_list[MAX_PP_AA_ARRAY]; +/*24*/ AA_Array aa_list[MAX_PP_AA_ARRAY]; }; struct Weather_Struct { diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index 7db21597c..a87b530c4 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -866,7 +866,7 @@ struct AA_Array { uint32 AA; uint32 value; - uint32 unknown08; // Looks like AA_Array is now 12 bytes in Live + uint32 charges; // expendable charges }; struct Disciplines_Struct { @@ -4252,9 +4252,9 @@ struct SendAA_Struct { /*0025*/ uint32 cost; /*0029*/ uint32 seq; /*0033*/ uint32 current_level; //1s, MQ2 calls this AARankRequired -/*0037*/ uint32 unknown037; // Introduced during HoT +/*0037*/ uint32 prereq_skill_count; // mutliple prereqs at least 1, even no prereqs /*0041*/ uint32 prereq_skill; //is < 0, abs() is category # -/*0045*/ uint32 unknown045; // New Mar 21 2012 - Seen 1 +/*0045*/ uint32 prereq_minpoints_count; // mutliple prereqs at least 1, even no prereqs /*0049*/ uint32 prereq_minpoints; //min points in the prereq /*0053*/ uint32 type; /*0057*/ uint32 spellid; @@ -4267,10 +4267,16 @@ struct SendAA_Struct { /*0081*/ uint32 last_id; /*0085*/ uint32 next_id; /*0089*/ uint32 cost2; -/*0093*/ uint8 unknown80[7]; +/*0093*/ uint8 unknown93; +/*0094*/ uint8 grant_only; // VetAAs, progression, etc +/*0095*/ uint8 unknown95; // 1 for skill cap increase AAs, Mystical Attuning, and RNG attack inc, doesn't seem to matter though +/*0096*/ uint32 expendable_charges; // max charges of the AA /*0100*/ uint32 aa_expansion; /*0104*/ uint32 special_category; -/*0108*/ uint32 unknown0096; +/*0108*/ uint8 shroud; +/*0109*/ uint8 unknown109; +/*0110*/ uint8 layonhands; // 1 for lay on hands -- doesn't seem to matter? +/*0111*/ uint8 unknown111; /*0112*/ uint32 total_abilities; /*0116*/ AA_Ability abilities[0]; }; @@ -4287,13 +4293,6 @@ struct AA_Action { /*16*/ }; -struct AA_Skills { //this should be removed and changed to AA_Array -/*00*/ uint32 aa_skill; // Total AAs Spent -/*04*/ uint32 aa_value; -/*08*/ uint32 unknown08; -/*12*/ -}; - struct AAExpUpdate_Struct { /*00*/ uint32 unknown00; //seems to be a value from AA_Action.ability /*04*/ uint32 aapoints_unspent; @@ -4312,14 +4311,7 @@ struct AltAdvStats_Struct { }; struct PlayerAA_Struct { // Is this still used? - AA_Skills aa_list[MAX_PP_AA_ARRAY]; -}; - -struct AA_Values { -/*00*/ uint32 aa_skill; -/*04*/ uint32 aa_value; -/*08*/ uint32 unknown08; -/*12*/ + AA_Array aa_list[MAX_PP_AA_ARRAY]; }; struct AATable_Struct { @@ -4329,7 +4321,7 @@ struct AATable_Struct { /*12*/ uint32 aa_spent_archetype; // Seen 40 /*16*/ uint32 aa_spent_class; // Seen 103 /*20*/ uint32 aa_spent_special; // Seen 0 -/*24*/ AA_Values aa_list[MAX_PP_AA_ARRAY]; +/*24*/ AA_Array aa_list[MAX_PP_AA_ARRAY]; }; struct Weather_Struct { diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 0a8073131..80201da1e 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -1558,6 +1558,7 @@ namespace SoD for (r = 0; r < MAX_PP_AA_ARRAY; r++) { OUT(aa_array[r].AA); OUT(aa_array[r].value); + OUT(aa_array[r].charges); } // OUT(unknown02220[4]); OUT(mana); @@ -1898,6 +1899,7 @@ namespace SoD OUT(cost2); eq->aa_expansion = emu->aa_expansion; eq->special_category = emu->special_category; + eq->expendable_charges = emu->special_category == 7 ? 1 : 0; // temp hack, this can actually be any number OUT(total_abilities); unsigned int r; for (r = 0; r < emu->total_abilities; r++) { diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 2b7178daf..2f5d2c2db 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -666,7 +666,7 @@ struct AA_Array { uint32 AA; uint32 value; - uint32 unknown08; // Looks like AA_Array is now 12 bytes in Live + uint32 charges; // expendable }; @@ -3819,10 +3819,16 @@ struct SendAA_Struct { /*0069*/ uint32 last_id; /*0073*/ uint32 next_id; /*0077*/ uint32 cost2; -/*0081*/ uint8 unknown80[7]; +/*0081*/ uint8 unknown81; +/*0082*/ uint8 grant_only; // VetAAs, progression, etc +/*0083*/ uint8 unknown83; // 1 for skill cap increase AAs, Mystical Attuning, and RNG attack inc, doesn't seem to matter though +/*0084*/ uint32 expendable_charges; // max charges of the AA /*0088*/ uint32 aa_expansion; /*0092*/ uint32 special_category; -/*0096*/ uint32 unknown0096; +/*0096*/ uint8 shroud; +/*0097*/ uint8 unknown97; +/*0098*/ uint8 layonhands; // 1 for lay on hands -- doesn't seem to matter? +/*0099*/ uint8 unknown99; /*0100*/ uint32 total_abilities; /*0104*/ AA_Ability abilities[0]; }; @@ -3838,12 +3844,6 @@ struct AA_Action { /*12*/ uint32 exp_value; }; -struct AA_Skills { //this should be removed and changed to AA_Array -/*00*/ uint32 aa_skill; // Total AAs Spent -/*04*/ uint32 aa_value; -/*08*/ uint32 unknown08; -}; - struct AAExpUpdate_Struct { /*00*/ uint32 unknown00; //seems to be a value from AA_Action.ability /*04*/ uint32 aapoints_unspent; @@ -3861,12 +3861,12 @@ struct AltAdvStats_Struct { }; struct PlayerAA_Struct { // Is this still used? - AA_Skills aa_list[MAX_PP_AA_ARRAY]; + AA_Array aa_list[MAX_PP_AA_ARRAY]; }; struct AATable_Struct { /*00*/ int32 aa_spent; // Total AAs Spent -/*04*/ AA_Skills aa_list[MAX_PP_AA_ARRAY]; +/*04*/ AA_Array aa_list[MAX_PP_AA_ARRAY]; }; struct Weather_Struct { diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index f0e8a2dbe..7b0e14c1c 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -1216,6 +1216,7 @@ namespace SoF for (r = 0; r < MAX_PP_AA_ARRAY; r++) { OUT(aa_array[r].AA); OUT(aa_array[r].value); + OUT(aa_array[r].charges); } // OUT(unknown02220[4]); OUT(mana); @@ -1557,6 +1558,7 @@ namespace SoF OUT(cost2); eq->aa_expansion = emu->aa_expansion; eq->special_category = emu->special_category; + eq->expendable_charges = emu->special_category == 7 ? 1 : 0; // temp hack, this can actually be any number OUT(total_abilities); unsigned int r; for (r = 0; r < emu->total_abilities; r++) { diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index f7ad59011..afd34ba68 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -645,7 +645,7 @@ struct AA_Array { uint32 AA; uint32 value; - uint32 unknown08; // Looks like AA_Array is now 12 bytes in Live + uint32 charges; // expendable charges }; @@ -3683,10 +3683,14 @@ struct SendAA_Struct { /*0069*/ uint32 last_id; /*0073*/ uint32 next_id; /*0077*/ uint32 cost2; -/*0081*/ uint8 unknown80[7]; +/*0081*/ uint8 unknown81; +/*0082*/ uint8 grant_only; // VetAAs, progression, etc +/*0083*/ uint8 unknown83; // 1 for skill cap increase AAs, Mystical Attuning, and RNG attack inc, doesn't seem to matter though +/*0084*/ uint32 expendable_charges; // max charges of the AA /*0088*/ uint32 aa_expansion; /*0092*/ uint32 special_category; -/*0096*/ uint16 unknown0096; +/*0096*/ uint8 shroud; +/*0097*/ uint8 unknown97; /*0098*/ uint32 total_abilities; /*0102*/ AA_Ability abilities[0]; }; @@ -3702,12 +3706,6 @@ struct AA_Action { /*12*/ uint32 exp_value; }; -struct AA_Skills { //this should be removed and changed to AA_Array -/*00*/ uint32 aa_skill; // Total AAs Spent -/*04*/ uint32 aa_value; -/*08*/ uint32 unknown08; -}; - struct AAExpUpdate_Struct { /*00*/ uint32 unknown00; //seems to be a value from AA_Action.ability /*04*/ uint32 aapoints_unspent; @@ -3725,12 +3723,12 @@ struct AltAdvStats_Struct { }; struct PlayerAA_Struct { - AA_Skills aa_list[MAX_PP_AA_ARRAY]; + AA_Array aa_list[MAX_PP_AA_ARRAY]; }; struct AATable_Struct { /*00*/ int32 aa_spent; // Total AAs Spent -/*04*/ AA_Skills aa_list[MAX_PP_AA_ARRAY]; +/*04*/ AA_Array aa_list[MAX_PP_AA_ARRAY]; }; struct Weather_Struct { diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index e23202c87..2fda4038a 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -1098,8 +1098,8 @@ namespace Titanium unsigned int r; for (r = 0; r < structs::MAX_PP_AA_ARRAY; r++) { - OUT(aa_list[r].aa_skill); - OUT(aa_list[r].aa_value); + OUT(aa_list[r].AA); + OUT(aa_list[r].value); } FINISH_ENCODE(); diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index bdca057d0..f836395c9 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -3179,11 +3179,6 @@ struct AA_Action { /*12*/ uint32 exp_value; }; -struct AA_Skills { //this should be removed and changed to AA_Array -/*00*/ uint32 aa_skill; -/*04*/ uint32 aa_value; -}; - struct AAExpUpdate_Struct { /*00*/ uint32 unknown00; //seems to be a value from AA_Action.ability /*04*/ uint32 aapoints_unspent; @@ -3201,11 +3196,11 @@ struct AltAdvStats_Struct { }; struct PlayerAA_Struct { - AA_Skills aa_list[MAX_PP_AA_ARRAY]; + AA_Array aa_list[MAX_PP_AA_ARRAY]; }; struct AATable_Struct { - AA_Skills aa_list[MAX_PP_AA_ARRAY]; + AA_Array aa_list[MAX_PP_AA_ARRAY]; }; struct Weather_Struct { diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index e963312e2..deedcec5f 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -1807,6 +1807,7 @@ namespace UF for (r = 0; r < MAX_PP_AA_ARRAY; r++) { OUT(aa_array[r].AA); OUT(aa_array[r].value); + OUT(aa_array[r].charges); } // OUT(unknown02220[4]); OUT(mana); @@ -2134,9 +2135,9 @@ namespace UF for (uint32 i = 0; i < MAX_PP_AA_ARRAY; ++i) { - eq->aa_list[i].aa_skill = emu->aa_list[i].aa_skill; - eq->aa_list[i].aa_value = emu->aa_list[i].aa_value; - eq->aa_list[i].unknown08 = emu->aa_list[i].unknown08; + eq->aa_list[i].AA = emu->aa_list[i].AA; + eq->aa_list[i].value = emu->aa_list[i].value; + eq->aa_list[i].charges = emu->aa_list[i].charges; } FINISH_ENCODE(); @@ -2181,6 +2182,7 @@ namespace UF OUT(cost2); eq->aa_expansion = emu->aa_expansion; eq->special_category = emu->special_category; + eq->expendable_charges = emu->special_category == 7 ? 1 : 0; // temp hack, this can actually be any number OUT(total_abilities); unsigned int r; for (r = 0; r < emu->total_abilities; r++) { diff --git a/common/patches/uf_structs.h b/common/patches/uf_structs.h index 79363d68a..c943c6ea5 100644 --- a/common/patches/uf_structs.h +++ b/common/patches/uf_structs.h @@ -713,7 +713,7 @@ struct AA_Array { uint32 AA; uint32 value; - uint32 unknown08; // Looks like AA_Array is now 12 bytes in Underfoot + uint32 charges; // expendable }; @@ -3892,10 +3892,16 @@ struct SendAA_Struct { /*0069*/ uint32 last_id; /*0073*/ uint32 next_id; /*0077*/ uint32 cost2; -/*0081*/ uint8 unknown80[7]; +/*0081*/ uint8 unknown81; +/*0082*/ uint8 grant_only; // VetAAs, progression, etc +/*0083*/ uint8 unknown83; // 1 for skill cap increase AAs, Mystical Attuning, and RNG attack inc, doesn't seem to matter though +/*0084*/ uint32 expendable_charges; // max charges of the AA /*0088*/ uint32 aa_expansion; /*0092*/ uint32 special_category; -/*0096*/ uint32 unknown0096; +/*0096*/ uint8 shroud; +/*0097*/ uint8 unknown97; +/*0098*/ uint8 layonhands; // 1 for lay on hands -- doesn't seem to matter? +/*0099*/ uint8 unknown99; /*0100*/ uint32 total_abilities; /*0104*/ AA_Ability abilities[0]; }; @@ -3911,11 +3917,6 @@ struct AA_Action { /*12*/ uint32 exp_value; }; -struct AA_Skills { //this should be removed and changed to AA_Array -/*00*/ uint32 aa_skill; // Total AAs Spent -/*04*/ uint32 aa_value; -/*08*/ uint32 unknown08; -}; struct AAExpUpdate_Struct { /*00*/ uint32 unknown00; //seems to be a value from AA_Action.ability @@ -3934,7 +3935,7 @@ struct AltAdvStats_Struct { }; struct PlayerAA_Struct { // Is this still used? - AA_Skills aa_list[MAX_PP_AA_ARRAY]; + AA_Array aa_list[MAX_PP_AA_ARRAY]; }; struct AATable_Struct { @@ -3944,7 +3945,7 @@ struct AATable_Struct { /*12*/ int32 unknown012; /*16*/ int32 unknown016; /*20*/ int32 unknown020; -/*24*/ AA_Skills aa_list[MAX_PP_AA_ARRAY]; +/*24*/ AA_Array aa_list[MAX_PP_AA_ARRAY]; }; struct Weather_Struct { diff --git a/common/version.h b/common/version.h index 78f7a67b3..1813f77c3 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 9082 +#define CURRENT_BINARY_DATABASE_VERSION 9083 #define COMPILE_DATE __DATE__ #define COMPILE_TIME __TIME__ #ifndef WIN32 diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 5ca25a029..e7aa46221 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -336,6 +336,7 @@ 9080|2015_05_23_PetBuffInstrumentMod.sql|SHOW COLUMNS FROM `character_pet_buffs` LIKE 'instrument_mod'|empty| 9081|2015_05_23_dbstr_us.sql|SHOW TABLES LIKE 'db_str'|empty| 9082|2015_05_25_npc_types_texture_fields.sql|SHOW COLUMNS FROM `npc_types` LIKE 'armtexture'|empty| +9083|2015_06_07_aa_update.sql|SHOW COLUMNS FROM `character_alternate_abilities` LIKE 'charges'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/zone/aa.cpp b/zone/aa.cpp index b6e220d60..3718e3391 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -1104,9 +1104,9 @@ void Client::SendAATable() { uint32 i; for(i=0;i < MAX_PP_AA_ARRAY;i++){ - aa2->aa_list[i].aa_skill = aa[i]->AA; - aa2->aa_list[i].aa_value = aa[i]->value; - aa2->aa_list[i].unknown08 = 0; + aa2->aa_list[i].AA = aa[i]->AA; + aa2->aa_list[i].value = aa[i]->value; + aa2->aa_list[i].charges = aa[i]->charges; } QueuePacket(outapp); safe_delete(outapp); @@ -1395,6 +1395,8 @@ uint32 Client::GetAA(uint32 aa_id) const { bool Client::SetAA(uint32 aa_id, uint32 new_value) { aa_points[aa_id] = new_value; uint32 cur; + auto sendaa = zone->FindAA(aa_id); // this is a bit hacky + uint32 charges = sendaa->special_category == 7 && new_value ? 1 : 0; for(cur=0;cur < MAX_PP_AA_ARRAY;cur++){ if((aa[cur]->value > 1) && ((aa[cur]->AA - aa[cur]->value + 1)== aa_id)){ aa[cur]->value = new_value; @@ -1402,6 +1404,7 @@ bool Client::SetAA(uint32 aa_id, uint32 new_value) { aa[cur]->AA++; else aa[cur]->AA = 0; + aa[cur]->charges = charges; return true; } else if((aa[cur]->value == 1) && (aa[cur]->AA == aa_id)){ @@ -1410,11 +1413,13 @@ bool Client::SetAA(uint32 aa_id, uint32 new_value) { aa[cur]->AA++; else aa[cur]->AA = 0; + aa[cur]->charges = charges; return true; } else if(aa[cur]->AA==0){ //end of list aa[cur]->AA = aa_id; aa[cur]->value = new_value; + aa[cur]->charges = charges; return true; } } @@ -1485,8 +1490,10 @@ void Client::ResetAA(){ for (i=0; i < MAX_PP_AA_ARRAY; i++) { aa[i]->AA = 0; aa[i]->value = 0; - m_pp.aa_array[MAX_PP_AA_ARRAY].AA = 0; - m_pp.aa_array[MAX_PP_AA_ARRAY].value = 0; + aa[i]->charges = 0; + m_pp.aa_array[i].AA = 0; + m_pp.aa_array[i].value = 0; + m_pp.aa_array[i].charges= 0; } std::map::iterator itr; diff --git a/zone/client.cpp b/zone/client.cpp index bdcbbbf68..f7e160851 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -550,13 +550,13 @@ bool Client::SaveAA(){ } m_pp.aapoints_spent = spentpoints + m_epp.expended_aa; for (int a = 0; a < MAX_PP_AA_ARRAY; a++) { - if (aa[a]->AA > 0 && aa[a]->value){ + if (aa[a]->AA > 0) { // those with value 0 will be cleaned up on next load if (first_entry != 1){ - rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, slot, aa_id, aa_value)" - " VALUES (%u, %u, %u, %u)", character_id, a, aa[a]->AA, aa[a]->value); + rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, slot, aa_id, aa_value, charges)" + " VALUES (%u, %u, %u, %u, %u)", character_id, a, aa[a]->AA, aa[a]->value, aa[a]->charges); first_entry = 1; } - rquery = rquery + StringFormat(", (%u, %u, %u, %u)", character_id, a, aa[a]->AA, aa[a]->value); + rquery = rquery + StringFormat(", (%u, %u, %u, %u, %u)", character_id, a, aa[a]->AA, aa[a]->value, aa[a]->charges); } } auto results = database.QueryDatabase(rquery); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index e4ce874fc..b9ac81d89 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1440,12 +1440,14 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) if (m_pp.ldon_points_available < 0 || m_pp.ldon_points_available > 2000000000){ m_pp.ldon_points_available = 0; } /* Initialize AA's : Move to function eventually */ - for (uint32 a = 0; a < MAX_PP_AA_ARRAY; a++){ aa[a] = &m_pp.aa_array[a]; } + for (uint32 a = 0; a < MAX_PP_AA_ARRAY; a++) + aa[a] = &m_pp.aa_array[a]; query = StringFormat( "SELECT " "slot, " "aa_id, " - "aa_value " + "aa_value, " + "charges " "FROM " "`character_alternate_abilities` " "WHERE `id` = %u ORDER BY `slot`", this->CharacterID()); @@ -1454,8 +1456,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) i = atoi(row[0]); m_pp.aa_array[i].AA = atoi(row[1]); m_pp.aa_array[i].value = atoi(row[2]); - aa[i]->AA = atoi(row[1]); - aa[i]->value = atoi(row[2]); + m_pp.aa_array[i].charges = atoi(row[3]); } for (uint32 a = 0; a < MAX_PP_AA_ARRAY; a++){ uint32 id = aa[a]->AA; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index e81a66308..08a3dd38a 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1710,10 +1710,10 @@ bool ZoneDatabase::SaveCharacterCurrency(uint32 character_id, PlayerProfile_Stru return true; } -bool ZoneDatabase::SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level){ - std::string rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, aa_id, aa_value)" - " VALUES (%u, %u, %u)", - character_id, aa_id, current_level); +bool ZoneDatabase::SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level, uint32 charges){ + std::string rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, aa_id, aa_value, charges)" + " VALUES (%u, %u, %u, %u)", + character_id, aa_id, current_level, charges); auto results = QueryDatabase(rquery); Log.Out(Logs::General, Logs::None, "Saving AA for character ID: %u, aa_id: %u current_level: %u", character_id, aa_id, current_level); return true; diff --git a/zone/zonedb.h b/zone/zonedb.h index cac380a96..1fb4d3b29 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -278,7 +278,7 @@ public: bool SaveCharacterBindPoint(uint32 character_id, uint32 zone_id, uint32 instance_id, const glm::vec4& position, uint8 is_home); bool SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); bool SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp); - bool SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level); + bool SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level, uint32 charges); bool SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); bool SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); bool SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color); From db307d865bd3fbd1bdfcd0d68c5090982f95e09a Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 7 Jun 2015 23:42:28 -0400 Subject: [PATCH 115/129] And SQL for last commit --- utils/sql/git/required/2015_06_07_aa_update.sql | 1 + 1 file changed, 1 insertion(+) create mode 100644 utils/sql/git/required/2015_06_07_aa_update.sql diff --git a/utils/sql/git/required/2015_06_07_aa_update.sql b/utils/sql/git/required/2015_06_07_aa_update.sql new file mode 100644 index 000000000..a888a75ea --- /dev/null +++ b/utils/sql/git/required/2015_06_07_aa_update.sql @@ -0,0 +1 @@ +ALTER TABLE character_alternate_abilities ADD COLUMN charges SMALLINT(11) UNSIGNED NOT NULL DEFAULT 0; From 6229b90451953f523e508e19ba7a6bf25f332e3f Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 8 Jun 2015 02:00:44 -0400 Subject: [PATCH 116/129] Fix exploit with expendable AAs --- changelog.txt | 418 +++++++++++++++++++++-------------------- zone/aa.cpp | 12 +- zone/client.cpp | 7 +- zone/client_packet.cpp | 11 +- 4 files changed, 233 insertions(+), 215 deletions(-) diff --git a/changelog.txt b/changelog.txt index bc96a8ef1..d97f20459 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) == 06/07/2015 == Uleat: Implemented optional rule for using disenchanted bags. Action triggers at the same point that temporary items are removed. Optional SQL: utils/sql/git/optional/2015_06_07_TransformSummonedBagsRule.sql +mackal: changes to AA packets since more fields have been identified +mackal: fix exploit with expendable AAs punching holes in the aa_array and staying around longer than they are welcomed == 05/25/2015 == Akkadius: Implemented disjointed zone based time, this can be triggered via quest methods @@ -67,7 +69,7 @@ Notes: == 02/23/2015 == Noudess: Allow for a rule to set starting swimming && SenseHeading value. -I moved the swimming override to char create instead of setting it +I moved the swimming override to char create instead of setting it every time a char enters a zone. Also added rules to not ignore, but rather forrce sense heading packets to be @@ -135,7 +137,7 @@ Uleat: Removed 'limbo' from the 'HasItem' series of checks - including lore chec Uleat: Updated command #iteminfo to show light source information and a few other things == 02/05/2015 == -Trevius: Fixed Environmental Damage for RoF2. +Trevius: Fixed Environmental Damage for RoF2. == 02/03/2015 == Trevius: Crashfix for TempName() when numbers are passed at the end of the name. @@ -158,19 +160,19 @@ Akkadius: Add Packet Logging Categories - 39 - Packet: Server -> Client - Logs::Server_Client_Packet - 40 - Packet: Client -> Server Unhandled - Logs::Client_Server_Packet_Unhandled See: http://wiki.eqemulator.org/p?Logging_System_Overhaul#packet-logging - + == 01/31/2015 == Trevius: Fixed FindGroundZ() and GetGroundZ() to once again utilize the X and Y arguments that are passed to them. == 01/30/2015 == Akkadius: Implemented event type "EVENT_ENVIRONMENTAL_DAMAGE" - - This event triggers when taking any sort of environmental damage. Example use: + - This event triggers when taking any sort of environmental damage. Example use: sub EVENT_ENVIRONMENTAL_DAMAGE{ quest::debug("EVENT_ENVIRONMENTAL_DAMAGE"); quest::debug("env_damage is " . $env_damage); quest::debug("env_damage_type is " . $env_damage_type); quest::debug("env_final_damage is " . $env_final_damage); - } + } Result: (Test falling in Velks): http://i.imgur.com/tPRL7yL.png - Implemented LUA counterpart of this same implementation above Akkadius (Bobaski): Add PoK New Merchant sql/git/optional/2015_01_30_poknowledge_spell_vendors.sql @@ -189,7 +191,7 @@ Akkadius: Added Logs::DebugQuest category per request from Trevius (Great idea) quest::debug("This is a test debug message, level 1", 1); quest::debug("This is a test debug message, level 2", 2); quest::debug("This is a test debug message, level 3", 3); - + Result: http://i.imgur.com/6VoafGE.png - Uses traditional logging system to output this category - Required MySQL Source in Database version 9070 @@ -220,7 +222,7 @@ Notes: == 01/22/2015 == Akkadius: Massive Log System overhaul, see: http://wiki.eqemulator.org/p?Logging_System_Overhaul&frm=Main - + == 01/21/2015 == Uleat: Added `light` field to npc_types load query (all six clients tested positive for functionality.) Note: This only affects 'innate' light. Equipment (other) light is still in-work. @@ -262,7 +264,7 @@ Uleat: Added text link translators for OP_Emote Uleat: Added text link translators for OP_FormattedMessage == 01/08/2015 == -Trevius: Added some extra checks and clean-up related to Groups and Mercenaries. +Trevius: Added some extra checks and clean-up related to Groups and Mercenaries. == 01/07/2015 == Uleat: Excluded text link body from message scrambling in Client::GarbleMessage() @@ -434,10 +436,10 @@ Akkadius: Created database revision define, this is located in version.h in comm # empty = If the query results in no results # not_empty = If the query is not empty # 4 = Text to match - - The manifest contains all database updates 'Required' to be made to the schema, and it will contain a working backport all the way back to SVN - + - The manifest contains all database updates 'Required' to be made to the schema, and it will contain a working backport all the way back to SVN - currently it is tested and backported through the beginning of our Github repo - On world bootup or standalone run of db_update.pl, users will be prompted with a simple menu that we will expand upon later: - + ============================================================ EQEmu: Automatic Database Upgrade Check ============================================================ @@ -466,7 +468,7 @@ Database Management Menu (Please Select): Akkadius: Created db_update.pl, placed in utils/scripts folder, used for the automatic database update routine (Linux/Windows) - db_update.pl script created db_version table if not created, if old one is present it will remove it Akkadius: Created db_dumper.pl, placed in utils/scripts folder, used for the automatic database update routine backups and standalone backups (Linux/Windows) -Akkadius: World will now check the db_update.pl script on bootup, if the db_update.pl script is not present, it will fetch it remotely before running - +Akkadius: World will now check the db_update.pl script on bootup, if the db_update.pl script is not present, it will fetch it remotely before running - when db_update.pl is done running, world will continue with bootup == 11/15/2014 == @@ -644,10 +646,10 @@ Akkadius: Removed #refundaa Akkadius: Removed a lot of debug code for blob conversion Akkadius: Changed status logging for loads/saves to Debug category -== 09/21/2014 == +== 09/21/2014 == Akkadius: Player Profile Blob to Database Conversion - Summary: HUGE difference in database speeds reads/writes and 1:10 datasize difference - - The new character storage engine unlike the character_ table before, is able to properly index data and make use of + - The new character storage engine unlike the character_ table before, is able to properly index data and make use of proper MySQL/MariaDB caching optimizations and performance has increased phenominally PERFORMANCE AND STATISTICS FIGURES (Varies on hardware): - EZ Server Character data size of 2.6GB `character_` table alone now takes up approx 600MB @@ -693,7 +695,7 @@ Akkadius: Player Profile Blob to Database Conversion SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level); SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); - SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color); + SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color); SaveCharacterSkill(uint32 character_id, uint32 skill_id, uint32 value); SaveCharacterLanguage(uint32 character_id, uint32 lang_id, uint32 value); SaveCharacterDisc(uint32 character_id, uint32 slot_id, uint32 disc_id); @@ -704,7 +706,7 @@ Akkadius: Player Profile Blob to Database Conversion - Deletes: DeleteCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); - DeleteCharacterDisc(uint32 character_id, uint32 slot_id); + DeleteCharacterDisc(uint32 character_id, uint32 slot_id); DeleteCharacterBandolier(uint32 character_id, uint32 band_id); DeleteCharacterLeadershipAAs(uint32 character_id); - Now occur all over the code and only trigger when necessary @@ -717,17 +719,17 @@ Akkadius: Player Profile Blob to Database Conversion - NOTE: These amount of excessive saves have caused scalability issues that cause the `character_` table to hang which causes process hangs that affect the whole server because of the slowness of the `character_` table and the blob not allowing any indexing to occur - All functions that once depended on the `character_` table are now rewritten to appropriately read from the `character_data` table - - Database query errors that occur during conversion or from and load/save/delete character functions are now leveraged via ThrowDBError and logs now go to + - Database query errors that occur during conversion or from and load/save/delete character functions are now leveraged via ThrowDBError and logs now go to Server_Folder_Root/eqemu_query_error_log.txt (You cannot log errors natively through MySQL) - DBASYNC IS NOW COMPLETELY REMOVED - This was mainly for Character data async loads/saves and merchantlist loads - - Side implementations: + - Side implementations: Perl Exports: - quest::crosszonesetentityvariablebynpctypeid(npctype_id, id, m_var) - Sets entity variables world wide with specified npctype_id - quest::crosszonesignalnpcbynpctypeid(npctype_id, data) - Signals all NPC entities world wide with specified npctype_id - $client->GetTaskActivityDoneCount(TaskID, ActivityID) - Gets task activity done count by task id and activity id for client entity - + VIEW TABLE SIZE AFTER CONVERT: - + SELECT CONCAT(table_schema, '.', table_name) as table_name, CONCAT(ROUND(table_rows / 1000000, 2), 'M') rows, CONCAT(ROUND(data_length / ( 1024 * 1024 * 1024 ), 2), 'G') DATA, @@ -854,10 +856,10 @@ Akkadius: Changed all QS Error related logging to 'QUERYSERV__ERROR' Akkadius: (Natedog) (Crash Fix) Legacy MySQL bug revert for loading AA's COALESCE( from COALESCE ( Akkadius: Implemented Perl Quest objects (LUA still needed to be exported): - quest::qs_send_query("MySQL query") - Will send a raw query to the QueryServ process, useful for custom logging - - quest::qs_player_event(char_id, event_desc); - Will process a quest type event to table `qs_player_events` + - quest::qs_player_event(char_id, event_desc); - Will process a quest type event to table `qs_player_events` Akkadius: Added MySQL Tables - `qs_player_aa_rate_hourly` - - `qs_player_events` + - `qs_player_events` - Source table structures from: - utils\sql\git\queryserv\required\08_23_2014_player_events_and_player_aa_rate_hourly To get the complete QueryServ schema, source from here: @@ -866,7 +868,7 @@ Akkadius: Added rules for each logging type, source rules here with them enabled - utils\sql\git\queryserv\required\Complete_QueryServ_Rules_Enabled.sql Akkadius: Spawn related logging cleanup Akkadius: General code cleanup -Akkadius: More to come for QueryServ +Akkadius: More to come for QueryServ == 08/22/2014 == Uleat: Rework of Trade::FinishedTrade() and Trade::ResetTrade() to parse items a little more intelligently. @@ -910,8 +912,8 @@ Uleat (Kingly_Krab): Fix for bot chest armor graphic glitch. (fix also caused Ro == 08/02/2014 == Kayen: Implemented spell_news fields -- npc_no_los (check if LOS is required for spells) -- InCombat, OutofCombat - Used together to restrict spells to only be cast while +- npc_no_los (check if LOS is required for spells) +- InCombat, OutofCombat - Used together to restrict spells to only be cast while in/out of combat (beneficial) or if target is in/out of combat (detrimental). -min_dist, min_dist_mod, max_dist, max_dist_mod - Scales spell power based on targets distance from caster. *This will require further work to fully implement but will work with 90% of live spells as is. @@ -944,7 +946,7 @@ KLS: Changes to CMake build == 07/10/2014 == -Kayen: Updated table npc_spells to now support defensive and ranged procs. +Kayen: Updated table npc_spells to now support defensive and ranged procs. Note: Proc rate modifier work as it does for spell effects (ie 200 = 200% baseline chance modifier) Table is also now contains 12 AI spell casting variables that can be set to fine tune casting behaviors per spell set. Global default rules have also been added that can further fine tune all content if no specific variables are set. @@ -974,7 +976,7 @@ Param2: Percent Chance to Hit modifier Param3: Percent Total Damage modifier Kayen: Updated to Chance to Hit code with how bonuses are applied to be consistent for all effects. -Added field to npc_types 'Avoidance' which will modify chance to avoid melee +Added field to npc_types 'Avoidance' which will modify chance to avoid melee Added rules to set max and min chance to hit from melee/ranged (Default 95% / 5%) Required SQL: utils/sql/git/required/2014_07_10_npc_spells.sql @@ -988,7 +990,7 @@ Sympathetic foci on items with proc rate mod will now benefit from that modifier Sympathetic foci can now be placed on AA's (This should always be slot1 in the AA) Kayen: Implemented SE_IllusionPersistence- Allows illusions to last until you die or the illusion is forcibly removed. Kayen: Added rule 'PreNerftBardAEDot' for SE_BardAEDot to allow it to once again do damage to moving targets. (Set to true) -Kayen: Completely revised SE_SkillProc, SE_LimitToSkill, SE_SkillProcSuccess to overall just work better and more accurately, AA support. +Kayen: Completely revised SE_SkillProc, SE_LimitToSkill, SE_SkillProcSuccess to overall just work better and more accurately, AA support. Required SQL: utils/sql/git/required/2014_07_04_AA_Update.sql @@ -1031,7 +1033,7 @@ Optional SQL: utils/sql/git/optiional/2014_06_29_HeadShotRules.sql == 06/17/2014 == Kayen: Implemented SE_AStacker, SE_BStacker, SE_CStacker, SE_DStacker. -These effects when present in buffs prevent each other from stacking, +These effects when present in buffs prevent each other from stacking, Any effect with B prevents A, C prevents B, D prevents C. Kayen: Implemented SE_DamageModifier2 (Stacks with SE_DamageModifier, mods damage by skill type) Kayen: Implemented SE_AddHatePct (Modifies +/- your total hate on NPC by percent) @@ -1081,7 +1083,7 @@ KLS: Implemented new map code based on some of Derision's earlier work. Old map == 04/27/2014 == Kayen: Implemented new table 'npc_spells_effects' and 'npc_spells_effects_entires'. Implemented new field in 'npc_spell_effects_id' in npc_types. - + These are used to directly apply spell effect bonuses to NPC's without requirings spells/buffs. Example: Allow an npc to spawn with an innate 50 pt damage shield and a 5% chance to critical hit. @@ -1098,7 +1100,7 @@ cavedude: Added strict column to spawn_events which will prevent an event from e cavedude: Prevented disabled or strict spawn_events from enabling when the zone first boots. cavedude: Fixed the quest function toggle_spawn_event under Perl. -If you're using the quest function toggle_spawn_event (worked on Lua only) it has changed syntax to: +If you're using the quest function toggle_spawn_event (worked on Lua only) it has changed syntax to: toggle_spawn_event(int event_id, bool enable, bool strict, bool reset_base) Required SQL: utils/sql/git/required/2014_04_25_spawn_events.sql @@ -1163,31 +1165,31 @@ Notes: See this thread for more information and to provide feedback: http://www.eqemulator.org/forums/showthread.php?p=229328#post229328 == 04/05/2014 == -Akkadius: Fix for the Fix for the Fix: Rule Combat:OneProcPerWeapon was created so that you can revert to the original proc functionality - for custom servers that have balanced their content around having more than 1 aug proc on weapons. By having this rule set to 'false' you revert this functionality. +Akkadius: Fix for the Fix for the Fix: Rule Combat:OneProcPerWeapon was created so that you can revert to the original proc functionality + for custom servers that have balanced their content around having more than 1 aug proc on weapons. By having this rule set to 'false' you revert this functionality. This rule is set to 'true' by default as the original functionality from Live was intended to be Akkadius: (Performance Adjustment) Removed AsyncLoadVariables from InterserverTimer.Check() in both zone and world. By watching the MySQL general.log file on mass zone idle activity, you can see that the query 'SELECT varname, value, unix_timestamp() FROM variables where unix_timestamp(ts) >= timestamp' is called every 10 seconds. This function is loading - variables that are initially loaded on World and Zone bootup. When running a large amount of zone servers, the amount of MySQL chatter that is produced is enormous and + variables that are initially loaded on World and Zone bootup. When running a large amount of zone servers, the amount of MySQL chatter that is produced is enormous and unnecessary. For example, if I ran 400 zone servers, I would see 3,456,000 unnecessary queries from all idle or active zone processes in a 24 hour interval. Secrets: Added a rule to enable multiple procs from the same weapon's other slots if a proc is deemed to trigger, Defaults to true. If Combat:OneProcPerWeapon is not enabled, we reset the try for that weapon regardless of if we procced or not. This is for some servers that may want to have as many procs triggering from weapons as possible in a single round. - + Optional SQL: utils/sql/git/optional/2014_04_05_ProcRules.sql - + == 04/04/2014 == Kayen: Implemented 'Physical Resists' (Resist Type 9) to be consistent with live based on extensive parsing. - SQL will add new field to npc_types 'PhR' and fill in database with values consistent with observations. + SQL will add new field to npc_types 'PhR' and fill in database with values consistent with observations. Required SQL: utils/sql/git/optional/2014_04_04_PhysicalResists.sql == 04/03/2014 == -Kayen: Implemented live like spell projectiles (ie. Mage Bolts). +Kayen: Implemented live like spell projectiles (ie. Mage Bolts). Optional SQL: utils/sql/git/optional/2014_04_03_SpellProjectileRules.sql Note: The rules in this SQL are for setting the item id for the graphic used by the projectile on different clients. - + == 04/01/2014 == demonstar55: Implemented ability for a merchant to open and close shop. Lua quest functions: e.self:MerchantOpenShop() and e.self:MerchantCloseShop() @@ -1293,7 +1295,7 @@ cavedude: Exported TrainDisc to Lua. Kayen: Implemented SE_FrenziedDevestation - increase critical spell chacnce and 2x mana cost for DD spells Kayen: Fixed SE_SpellProcChance - Now works on spell dervived procs cavedude: Added two new NPC special_abilities. ALWAYS_FLEE, which forces the NPC to always flee ignoring FleeIfNotAlone and FLEE_PERCENT which allows you to change the HP an individual NPC will flee at. If no value is set, the rule is used as normal. -cavedude: Fixed an issue where rectangular roamboxes could cause an NPC to get stuck on a single coord. +cavedude: Fixed an issue where rectangular roamboxes could cause an NPC to get stuck on a single coord. cavedude: Added a new roambox column, mindelay allowing you to have more control over the roambox delay. Uleat: Fix for 'sqrt' failure on vs2010 clients image: Added idle zone timer to save CPU cycles. @@ -1317,7 +1319,7 @@ Required SQL: utils/sql/git/2014_02_20_buff_updates.sql == 02/18/2014 == Kayen: Implemented SE_TriggerOnReqCaster - triggers a spell which a certain criteria are met (below X amount of hp,mana,end, number of pets on hatelist) Kayen: Implemented SE_ImprovedTaunt - Locks Aggro On Caster and Decrease other Players Aggro by X% on NPC targets below level Y -Kayen: Fixed an error where SE_ChangeAggro was adding its bonus x 2 for spell generated aggro. (this applies also to spell casting subtlety AA reduction) +Kayen: Fixed an error where SE_ChangeAggro was adding its bonus x 2 for spell generated aggro. (this applies also to spell casting subtlety AA reduction) == 02/14/2014 == Kayen: Fixes for buffs not fading under certain conditions in revised numhits system, and other fixes. @@ -1382,9 +1384,9 @@ Kayen: Changed SE_MitigateMeleeDamageSP -> SE_MeleeThresholdGuard Kayen: Implemented SE_SpellThresholdGuard (Partial Spell Rune that only is lowered if spell hits are over X amount of damage) Kayen: Implemented SE_TriggerSpellThreshold (implemented Trigger effect on X amount of spell damage taken) Kayen: Changed SE_ReduceHealing -> SE_FcHealAmtIncoming (focus limited Add/Remove amount of healing on target by X amount) -Kayen: Change SE_CriticalHealChance2 -> SE_CriticalHealDecay -Kayen: Change SE_CriticalHealOverTime2 -> SE_CriticalRegenDecay -Kayen: Implemented SE_CriticalDotDecay +Kayen: Change SE_CriticalHealChance2 -> SE_CriticalHealDecay +Kayen: Change SE_CriticalHealOverTime2 -> SE_CriticalRegenDecay +Kayen: Implemented SE_CriticalDotDecay Note: 'Decay' effects means the chance to critical decays based on the level of the spell using the effect (like focus decay) Kayen: Implemented SE_FfLimitUseMin (focus limit to require a min amount of numhits value) Kayen: Implemented SE_FcLimitUse (focus to increases numhits count by percent) @@ -1407,14 +1409,14 @@ demonstar55: Stuns from beneficial spells (Harvest) ignore immunity demonstar55: Added classes_required to merchantlist (same bitmask as items) == 12/24/2013 == -Secrets (Akkadius): Perl $client->SilentMessage("Message"); addition, this is a pre-req for a Perl plugin I've shared with EQEmu. This function essentially mimics a player speaking with an NPC - which is used in popup window responses -Secrets: Added functionality to Perl for $client->PlayMP3("name of file"). +Secrets (Akkadius): Perl $client->SilentMessage("Message"); addition, this is a pre-req for a Perl plugin I've shared with EQEmu. This function essentially mimics a player speaking with an NPC - which is used in popup window responses +Secrets: Added functionality to Perl for $client->PlayMP3("name of file"). Usage varies, but typically you can place an MP3/WAV/XMI in the EQDir//sounds, pfs, s3d, or root client folder and it will play through this Perl function. Example, $client->PlayMP3("combattheme1.mp3") or $client->PlayMP3("TUTBTrade1.mp3") - All clients except Secrets of Faydwer and 6.2 have their opcodes identified for this function. The struct + supported params is the same throughout versions. + All clients except Secrets of Faydwer and 6.2 have their opcodes identified for this function. The struct + supported params is the same throughout versions. Use $client->PlayMP3 with an invalid sound file to stop playback or simply wait for it to end. KLS: Added functionality to Lua for Client:PlayMP3(filename) KLS: Added functionality to Lua for Client:SendMarqueeMessage(type, priority/opacity, fade_in_time_ms, fade_out_time_ms, duration_ms, msg) - + == 12/16/2013 == Kayen: Implemented SE_ArcheryDoubleAttack (Chance to do an extra archery attack) Kayen: Implemented SE_ShieldEquipDmgMod (Increase damage in primary hand if shield equiped) @@ -1634,7 +1636,7 @@ Param2: Percent of a normal attack damage to deal (default: 100) Param3: Flat damage bonus to add to the rampage attack (default: 0) Param4: Ignore % armor for this attack (default 0) Param5: Ignore flat armor for this attack (default 0) -Param6: Percent of npc's natual crit that can go toward this rampage (default: 100) +Param6: Percent of npc's natual crit that can go toward this rampage (default: 100) Param7: Flat crit bonus on top of npc's natual crit that can go toward this attack (default 0) SPECATK_AREA_RAMPAGE = 4 @@ -1644,7 +1646,7 @@ Param2: Percent of a normal attack damage to deal (default: 100) Param3: Flat damage bonus to add to the rampage attack (default: 0) Param4: Ignore % armor for this attack (default 0) Param5: Ignore flat armor for this attack (default 0) -Param6: Percent of npc's natual crit that can go toward this rampage (default: 100) +Param6: Percent of npc's natual crit that can go toward this rampage (default: 100) Param7: Flat crit bonus on top of npc's natual crit that can go toward this attack (default 0) SPECATK_FLURRY = 5 @@ -1654,7 +1656,7 @@ Param2: Percent of a normal attack damage to deal (default: 100) Param3: Flat damage bonus to add to the flurry attack (default: 0) Param4: Ignore % armor for this attack (default 0) Param5: Ignore flat armor for this attack (default 0) -Param6: Percent of npc's natual crit that can go toward this attack (default: 100) +Param6: Percent of npc's natual crit that can go toward this attack (default: 100) Param7: Flat crit bonus on top of npc's natual crit that can go toward this attack (default 0) Ex: Normal Flurry with 25% proc rate and 100% crit chance that ignores 500 armor. @@ -1741,7 +1743,7 @@ Upgrade notes: -Some item quests have changed in a subtle way, though it's unlikely any quests are impacted and the thread has more information if you found any of your quests broke. As far as I know for example: PEQ didn't have to update any of its nearly 70 item quests. -Cazic Touch (982) no longer shouts the name of the thing it is targeting without a script. -EVENT_DEATH now triggers before the death is complete. For the old functionality you may use EVENT_DEATH_COMPLETE. It might be a good idea to replace all EVENT_DEATH with EVENT_DEATH_COMPLETE in existing spells. - + We sought to minimize changes required but it's still a bit disruptive so take a few minutes when upgrading to make sure everything is correct. Most notably quest::clearhandin was used in some popular plugins to avoid a dupe involved with its code and now that it's gone those will not function if fixes are not applied. == 06/16/2013 == @@ -1838,7 +1840,7 @@ demonstar55: Fixed stacking issues with SE_Limit* (ex. Unholy Aura Discipline an == 03/18/2013 == Bad_Captain: Fixed zone crash due to merc focus effects & tribute. -Bad_Captain: Fixed merc aggro issues when client in melee range & spell recast timers. +Bad_Captain: Fixed merc aggro issues when client in melee range & spell recast timers. Bad_Captain: Added melee DPS spells/disciplines & support. == 03/17/2013 == @@ -1851,9 +1853,9 @@ Derision: Fixed a couple of memory leaks in Rez code. == 03/14/2013 == JJ: (NatedogEZ) Fix for hate list random never selecting last member of hate list. -Bad_Captain: Fixed Merc spell recast timers. +Bad_Captain: Fixed Merc spell recast timers. Bad_Captain: Changed how Mercs add mobs to their hate lists (should prevent IsEngaged() issues). -Bad_Captain: Initial Caster DPS Merc spell casting AI, including initial Merc stance implementation. +Bad_Captain: Initial Caster DPS Merc spell casting AI, including initial Merc stance implementation. Bad_Captain: Mercs now suspend when their owner dies to prevent them being bugged (until it can be fixed). OPTIONAL SQL: 2013_03_14_Merc_Spells.sql @@ -1887,7 +1889,7 @@ REQUIRED SQL: 2013_03_1_Merc_Rules_and_Equipment.sql KLS: Changed how shared memory works: Instead of System V/windows pagefile shared memory we now have shared memory that's backed by the filesystem. What that means is basically instead of EMuSharedMem(shared library) we now have shared_memory(executable), shared memory will be persistent between runs until you delete or reload it using the shared_memory executable. - + STEPS FOR PEOPLE WHO CAN'T BE BOTHERED TO FIGURE IT OUT: 1) Create a directory in the place you run world/zone named shared and make sure files can write there. 2) Run the shared_memory executable from the same place you run world/zone (it's basically doing the loading we would do on startup so will take a moment). @@ -1946,7 +1948,7 @@ Uleat: Changed conversion of bot armor colors from long to unsigned long. Conver cavedude00: Added heading to start_zones Uleat: Fixed the 'nude' bot issue. Mob::texture was not set to the appropriate value and forcing an unclad body model. Uleat: Fixed the show/hide helm feature. Added rebroadcast of packet so that changes take place immediately instead of after zoning. -KLS: Addressed several (completely stupid and inexcusable) bugs in the avoidance code that made it impossible to dodge and parry in certain situations. +KLS: Addressed several (completely stupid and inexcusable) bugs in the avoidance code that made it impossible to dodge and parry in certain situations. As a note: please don't touch the avoidance code if you don't know what you're doing, seriously. Required SQL: utils/sql/svn/2482_required_start_zones.sql @@ -2033,7 +2035,7 @@ Uleat: Fixed a corpse looting issue where the power source item (slot 9999) was Uleat: Power Source items will now report in 'worn' instead of 'inv' when using #peekinv. == 01/20/2013 == -KLS: intN types have changed to more closely reflect C99 and C++11 types: +KLS: intN types have changed to more closely reflect C99 and C++11 types: intN was an unsigned int of N bits -> it is now a signed int of N bits. sintN was a signed int of N bits -> it has been removed in favor of intN. uintN is still unsigned. @@ -2087,7 +2089,7 @@ Trevius: RoF: Disciplines now update without zoning. == 01/12/2013 == Derision: RoF: Personal Tribute and the Pet Buff Window now work. -Derision: Fixed potential crash in SendPetBuffsToClient. +Derision: Fixed potential crash in SendPetBuffsToClient. Derision: RoF: Accounted for the fact the Duplicate Lore item message now includes the item's name. Trevius: RoF: The Task Selector Window is now functional. @@ -2117,7 +2119,7 @@ REQUIRED SQL: utils/sql/svn/2383_required_group_ismerc.sql -- adds ismerc col OPTIONAL SQL: utils/sql/svn/2380_optional_merc_rules.sql -- Contains rules for mercs including rule to enable mercs OPTIONAL SQL: utils/sql/svn/2380_optional_merc_merchant_npctypes_update.sql -- Contains npc_types & spawn updates for merc merchants in PoK OPTIONAL SQL: utils/sql/svn/2380_optional_merc_data.sql -- Contains basic merc data, template info, & merc merchant entries -OPTIONAL SQL: utils/sql/svn/mercs.sql -- Contains merc stats & armor - to be replaced as needed with updated stats, spells, etc. Allows a complete resourcing of file +OPTIONAL SQL: utils/sql/svn/mercs.sql -- Contains merc stats & armor - to be replaced as needed with updated stats, spells, etc. Allows a complete resourcing of file == 01/07/2013 == Trevius: RoF: /who and /who all now function properly. @@ -2274,7 +2276,7 @@ Secrets/Akkadius: Implemented the customary ability to scale an NPC's spell dama This will allow an NPC to do for example 150% of the damage of their damage spells if spellscale is set to '150' 'healscale' field needs to be set to affect heals in a similar manner Both of these can also be accessed via ModifyNPCStat through 'healscale' and 'spellscale' - + REQUIRED SQL: utils/sql/svn/2283_required_npc_changes.sql ==12/06/2012== @@ -2401,7 +2403,7 @@ Kayen: Fix for crippling blow chance, other minor fixes related to bonuses. ==09/20/2012== Kayen: AA dbase table fixes - Archery Mastery, Fury of Magic. -REQUIRED SQL: utils/sql/svn/2215_required_aa_updates +REQUIRED SQL: utils/sql/svn/2215_required_aa_updates ==09/19/2012== KLS: Faction mods (Race, Class, Deity) can now be added on the fly, without adding database columns or editing the code. Source in the database change, run utils/factionmod.pl, and then drop the faction_list table to convert. @@ -2423,7 +2425,7 @@ Kayen: Removed SE_Twinproc - This is not an actual spell effect, Twin proc is no Kayen: Implemented SE_TwoHandBluntBlock - Chance to block when using two hand blunt weapon (similiar to shield block). Kayen: SE_NegateEffect will now negate all AA, item and spell bonuses for the specified effects. Kayen: Added support to allow for certain bonuses to now properly be calculated when cast as debuffs (ie decrease chance to critical hit) -Kayen: Implemented rule to allow certain bonuses to be calculated by adding together the value from each item, instead of taking the highest value. +Kayen: Implemented rule to allow certain bonuses to be calculated by adding together the value from each item, instead of taking the highest value. *Ie. Add together all worn cleave effects, ferocity effects ect. OPTIONAL SQL: utils/sql/svn/2209_optional_additive_bonus_rule.sql (disabled by default) @@ -2450,7 +2452,7 @@ references: http://www.eqemulator.org/forums/showthread.php?t=35604 - CSD Support Patch http://www.eqemulator.org/forums/showthread.php?t=35629 - CSD Bugged Corpse Patch http://www.eqemulator.org/forums/showthread.php?t=35699 - CSD Bandolier Patch - + cavedude: (demonstar55) Damage shields by default will no longer count towards EXP gain. (Rule also added to change this behaviour.) cavedude: (demonstar55) Extended targets should now clear when aggro is lost using skills. cavedude: (demonstar55) AAs with shorter reuse timers should now reset if the cast failed (interrupted.) @@ -2474,7 +2476,7 @@ ENC AA: Total Domination now implemented with bonus SE_CharmBreakChance. SK AA: Soul Abrasion now implemented with bonus SE_ImprovedDamage2. *Many fixes to previously implemented AA effects. -REQUIRED SQL: utils/sql/svn/2208_required_aa_updates +REQUIRED SQL: utils/sql/svn/2208_required_aa_updates OPTIONAL SQL: utils/sql/svn/2208_optional_aa_stacking_rule.sql (If false will disable AA stacking for all clients) OPTIONAL SQL: utils/sql/svn/2208_optional_EnableSoulAbrasionAA *If using an older server spell file (pre SOF), will need to run this to correctly populate the 'spellgroups' field. @@ -2528,11 +2530,11 @@ Kayen: Taunt skill updated to work consistent with live. *Taunt success chance should be accurate to live. Penalty of not being at max skill lv can be adjusted with (RULE: Combat:TauntSkillFalloff) *Live messages for taunt success and failure are now implemented. (Note: Only NPC races that can 'talk' will say the success message). Bad_Captain: Bots- Fixed an issue when with bot spell timers that could lead to a crash. -Kayen: Fixed SQL files from rev2185+ that were not saved as .sql +Kayen: Fixed SQL files from rev2185+ that were not saved as .sql -OPTIONAL SQL: utils/sql/svn/2189_optional_taunt_rules +OPTIONAL SQL: utils/sql/svn/2189_optional_taunt_rules OPTIONAL SQL: utils/sql/svn/2185_optional_NPCFlurryChance_rule (run this again) - + ==08/16/2012== Kayen: Complete revision of the Death Save and Divine Save effects and related bonuses to be consistent with live. *Death Save (Death Pact/DI) will no longer fire on death. It should only fire when less 15% HP but not killed. @@ -2542,7 +2544,7 @@ Kayen: Complete revision of the Death Save and Divine Save effects and related b do a portion of the original heal value. (Ie DI=8000HP with ToTD-3(60%) your heal will be 8000*0.6 = 4800HP) *Implemented functionality for later expansion Death Save effects (Divine Intercession ect) These can add heal value to the base heal which is limited by level (ie Heal 10,0000+8000 if client less then level 80) - + *Divine Save (AA Unfailing Divinity) only fire upon death of client with this effect and is independent of Death Save effect. *Increased ranks of AA only increase the chance of firing upon death. Heal value does NOT increase with rank. *Upon firing you will still recieve the Divine Aura like buff, which now also correctly removes all determental effects. @@ -2564,7 +2566,7 @@ DRU AA: Enchanted root/Viscid Root (was implemented completely wrong), now uses Added RULE: RootBreakFromSpells - Baseline is/was set at 20% chance from nukes. -REQUIRED SQL: utils/sql/svn/2188_required_aa_updates +REQUIRED SQL: utils/sql/svn/2188_required_aa_updates OPTIONAL SQL: utils/sql/svn/2188_optional_miscspelleffect_rules ==08/13/2012== @@ -2576,10 +2578,10 @@ Kayen: Minor update to duel wield, chance bonuses will be correctly applied to b Kayen: Implemented Perl MOB Quest Object SetFlurryChance(value) (ie 50 = 50% chance for NPC flurry if special atk "F") Kayen: Implemented Perl MOB Quest Object GetFlurryChance(value) returns flurry chance. Kayen: Added rule to adjust server wide flurry chance (Default = 20%) *Perl object will override this. - + Following AA effects are now implemented as bonuses: You must update your 'aa_effects' table with 'required_aa_updates.sql' for these to work. MISC AA: Dead Aim, Precision of the Hunter, Scout's Efficiency will now calculate from bonus SE_Accuracy. -MISC AA: Combat Agility line will now calculate from bonus SE_AvoidMeleeChance. (Now implemented through SoD) +MISC AA: Combat Agility line will now calculate from bonus SE_AvoidMeleeChance. (Now implemented through SoD) MISC AA: Combat Stability line will now calculate from bonus SE_CombatStability. (Now implemented through SoD) MISC AA: Double Riposte, Return Kick now calculated from bonus SE_GiveDoubleRiposte. (New AA's implemented that use this) MISC AA: Natural Durability, should be working correctly now from bonus SE_MaxHP. @@ -2589,33 +2591,33 @@ MISC AA: Pet AA's that give flurry chance will now be applied by bonus, SE_PetFl MAG AA: Elemental Agility was incorrectly giving melee mitigation instead of avoidance, to now use SE_PetAvoidance. MAG AA: Elemental Durability will now add to pet max hp with bonus, SE_PetMaxHP. -REQUIRED SQL: utils/sql/svn/2185_required_aa_updates +REQUIRED SQL: utils/sql/svn/2185_required_aa_updates OPTIONAL SQL: utils/sql/svn/2185_optional_NPCFlurryChance_rule ==08/12/2012== Bad_Captain: Fixed an issue when using bots where you would not get xp when your pet did most of the damage and you are not grouped. -Bad_Captain: Added rule to enable receiving xp from bots not in your group. Defaults to false. +Bad_Captain: Added rule to enable receiving xp from bots not in your group. Defaults to false. -OPTIONAL SQL: utils/sql/svn/2183_optional_bot_xp_rule.sql +OPTIONAL SQL: utils/sql/svn/2183_optional_bot_xp_rule.sql ==08/08/2012== -Kayen: Updates to critical hit calcuations to be consistent with live. - *Baseline critical rate is determined by DEX stat (255 dex = 2%),this baseline is then modified by item,spell and AA critical chance bonuses. +Kayen: Updates to critical hit calcuations to be consistent with live. + *Baseline critical rate is determined by DEX stat (255 dex = 2%),this baseline is then modified by item,spell and AA critical chance bonuses. *Pet critical baseline is determined by the rate from AA effects that give pets the ability to critical. (Can not crit w/o this effect) -Kayen: Slay Undead effect will now be working consistent with live. - *Slay rate is no longer effected by critical hit rate, it uses it's own predetermined rates from the spell effect data. - *Damage modification will now be much closer to that of lives utilizing the spell effects damage modifier. +Kayen: Slay Undead effect will now be working consistent with live. + *Slay rate is no longer effected by critical hit rate, it uses it's own predetermined rates from the spell effect data. + *Damage modification will now be much closer to that of lives utilizing the spell effects damage modifier. *Example(AA Slay Undead I - Rate: 225 (2.25%) Damage Mod: 680%) *Note that that spell effects using Slay Undead (Holyforge) will stack with AA and increase the damage and the rate. -Kayen: Crippling Blow's derived from spell effects will now be calculated consistent with live. - *Cippling blow chance is determined by modifying your critical hit chance. - *Example (Savage Onslaught - 200% chance to crippling blow) If you have a base chance to critical hit of 10% +Kayen: Crippling Blow's derived from spell effects will now be calculated consistent with live. + *Cippling blow chance is determined by modifying your critical hit chance. + *Example (Savage Onslaught - 200% chance to crippling blow) If you have a base chance to critical hit of 10% and you score a critical hit, you will then have a 20% chance to crippling blow. Kayen: Fixed effect for AA 'War Cry' will now provide group fear immunity for duration. Kayen: Few new AA's added including shaman Ancestral Aid and Spirit Channeling. - + Following AA effects are now implemented as bonuses: You must update your 'aa_effects' table with 'required_aa_updates.sql' for these to work. -MISC AA: 'Combat Fury', 'Fury of the Ages' will now calculate from bonus SE_CriticalHitChance. +MISC AA: 'Combat Fury', 'Fury of the Ages' will now calculate from bonus SE_CriticalHitChance. MISC AA: 'Veteran's Wrath' will now calculate from bonus SE_CriticalDmgMod. MISC AA: All AA that give pet criticals will be calculated from SE_PetCriticalHit. PAL AA: 'Slay Undead', 'Vanquish Undead' will now calculate from bonus SE_SlayUndead. (Holyforge now works correctly) @@ -2626,7 +2628,7 @@ REQUIRED SQL: utils/sql/svn/2178_required_aa_updates Kayen: Fixes to rev2176 - Double Attack, EndlessQuiver, Run Speed. REQUIRED SQL: utils/sql/svn/2176_required_aa_updates will need to be applied again for Run Speed fix. -==08/05/2012== +==08/05/2012== Kayen: Complete revision of how double attack chance is calculate to be consistent with live. Kayen: Berserker 'Frenzy' skill attack will now function as it does on live. Gives a chance for 1-3 attacks using frenzy skill specific damage. *Note: This will be a considerable nerf to bersekers because it was previously coded to give actual melee rounds using weapon damage. @@ -2634,19 +2636,19 @@ Kayen: Implemented a few miscellaneous new AA's, mostly from DODH. (These will s Kayen: Added almost all remaining spell effects into spdat.h (~90% defined). Kayen: Alternate Advancement Update: In the process of coverting most of the hard coded AA data out of the source and into the 'aa_effects' table using live data. These effects are then reimplemented using the bonus system in the broadest possible way, most of these effects will -also be useable as regular spell/item effects. This will allow developers in the future to adjust,implement and customize AA effects -without requiring source changes. Beware in doing this, many AA effects that were previously either implemented incorrectly or with values +also be useable as regular spell/item effects. This will allow developers in the future to adjust,implement and customize AA effects +without requiring source changes. Beware in doing this, many AA effects that were previously either implemented incorrectly or with values not consistent with live data will be adjusted to be as accurate as possible. Following AA effects are now implemented as bonuses: You must update your 'aa_effects' table with 'required_aa_updates.sql' for these to work. -MISC AA: All innate run speed AA's have been converted to use bonus SE_BaseMovementSpeed. +MISC AA: All innate run speed AA's have been converted to use bonus SE_BaseMovementSpeed. MISC AA: All bind wound related AA have been converted to use bonuses, SE_ImprovedBindWound, SE_MaxBindWound. MISC AA: Shield Block will now be calculated using bonuses, SE_ShieldBlock. MISC AA: Sinister Strike will now correctly allow your off hand weapon to recieve a weapon damage bonus, SE_SecondaryDmgInc. MISC AA: Strengthened Strike/Vicious Smash/Kick Mastery will now accurately to live add skill damage to respective special abilities. MISC AA: StrikeThrough, Tactical Mastery - will now be calculated from bonus, SE_Strikethrough2. MISC AA: Ferocity, Knight's Advantage ect - will now be calculated from bonus SE_DoubleAttackChance. -MISC AA: Harmonious Attacks, Bestial Frenzy - will allow double attack chance using bonus SE_GiveDoubleAttack (any class can be given this) +MISC AA: Harmonious Attacks, Bestial Frenzy - will allow double attack chance using bonus SE_GiveDoubleAttack (any class can be given this) MISC AA: Weapon Affinity, will now use SE_ProcChance. MISC AA: PunishingBlade, SpeedoftheKnight will now allow an extra 2 Handed attack using bonus, SE_ExtraAttackChance. BRD AA: Fleet of Foot will now correctly allow bards to run over the speed cap using, SE_IncreaseRunSpeedCap. @@ -2658,42 +2660,42 @@ RNG AA: Endless Quiver - Implemented as SE_ConsumeProjectile which gives a perc RNG AA: Archery Mastery- Implemented as SE_ArcheryDamageModifier which gives a percent increase to archery attacks. BER AA: Throwing Mastery - Implemented using SE_DamageModifier. BER AA: Blur of Axes, Vicious Frenzy - will now correctly add skill damage to 'Frenzy' skill attacks. -ROG AA: Triple Backstab, SiezedOpportunity, Chaotic Stab - as bonus, SE_TripleBackstab, SE_FrontalBackstabChance,SE_FrontalBackstabMinDmg. +ROG AA: Triple Backstab, SiezedOpportunity, Chaotic Stab - as bonus, SE_TripleBackstab, SE_FrontalBackstabChance,SE_FrontalBackstabMinDmg. REQUIRED SQL: utils/sql/svn/2176_required_aa_updates Optional SQL: utils/sql/svn/2176_optional_FrenzyBonus_rule Optional SQL: utils/sql/svn/2176_optional_aa_expansion_sof_fix (Allow AA to show proper expansion in SOF+ clients) -==08/01/2012== +==08/01/2012== Bad_Captain: Fixed bot compile issue introduced in Rev 2171. Bad_Captain: Integrated Kayen's skill attack code with bots' code, as well as other changes from Rev 2171. ==07/31/2012== Akkadius: (KLS) Fix for global_player.pl synchronization. There was an issue where certain subroutines were not passing as global -==07/26/2012== +==07/26/2012== Kayen: Fixed: SE_ImmuneFleeing - Will now disable fleeing if used after mob begins to run, will not effect fleeing from fear. -Kayen: Implemented Perl MOB Quest Object SetDisableMelee(1=Disabled) - Prevents the ability to auto attack. +Kayen: Implemented Perl MOB Quest Object SetDisableMelee(1=Disabled) - Prevents the ability to auto attack. Kayen: Implemented Perl MOB Quest Object IsMeleeDisabled() Sorvani: corrected build name for queryserv project in all the various build types. - -==07/24/2012== + +==07/24/2012== Kayen: Implemented: SE_HundredHands (incorrectly marked as already implemented) - Increases/Decrease actual weapon delay by % of value. Kayen: Implemented: Missing modifications from the Archery/Throw damage pathways. *SkillAmount/SkillDamageTaken mods, ability to Block/Dodge/Parry ranged attacks (Can not riposte), other new focus/mod effects. -Kayen: Fixed: aaThrowingMastery will no longer be applied 2x per throw. +Kayen: Fixed: aaThrowingMastery will no longer be applied 2x per throw. Kayen: Implemented: Damage bonus to skill attacks from specific armor slot AC (ie KICK from BOOT AC) can adjust with rule 'SpecialAttackACBonus' Kayen: SE_MinDamageModifier should now apply to skill specific effects and to special attacks. Kayen: Implemented: Complete revision of SE_SkillAttack. (+ required various fixes/adjustments to functions related to special attacks) *This spell effect performs a physical attack from a specific skill with a set weapon damage value and chance to hit modifier. *Attacks will now calculate correctly and use their actual respected pathways utilizing all skill specific mods/bonus. -Kayen: Implemented Perl MOB Quest Object DoMeleeSkillAttackDmg(target, weapon_damage, skill, chance_mod, focus, CanRiposte) -Kayen: Implemented Perl MOB Quest Object DoArcheryAttackDmg(target,NULL,NULL, weapon_damage, chance_mod, focus) -Kayen: Implemented Perl MOB Quest Object DoThrowingAttackDmg(target,NULL,NULL, weapon_damage, chance_mod, focus) +Kayen: Implemented Perl MOB Quest Object DoMeleeSkillAttackDmg(target, weapon_damage, skill, chance_mod, focus, CanRiposte) +Kayen: Implemented Perl MOB Quest Object DoArcheryAttackDmg(target,NULL,NULL, weapon_damage, chance_mod, focus) +Kayen: Implemented Perl MOB Quest Object DoThrowingAttackDmg(target,NULL,NULL, weapon_damage, chance_mod, focus) Optional SQL: utils/sql/svn/2171_optional_SpecialAttackACBonus_rule - + ==07/18/2012== Kayen: Implemented NPC Special Attack 'K'. Immune to Dispell. Kayen: Fixed SE_RangedProc to be applied after zoning, calculate proc rate correctly, utilize numhits. @@ -2708,7 +2710,7 @@ Kayen: Implemented: SE_SpellProcChance, SE_CharmBreakChance, SE_BalanceMana, SE_ SE_CriticalHealOverTime2, SE_CriticalHealChance2, SE_SkillDamageAmount2, SE_LimitSpellSkill, SE_LimitClass SE_LimitExcludeSkill, SE_ShieldBlock, SE_BlockBehind Kayen: Implemented: SE_Empathy - A focus limited debuff which causes (spells/skill attacks) cast on target to do +X amount more damage. - *This spell effect is often limited using SE_LimitSpellSkill. + *This spell effect is often limited using SE_LimitSpellSkill. *(Ie. If empathy value is 1000 and limited to evocation, all nukes on target using evoc. will get +1000 damage) Kayen: Implemented: SE_SpellPowerIncrease - Can be used to apply a worn/buff focus effect with limits for... 1) Additional bard instrument modifiers. @@ -2716,16 +2718,16 @@ Kayen: Implemented: SE_SpellPowerIncrease - Can be used to apply a worn/buff foc 3) Increase effectiviess of casted/disc Skill Attacks. 4) General use stackable Healing/Damage focus. 5) Increase the value of melee runes by focus amount. -Kayen: SE_HPtoMana will no longer drain your HP before the spell is finished casting. Effect now handled as a bonus using best value. -Kayen: SE_SpellVulunerability will now correctly calculate and apply the highest value if target has mulitple effects. -Kayen: Fixed an issue where DOTs cast by an NPC on another NPC would not generate hate per tick. Improved how we calc damage from DOTs. +Kayen: SE_HPtoMana will no longer drain your HP before the spell is finished casting. Effect now handled as a bonus using best value. +Kayen: SE_SpellVulunerability will now correctly calculate and apply the highest value if target has mulitple effects. +Kayen: Fixed an issue where DOTs cast by an NPC on another NPC would not generate hate per tick. Improved how we calc damage from DOTs. Kayen: SE_BlockSpellEffect was implemented incorrectly and has been revised to SE_NegateSpellEffect. *It is not meant to block buffs, rather it negates the specific spell bonuses or effects from buffs you already have. *Currently functional with any effect that is handled under bonuses, and focus effects. Kayen: Vastly improved how we handle melee/spell runes/partial mitigation runes ect. Should be significantly more efficient. *Melee/Spell Mitigation runes and ManaAbsorbPercentDamage effects will now use the best mitigation value if multiple effects. Kayen: Improved the process we use to get spell/item focus effects. An initial check if the client has a specific focusType -is done while checking for item/spell bonuses. GetFocusEffect will no longer check all inventory and buff slots every cast for the +is done while checking for item/spell bonuses. GetFocusEffect will no longer check all inventory and buff slots every cast for the focusType if that focusType doesn't exist on the client. This should improve performance since these checks are done 10-20x per cast/proc. ==07/15/2012== @@ -2735,7 +2737,7 @@ Secrets: Fixed an issue with saylinks above 255 characters. If you do want to us Bad_Captain: Fixed another bug that allowed pets to steal xp when using bots. Bad_Captain: Bots: Persisting spell & discipline timers. Bad_Captain: Bots: Bot pets will no longer continuously try to get behind a mob if they are tanking. -Bad_Captain: Bots: Fixed potential duplicate lore item bug when trading with bots. +Bad_Captain: Bots: Fixed potential duplicate lore item bug when trading with bots. Bad_Captain: Bots: Fixed bot compile issue from Rev 2160. Bad_Captain: Bots: Fixed multiple issues with #bot spawn and #bot botgroup load (c0ncrete). Bad_Captain: Bots: Implemented new #bot defensive command for Warriors and Knights. Includes most of the code required to implement disciplines for bots. Requires disc AI & disc lists. @@ -2758,9 +2760,9 @@ Kayen: Updated spdat.h with many new live spell effects that were previously 'un ==07/05/2012== Kayen: Implemented: SE_Manaburn: Drains mana for damage/heal at a defined ratio up to a defined maximum amount of mana. -Kayen: Implemented: SE_CastonNumHitFade: Casts a spell when a buff fades due to its numhits being depleted. +Kayen: Implemented: SE_CastonNumHitFade: Casts a spell when a buff fades due to its numhits being depleted. Kayen: Implemented: support for all remaining live spell effects that use 'numhits'. - Significantly optimized how all spell effects that utilize 'numhits' are handled throughout the source. + Significantly optimized how all spell effects that utilize 'numhits' are handled throughout the source. Kayen: Fixed: SE_DefensiveProc - Proc rate will now be calculated similiar to live. *Spell specific rate modifcations will now work. Kayen: Added Rule: Combat:AvgDefProcsPerMinute (Default = 2.0) Determine defensive procs per minute. Kayen: Added Rule: Combat:DefProcPerMinAgiContrib (Default = 0.075) Determines how much agility effects proc rate. @@ -2768,9 +2770,9 @@ Kayen: Added Rule: Combat:DefProcPerMinAgiContrib (Default = 0.075) Determines h Optional SQL: utils/sql/svn/2159_optional_defensiveproc_rules ==06/29/2012== -Kayen: Implemented Perl NPC Quest Object GetSlowMitigation() -Kayen: Implemented Perl NPC Quest Object GetAttackSpeed() -Kayen: Implemented Perl NPC Quest Object GetAccuracyRating() +Kayen: Implemented Perl NPC Quest Object GetSlowMitigation() +Kayen: Implemented Perl NPC Quest Object GetAttackSpeed() +Kayen: Implemented Perl NPC Quest Object GetAccuracyRating() Kayen: Fixed: Slow Mitigation (was not loading from dbase),optimized its application in source and added lives slow mitigation messages. Slow mitigation Messages: 'slighty' 0.00 - 0.25 'partial' 0.25-0.75 'mostly' 0.75-1 Kayen: Implemented: SE_AttackSpeed4: 'Inhibit Melee' effect works different then regular haste/slow effect @@ -2781,13 +2783,13 @@ Kayen: Added Rule: Spells:CharismaEffectiveness (Default = 10) Deterimes how muc Kayen: Added Rule: Spells:CharmBreakCheckChance (Default = 25) Determines percent chance for a charm break check to occur each buff tick. Kayen: Reworked code to that handles charm breaks/lull to be accurate to live and more functional in general. 1) Each charm buff tick there is a default 25% [Rule.CharmBreakCheckChance] chance to trigger a spell resistance check. - 2) Spell resistance check then adds an additional bonus resist modification based casters Charisma at a default + 2) Spell resistance check then adds an additional bonus resist modification based casters Charisma at a default ratio of 10 CHA per -1 resist mod [Rule.CharismaEffectiveness] 3) If resisted (ie Charm is to break) Total Domination AA is then applied to give another chance to maintain the charm. Kayen: Implemented: SE_AdditionalHeal: Focus Effect that adds an additional heal amount to the casted spell. Kayen: Implemented: SE_CastOnCure: Casts a spell on cured target. Kayen: Implemented: SE_CastOnCurer: Casts a spell on the curer of the target. -Kayen: Modified: Mob::TryFadeEffect to avoid interaction wtih Twinproc effect. +Kayen: Modified: Mob::TryFadeEffect to avoid interaction wtih Twinproc effect. cavedude: (Uleat) Multiple changes and additions to doors, per the forums. Optional SQL: utils/sql/svn/2156_optional_charm_break_rule.sql @@ -2812,8 +2814,8 @@ Kayen: Implemented SE_FF_Damage_Amount - Focus/Buff that adds damage to the cast Kayen: Adjusted SE_SpellDamage to be handled properly as a focus effect. Kayen: Extra damage from Focuses is now calculated correctly for DoTs Kayen: Implemented Perl NPC Quest Object SetSpellFocusDMG(focus amount) - Focus all npc direct/dot damage spells by value -Kayen: Implemented Perl NPC Quest Object SetSpellFocusHeal(focus amount) - Focus all npc healing spells by value -Kayen: Implemented Perl Mob Quest Object ModSkillDmgTaken(skill_num,value) - Set a weakness/bonus of weapon attacks to a Mob +Kayen: Implemented Perl NPC Quest Object SetSpellFocusHeal(focus amount) - Focus all npc healing spells by value +Kayen: Implemented Perl Mob Quest Object ModSkillDmgTaken(skill_num,value) - Set a weakness/bonus of weapon attacks to a Mob - Example: mob can be set to take 5% more damage from blunt weapons - ModSkillDmgTaken(0,5) - Stacks with spell/item bonuses - Setting skill to -1 will effect all skills Kayen: Implemented Perl Mob Quest Object GetModSkillDmgTaken(skill_num) - Returns only the quest skill mod for specified skill @@ -2825,7 +2827,7 @@ Kayen: Implemented Perl Mob Quest Object ModVulnerability(resist type, value) - - Example: mob can be set to take 5% more damage from Fire spells (2,5); - Applied effect will stack with spell buff effects. - If a resist type SPECIFIC and a resist type ALL mod are applied to the same MOB, the SPECIFIC value will be used if spell cast on MOB is the same resist type. -Kayen: Implemented Perl Mob Quest Object GetModVulnerability(resist type) - Return the quest applied value for each resist (ALL = -1) +Kayen: Implemented Perl Mob Quest Object GetModVulnerability(resist type) - Return the quest applied value for each resist (ALL = -1) Optional SQL: utils/sql/svn/2154_optional_rule_spell_procs_resists_falloff.sql @@ -2885,12 +2887,12 @@ Trevius: VoA - The AA Window now populated AAs. AA hotkeys can be created, but sorvani: Bard AE DoTs should now be affected by mods correctly. ==04/19/2012== -cavedude: quest::buryplayercorpse will now despawn corpses in zones other than the client's current location, preventing dupes. +cavedude: quest::buryplayercorpse will now despawn corpses in zones other than the client's current location, preventing dupes. cavedude: Added quest::summonallplayercorpses (same syntax as quest::summonburriedplayercorpse) which will depop and summon all of the player's corpses, buried or not. cavedude: Fixed a bug that caused some summoned corpses to use a static 5 minute decay timer. cavedude: Changed player_corpses_backup schema to match player_corpses. If the rule is enabled, corpse backups will now be created for players that are high enough level to drop items to their corpses. cavedude: Added a rule to determine whether or not to remove player corpse backups older than 2 weeks. -cavedude: #corpse now has options to depop a single player corpse, or every corpse belonging to a single player. +cavedude: #corpse now has options to depop a single player corpse, or every corpse belonging to a single player. cavedude: You can now specify if a faction hit is temporary (is removed when player camps/zones) or whether or not to display the faction hit to the player in-game. If temp in npc_faction_entries is set to: 0 (Default): Faction is permanent, player recieves a message. (Same functionality as we had previously.) @@ -2898,7 +2900,7 @@ If temp in npc_faction_entries is set to: 2: Faction is temporary, player recieves a message. 3: Faction is permanent, but player does not recieve a message. cavedude: Added an optional argument to quest::faction to utilize the functionality and values listed above. -cavedude: Added minlevel and maxlevel to lootdrop_entries. The player credited with the kill (most hate) has their level checked against both columns, and if they are lower than the specified minlevel, or higer than the max, that item entry if it exists is removed from the NPC before it becomes a corpse. +cavedude: Added minlevel and maxlevel to lootdrop_entries. The player credited with the kill (most hate) has their level checked against both columns, and if they are lower than the specified minlevel, or higer than the max, that item entry if it exists is removed from the NPC before it becomes a corpse. cavedude: You can now specify if a NPC automatically repops (rerolls against their spawngroup) or depops after the reverse spawn timer is up. If despawn in spawngroup is set to: 0 (Default): Do not depop or repop, no depop timer is set. (Same functionality as we had previously.) @@ -2991,10 +2993,10 @@ JJ: Removed additional library and include directories from windows projects as Sorvani: Resurrections effects will again be applied to characters who are in a zone where combat is not allowed (GL, PoK etc) when they receive a rez. ==03/19/2012== -Bad_Captain: Bots: A few fixed for #bot stance command +Bad_Captain: Bots: A few fixed for #bot stance command ==03/19/2012== -Bad_Captain: Bots: Implemented bot stances. See forum post. +Bad_Captain: Bots: Implemented bot stances. See forum post. Required SQL: utils/sql/svn/2107_required_bot_stances.sql @@ -3078,11 +3080,11 @@ Reccomended SQL: (ON QueryServ's database, NOT the main db) utils/sql/queryserve Secrets: v90 toolset bugs reported on forums. In order to use Perl 5.14, you MUST upgrade to VS2010 (preferrably ultimate), if you do not have vs2010, it will default to Perl 5.10 and you will be unable to use the much more stable Perl 5.14, an upgrade is reccomended. Secrets: Fixed Windows x64 rulesys bug where it would store them as an x64 version of an int (long long) and mess up the ordering of pointers, resulting in certain rules not loading properly. NOTE: For those having issues compiling vs2010, there will be a guide up shortly to help you set up your dev environment. In the meantime, make sure your "Additional Include" and "Additional Library" paths -are correct and pointing at the right location. Failure to do so will result in mysql header/lib and perl header/lib issues. If you are looking for zlib x64, it's in SVN. +are correct and pointing at the right location. Failure to do so will result in mysql header/lib and perl header/lib issues. If you are looking for zlib x64, it's in SVN. ==11/30/2011== Secrets: Implemented Visual Studio 11 and Visual Studio 10 project/sln files. -Secrets: Added x64 configuration settings to VS11 and VS10 project files/SLN files. +Secrets: Added x64 configuration settings to VS11 and VS10 project files/SLN files. Secrets: Changed WIN32 define to _WINDOWS across the board. In the case where WIN32 is needed over WIN64 (ie; assembly references) there is still WIN32 defines and a new WIN64 define. Secrets: Upgraded ActiveState Perl lib define to 5.14 -- others still work but it is strongly reccomended to use 5.14 as it contains less memory leaks. Secrets: Added Zlib 1.2.3 x64 to the SVN for use with the windows solution files. @@ -3117,7 +3119,7 @@ Lerxst: Fixed crash when calculating random focus effect from an augment when th Also added some bulletproofing in case that isn't the only place an invalid spell id might be passed. ==11/16/2011== -Akkadius: Added the ability to specify doors/objects/ground spawns to load for all versions of the same zone regardless by setting the value to -1. +Akkadius: Added the ability to specify doors/objects/ground spawns to load for all versions of the same zone regardless by setting the value to -1. This reduces serious redundancy of copying the same data over and over for instances. Required SQL: @@ -3325,8 +3327,8 @@ Congdar: update check for Lore ==07/17/2011== Congdar: (pfyon, Criimson)Various bot tweaks Caryatis: Updated My/Showstats window. - -Required SQL: + +Required SQL: utils/sql/svn/1974_required_bot_spells_update ==07/16/2011== @@ -3468,9 +3470,9 @@ JJ: Implemented New Tanaan Crafting Mastery tradeskill AA. Each rank allows an a Note: For servers with players who have tradeskills already above the limit without previously purchasing these AAs will freeze the chance to increase until they purchase the proper amount of NTCM AAs. ==05/24/2011== -KLS: Changed Mob::NPCSpecialAttacks(atk, perm) to Mob::NPCSpecialAttacks(atk, perm, [reset = 1], [remove = 0]). -This should allow one to add and remove flags individually without having to reset everything each time. -ex: +KLS: Changed Mob::NPCSpecialAttacks(atk, perm) to Mob::NPCSpecialAttacks(atk, perm, [reset = 1], [remove = 0]). +This should allow one to add and remove flags individually without having to reset everything each time. +ex: $npc->NPCSpecialAttacks(RQ, 0); //would enable the npc to rampage and quad. $npc->NPCSpecialAttacks(S, 0, 0); //Would enable the NPC to summon as well as rampage and quad by telling it to set S but don't reset the earlier flags. @@ -3482,11 +3484,11 @@ $npc->NPCSpecialAttacks(S, 0, 0, 1); //Would enable the NPC rampage and quad by JJ: (Akkadius) Fixed camera shake usage output. ==05/22/2011== -KLS and Co: +KLS and Co: -All liquid should count for skill ups. --Finished unified quest interface... this is fairly large and will probably have a few problems here and there; report them to me and ill fix them asap. -The goal behind the system is to allow more than one scripting system to work at a time (though with limited interaction due to pre-existing implementation limitation). -This was a feature requested by the Dalaya community as they plan to merge back to the eqemu codebase and clients but are stuck with an old parser and thousands of files +-Finished unified quest interface... this is fairly large and will probably have a few problems here and there; report them to me and ill fix them asap. +The goal behind the system is to allow more than one scripting system to work at a time (though with limited interaction due to pre-existing implementation limitation). +This was a feature requested by the Dalaya community as they plan to merge back to the eqemu codebase and clients but are stuck with an old parser and thousands of files that can't be realistically rewritten in a short time frame. ==05/20/2011== @@ -3515,7 +3517,7 @@ Optional SQL: utils/sql/svn/1889_optional_skill_cap_rule.sql ==05/02/2011== Secrets: Added OP_CameraEffect for Titanium. -Secrets: Added commands: #reloadallrules, #reloadrulesworld, and #camerashake. These default to +Secrets: Added commands: #reloadallrules, #reloadrulesworld, and #camerashake. These default to Secrets: Added optional "global" flag as item 5 in the $mob->CameraEffect() quest object. This does #camerashake, but in quest form. Secrets: #reloadallrules reloads rules in every single zone plus world. #reloadrulesworld reloads the rules in world only. Secrets: #camerashake shakes the camera in every zone with required args intensity and duration. @@ -3835,7 +3837,7 @@ utils/sql/svn/1747_optional_HoT_zone_and_zonepoints.sql For spawns etc, go to the PEQ logs repo for data collected by robregen. -Optional SQL: +Optional SQL: utils/sql/svn/1750_optional_sql_reflect_rule.sql @@ -3848,7 +3850,7 @@ Caryatis: (bad_captain) Removed the level bonus granted to standing mana regen, Caryatis: (bad_captain) Bots have been updated, see forums for complete details(new command: #bot showstats). Caryatis: (Secrets) Haste values can exceed 127%(ie Can o' Whoop Ass). -Optional SQL: +Optional SQL: utils/sql/svn/1746_optional_sql_bot_manaregen ==11/22/2010== @@ -3865,10 +3867,10 @@ Caryatis: Fix for Healrate effect Caryatis: Implemented ManaAbsorbPercentDamage, ReduceSkillTimer, HpToMana and LimitSpellGroup effects. Caryatis: Updated DamageModifier effect to be more robust. -Required SQL: +Required SQL: utils/sql/svn/1737_required_sql_rule_and_aa_update -Optional SQL: +Optional SQL: utils/sql/svn/1736_optional_sql_feral_swipe ==11/14/2010== @@ -3917,7 +3919,7 @@ Caryatis: hStr = Increases endurance pool, endurance regen(25), and the maximum Caryatis: hSta = Increases hit point pool, hit point regen(25), and the maximum amount of hit point regen a character can have(25). Also increases endurance pool, endurance regen(25), and the maximum amount of endurance regen a character can have(25). Caryatis: hAgi = Increases endurance pool, endurance regen(25), and the maximum amount of endurance regen a character can have(25). Also increases the chance to dodge an attack(25), grants a bonus to defense skill(10). Caryatis: hDex = Increases endurance pool, endurance regen(25), and the maximum amount of endurance regen a character can have(25). Also increases damage done by ranged attacks(1), improves chance to successfully assassinate or headshot(10), and improves the chance to riposte, block, and parry incoming attacks(25). -Caryatis: hCha = Improves reaction rolls with some NPCs(25) and increases the amount of faction you gain or lose when faction is adjusted(5). +Caryatis: hCha = Improves reaction rolls with some NPCs(25) and increases the amount of faction you gain or lose when faction is adjusted(5). Caryatis: AA Focus revamped to support new effects Caryatis: Disciplines will no longer be dispelled @@ -3927,7 +3929,7 @@ utils/sql/svn/1719_optional_triggerOnCastAAs.sql ==11/09/2010== Caryatis: Implemented MaxHPChange, SkillDmgTaken, Endurance Pool and Stun Resist. -Optional SQL: +Optional SQL: utils/sql/svn/1717_optional_rule_bash_stun_chance.sql ==11/07/2010== @@ -4106,9 +4108,9 @@ Trevius: (Secrets) Added quest::creategroundobjectfrommodel(modelname, x, y, z, Trevius: (Secrets) quest::creategroundobject(itemid, x, y, z, heading) now returns object ID. Trevius: (Secrets) Added Object List Iteration functions GetObjectByDBID(id), GetObjectByID(id), GetObjectList(). Trevius: (Secrets) exported Objects to Perl with the following commands: - IsGroundSpawn(), Close(), Delete(reset_state=false), StartDecay(), DeleteItem(index), IsObject(), - Save(), SetID(set_id), ClearUser(), DBID(), GetID(), GetX(), GetY(), GetZ(), GetHeading(), VarSave(), - GetType(), SetType(type), GetIcon(), SetIcon(icon), GetItemID(), SetItemID(itemid), SetLocation(x, y, z), + IsGroundSpawn(), Close(), Delete(reset_state=false), StartDecay(), DeleteItem(index), IsObject(), + Save(), SetID(set_id), ClearUser(), DBID(), GetID(), GetX(), GetY(), GetZ(), GetHeading(), VarSave(), + GetType(), SetType(type), GetIcon(), SetIcon(icon), GetItemID(), SetItemID(itemid), SetLocation(x, y, z), SetX(XPos), SetY(YPos), SetZ(ZPos), SetHeading(heading), SetModelName(name), GetModelName(), Repop(), Depop(). ==07/25/2010== @@ -4429,7 +4431,7 @@ KLS: Other merges from spell branch, some more to come. Tell me if there are an ==04/15/2010== cavedude (Leere): Increased the number of tradeskill favorites that can be stored client side to 500. -cavedude (Leere): Fixed avgcoin. +cavedude (Leere): Fixed avgcoin. cavedude (renoofturks): Fixes to SK harm touch. cavedude: Lowered snare movement speed when fleeing to 41% or higher to allow "Snare" to prevent movement at all levels. gaeorn: multiple login server support in world. protocol to update login server account information from within game. "trusted" field for world accounts in loginserver to limit where account updates can come from. in eqemu_config.xml, use to specify first of multiple login servers. increment the number to specify additional login servers. NOTE: be sure to replace the entry or you will be limited to just one login server. MULTIPLE LOGIN SERVERS WILL NOT WORK WITH MINILOGIN. @@ -5214,7 +5216,7 @@ Derision: Fix for mobs in pathing enabled zones fleeing at low health at full ru Derision: Made rule Map:UseClosestZ default to false. Only set true if using azone2 generated maps (specifically those for EQG zones). realityincarnate: Starting cities are now tracked for newly created characters. realityincarnate: Enabled the /setstartcity command for characters without a start city assigned. -realityincarnate: Added $client->SetStartZone and $client->GetStartZone perl commands. +realityincarnate: Added $client->SetStartZone and $client->GetStartZone perl commands. KLS: Petitions will assign id based on character id instead of this stupid desyncing junk. KLS: Max NPC name length increased to 50 characters up from 30. KLS: Save on zone success will now commit immediately instead of possibly delaying. Hopefully will help with some of the situations where people zone to bad locations. @@ -5277,7 +5279,7 @@ KLS: Implemented OP_ClearSurname KLS: (gaeorn) Several 64-bit compile and runtime fixes KLS: (gaeorn) NPC wander back rules. Trevius: #texture will now allow player races to retain armor tint for both PCs and NPCs. -realityincarnate: bug fix for changing max_hp with the modifynpcstat command +realityincarnate: bug fix for changing max_hp with the modifynpcstat command Required SQL: /utils/sql/svn/704_rules.sql @@ -5361,7 +5363,7 @@ Warning: If you do not have a spells_new table yet, you must create one and load After creating the table, you can import or export your spells from the spells_us.txt file by using the scripts: import_spells.pl export_spells.pl - + The easiest way to import your spell file to the table is to move the import_spells.pl file to your main server directory where your spells_us.txt file and your eqemu_config.xml file are. Then run the following command at a command prompt: perl import_spells.pl @@ -5426,7 +5428,7 @@ KLS: Added support for item clicklevel and item clicklevel2 Trevius: Increased spellbook from 400 to 480 in the Player Profile for use with SoF (60 page spell book) Trevius: SoF - Added new functions to SoF.cpp for converting Slot IDs between Titanium and SoF Trevius: SoF - Corrected the location of the spellbook field in the Player Profile to fix an issue with loading spells -WildcardX: Check in of the start of what will become the new BOTS subsystem/framework. This is far from done so just continue to use the existing EQBOTS code you have been using. +WildcardX: Check in of the start of what will become the new BOTS subsystem/framework. This is far from done so just continue to use the existing EQBOTS code you have been using. KLS: Change to dangerous item inst aug creation, hopefully addresses segfault on 64 bit linux. KLS: Hopefully fix for pets not giving adventure credit to players in ldon instances. @@ -5572,7 +5574,7 @@ KLS: Added Rules: (Character, UseXPConScaling), (Character, LightBlueModifier), Derision: Bandolier bug fix. Derision: Equipped items that should confer an extra potion belt slot now do so in Titanium and the 6.2 client (was already working in SoF). Angelox: Bots- (Congdar) fixed Bot illusion / change form spells as to who is affected. -Angelox: Bots- added check to Bot pacify for casting from a distance. +Angelox: Bots- added check to Bot pacify for casting from a distance. ==05/18/2009== @@ -5653,7 +5655,7 @@ Derision: Fixed a bug where a mob aggroed would sometimes appear to run past it' Trevius: (Erde) The Web Tool now shows the name of the process running each Dynamic zone (example: dynamic_01) ==05/06/2009== -Angelox: Bots: Added command '#bot shrinkme' requires Shaman or Beastlord (defaults to Shaman). +Angelox: Bots: Added command '#bot shrinkme' requires Shaman or Beastlord (defaults to Shaman). Derision: Added redux_aa2, redux_rate2 fields to aa_actions. Derision: Improved Hasty Exit should now reduce the reuse time of Escape. Derision: The reuse timer in the AA window now shows the reuse time reduced by applicable AAs. @@ -5714,7 +5716,7 @@ Cripp: Fix for the web interface for those using Perl 5.10. Congdar: Bots - randomized face/hair etc. so they don't all look the same. Fixed Bard AE songs. ==4/30/2009== -Congdar: Bots - bots can now use bows, new command '#bot archery'. Added Ranger archery AA's. Reduced chat mana spam. Tweaked spell ai. Fixed memory leak. Updated '#bot corpse summon'. +Congdar: Bots - bots can now use bows, new command '#bot archery'. Added Ranger archery AA's. Reduced chat mana spam. Tweaked spell ai. Fixed memory leak. Updated '#bot corpse summon'. Derision: SoF - AAs affecting stats now show the correct stats in the client. Derision: Tweaked base resists to match the client. gatorman: Fix for QuickSummoning AA (to include Call of the Hero) @@ -5733,7 +5735,7 @@ Derision: SoF: Ranged attack animations. Derision: When shooting a bow, there is no longer a superfluous 1HS animation. ==4/23/2009== -Trevius: (realityincarnate) Added new quest command quest::varlink(item_id) for putting item links into variables. +Trevius: (realityincarnate) Added new quest command quest::varlink(item_id) for putting item links into variables. Derision: Fix for Tradeskill combines where a LORE ingredient is returned. Derision: Fix for pet names containing spaces losing the space after zoning/camping. Derision: Fixed bug where Return Home sent players bound in Grobb to Qeynos/Unknown Zone. @@ -5752,7 +5754,7 @@ Wolftousen: Rage Volley no longer requires you to have an thrown weapon in your Wolftousen: Rage Volley now uses the proper damage calculation and is not based on the item you have in the ranged slot Wolftousen: Rave Volley can no longer be dodged/blocked/parried/reposted. Wolftousen: Procs from Buffs have been tweaked to go off more often Wolftousen: Players will now receive the "proper" bonus HP for stamina above 255. -Wolftousen: Knight class Tactical Mastery AA was implemented and should now give the strike through message +Wolftousen: Knight class Tactical Mastery AA was implemented and should now give the strike through message renoofturks: Created rule Aggro:StunAggroMod to dial in on aggro of stun based attacks. cavedude: Reverse DS and some DS will now cause aggro on intial cast. realityincarnate: Controllable boats should now work. Please see: http://eqemulator.net/forums/showthread.php?p=167892#post167892 for additonal information. @@ -5928,7 +5930,7 @@ cavedude: Corrected ZEM for AAs. cavedude: (Thanks to demonstar55) Pet Affinity will no longer effect charmed pets. cavedude: (realityincarnate) Bard songs that require instruments will now require them. -Please note: XP gain has pretty much been overhauled. You may need tweak the multiplier rules for your server. +Please note: XP gain has pretty much been overhauled. You may need tweak the multiplier rules for your server. Optional SQL: @@ -6042,7 +6044,7 @@ cybernine186: Optional system to ensure GMs are logging on from a known IP. Trevious: SoF - Drakkin now gain stats from items and weapons now work for combat Trevious: SoF - Drakkin now start with Common Tongue, Dragon and Elder Dragon Languages maxed -Required SQL: utils/sql/svn/340_gm_ips.sql +Required SQL: utils/sql/svn/340_gm_ips.sql ==02/16/2009== Trevius: SoF - The Item Struct should be aligned almost perfectly now. @@ -6153,9 +6155,9 @@ Note: You must move the patch_SoF.conf file from /utils into your server directo ==01/31/2009== Derision: Tweaks to temp merchant list window updates. -cavedude00: Renamed the AugSlotUnk items columns to AugSlotVisible. +cavedude00: Renamed the AugSlotUnk items columns to AugSlotVisible. -Required SQL: utils/sql/svn/292_augslots.sql +Required SQL: utils/sql/svn/292_augslots.sql ==01/29/2009== KLS: VC71 solution files. @@ -6165,7 +6167,7 @@ Angelox: Bots: Added a directory with the BOT Makefiles for Windows and Linux. ==01/27/2009== Derision: Bazaar bug fix. -Angelox: Bots: Added command '#bot runeme' (Enchanter Rune spells) +Angelox: Bots: Added command '#bot runeme' (Enchanter Rune spells) Optional SQL: utils/sql/svn/285_optional_bot_spell_update.sql (removes auto-runes) @@ -6177,7 +6179,7 @@ Derision: Fixed a buffer overflow problem. ==01/19/2009== cavedude00: Increased itemid limit to 120,000. -cavedude00: Meditate will now skill up at a more Live Like speed. +cavedude00: Meditate will now skill up at a more Live Like speed. ==01/18/2009== Derision: Fixed a cause of zone crashes. @@ -6205,7 +6207,7 @@ Derision: Mapped field in LogServer_Struct to allow voice macro window to be ope Derision: Removed code that stole all your money if you logged on with more than 1 million of any denomination of coin on your person. ==01/11/2009== -Angelox: Bots: Added rule 'EQOffline:BotCount' defaults to 5, for desired amount of bots in the group - values are 0-5 (0 means bots are disabled, max limit is 5). +Angelox: Bots: Added rule 'EQOffline:BotCount' defaults to 5, for desired amount of bots in the group - values are 0-5 (0 means bots are disabled, max limit is 5). Derision: Mail/Chatchannels: Added sanity check on packet size in EQPacket::ChatDecode. Derision: Mail/Chatchannels: Increased stream timeout from 45 to 135 seconds. Derision: Mail: Use correct opcodes for sending Headers. **utils/mail_opcodes.conf updated** @@ -6225,7 +6227,7 @@ Angelox: Bots: '#bot sow wolf' should not affect pets anymore. Derision: Chatserver: Added some extra syntax error checking to prevent crashes. Angelox: Bots: Added Wizard class and level check to the '#bot evac' command Angelox: Bots: Added '#bot invis see' for see invisible -Required SQL: +Required SQL: DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=80; Congdar: Fix NPC::RemoveItem(uint16 item_id) to uint32 to work with items that have id's larger than 65535. '#npcloot remove [itemid]' will now successfully remove items with larger id's from npc loot. Congdar: Bots: Visible gear will now show correctly when trading/equipping bots. @@ -6245,7 +6247,7 @@ Congdar: Bots: update command #bot inventory list Now shows gear as item links Optional SQL: update npc_types set lastname='' where isbot=1; ==01/01/2009== -Angelox: Bots: Added command '#bot sow' (Druid has options) +Angelox: Bots: Added command '#bot sow' (Druid has options) Angelox: Bots: Added command '#bot levitate'. Angelox: Bots: Added command '#bot invis' - (has options). @@ -6558,8 +6560,8 @@ Congdar: Clone NoDrop removal code to NoRent, Lore, NoTrade. Optionally enabled Congdar: Bot code cleanup, method call reduction Congdar: Bot DoubleAttack method is more like clients Congdar: Remove AFK leveling with bots -Angelox: Added command '#Bot evac' for Druid bots -Angelox: Added command '#Bot target calm' for Enchanter or Cleric bots +Angelox: Added command '#Bot evac' for Druid bots +Angelox: Added command '#Bot target calm' for Enchanter or Cleric bots Trevius: Removed the * 10 multiplier from the SE_ProcChance since it isn't needed KLS: Zone appearance should update for players zoning. KLS: Added command #modifynpcstat @@ -6609,7 +6611,7 @@ AndMetal: (seveianrex) Hate w[h]iped on CoH AndMetal: (via Yeahlight) New command: #aggrozone. Requires 100 status by default ==10/19/2008 -Angelox: Added a start to Bot tracking - Thanks Derision for all the help and know-how. +Angelox: Added a start to Bot tracking - Thanks Derision for all the help and know-how. Derision: Altered damage shield message processing to be more like (the same?) as live. Derision: If it exists, damage shield types are read from a new table (damageshieldtypes) Derision: If no entry exists in the table, a default based on resist type is used. @@ -6627,10 +6629,10 @@ KLS: Disarm trap will no longer fail to disarm traps on success. KLS: Increased range on disarm trap slightly. KLS: Change to MakeRandomInt() and MakeRandomFloat() KLS: Int generation be nearly 100% or more faster in most cases and float generation should be slightly faster in most cases -cavedude00: (seveianrex) Slay Undead Fix -cavedude00: (seveianrex) Levitate effect will no longer be removed in cases where you have two stacked lev spells and the first wears off. +cavedude00: (seveianrex) Slay Undead Fix +cavedude00: (seveianrex) Levitate effect will no longer be removed in cases where you have two stacked lev spells and the first wears off. cavedude00: (seveianrex) Hate list will now be cleared following CoH -cavedude00: (seveianrex) Group members will now see tradeskill emotes. +cavedude00: (seveianrex) Group members will now see tradeskill emotes. Required SQL: ALTER TABLE `traps` DROP `spawnchance`; @@ -6737,7 +6739,7 @@ Derision: (Spoon/Andmetal) Rez spells with effectdescnum != 82 or 39067 (6.2 spe Derision: Corrected message when interrupting a spell with Shift-S. Derision: Fix to display Frogloks correctly in /who all and /who all froglok (half of the change came from a post by Theeper on the forums). -Required SQL: +Required SQL: ALTER TABLE `tasks` ADD `minlevel` TINYINT UNSIGNED NOT NULL DEFAULT '0', ADD `maxlevel` TINYINT UNSIGNED NOT NULL DEFAULT '0'; @@ -6793,7 +6795,7 @@ cavedude00: (AndMetal) Slippery Attacks AA cavedude00: (trevius/Derision) Additional IP limiting rules cavedude00: (Congdar) Further PC main and second hand attack fixes cavedude00: (Derision) Fix for percent heals -cavedude00: (Theeper) Fix for quest:itemlink() +cavedude00: (Theeper) Fix for quest:itemlink() cavedude00: (Rocker8956) More work on zone instancing Optional SQL: @@ -6950,7 +6952,7 @@ KLS: (irv0) Fix for out of order ack not being sent in some situations. KLS: (Derision) Pet bar OOC update fix. KLS: Should have made client pets unable to give experience, untested but should work. KLS: Healing aggro should function better for people not on the hate list. -KLS: Some work on public tradeskill objects saving their content between uses. +KLS: Some work on public tradeskill objects saving their content between uses. ==06/22/2008 KLS: Changed world/clientlist.cpp's line endings back to unix style line endings @@ -7025,7 +7027,7 @@ CREATE TABLE `hackers` ( `zone` text, `date` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, PRIMARY KEY (`id`) -) AUTO_INCREMENT=8; +) AUTO_INCREMENT=8; ==04/26/2008 KLS: Should have fixed discipline and combat ability timer overlap. @@ -7051,7 +7053,7 @@ Rogean: Fixed #hideme, it won't show you zone in and then disappear anymore. (Re Rogean: Fixed /summon. Rogean: Changes to /who all and GM's: * GM * Tags will no longer show up unless your #gm on - You will not show up to players of lower status if your /anon and #gm on, if your #gm off and /anon you will show up as + You will not show up to players of lower status if your /anon and #gm on, if your #gm off and /anon you will show up as a normal player, regardless of statuses. ==04/09/2008 @@ -7158,7 +7160,7 @@ KLS: Ironed out some quirks between min hit and the AC code that was keeping it KLS: (Knightly)#time should no longer be off by an hour, $zonehour, $zonemin and $zonetime are now exported to perl KLS: Added player quests, player quests are quests designed to be attached to player processes such as zoning and clicking objects. KLS: Player quests are loaded as either 'quests/zonename/player.pl' or as a template in 'quests/template/player.pl' -KLS: Player quests will export qglobals the client should be able to see and the following events are available for player quests: +KLS: Player quests will export qglobals the client should be able to see and the following events are available for player quests: EVENT_TIMER with var $timer EVENT_CLICKDOOR with var $doorid EVENT_LOOT with vars $looted_id and $looted_charges @@ -7286,7 +7288,7 @@ NPC:OOCRegen (Default: 0) KLS: Fixed logic on duration 7 formula.. again. ==12/02/2007 -KLS: Reworked buff duration formula 7 calculations. +KLS: Reworked buff duration formula 7 calculations. KLS: (Cripp)Added (Missing?) pathing z rules. ==11/30/2007 @@ -7400,7 +7402,7 @@ KLS: NPCs should now accept signals while engaged in combat. FatherNitwit: Quick fix for Divine Aura on pets exploit (KingMort) ==10/09/2007 -KLS: Tweaked my AC system a bit, should get less default defense and the system should be much more lienent overall, +KLS: Tweaked my AC system a bit, should get less default defense and the system should be much more lienent overall, goal is to allow people to see more of a benefit from having AC. ==09/24/2007 @@ -7412,7 +7414,7 @@ KLS: Small changes to the order in the getweapondamage code to make it a little KLS: Added a field that allows you to override a factions innate desire to assist their own faction in battle to the npc_faction table Required SQL: -alter table npc_faction add column ignore_primary_assist tinyint(3) not null default 0; +alter table npc_faction add column ignore_primary_assist tinyint(3) not null default 0; ==09/02/2007 KLS: Fixed an issue with items adding stats to NPCs exponentially. @@ -7431,7 +7433,7 @@ KLS: The error in acmod() should no longer complain as much. Required: Opcode files have changed, be sure to update to the latest .conf files. ==08/30/2007 -KLS: Reworked Mob::GetWeaponDamage(), it will now return damage done by any item and NULL and will return a +KLS: Reworked Mob::GetWeaponDamage(), it will now return damage done by any item and NULL and will return a value zero or less if we can't hit a mob with said item KLS: Applied new GetWeaponDamage() to Attack and Special Attack code KLS: Seperated Finishing Blow code to it's own function @@ -7601,8 +7603,8 @@ WildcardX: Area of effect beneficial spells will no longer affect non-player cha ==2/16/2007 KLS: Updating Changelog AA work to be more clear as well as the required SQL. -Required SQL: -alter table altadv_vars add column cost_inc tinyint(4) not null default 0; +Required SQL: +alter table altadv_vars add column cost_inc tinyint(4) not null default 0; Optionally: Source in AA_data.sql @@ -7817,7 +7819,7 @@ CREATE TABLE skill_caps ( ); ==11/07/2006 -KLS: Changed how critical hits work, as a result things that could not crit before like special attacks, now can. +KLS: Changed how critical hits work, as a result things that could not crit before like special attacks, now can. KLS: Cleave like effects should be fixed to do an increase of your already standing chance to critical instead of a flat increase. KLS: Reworked much of the special attack code. KLS: Applied haste to combat skill timers, they should give trouble much less often. @@ -7863,7 +7865,7 @@ FatherNitwit: Tweak for 64 bit pointer support in the item code of the struct st FatherNitwit: More const cleanup in npc.h ==10/22/2006 -FatherNitwit: added EVENT_COMBAT triggered when an NPC engages any form of +FatherNitwit: added EVENT_COMBAT triggered when an NPC engages any form of FatherNitwit: combat and when combat is over. $combat_state signals which. FatherNitwit: Hopefully fixed d_meele_texture FatherNitwit: Cleaned up a lot of const related issues (attrs, AAs, and more) @@ -8172,7 +8174,7 @@ FatherNitwit: Fixed countless rediculous things about guilds. FatherNitwit: Any change in guild state should update in real time now. FatherNitwit: Guild leadership may need re-establishing since it changed from account to char based. FatherNitwit: Guild structure in database has changed significantly. -Note: custom rank stuff is too much work for me to preserve in the DB, since I doubt +Note: custom rank stuff is too much work for me to preserve in the DB, since I doubt anybody uses it, so if you have them, you can figure it out yourself or remake them. Required SQL: ALTER TABLE character_ ADD class TINYINT NOT NULL DEFAULT 0; @@ -8515,7 +8517,7 @@ ALTER TABLE npc_types ADD AGI MEDIUMINT UNSIGNED NOT NULL DEFAULT '75'; ALTER TABLE npc_types ADD _INT MEDIUMINT UNSIGNED NOT NULL DEFAULT '75'; ALTER TABLE npc_types ADD WIS MEDIUMINT UNSIGNED NOT NULL DEFAULT '75'; ALTER TABLE npc_types ADD CHA MEDIUMINT UNSIGNED NOT NULL DEFAULT '75'; - + ==09/23/2005 FatherNitwit: Inverted XY coordinates on in zone objects and ground spawns. FatherNitwit: Maybe fixed quests setting a waypoint in EVENT_WAYPOINT @@ -8679,7 +8681,7 @@ Doodman: New item structure is in: Item fields now match the dump from (utils/load_13thfloor_items.pl) eqitems.13th-floor.org Item table convert script: utils/items-0.6.0-DR2-0.6.1-DR1-convert.sql - + ==01/10/2005 FatherNitwit: Disable sleep() in perl, it is bad news. FatherNitwit: Fixed guild MOTD at login (hopefully). @@ -8949,7 +8951,7 @@ WR Merges: - pets should actually taunt now. - rouge pets should backstab now. - optional: group buffs hit group pets now. -- fixed memory blur chances +- fixed memory blur chances - several minor group tweaks, should make groups more stable - improved duel messages - optional random luclin attributes for NPCs with boring faces @@ -9341,7 +9343,7 @@ kathgar: Fixed, SE_Fear, SE_SpinStun, SE_Charm for fixed duration spells and no Added member bool client to Buff_Struct to aid in above fix Fixed solar's changelog entry where he spelled my name wrong No EQLive fixes in this change -solar: fixed a bug that was causing people to be set as gm when they shouldn't +solar: fixed a bug that was causing people to be set as gm when they shouldn't ==2/15/2004 solar: characters being created are checked for validity now. thanks to @@ -9457,7 +9459,7 @@ solar: Buff fading should work for slots besides the first one now. This will solar: sense heading skill will now improve as you move around ==1/19/2004= -Scorpious2k: fixed faction command for quests +Scorpious2k: fixed faction command for quests LethalEncounter: Fixed a problem with queued cursor items. solar: fixes to #gassign @@ -9512,24 +9514,24 @@ LethalEncounter: Fixed loot messages. LethalEncounter: Fixed loot so right click autoequips correctly now. ==1/4/04== -MYRA: changed to new opcode for follow -MYRA: changed to new opcode for taunt -MYRA: use new (5.x) Status labels in who for telnet connection -MYRA: Added code to depop at end of grid for wander type 4 -MYRA: Added wander type 4 (single run) -MYRA: fixed eval in ExportVar per Eglin -MYRA: corrected spelling for var $uguildrank for event_timer (was $uguildrang) -MYRA: added vars $status & $cumflag per Eglin -MYRA: added vars $mobid & $mlevel per Eglin -MYRA: added missing commands + itemlink to perl -MYRA: added EVAL & KEEPERR to eval per Eglin's recommendation -MYRA: restore missing commands for qst type files & add itemlink -MYRA: fixed comma bug for me command -MYRA: fixed comma bug for echo command -MYRA: fixed comma bug for say command -MYRA: fixed comma bug for emote command -MYRA: fixed comma bug for shout command -MYRA: added itemlink(ItemNumber) command +MYRA: changed to new opcode for follow +MYRA: changed to new opcode for taunt +MYRA: use new (5.x) Status labels in who for telnet connection +MYRA: Added code to depop at end of grid for wander type 4 +MYRA: Added wander type 4 (single run) +MYRA: fixed eval in ExportVar per Eglin +MYRA: corrected spelling for var $uguildrank for event_timer (was $uguildrang) +MYRA: added vars $status & $cumflag per Eglin +MYRA: added vars $mobid & $mlevel per Eglin +MYRA: added missing commands + itemlink to perl +MYRA: added EVAL & KEEPERR to eval per Eglin's recommendation +MYRA: restore missing commands for qst type files & add itemlink +MYRA: fixed comma bug for me command +MYRA: fixed comma bug for echo command +MYRA: fixed comma bug for say command +MYRA: fixed comma bug for emote command +MYRA: fixed comma bug for shout command +MYRA: added itemlink(ItemNumber) command ==1/2/04== @@ -9678,7 +9680,7 @@ solar: HP wasn't being updated to client properly and would fall out of sync LethalEncounter: Fixed a bug in doors that would cause triggered doors to go into an endless loop and crash. ==11/13/03== -kathgar: Fixed a crash when calling ZSList::FindByZoneID() when sending an invalid zone number. +kathgar: Fixed a crash when calling ZSList::FindByZoneID() when sending an invalid zone number. Stack was corrupt in the backtrace, so I am not sure what called it in this way. LethalEncounter: Fixed animation bug with attack. LethalEncounter: Fixed Tradeskills (again) @@ -9712,7 +9714,7 @@ IsEngaged(), so it always returned false, now it uses AutoAttackEnabled() in its Image: Reversed the AICheckCloseSpells if statements, now sanity checks before distance check, uses less CPU usage. ==11/05/03== -LethalEncounter: Updated all of the opcodes that were changed in the patch today. +LethalEncounter: Updated all of the opcodes that were changed in the patch today. LethalEncounter: Refined AA's some, added table to hold the timers for AA's so users can exploit them. Look in db.sql for the table. diff --git a/zone/aa.cpp b/zone/aa.cpp index 3718e3391..e81345086 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -1104,7 +1104,7 @@ void Client::SendAATable() { uint32 i; for(i=0;i < MAX_PP_AA_ARRAY;i++){ - aa2->aa_list[i].AA = aa[i]->AA; + aa2->aa_list[i].AA = aa[i]->value ? aa[i]->AA : 0; // bit of a hack to prevent expendables punching a hole aa2->aa_list[i].value = aa[i]->value; aa2->aa_list[i].charges = aa[i]->charges; } @@ -1402,8 +1402,6 @@ bool Client::SetAA(uint32 aa_id, uint32 new_value) { aa[cur]->value = new_value; if(new_value > 0) aa[cur]->AA++; - else - aa[cur]->AA = 0; aa[cur]->charges = charges; return true; } @@ -1411,8 +1409,12 @@ bool Client::SetAA(uint32 aa_id, uint32 new_value) { aa[cur]->value = new_value; if(new_value > 0) aa[cur]->AA++; - else - aa[cur]->AA = 0; + aa[cur]->charges = charges; + return true; + } + // hack to prevent expendable exploit, we should probably be reshuffling the array to fix the hole + else if(aa[cur]->value == 0 && new_value == 1 && aa[cur]->AA == aa_id) { + aa[cur]->value = new_value; aa[cur]->charges = charges; return true; } diff --git a/zone/client.cpp b/zone/client.cpp index f7e160851..ef41578ef 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -549,17 +549,22 @@ bool Client::SaveAA(){ } } m_pp.aapoints_spent = spentpoints + m_epp.expended_aa; + int highest = 0; for (int a = 0; a < MAX_PP_AA_ARRAY; a++) { if (aa[a]->AA > 0) { // those with value 0 will be cleaned up on next load if (first_entry != 1){ rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, slot, aa_id, aa_value, charges)" " VALUES (%u, %u, %u, %u, %u)", character_id, a, aa[a]->AA, aa[a]->value, aa[a]->charges); first_entry = 1; + } else { + rquery = rquery + StringFormat(", (%u, %u, %u, %u, %u)", character_id, a, aa[a]->AA, aa[a]->value, aa[a]->charges); } - rquery = rquery + StringFormat(", (%u, %u, %u, %u, %u)", character_id, a, aa[a]->AA, aa[a]->value, aa[a]->charges); + highest = a; } } auto results = database.QueryDatabase(rquery); + /* This is another part of the hack to clean up holes left by expendable AAs */ + rquery = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u AND `slot` >= %d", character_id, highest); return true; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index b9ac81d89..5603023e5 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1452,11 +1452,20 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) "`character_alternate_abilities` " "WHERE `id` = %u ORDER BY `slot`", this->CharacterID()); results = database.QueryDatabase(query); i = 0; + int offset = 0; // offset to fix the hole from expendables for (auto row = results.begin(); row != results.end(); ++row) { - i = atoi(row[0]); + i = atoi(row[0]) - offset; m_pp.aa_array[i].AA = atoi(row[1]); m_pp.aa_array[i].value = atoi(row[2]); m_pp.aa_array[i].charges = atoi(row[3]); + /* A used expendable could cause there to be a "hole" in the array, this is very bad. Bad things like keeping your expendable after use. + We could do a few things, one of them being reshuffling when the hole is created or defer the fixing until a later point, like during load! + Or just never making a hole in the array and just have hacks every where. Fixing the hole at load really just keeps 1 hack in Client::SendAATable + and keeping this offset that will cause the next AA to be pushed back over the hole. We also need to clean up on save so we don't have multiple + entries for a single AA. + */ + if (m_pp.aa_array[i].value == 0) + offset++; } for (uint32 a = 0; a < MAX_PP_AA_ARRAY; a++){ uint32 id = aa[a]->AA; From 226bb4f3b2dc87bea704d1d47507f61f2ab0c4fc Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 8 Jun 2015 02:08:32 -0400 Subject: [PATCH 117/129] Fix delete statement --- zone/client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/client.cpp b/zone/client.cpp index ef41578ef..53d39ea7b 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -564,7 +564,7 @@ bool Client::SaveAA(){ } auto results = database.QueryDatabase(rquery); /* This is another part of the hack to clean up holes left by expendable AAs */ - rquery = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u AND `slot` >= %d", character_id, highest); + rquery = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u AND `slot` > %d", character_id, highest); return true; } From 5186d3a2eff3a6f8f4bc05097c41469e5dbdc718 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 8 Jun 2015 18:04:08 -0400 Subject: [PATCH 118/129] Make filtering out OP_ClientUpdate less aggressive to fix spinning toons If we are too aggressive filtering out the same position packets it's possible for toons to continue to spin indefinitely. Instead of just not sending the update when the position is the same we keep a tally of how many we get and stop once a threshold (6) is reached. --- zone/client.cpp | 1 + zone/client.h | 3 +++ zone/client_packet.cpp | 13 ++++++++++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/zone/client.cpp b/zone/client.cpp index 53d39ea7b..e4d49e979 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -208,6 +208,7 @@ Client::Client(EQStreamInterface* ieqs) npclevel = 0; pQueuedSaveWorkID = 0; position_timer_counter = 0; + position_update_same_count = 0; fishing_timer.Disable(); shield_timer.Disable(); dead_timer.Disable(); diff --git a/zone/client.h b/zone/client.h index de093990c..4a166feed 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1447,6 +1447,9 @@ private: Timer position_timer; uint8 position_timer_counter; + // this is used to try to cut back on position update reflections + int position_update_same_count; + PTimerList p_timers; //persistent timers Timer hpupdate_timer; Timer camp_timer; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 5603023e5..2ae2babac 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4457,9 +4457,20 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) // Outgoing client packet float tmpheading = EQ19toFloat(ppu->heading); + /* The clients send an update at best every 1.3 seconds + * We want to avoid reflecting these updates to other clients as much as possible + * The client also sends an update every 280 ms while turning, if we prevent + * sending these by checking if the location is the same too aggressively, clients end up spinning + * so keep a count of how many packets are the same within a tolerance and stop when we get there */ - if (!FCMP(ppu->y_pos, m_Position.y) || !FCMP(ppu->x_pos, m_Position.x) || !FCMP(tmpheading, m_Position.w) || ppu->animation != animation) + bool pos_same = FCMP(ppu->y_pos, m_Position.y) && FCMP(ppu->x_pos, m_Position.x) && FCMP(tmpheading, m_Position.w) && ppu->animation == animation; + if (!pos_same || (pos_same && position_update_same_count < 6)) { + if (pos_same) + position_update_same_count++; + else + position_update_same_count = 0; + m_Position.x = ppu->x_pos; m_Position.y = ppu->y_pos; m_Position.z = ppu->z_pos; From 81744281895e777b029fd6ef7abdce9c5c58c325 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 10 Jun 2015 23:10:00 -0400 Subject: [PATCH 119/129] Export SetPseudoRoot to Lua --- zone/lua_mob.cpp | 7 ++++++- zone/lua_mob.h | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index 60a42760c..f4d7d64cb 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -1851,6 +1851,10 @@ int Lua_Mob::CanBuffStack(int spell_id, int caster_level, bool fail_if_overwrite return self->CanBuffStack(spell_id, caster_level, fail_if_overwrite); } +void Lua_Mob::SetPseudoRoot(bool in) { + Lua_Safe_Call_Void(); + self->SetPseudoRoot(in); +} luabind::scope lua_register_mob() { return luabind::class_("Mob") @@ -2168,7 +2172,8 @@ luabind::scope lua_register_mob() { .def("BuffFadeBySlot", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeBySlot) .def("BuffFadeBySlot", (void(Lua_Mob::*)(int,bool))&Lua_Mob::BuffFadeBySlot) .def("CanBuffStack", (int(Lua_Mob::*)(int,int))&Lua_Mob::CanBuffStack) - .def("CanBuffStack", (int(Lua_Mob::*)(int,int,bool))&Lua_Mob::CanBuffStack); + .def("CanBuffStack", (int(Lua_Mob::*)(int,int,bool))&Lua_Mob::CanBuffStack) + .def("SetPseudoRoot", (void(Lua_Mob::*)(bool))&Lua_Mob::SetPseudoRoot); } luabind::scope lua_register_special_abilities() { diff --git a/zone/lua_mob.h b/zone/lua_mob.h index 54c388ed4..023fedd2a 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -169,7 +169,7 @@ public: bool CastSpell(int spell_id, int target_id, int slot, int cast_time, int mana_cost); bool CastSpell(int spell_id, int target_id, int slot, int cast_time, int mana_cost, int item_slot); bool CastSpell(int spell_id, int target_id, int slot, int cast_time, int mana_cost, int item_slot, int timer, int timer_duration); - bool CastSpell(int spell_id, int target_id, int slot, int cast_time, int mana_cost, int item_slot, int timer, int timer_duration, + bool CastSpell(int spell_id, int target_id, int slot, int cast_time, int mana_cost, int item_slot, int timer, int timer_duration, int resist_adjust); bool SpellFinished(int spell_id, Lua_Mob target); bool SpellFinished(int spell_id, Lua_Mob target, int slot); @@ -352,6 +352,7 @@ public: void BuffFadeBySlot(int slot, bool recalc_bonuses); int CanBuffStack(int spell_id, int caster_level); int CanBuffStack(int spell_id, int caster_level, bool fail_if_overwrite); + void SetPseudoRoot(bool in); }; #endif From 8dccc8bf90fbd66b0b99bbc8fe3ad2a6b332c605 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 10 Jun 2015 23:52:56 -0400 Subject: [PATCH 120/129] Fix Pseudo Rooted for runspeed --- zone/mob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index 1591cc057..b107565bc 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -615,7 +615,7 @@ int Mob::_GetWalkSpeed() const { } int Mob::_GetRunSpeed() const { - if (IsRooted() || IsStunned() || IsMezzed()) + if (IsRooted() || IsStunned() || IsMezzed() || IsPseudoRooted()) return 0; int aa_mod = 0; From b1829e929e641d5f9f0a96a353f368b521d4f2c1 Mon Sep 17 00:00:00 2001 From: Uleat Date: Fri, 12 Jun 2015 19:25:43 -0400 Subject: [PATCH 121/129] Updated SessionStats methodology --- changelog.txt | 3 +++ common/eq_stream.cpp | 42 ++++++++++++++++++++++++++---------------- common/eq_stream.h | 20 +++++++++++++++++++- 3 files changed, 48 insertions(+), 17 deletions(-) diff --git a/changelog.txt b/changelog.txt index d97f20459..6c42a23ca 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 06/12/2015 == +Uleat: Adjusted SessionStats to better reflect a sister implementation + == 06/07/2015 == Uleat: Implemented optional rule for using disenchanted bags. Action triggers at the same point that temporary items are removed. Optional SQL: utils/sql/git/optional/2015_06_07_TransformSummonedBagsRule.sql diff --git a/common/eq_stream.cpp b/common/eq_stream.cpp index c48cba0fd..3438f533c 100644 --- a/common/eq_stream.cpp +++ b/common/eq_stream.cpp @@ -72,6 +72,8 @@ void EQStream::init(bool resetSession) { RateThreshold=RATEBASE/250; DecayRate=DECAYBASE/250; BytesWritten=0; + sent_packet_count = 0; + received_packet_count = 0; SequencedBase = 0; NextSequencedSend = 0; @@ -464,37 +466,45 @@ void EQStream::ProcessPacket(EQProtocolPacket *p) } break; case OP_SessionStatRequest: { - if(p->Size() < sizeof(SessionStats)) + if(p->Size() < sizeof(ClientSessionStats)) { Log.Out(Logs::Detail, Logs::Netcode, _L "Received OP_SessionStatRequest that was of malformed size" __L); break; } #ifndef COLLECTOR - SessionStats *Stats=(SessionStats *)p->pBuffer; + ClientSessionStats *ClientStats=(ClientSessionStats *)p->pBuffer; Log.Out(Logs::Detail, Logs::Netcode, _L "Received Stats: %lu packets received, %lu packets sent, Deltas: local %lu, (%lu <- %lu -> %lu) remote %lu" __L, - (unsigned long)ntohl(Stats->packets_received), (unsigned long)ntohl(Stats->packets_sent), (unsigned long)ntohl(Stats->last_local_delta), - (unsigned long)ntohl(Stats->low_delta), (unsigned long)ntohl(Stats->average_delta), - (unsigned long)ntohl(Stats->high_delta), (unsigned long)ntohl(Stats->last_remote_delta)); - uint64 x=Stats->packets_received; - Stats->packets_received=Stats->packets_sent; - Stats->packets_sent=x; - NonSequencedPush(new EQProtocolPacket(OP_SessionStatResponse,p->pBuffer,p->size)); - AdjustRates(ntohl(Stats->average_delta)); + (unsigned long)ntohl(ClientStats->packets_received), (unsigned long)ntohl(ClientStats->packets_sent), (unsigned long)ntohl(ClientStats->last_local_delta), + (unsigned long)ntohl(ClientStats->low_delta), (unsigned long)ntohl(ClientStats->average_delta), + (unsigned long)ntohl(ClientStats->high_delta), (unsigned long)ntohl(ClientStats->last_remote_delta)); + + AdjustRates(ntohl(ClientStats->average_delta)); if(GetExecutablePlatform() == ExePlatformWorld || GetExecutablePlatform() == ExePlatformZone) { - if(RETRANSMIT_TIMEOUT_MULT && ntohl(Stats->average_delta)) { + if (RETRANSMIT_TIMEOUT_MULT && ntohl(ClientStats->average_delta)) { //recalculate retransmittimeout using the larger of the last rtt or average rtt, which is multiplied by the rule value - if((ntohl(Stats->last_local_delta) + ntohl(Stats->last_remote_delta)) > (ntohl(Stats->average_delta) * 2)) { - retransmittimeout = (ntohl(Stats->last_local_delta) + ntohl(Stats->last_remote_delta)) + if ((ntohl(ClientStats->last_local_delta) + ntohl(ClientStats->last_remote_delta)) > (ntohl(ClientStats->average_delta) * 2)) { + retransmittimeout = (ntohl(ClientStats->last_local_delta) + ntohl(ClientStats->last_remote_delta)) * RETRANSMIT_TIMEOUT_MULT; } else { - retransmittimeout = ntohl(Stats->average_delta) * 2 * RETRANSMIT_TIMEOUT_MULT; + retransmittimeout = ntohl(ClientStats->average_delta) * 2 * RETRANSMIT_TIMEOUT_MULT; } if(retransmittimeout > RETRANSMIT_TIMEOUT_MAX) retransmittimeout = RETRANSMIT_TIMEOUT_MAX; Log.Out(Logs::Detail, Logs::Netcode, _L "Retransmit timeout recalculated to %dms" __L, retransmittimeout); } } + + ServerSessionStats *ServerStats = (ServerSessionStats *)p->pBuffer; + + //ServerStats->RequestID = ClientStats->RequestID; // no change + ServerStats->ServerTime = htonl(Timer::GetCurrentTime()); + ServerStats->packets_sent_echo = ClientStats->packets_sent; // still in htonll format + ServerStats->packets_received_echo = ClientStats->packets_received; // still in htonll format + ServerStats->packets_sent = htonll(GetPacketsSent()); + ServerStats->packets_received = htonll(GetPacketsReceived()); + + NonSequencedPush(new EQProtocolPacket(OP_SessionStatResponse, p->pBuffer, p->size)); #endif } break; @@ -1103,8 +1113,8 @@ EQProtocolPacket *p=nullptr; void EQStream::Process(const unsigned char *buffer, const uint32 length) { -static unsigned char newbuffer[2048]; -uint32 newlength=0; + static unsigned char newbuffer[2048]; + uint32 newlength=0; if (EQProtocolPacket::ValidateCRC(buffer,length,Key)) { if (compressed) { newlength=EQProtocolPacket::Decompress(buffer,length,newbuffer,2048); diff --git a/common/eq_stream.h b/common/eq_stream.h index 72eb53cdd..0bdfeab53 100644 --- a/common/eq_stream.h +++ b/common/eq_stream.h @@ -71,7 +71,7 @@ struct SessionResponse { }; //Deltas are in ms, representing round trip times -struct SessionStats { +struct ClientSessionStats { /*000*/ uint16 RequestID; /*002*/ uint32 last_local_delta; /*006*/ uint32 average_delta; @@ -83,6 +83,16 @@ struct SessionStats { /*038*/ }; +struct ServerSessionStats { +/*000*/ uint16 RequestID; +/*002*/ uint32 ServerTime; +/*006*/ uint64 packets_sent_echo; +/*014*/ uint64 packets_received_echo; +/*022*/ uint64 packets_sent; +/*030*/ uint64 packets_received; +/*038*/ +}; + #pragma pack() class OpcodeManager; @@ -158,6 +168,9 @@ class EQStream : public EQStreamInterface { int32 BytesWritten; + uint64 sent_packet_count; + uint64 received_packet_count; + Mutex MRate; int32 RateThreshold; int32 DecayRate; @@ -265,11 +278,13 @@ class EQStream : public EQStreamInterface { void AddBytesSent(uint32 bytes) { bytes_sent += bytes; + ++sent_packet_count; } void AddBytesRecv(uint32 bytes) { bytes_recv += bytes; + ++received_packet_count; } virtual const uint32 GetBytesSent() const { return bytes_sent; } @@ -288,6 +303,9 @@ class EQStream : public EQStreamInterface { return bytes_recv / (Timer::GetTimeSeconds() - create_time); } + const uint64 GetPacketsSent() { return sent_packet_count; } + const uint64 GetPacketsReceived() { return received_packet_count; } + //used for dynamic stream identification class Signature { public: From de81850dd9f471fa897206d099dc3b8615b9cf81 Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Thu, 18 Jun 2015 23:42:59 -0400 Subject: [PATCH 122/129] Fixed possible bot crashes due to nullptr conflict. --- zone/attack.cpp | 2 +- zone/mob_ai.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index a229f197e..ffdb0ec41 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -4249,7 +4249,7 @@ void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttack } #ifdef BOTS - if (this->IsPet() && this->GetOwner()->IsBot()) { + if (this->IsPet() && this->GetOwner() && this->GetOwner()->IsBot()) { this->TryPetCriticalHit(defender,skill,damage); return; } diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 650ba350c..045c2cb75 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1105,7 +1105,7 @@ void Mob::AI_Process() { } #ifdef BOTS - if (IsPet() && GetOwner()->IsBot() && target == GetOwner()) + if (IsPet() && GetOwner() && GetOwner()->IsBot() && target == GetOwner()) { // this blocks all pet attacks against owner..bot pet test (copied above check) RemoveFromHateList(this); From 56e064751baac7e17ebc7f6f11089306cdebe687 Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Fri, 19 Jun 2015 01:42:01 -0400 Subject: [PATCH 123/129] Fixed more possible nullptr related bot crashes. --- zone/bot.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 8b3741443..bb711edbf 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -11359,7 +11359,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { // pull if(!strcasecmp(sep->arg[1], "pull")) { Mob *target = c->GetTarget(); - if(target == nullptr || target == c || target->IsBot() || (target->IsPet() && target->GetOwner()->IsBot())) + if(target == nullptr || target == c || target->IsBot() || (target->IsPet() && target->GetOwner() && target->GetOwner()->IsBot())) { c->Message(15, "You must select a monster"); return; @@ -12381,7 +12381,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(!strcasecmp(sep->arg[1], "ai") && !strcasecmp(sep->arg[2], "mez")) { Mob *target = c->GetTarget(); - if(target == nullptr || target == c || target->IsBot() || (target->IsPet() && target->GetOwner()->IsBot())) + if(target == nullptr || target == c || target->IsBot() || (target->IsPet() && target->GetOwner() && target->GetOwner()->IsBot())) { c->Message(15, "You must select a monster"); return; @@ -12631,7 +12631,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { { Mob *target = c->GetTarget(); - if(target == nullptr || target->IsClient() || target->IsBot() || target->IsPet() && target->GetOwner()->IsBot()) + if(target == nullptr || target->IsClient() || target->IsBot() || (target->IsPet() && target->GetOwner() && target->GetOwner()->IsBot())) c->Message(15, "You must select a monster"); else { if(c->IsGrouped()) { @@ -12683,7 +12683,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(!strcasecmp(sep->arg[1], "charm")) { Mob *target = c->GetTarget(); - if(target == nullptr || target->IsClient() || target->IsBot() || (target->IsPet() && target->GetOwner()->IsBot())) + if(target == nullptr || target->IsClient() || target->IsBot() || (target->IsPet() && target->GetOwner() && target->GetOwner()->IsBot())) { c->Message(15, "You must select a monster"); return; @@ -12796,7 +12796,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(!strcasecmp(sep->arg[1], "Dire") && !strcasecmp(sep->arg[2], "Charm")) { Mob *target = c->GetTarget(); - if(target == nullptr || target->IsClient() || target->IsBot() || (target->IsPet() && target->GetOwner()->IsBot())) + if(target == nullptr || target->IsClient() || target->IsBot() || (target->IsPet() && target->GetOwner() && target->GetOwner()->IsBot())) { c->Message(15, "You must select a monster"); return; From 08f8e2e55c5edbf0464159301d9fe7c67e6b97c4 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 19 Jun 2015 01:56:58 -0400 Subject: [PATCH 124/129] Fix some RoF2 ops --- utils/patches/patch_RoF2.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index 1ebf05442..49f5f28b3 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -295,8 +295,8 @@ OP_MarkNPC=0x1fb5 OP_MarkRaidNPC=0x5a58 #unimplemented OP_ClearNPCMarks=0x2003 OP_ClearRaidNPCMarks=0x20d3 #unimplemented -OP_DelegateAbility=0x4c9d -OP_SetGroupTarget=0x026 +OP_DelegateAbility=0x76b8 +OP_SetGroupTarget=0x2814 OP_Charm=0x5d92 OP_Stun=0x36a4 OP_SendFindableNPCs=0x4613 From 0dcf34d62b10f2ed38b001dc3d59c9e145ae981e Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Fri, 19 Jun 2015 04:06:41 -0400 Subject: [PATCH 125/129] Bot changes. - Added support for Powersource. - Changed all messages to group messages (Defaults to say if they are not in a group) --- zone/bot.cpp | 687 ++++++++++++++++++++++++++------------------------- 1 file changed, 344 insertions(+), 343 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index bb711edbf..c27ae20ce 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -270,7 +270,7 @@ void Bot::ChangeBotArcherWeapons(bool isArcher) { //archerbot->SendWearChange(MATERIAL_PRIMARY); //archerbot->SendWearChange(MATERIAL_SECONDARY); SetAttackTimer(); - Say("My blade is ready."); + BotGroupSay(this, "My blade is ready."); } else { //archerbot->SendWearChange(MATERIAL_PRIMARY); @@ -281,11 +281,11 @@ void Bot::ChangeBotArcherWeapons(bool isArcher) { BotAddEquipItem(MainAmmo, GetBotItemBySlot(MainAmmo)); BotAddEquipItem(MainSecondary, GetBotItemBySlot(MainRange)); SetAttackTimer(); - Say("My bow is true and ready."); + BotGroupSay(this, "My bow is true and ready."); } } else { - Say("I don't know how to use a bow."); + BotGroupSay(this, "I don't know how to use a bow."); } } @@ -4196,10 +4196,10 @@ void Bot::GetBotItems(std::string* errorMessage, Inventory &inv) { } for (auto row = results.begin(); row != results.end(); ++row) { - int16 slot_id = atoi(row[0]); - uint32 item_id = atoi(row[1]); - uint16 charges = atoi(row[2]); - uint32 color = atoul(row[3]); + int16 slot_id = atoi(row[0]); + uint32 item_id = atoi(row[1]); + uint16 charges = atoi(row[2]); + uint32 color = atoul(row[3]); uint32 aug[EmuConstants::ITEM_COMMON_SIZE]; aug[0] = (uint32)atoul(row[4]); aug[1] = (uint32)atoul(row[5]); @@ -4216,13 +4216,13 @@ void Bot::GetBotItems(std::string* errorMessage, Inventory &inv) { int16 put_slot_id = INVALID_INDEX; - if (instnodrop || ((slot_id >= EmuConstants::EQUIPMENT_BEGIN) && (slot_id <= EmuConstants::EQUIPMENT_END) && inst->GetItem()->Attuneable)) + if (instnodrop || (((slot_id >= EmuConstants::EQUIPMENT_BEGIN) && (slot_id <= EmuConstants::EQUIPMENT_END) || slot_id == 9999) && inst->GetItem()->Attuneable)) inst->SetAttuned(true); if (color > 0) inst->SetColor(color); - if (charges==255) + if (charges == 255) inst->SetCharges(-1); else inst->SetCharges(charges); @@ -5650,7 +5650,7 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli uint8 charges[MAX_SLOT_ID] = {0}; bool botCanWear[MAX_SLOT_ID] = {0}; - for(int16 i=beginSlotID; i<=endSlotID; ++i) { + for(int16 i = beginSlotID; i <= endSlotID; ++i) { bool BotCanWear = false; bool UpdateClient = false; bool already_returned = false; @@ -5690,13 +5690,16 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli botCanWear[i] = BotCanWear; ItemInst* swap_item = nullptr; - const char* equipped[EmuConstants::EQUIPMENT_SIZE] = {"Charm", "Left Ear", "Head", "Face", "Right Ear", "Neck", "Shoulders", "Arms", "Back", + const char* equipped[EmuConstants::EQUIPMENT_SIZE + 1] = {"Charm", "Left Ear", "Head", "Face", "Right Ear", "Neck", "Shoulders", "Arms", "Back", "Left Wrist", "Right Wrist", "Range", "Hands", "Primary Hand", "Secondary Hand", - "Left Finger", "Right Finger", "Chest", "Legs", "Feet", "Waist", "Ammo" }; + "Left Finger", "Right Finger", "Chest", "Legs", "Feet", "Waist", "Ammo", "Powersource" }; bool success = false; int how_many_slots = 0; - for(int j = EmuConstants::EQUIPMENT_BEGIN; j <= EmuConstants::EQUIPMENT_END; ++j) { + for(int j = EmuConstants::EQUIPMENT_BEGIN; j <= (EmuConstants::EQUIPMENT_END + 1); ++j) { if((mWeaponItem->Slots & (1 << j))) { + if (j == 22) + j = 9999; + how_many_slots++; if(!GetBotItem(j)) { if(j == MainPrimary) { @@ -5726,7 +5729,7 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli success = true; } else { - Say("I can't Dual Wield yet."); + BotGroupSay(this, "I can't Dual Wield yet."); --how_many_slots; } } @@ -5753,8 +5756,11 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli } } if(!success) { - for(int j = EmuConstants::EQUIPMENT_BEGIN; j <= EmuConstants::EQUIPMENT_END; ++j) { + for(int j = EmuConstants::EQUIPMENT_BEGIN; j <= (EmuConstants::EQUIPMENT_END + 1); ++j) { if((mWeaponItem->Slots & (1 << j))) { + if (j == 22) + j = 9999; + swap_item = GetBotItem(j); failedLoreCheck = false; for (int k = AUG_BEGIN; k < EmuConstants::ITEM_COMMON_SIZE; ++k) { @@ -5796,7 +5802,7 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli } else { botCanWear[i] = false; - Say("I can't Dual Wield yet."); + BotGroupSay(this, "I can't Dual Wield yet."); } } else { @@ -5842,14 +5848,14 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli } const Item_Struct* item2 = 0; - for(int y=beginSlotID; y<=endSlotID; ++y) { + for(int y = beginSlotID; y <= endSlotID; ++y) { item2 = database.GetItem(items[y]); if(item2) { if(botCanWear[y]) { - Say("Thank you for the %s, %s.", item2->Name, client->GetName()); + BotGroupSay(this, "Thank you for the %s, %s!", item2->Name, client->GetName()); } else { - Say("I can't use this %s!", item2->Name); + BotGroupSay(this, "I can't use this %s!", item2->Name); } } } @@ -7752,7 +7758,7 @@ void Bot::TryBackstab(Mob *other, int ReuseTime) { if(inst) botpiercer = inst->GetItem(); if(!botpiercer || (botpiercer->ItemType != ItemType1HPiercing)) { - Say("I can't backstab with this weapon!"); + BotGroupSay(this, "I can't backstab with this weapon!"); return; } @@ -7963,7 +7969,7 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { //Only taunt if we are not top on target's hate list //This ensures we have taunt available to regain aggro if needed if(GetTarget() && GetTarget()->GetHateTop() && GetTarget()->GetHateTop() != this) { - Say("Taunting %s", target->GetCleanName()); + BotGroupSay(this, "Taunting %s", target->GetCleanName()); Taunt(target->CastToNPC(), false); taunt_timer.Start(TauntReuseTime * 1000); } @@ -9538,7 +9544,7 @@ bool Bot::DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, uint16 s //} if(isMainGroupMGB && (GetClass() != BARD)) { - Say("MGB %s", spells[spell_id].name); + BotGroupSay(this, "MGB %s", spells[spell_id].name); SpellOnTarget(spell_id, this); entity_list.AESpell(this, this, spell_id, true); } @@ -10554,11 +10560,11 @@ void Bot::BotGroupOrderFollow(Group* group, Client* client) { if(group->IsLeader(botGroupMember) && botGroupMember->GetBotOwner()) { botGroupMember->SetFollowID(botGroupMember->GetBotOwner()->GetID()); if(botGroupMember->GetBotOwner()) - botGroupMember->Say("Following %s.", botGroupMember->GetBotOwner()->GetName()); + botGroupMember->BotGroupSay(botGroupMember, "Following %s.", botGroupMember->GetBotOwner()->GetName()); } else { botGroupMember->SetFollowID(groupLeader->GetID()); - botGroupMember->Say("Following %s.", groupLeader->GetCleanName()); + botGroupMember->BotGroupSay(botGroupMember, "Following %s.", groupLeader->GetCleanName()); } botGroupMember->WipeHateList(); @@ -10582,7 +10588,7 @@ void Bot::BotGroupOrderGuard(Group* group, Client* client) { if(botGroupMember && botGroupMember->GetBotOwnerCharacterID() == client->CharacterID()) { botGroupMember->SetFollowID(0); - botGroupMember->Say("Guarding here."); + botGroupMember->BotGroupSay(botGroupMember, "Guarding here."); botGroupMember->WipeHateList(); @@ -10817,8 +10823,8 @@ void Bot::CalcItemBonuses(StatBonuses* newbon) { const Item_Struct* itemtmp = 0; - for (int i = EmuConstants::EQUIPMENT_BEGIN; i <= EmuConstants::EQUIPMENT_END; ++i) { - const ItemInst* item = GetBotItem(i); + for (int i = EmuConstants::EQUIPMENT_BEGIN; i <= (EmuConstants::EQUIPMENT_END + 1); ++i) { + const ItemInst* item = GetBotItem((i == 22 ? 9999 : i)); if(item) { AddItemBonuses(item, newbon); } @@ -11262,9 +11268,7 @@ void Bot::CalcBotStats(bool showtext) { } bool Bot::CheckLoreConflict(const Item_Struct* item) { - if (!item) - return false; - if (!(item->LoreFlag)) + if (!item || !(item->LoreFlag)) return false; if (item->LoreGroup == -1) // Standard lore items; look everywhere except the shared bank, return the result @@ -11300,19 +11304,19 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { return; } if(!strcasecmp( sep->arg[1], "help") && !strcasecmp( sep->arg[2], "\0")){ - c->Message(0, "List of commands availables for bots :"); + c->Message(0, "List of commands availables for bots:"); c->Message(0, "#bot help - show this"); c->Message(0, "#bot create [name] [class (id)] [race (id)] [model (male/female)] - create a permanent bot. See #bot help create."); c->Message(0, "#bot help create - show all the race/class id. (make it easier to create bots)"); c->Message(0, "#bot delete - completely destroy forever the targeted bot and all its items."); - c->Message(0, "#bot list [all/class(1-16)] - list your bots all or by class. Classes: 1(Warrior), 2(Cleric), 3(Paladin), 4(Ranger), 5(Sk), 6(Druid), 7(Monk), 8(Bard), 9(Rogue), 10(Shaman), 11(Necro), 12(Wiz), 13(Mag), 14(Ench), 15(Beast), 16(Bersek)"); + c->Message(0, "#bot list [all/class(1-16)] - list your bots all or by class. Classes: 1(WAR), 2(CLR), 3(PAL), 4(RNG), 5(SHD), 6(DRU), 7(MNK), 8(BRD), 9(ROG), 10(SHM), 11(NEC), 12(WIZ), 13(MAG), 14(ENC), 15(BST), 16(BER)"); c->Message(0, "#bot spawn [bot name] - spawn a bot from it's name (use list to see all the bots). "); - c->Message(0, "#bot inventory list - show the inventory (and the slots IDs) of the targetted bot."); - c->Message(0, "#bot inventory remove [slotid] - remove the item at the given slot in the inventory of the targetted bot."); + c->Message(0, "#bot inventory list - show the inventory (and the slots IDs) of the targeted bot."); + c->Message(0, "#bot inventory remove [slotid] - remove the item at the given slot in the inventory of the targeted bot."); c->Message(0, "#bot update - you must type that command once you gain a level."); c->Message(0, "#bot summon - It will summon your targeted bot to you."); - c->Message(0, "#bot ai mez - If you're grouped with an enchanter, he will mez your target."); - c->Message(0, "#bot picklock - You must have a targeted rogue bot in your group and be right on the door."); + c->Message(0, "#bot ai mez - If you're grouped with an Enchanter, he will mez your target."); + c->Message(0, "#bot picklock - You must have a targeted Rogue bot in your group and be right on the door."); c->Message(0, "#bot cure [poison|disease|curse|blindness] Cleric has most options"); c->Message(0, "#bot bindme - You must have a Cleric in your group to get Bind Affinity cast on you."); c->Message(0, "#bot track - look at mobs in the zone (ranger has options)"); @@ -11334,7 +11338,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { c->Message(0, "#bot gate - you need a Druid or Wizard in your group)"); c->Message(0, "#bot archery - Toggle Archery Skilled bots between using a Bow or using Melee weapons."); c->Message(0, "#bot magepet [earth|water|air|fire|monster] - Select the pet type you want your Mage bot to use."); - c->Message(0, "#bot giveitem - Gives your targetted bot the item you have on your cursor."); + c->Message(0, "#bot giveitem - Gives your targeted bot the item you have on your cursor."); c->Message(0, "#bot augmentitem - Allows you to augment items for other classes. You must have the Augmentation Sealer window filled."); c->Message(0, "#bot camp - Tells your bot to camp out of the game."); c->Message(0, "#bot group help - Displays the commands available to manage any BOTs in your group."); @@ -11359,29 +11363,23 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { // pull if(!strcasecmp(sep->arg[1], "pull")) { Mob *target = c->GetTarget(); - if(target == nullptr || target == c || target->IsBot() || (target->IsPet() && target->GetOwner() && target->GetOwner()->IsBot())) - { + if(target == nullptr || target == c || target->IsBot() || (target->IsPet() && target->GetOwner() && target->GetOwner()->IsBot())) { c->Message(15, "You must select a monster"); return; } - if(c->IsGrouped()) - { + if(c->IsGrouped()) { bool haspuller = false; Group *g = c->GetGroup(); - for(int i=0; imembers[i] && g->members[i]->IsBot() && !strcasecmp(g->members[i]->GetName() , sep->arg[2])) - { + for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(g && g->members[i] && g->members[i]->IsBot() && !strcasecmp(g->members[i]->GetName() , sep->arg[2])) { haspuller = true; Mob *puller = g->members[i]; - if (puller->CastToBot()->IsArcheryRange(target)) - { - puller->Say("Trying to Pull %s \n", target->GetCleanName()); + if (puller->CastToBot()->IsArcheryRange(target)) { + puller->CastToBot()->BotGroupSay(puller->CastToBot(), "Trying to Pull %s \n", target->GetCleanName()); puller->CastToBot()->BotRangedAttack(target); - } - else { - puller->Say("Out of Range %s \n", target->GetCleanName()); + } else { + puller->CastToBot()->BotGroupSay(puller->CastToBot(), "Out of Range %s \n", target->GetCleanName()); } } } @@ -11400,7 +11398,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { else { uint32 BotFollowDistance = atoi(sep->arg[2]); c->GetTarget()->SetFollowDistance(BotFollowDistance); - } return; @@ -11418,33 +11415,43 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { uint32 botid = c->GetTarget()->CastToBot()->GetBotID(); std::string errorMessage; - int setslot = atoi(sep->arg[2]); uint8 red = atoi(sep->arg[3]); uint8 green = atoi(sep->arg[4]); uint8 blue = atoi(sep->arg[5]); uint32 setcolor = (red << 16) | (green << 8) | blue; - std::string query = StringFormat("UPDATE botinventory SET color = %u " - "WHERE slotID = %i AND botID = %u", - setcolor, setslot, botid); - auto results = database.QueryDatabase(query); - if(!results.Success()) - return; + std::string query; + if (setslot == -1) { + int slots[] = { 2, 7, 9, 12, 17, 18, 19 }; + query = StringFormat("UPDATE botinventory SET color = %u WHERE slotID IN (2, 7, 9, 12, 17, 18, 19) AND botID = %u", setcolor, botid); + auto results = database.QueryDatabase(query); + if (!results.Success()) + return; + + for (int i = 0; i < 7; i++) { + uint8 slotmaterial = Inventory::CalcMaterialFromSlot((uint8)slots[i]); + c->GetTarget()->CastToBot()->SendWearChange(slotmaterial); + } + } else { + query = StringFormat("UPDATE botinventory SET color = %u WHERE slotID = %i AND botID = %u", setcolor, setslot, botid); + auto results = database.QueryDatabase(query); + if (!results.Success()) + return; + + uint8 slotmaterial = Inventory::CalcMaterialFromSlot(setslot); + c->GetTarget()->CastToBot()->SendWearChange(slotmaterial); + } - uint8 slotmaterial = Inventory::CalcMaterialFromSlot(setslot); - c->GetTarget()->CastToBot()->SendWearChange(slotmaterial); } else { c->Message(15, "You must target a bot you own to do this."); } return; } - // Help for coloring bot armor - if(!strcasecmp(sep->arg[1], "help") && !strcasecmp(sep->arg[2], "armorcolor") ){ - //read from db - + + if(!strcasecmp(sep->arg[1], "help") && !strcasecmp(sep->arg[2], "armorcolor")){ c->Message(0, "-----------------#bot armorcolor help-----------------------------"); - c->Message(0, "Armor: 17(Chest/Robe), 7(Arms), 9(Bracer), 12(Hands), 18(Legs), 19(Boots), 2(Helm)"); + c->Message(0, "Armor: -1(All), 2(Helm), 7(Arms), 9(Bracer), 12(Hands), 17(Chest/Robe), 18(Legs), 19(Boots)"); c->Message(0, "------------------------------------------------------------------"); c->Message(0, "Color: [red] [green] [blue] (enter a number from 0-255 for each"); c->Message(0, "------------------------------------------------------------------"); @@ -11565,9 +11572,9 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } if(!strcasecmp(sep->arg[1], "help") && !strcasecmp(sep->arg[2], "create") ){ - c->Message(0, "Classes: 1(Warrior), 2(Cleric), 3(Paladin), 4(Ranger), 5(Sk), 6(Druid), 7(Monk), 8(Bard), 9(Rogue), 10(Shaman), 11(Necro), 12(Wiz), 13(Mag), 14(Ench), 15(Beast), 16(Bersek)"); + c->Message(0, "Classes: 1(WAR), 2(CLR), 3(PAL), 4(RNG), 5(SHD), 6(DRU), 7(MNK), 8(BRD), 9(ROG), 10(SHM), 11(NEC), 12(WIZ), 13(MAG), 14(ENC), 15(BST), 16(BER)"); c->Message(0, "------------------------------------------------------------------"); - c->Message(0, "Races: 1(Human), 2(Barb), 3(Erudit), 4(Wood elf), 5(High elf), 6(Dark elf), 7(Half elf), 8(Dwarf), 9(Troll), 10(Ogre), 11(Halfling), 12(Gnome), 128(Iksar), 130(Vah shir), 330(Froglok), 522(Drakkin)"); + c->Message(0, "Races: 1(Human), 2(Barbarian), 3(Erudite), 4(Wood Elf), 5(High Elf), 6(Dark Elf), 7(Half Elf), 8(Dwarf), 9(Troll), 10(Ogre), 11(Halfling), 12(Gnome), 128(Iksar), 130(Vah Shir), 330(Froglok), 522(Drakkin)"); c->Message(0, "------------------------------------------------------------------"); c->Message(0, "Usage: #bot create [name] [class(1-16)] [race(1-12,128,130,330,522)] [gender(male/female)]"); c->Message(0, "Example: #bot create Sneaky 9 6 male"); @@ -11640,12 +11647,9 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { listAll = true; else { std::string botName = std::string(sep->arg[2]); - Bot* tempBot = entity_list.GetBotByBotName(botName); - - if(tempBot && tempBot->GetBotOwner() == c) { + if(tempBot && tempBot->GetBotOwner() == c) bot = tempBot; - } } } else { @@ -11784,7 +11788,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { return; } - TempBot->CastToMob()->Say("I am ready for battle."); + TempBot->BotGroupSay(TempBot, "I am ready for battle."); } else { // We did not find a bot for the specified bot id from the database @@ -11844,7 +11848,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } else if((b->CastToBot()->GetBotOwnerCharacterID() != c->CharacterID())) { - b->Say("You can only summon your own bots."); + b->CastToBot()->BotGroupSay(b->CastToBot(), "You can only summon your own bots."); } else { @@ -11868,9 +11872,9 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { return; } - const char* equipped[EmuConstants::EQUIPMENT_SIZE] = {"Charm", "Left Ear", "Head", "Face", "Right Ear", "Neck", "Shoulders", "Arms", "Back", + const char* equipped[EmuConstants::EQUIPMENT_SIZE + 1] = {"Charm", "Left Ear", "Head", "Face", "Right Ear", "Neck", "Shoulders", "Arms", "Back", "Left Wrist", "Right Wrist", "Range", "Hands", "Primary Hand", "Secondary Hand", - "Left Finger", "Right Finger", "Chest", "Legs", "Feet", "Waist", "Ammo" }; + "Left Finger", "Right Finger", "Chest", "Legs", "Feet", "Waist", "Ammo", "Powersource" }; const ItemInst* inst = nullptr; const Item_Struct* item = nullptr; @@ -11880,12 +11884,12 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { Client::TextLink linker; linker.SetLinkType(linker.linkItemInst); - for(int i = EmuConstants::EQUIPMENT_BEGIN; i <= EmuConstants::EQUIPMENT_END; ++i) { + for(int i = EmuConstants::EQUIPMENT_BEGIN; i <= (EmuConstants::EQUIPMENT_END + 1); ++i) { if((i == MainSecondary) && is2Hweapon) { continue; } - inst = b->CastToBot()->GetBotItem(i); + inst = b->CastToBot()->GetBotItem(i == 22 ? 9999 : i); if (inst) item = inst->GetItem(); else @@ -11896,21 +11900,18 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { return; } if(item == nullptr) { - c->Message(15, "I need something for my %s (Item %i)", equipped[i], i); + c->Message(15, "I need something for my %s (Item %i)", equipped[i], (i == 22 ? 9999 : i)); continue; } if((i == MainPrimary) && ((item->ItemType == ItemType2HSlash) || (item->ItemType == ItemType2HBlunt) || (item->ItemType == ItemType2HPiercing))) { is2Hweapon = true; } - // I could not find a difference between the criteria positive code and the criteria negative code.. - // ..so, I deleted the check (old criteria: i = { MainCharm, MainRange, MainPrimary, MainSecondary, MainAmmo }) - linker.SetItemInst(inst); item_link = linker.GenerateLink(); - c->Message(15, "Using %s in my %s (Item %i)", item_link.c_str(), equipped[i], i); + c->Message(15, "Using %s in my %s (Item %i)", item_link.c_str(), equipped[i], (i == 22 ? 9999 : i)); } } else { @@ -11926,7 +11927,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(!strcasecmp(sep->arg[1], "inventory") && !strcasecmp(sep->arg[2], "remove")) { if((c->GetTarget() == nullptr) || (sep->arg[3][0] == '\0') || !c->GetTarget()->IsBot()) { - c->Message(15, "Usage: #bot inventory remove [slotid] (You must have a bot targetted) "); + c->Message(15, "Usage: #bot inventory remove [slotid] (You must have a bot targeted) "); return; } else if(c->GetTarget()->IsBot() && c->GetTarget()->CastToBot()->GetBotOwnerCharacterID() == c->CharacterID()) @@ -11935,13 +11936,13 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { return; int slotId = atoi(sep->arg[3]); - if(slotId > EmuConstants::EQUIPMENT_END || slotId < EmuConstants::EQUIPMENT_BEGIN) { - c->Message(15, "A bot has 21 slots in its inventory, please choose a slot between 0 and 21."); + if((slotId > EmuConstants::EQUIPMENT_END || slotId < EmuConstants::EQUIPMENT_BEGIN) && slotId != 9999) { + c->Message(15, "A bot has 22 slots in its inventory, please choose a slot between 0 and 21 or 9999."); return; } - const char* equipped[EmuConstants::EQUIPMENT_SIZE] = {"Charm", "Left Ear", "Head", "Face", "Right Ear", "Neck", "Shoulders", "Arms", "Back", + const char* equipped[EmuConstants::EQUIPMENT_SIZE + 1] = {"Charm", "Left Ear", "Head", "Face", "Right Ear", "Neck", "Shoulders", "Arms", "Back", "Left Wrist", "Right Wrist", "Range", "Hands", "Primary Hand", "Secondary Hand", - "Left Finger", "Right Finger", "Chest", "Legs", "Feet", "Waist", "Ammo" }; + "Left Finger", "Right Finger", "Chest", "Legs", "Feet", "Waist", "Ammo", "Powersource" }; const Item_Struct* itm = nullptr; const ItemInst* itminst = c->GetTarget()->CastToBot()->GetBotItem(slotId); @@ -12002,16 +12003,16 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { case MainFinger2: case MainChest: case MainWaist: - //case MainPowerSource: + case MainPowerSource: case MainAmmo: - gearbot->Say("My %s is now unequipped.", equipped[slotId]); + gearbot->BotGroupSay(gearbot, "My %s is now unequipped.", equipped[slotId]); break; case MainShoulders: case MainArms: case MainHands: case MainLegs: case MainFeet: - gearbot->Say("My %s are now unequipped.", equipped[slotId]); + gearbot->BotGroupSay(gearbot, "My %s are now unequipped.", equipped[slotId]); break; default: break; @@ -12035,16 +12036,16 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { case MainFinger2: case MainChest: case MainWaist: - //case MainPowerSource: + case MainPowerSource: case MainAmmo: - c->GetTarget()->Say("My %s is already unequipped.", equipped[slotId]); + c->GetTarget()->CastToBot()->BotGroupSay(c->GetTarget()->CastToBot(), "My %s is already unequipped.", equipped[slotId]); break; case MainShoulders: case MainArms: case MainHands: case MainLegs: case MainFeet: - c->GetTarget()->Say("My %s are already unequipped.", equipped[slotId]); + c->GetTarget()->CastToBot()->BotGroupSay(c->GetTarget()->CastToBot(), "My %s are already unequipped.", equipped[slotId]); break; default: break; @@ -12123,7 +12124,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } } if(hasbinder) { - binder->Say("Attempting to bind you %s.", c->GetName()); + binder->CastToBot()->BotGroupSay(binder->CastToBot(), "Attempting to bind you %s.", c->GetName()); binder->CastToNPC()->CastSpell(35, c->GetID(), 1, -1, -1); } return; @@ -12152,30 +12153,30 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } if(hasruneer) { if (c->GetLevel() <= 12) { - runeer->Say("I need to be level 13 or higher for this..."); + runeer->CastToBot()->BotGroupSay(runeer->CastToBot(), "I need to be level 13 or higher for this..."); } else if ((c->GetLevel() >= 13) && (c->GetLevel() <= 21)) { - runeer->Say("Casting Rune I..."); + runeer->CastToBot()->BotGroupSay(runeer->CastToBot(), "Casting Rune I..."); runeer->CastSpell(481, c->GetID(), 1, -1, -1); } else if ((c->GetLevel() >= 22) && (c->GetLevel() <= 32)) { - runeer->Say("Casting Rune II..."); + runeer->CastToBot()->BotGroupSay(runeer->CastToBot(), "Casting Rune II..."); runeer->CastSpell(482, c->GetID(), 1, -1, -1); } else if ((c->GetLevel() >= 33) && (c->GetLevel() <= 39)) { - runeer->Say("Casting Rune III..."); + runeer->CastToBot()->BotGroupSay(runeer->CastToBot(), "Casting Rune III..."); runeer->CastSpell(483, c->GetID(), 1, -1, -1); } else if ((c->GetLevel() >= 40) && (c->GetLevel() <= 51)) { - runeer->Say("Casting Rune IV..."); + runeer->CastToBot()->BotGroupSay(runeer->CastToBot(), "Casting Rune IV..."); runeer->CastSpell(484, c->GetID(), 1, -1, -1); } else if ((c->GetLevel() >= 52) && (c->GetLevel() <= 60)) { - runeer->Say("Casting Rune V..."); + runeer->CastToBot()->BotGroupSay(runeer->CastToBot(), "Casting Rune V..."); runeer->CastSpell(1689, c->GetID(), 1, -1, -1); } else if (c->GetLevel() >= 61){ - runeer->Say("Casting Rune of Zebuxoruk..."); + runeer->CastToBot()->BotGroupSay(runeer->CastToBot(), "Casting Rune of Zebuxoruk..."); runeer->CastSpell(3343, c->GetID(), 1, -1, -1); } } @@ -12223,33 +12224,33 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { switch(TrackerClass) { case RANGER: if(!strcasecmp(sep->arg[2], "all")) { - Tracker->Say("Tracking everything", c->GetName()); + Tracker->CastToBot()->BotGroupSay(Tracker->CastToBot(), "Tracking everything", c->GetName()); entity_list.ShowSpawnWindow(c, RangeR, false); } else if(!strcasecmp(sep->arg[2], "rare")) { - Tracker->Say("Selective tracking", c->GetName()); + Tracker->CastToBot()->BotGroupSay(Tracker->CastToBot(), "Selective tracking", c->GetName()); entity_list.ShowSpawnWindow(c, RangeR, true); } else if(!strcasecmp(sep->arg[2], "near")) { - Tracker->Say("Tracking mobs nearby", c->GetName()); + Tracker->CastToBot()->BotGroupSay(Tracker->CastToBot(), "Tracking mobs nearby", c->GetName()); entity_list.ShowSpawnWindow(c, RangeD, false); } else - Tracker->Say("You want to [track all], [track near], or [track rare]?", c->GetName()); + Tracker->CastToBot()->BotGroupSay(Tracker->CastToBot(), "You want to [track all], [track near], or [track rare]?", c->GetName()); break; case BARD: if(TrackerClass != RANGER) - Tracker->Say("Tracking up", c->GetName()); + Tracker->CastToBot()->BotGroupSay(Tracker->CastToBot(), "Tracking up", c->GetName()); entity_list.ShowSpawnWindow(c, RangeB, false); break; case DRUID: if(TrackerClass = BARD) - Tracker->Say("Tracking up", c->GetName()); + Tracker->CastToBot()->BotGroupSay(Tracker->CastToBot(), "Tracking up", c->GetName()); entity_list.ShowSpawnWindow(c, RangeD, false); break; @@ -12293,80 +12294,80 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { switch(CurerClass) { case CLERIC: if (!strcasecmp(sep->arg[2], "poison") && (c->GetLevel() >= 1)) { - Curer->Say("Trying to cure us of %s.", sep->arg[2]); + Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Trying to cure us of %s.", sep->arg[2]); Curer->CastToBot()->Bot_Command_Cure(1, Curer->GetLevel()); } else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 4)) { - Curer->Say("Trying to cure us of %s.", sep->arg[2]); + Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Trying to cure us of %s.", sep->arg[2]); Curer->CastToBot()->Bot_Command_Cure(2, Curer->GetLevel()); } else if(!strcasecmp(sep->arg[2], "curse") && (c->GetLevel() >= 8)) { - Curer->Say("Trying to cure us of %s.", sep->arg[2]); + Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Trying to cure us of %s.", sep->arg[2]); Curer->CastToBot()->Bot_Command_Cure(3, Curer->GetLevel()); } else if(!strcasecmp(sep->arg[2], "blindness") && (c->GetLevel() >= 3)) { - Curer->Say("Trying to cure us of %s.", sep->arg[2]); + Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Trying to cure us of %s.", sep->arg[2]); Curer->CastToBot()->Bot_Command_Cure(4, Curer->GetLevel()); } else if (!strcasecmp(sep->arg[2], "curse") && (c->GetLevel() <= 8) || !strcasecmp(sep->arg[2], "blindness") && (c->GetLevel() <= 3) || !strcasecmp(sep->arg[2], "disease") && (c->GetLevel() <= 4) || !strcasecmp(sep->arg[2], "poison") && (c->GetLevel() <= 1)) { - Curer->Say("I don't have the needed level yet", sep->arg[2]); + Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "I don't have the needed level yet", sep->arg[2]); } else - Curer->Say("Do you want [cure poison], [cure disease], [cure curse], or [cure blindness]?", c->GetName()); + Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Do you want [cure poison], [cure disease], [cure curse], or [cure blindness]?", c->GetName()); break; case SHAMAN: if (!strcasecmp(sep->arg[2], "poison") && (c->GetLevel() >= 2)) { - Curer->Say("Trying to cure us of %s.", sep->arg[2]); + Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Trying to cure us of %s.", sep->arg[2]); Curer->CastToBot()->Bot_Command_Cure(1, Curer->GetLevel()); } else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 1)) { - Curer->Say("Trying to cure us of %s.", sep->arg[2]); + Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Trying to cure us of %s.", sep->arg[2]); Curer->CastToBot()->Bot_Command_Cure(2, Curer->GetLevel()); } else if(!strcasecmp(sep->arg[2], "curse")) { - Curer->Say("I don't have that spell", sep->arg[2]); + Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "I don't have that spell", sep->arg[2]); } else if(!strcasecmp(sep->arg[2], "blindness") && (c->GetLevel() >= 7)) { - Curer->Say("Trying to cure us of %s.", sep->arg[2]); + Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Trying to cure us of %s.", sep->arg[2]); Curer->CastToBot()->Bot_Command_Cure(4, Curer->GetLevel()); } else if (!strcasecmp(sep->arg[2], "blindness") && (c->GetLevel() <= 7) || !strcasecmp(sep->arg[2], "disease") && (c->GetLevel() <= 1) || !strcasecmp(sep->arg[2], "poison") && (c->GetLevel() <= 2)) { - Curer->Say("I don't have the needed level yet", sep->arg[2]); + Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "I don't have the needed level yet", sep->arg[2]); } else - Curer->Say("Do you want [cure poison], [cure disease], or [cure blindness]?", c->GetName()); + Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Do you want [cure poison], [cure disease], or [cure blindness]?", c->GetName()); break; case DRUID: if (!strcasecmp(sep->arg[2], "poison") && (c->GetLevel() >= 5)) { - Curer->Say("Trying to cure us of %s.", sep->arg[2]); + Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Trying to cure us of %s.", sep->arg[2]); Curer->CastToBot()->Bot_Command_Cure(1, Curer->GetLevel()); } else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 4)) { - Curer->Say("Trying to cure us of %s.", sep->arg[2]); + Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Trying to cure us of %s.", sep->arg[2]); Curer->CastToBot()->Bot_Command_Cure(2, Curer->GetLevel()); } else if(!strcasecmp(sep->arg[2], "curse")) { // Fire level 1 - Curer->Say("I don't have that spell", sep->arg[2]); + Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "I don't have that spell", sep->arg[2]); } else if(!strcasecmp(sep->arg[2], "blindness") && (c->GetLevel() >= 13)) { - Curer->Say("I don't have that spell", sep->arg[2]); + Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "I don't have that spell", sep->arg[2]); } else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() <= 4) || !strcasecmp(sep->arg[2], "poison") && (c->GetLevel() <= 5)) { - Curer->Say("I don't have the needed level yet", sep->arg[2]) ; + Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "I don't have the needed level yet", sep->arg[2]) ; } else - Curer->Say("Do you want [cure poison], or [cure disease]?", c->GetName()); + Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Do you want [cure poison], or [cure disease]?", c->GetName()); break; @@ -12397,7 +12398,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { { hasmezzer = true; Mob *mezzer = g->members[i]; - mezzer->Say("Trying to mez %s \n", target->GetCleanName()); + mezzer->CastToBot()->BotGroupSay(mezzer->CastToBot(), "Trying to mez %s \n", target->GetCleanName()); mezzer->CastToBot()->MesmerizeTarget(target); } } @@ -12443,7 +12444,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { break; } if(hascaster) { - g->members[i]->Say("Trying to Identify your item..."); + g->members[i]->CastToBot()->BotGroupSay(g->members[i]->CastToBot(), "Trying to Identify your item..."); g->members[i]->CastSpell(305, c->GetID(), 1, -1, -1); break; } @@ -12479,7 +12480,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { { hasrezzer = true; Mob *rezzer = g->members[i]; - rezzer->Say("Trying to rez %s", target->GetCleanName()); + rezzer->CastToBot()->BotGroupSay(rezzer->CastToBot(), "Trying to rez %s", target->GetCleanName()); rezzer->CastToBot()->Bot_Command_RezzTarget(target); break; } @@ -12504,7 +12505,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { c->GetTarget()->CastToBot()->SetPetChooser(true); if(botlevel == 1) { - c->GetTarget()->Say("I don't have any pets yet."); + c->GetTarget()->CastToBot()->BotGroupSay(c->GetTarget()->CastToBot(), "I don't have any pets yet."); return; } if(!strcasecmp(sep->arg[2], "water")) @@ -12515,7 +12516,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { { if(botlevel < 3) { - c->GetTarget()->Say("I don't have that pet yet."); + c->GetTarget()->CastToBot()->BotGroupSay(c->GetTarget()->CastToBot(), "I don't have that pet yet."); return; } else @@ -12527,7 +12528,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { { if(botlevel < 4) { - c->GetTarget()->Say("I don't have that pet yet."); + c->GetTarget()->CastToBot()->BotGroupSay(c->GetTarget()->CastToBot(), "I don't have that pet yet."); return; } else @@ -12539,7 +12540,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { { if(botlevel < 5) { - c->GetTarget()->Say("I don't have that pet yet."); + c->GetTarget()->CastToBot()->BotGroupSay(c->GetTarget()->CastToBot(), "I don't have that pet yet."); return; } else @@ -12551,7 +12552,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { { if(botlevel < 30) { - c->GetTarget()->Say("I don't have that pet yet."); + c->GetTarget()->CastToBot()->BotGroupSay(c->GetTarget()->CastToBot(), "I don't have that pet yet."); return; } else @@ -12592,27 +12593,27 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { summonerlevel = g->members[i]->GetLevel(); g->members[i]->InterruptSpell(); if(!t->IsClient()) { - g->members[i]->Say("You have to target a player with a corpse in the zone"); + g->members[i]->CastToBot()->BotGroupSay(g->members[i]->CastToBot(), "You have to target a player with a corpse in the zone"); return; } else { g->members[i]->SetTarget(t); if(summonerlevel < 12) { - g->members[i]->Say("I don't have that spell yet."); + g->members[i]->CastToBot()->BotGroupSay(g->members[i]->CastToBot(), "I don't have that spell yet."); } else if((summonerlevel > 11) && (summonerlevel < 35)) { - g->members[i]->Say("Attempting to summon %s\'s corpse.", t->GetCleanName()); + g->members[i]->CastToBot()->BotGroupSay(g->members[i]->CastToBot(), "Attempting to summon %s\'s corpse.", t->GetCleanName()); g->members[i]->CastSpell(2213, t->GetID(), 1, -1, -1); return; } else if((summonerlevel > 34) && (summonerlevel < 71)) { - g->members[i]->Say("Attempting to summon %s\'s corpse.", t->GetCleanName()); + g->members[i]->CastToBot()->BotGroupSay(g->members[i]->CastToBot(), "Attempting to summon %s\'s corpse.", t->GetCleanName()); g->members[i]->CastSpell(3, t->GetID(), 1, -1, -1); return; } else if(summonerlevel > 70) { - g->members[i]->Say("Attempting to summon %s\'s corpse.", t->GetCleanName()); + g->members[i]->CastToBot()->BotGroupSay(g->members[i]->CastToBot(), "Attempting to summon %s\'s corpse.", t->GetCleanName()); g->members[i]->CastSpell(10042, t->GetID(), 1, -1, -1); return; } @@ -12641,7 +12642,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { // seperated cleric and chanter so chanter is primary if(g && g->members[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == ENCHANTER)) { Bot *pacer = g->members[i]->CastToBot(); - pacer->Say("Trying to pacify %s \n", target->GetCleanName()); + pacer->BotGroupSay(pacer, "Trying to pacify %s \n", target->GetCleanName()); if(pacer->Bot_Command_CalmTarget(target)) { if(target->FindType(SE_Lull) || target->FindType(SE_Harmony) || target->FindType(SE_InstantHate)) @@ -12657,7 +12658,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { // seperated cleric and chanter so chanter is primary if(g && g->members[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == CLERIC) && (GroupHasEnchanterClass(g) == false)) { Bot *pacer = g->members[i]->CastToBot(); - pacer->Say("Trying to pacify %s \n", target->GetCleanName()); + pacer->BotGroupSay(pacer, "Trying to pacify %s \n", target->GetCleanName()); if(pacer->Bot_Command_CalmTarget(target)) { if(target->FindType(SE_Lull) || target->FindType(SE_Harmony) || target->FindType(SE_InstantHate)) @@ -12720,38 +12721,38 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { switch(CharmerClass) { case ENCHANTER: if (c->GetLevel() >= 11) { - Charmer->Say("Trying to charm %s \n", target->GetCleanName(), sep->arg[2]); + Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "Trying to charm %s \n", target->GetCleanName(), sep->arg[2]); Charmer->CastToBot()->Bot_Command_CharmTarget (1,target); } else if (c->GetLevel() <= 10){ - Charmer->Say("I don't have the needed level yet", sep->arg[2]); + Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "I don't have the needed level yet", sep->arg[2]); } else - Charmer->Say("Mob level is too high or can't be charmed", c->GetName()); + Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "Mob level is too high or can't be charmed", c->GetName()); break; case NECROMANCER: if ((c->GetLevel() >= 18) && (DBtype == 3)) { - Charmer->Say("Trying to Charm %s \n", target->GetCleanName(), sep->arg[2]); + Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "Trying to Charm %s \n", target->GetCleanName(), sep->arg[2]); Charmer->CastToBot()->Bot_Command_CharmTarget (2,target); } else if (c->GetLevel() <= 17){ - Charmer->Say("I don't have the needed level yet", sep->arg[2]); + Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "I don't have the needed level yet", sep->arg[2]); } else - Charmer->Say("Mob Is not undead...", c->GetName()); + Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "Mob Is not undead...", c->GetName()); break; case DRUID: if ((c->GetLevel() >= 13) && (DBtype == 21)) { - Charmer->Say("Trying to charm %s \n", target->GetCleanName(), sep->arg[2]); + Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "Trying to charm %s \n", target->GetCleanName(), sep->arg[2]); Charmer->CastToBot()->Bot_Command_CharmTarget (3,target); } else if (c->GetLevel() <= 12){ - Charmer->Say("I don't have the needed level yet", sep->arg[2]); + Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "I don't have the needed level yet", sep->arg[2]); } else - Charmer->Say("Mob is not an animal...", c->GetName()); + Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "Mob is not an animal...", c->GetName()); break; default: @@ -12768,7 +12769,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { ((c->GetTarget()->GetClass() == NECROMANCER) || (c->GetTarget()->GetClass() == ENCHANTER) || (c->GetTarget()->GetClass() == DRUID))) { if(c->GetTarget()->CastToBot()->IsBotCharmer()) { c->GetTarget()->CastToBot()->SetBotCharmer(false); - c->GetTarget()->Say("Using a summoned pet."); + c->GetTarget()->CastToBot()->BotGroupSay(c->GetTarget()->CastToBot(), "Using a summoned pet."); } else { if(c->GetTarget()->GetPet()) @@ -12779,7 +12780,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { c->GetTarget()->SetPetID(0); } c->GetTarget()->CastToBot()->SetBotCharmer(true); - c->GetTarget()->Say("Available for Dire Charm command."); + c->GetTarget()->CastToBot()->BotGroupSay(c->GetTarget()->CastToBot(), "Available for Dire Charm command."); } } else { @@ -12833,38 +12834,38 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { switch(DirerClass) { case ENCHANTER: if (c->GetLevel() >= 55) { - Direr->Say("Trying to dire charm %s \n", target->GetCleanName(), sep->arg[2]); + Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "Trying to dire charm %s \n", target->GetCleanName(), sep->arg[2]); Direr->CastToBot()->Bot_Command_DireTarget (1,target); } else if (c->GetLevel() <= 55){ - Direr->Say("I don't have the needed level yet", sep->arg[2]); + Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "I don't have the needed level yet", sep->arg[2]); } else - Direr->Say("Mob level is too high or can't be charmed", c->GetName()); + Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "Mob level is too high or can't be charmed", c->GetName()); break; case NECROMANCER: if ((c->GetLevel() >= 55) && (DBtype == 3)) { - Direr->Say("Trying to dire charm %s \n", target->GetCleanName(), sep->arg[2]); + Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "Trying to dire charm %s \n", target->GetCleanName(), sep->arg[2]); Direr->CastToBot()->Bot_Command_DireTarget (2,target); } else if (c->GetLevel() <= 55){ - Direr->Say("I don't have the needed level yet", sep->arg[2]); + Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "I don't have the needed level yet", sep->arg[2]); } else - Direr->Say("Mob Is not undead...", c->GetName()); + Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "Mob Is not undead...", c->GetName()); break; case DRUID: if ((c->GetLevel() >= 55) && (DBtype == 21)) { - Direr->Say("Trying to dire charm %s \n", target->GetCleanName(), sep->arg[2]); + Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "Trying to dire charm %s \n", target->GetCleanName(), sep->arg[2]); Direr->CastToBot()->Bot_Command_DireTarget (3,target); } else if (c->GetLevel() <= 55){ - Direr->Say("I don't have the needed level yet", sep->arg[2]); + Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "I don't have the needed level yet", sep->arg[2]); } else - Direr->Say("Mob is not an animal...", c->GetName()); + Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "Mob is not an animal...", c->GetName()); break; default: @@ -12897,11 +12898,11 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } } if((hasevac) && (c->GetLevel() >= 18)) { - evac->Say("Attempting to Evac you %s.", c->GetName()); + evac->CastToBot()->BotGroupSay(evac->CastToBot(), "Attempting to Evac you %s.", c->GetName()); evac->CastToClient()->CastSpell(2183, c->GetID(), 1, -1, -1); } else if((hasevac) && (c->GetLevel() <= 17)) { - evac->Say("I'm not level 18 yet.", c->GetName()); + evac->CastToBot()->BotGroupSay(evac->CastToBot(), "I'm not level 18 yet.", c->GetName()); } return; } @@ -12945,87 +12946,87 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { switch(SowerClass) { case DRUID: if ((!strcasecmp(sep->arg[2], "regular")) && (zone->CanCastOutdoor()) && (c->GetLevel() >= 10) ) { - Sower->Say("Casting sow..."); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "Casting sow..."); Sower->CastSpell(278, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "regular")) && (zone->CanCastOutdoor()) && (c->GetLevel() <= 10) ) { - Sower->Say("I'm not level 10 yet."); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I'm not level 10 yet."); } else if ((!strcasecmp(sep->arg[2], "wolf")) && zone->CanCastOutdoor() && (c->GetLevel() >= 20)) { - Sower->Say("Casting group wolf..."); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "Casting group wolf..."); Sower->CastSpell(428, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "wolf")) && (c->GetLevel() <= 20)) { - Sower->Say("I'm not level 20 yet."); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I'm not level 20 yet."); } else if ((!strcasecmp(sep->arg[2], "feral")) && (c->GetLevel() >= 50)) { - Sower->Say("Casting Feral Pack..."); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "Casting Feral Pack..."); Sower->CastSpell(4058, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "feral")) && (c->GetLevel() <= 50)) { - Sower->Say("I'm not level 50 yet."); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I'm not level 50 yet."); } else if ((!strcasecmp(sep->arg[2], "shrew")) && (c->GetLevel() >= 35)) { - Sower->Say("Casting Pack Shrew..."); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "Casting Pack Shrew..."); Sower->CastSpell(4055, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "wolf")) && (c->GetLevel() <= 35)) { - Sower->Say("I'm not level 35 yet."); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I'm not level 35 yet."); } else if ((!zone->CanCastOutdoor()) && (!strcasecmp(sep->arg[2], "regular")) || (!zone->CanCastOutdoor()) && (!strcasecmp(sep->arg[2], "wolf"))) { - Sower->Say("I can't cast this spell indoors, try [sow shrew] if you're 35 or higher, or [sow feral] if you're 50 or higher,", c->GetName()); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I can't cast this spell indoors, try [sow shrew] if you're 35 or higher, or [sow feral] if you're 50 or higher,", c->GetName()); } else if (!zone->CanCastOutdoor()) { - Sower->Say("I can't cast this spell indoors, try [sow shrew] if you're 35 or higher, or [sow feral] if you're 50 or higher,", c->GetName()); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I can't cast this spell indoors, try [sow shrew] if you're 35 or higher, or [sow feral] if you're 50 or higher,", c->GetName()); } else if (zone->CanCastOutdoor()) { - Sower->Say("Do you want [sow regular] or [sow wolf]?", c->GetName()); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "Do you want [sow regular] or [sow wolf]?", c->GetName()); } else if (!zone->CanCastOutdoor()) { - Sower->Say("I can't cast this spell indoors, try [sow shrew] if you're 35 or higher, or [sow feral] if you're 50 or higher,", c->GetName()); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I can't cast this spell indoors, try [sow shrew] if you're 35 or higher, or [sow feral] if you're 50 or higher,", c->GetName()); } break; case SHAMAN: if ((zone->CanCastOutdoor()) && (c->GetLevel() >= 9)) { - Sower->Say("Casting SoW..."); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "Casting SoW..."); Sower->CastToClient()->CastSpell(278, c->GetID(), 1, -1, -1); } else if (!zone->CanCastOutdoor()) { - Sower->Say("I can't cast this spell indoors", c->GetName()); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I can't cast this spell indoors", c->GetName()); } else if (c->GetLevel() <= 9) { - Sower->Say("I'm not level 9 yet."); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I'm not level 9 yet."); } break; case RANGER: if ((zone->CanCastOutdoor()) && (c->GetLevel() >= 28)){ - Sower->Say("Casting SoW..."); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "Casting SoW..."); Sower->CastToClient()->CastSpell(278, c->GetID(), 1, -1, -1); } else if (!zone->CanCastOutdoor()) { - Sower->Say("I can't cast this spell indoors", c->GetName()); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I can't cast this spell indoors", c->GetName()); } else if (c->GetLevel() <= 28) { - Sower->Say("I'm not level 28 yet."); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I'm not level 28 yet."); } break; case BEASTLORD: if((zone->CanCastOutdoor()) && (c->GetLevel() >= 24)) { - Sower->Say("Casting SoW..."); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "Casting SoW..."); Sower->CastToClient()->CastSpell(278, c->GetID(), 1, -1, -1); } else if (!zone->CanCastOutdoor()) { - Sower->Say("I can't cast this spell indoors", c->GetName()); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I can't cast this spell indoors", c->GetName()); } else if (c->GetLevel() <= 24) { - Sower->Say("I'm not level 24 yet."); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I'm not level 24 yet."); } break; @@ -13070,22 +13071,22 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { case SHAMAN: if (c->GetLevel() >= 15) { - Shrinker->Say("Casting Shrink..."); + Shrinker->CastToBot()->BotGroupSay(Shrinker->CastToBot(), "Casting Shrink..."); Shrinker->CastToBot()->SpellOnTarget(345, target); } else if (c->GetLevel() <= 14) { - Shrinker->Say("I'm not level 15 yet."); + Shrinker->CastToBot()->BotGroupSay(Shrinker->CastToBot(), "I'm not level 15 yet."); } break; case BEASTLORD: if (c->GetLevel() >= 23) { - Shrinker->Say("Casting Shrink..."); + Shrinker->CastToBot()->BotGroupSay(Shrinker->CastToBot(), "Casting Shrink..."); Shrinker->CastToBot()->SpellOnTarget(345, target); } else if (c->GetLevel() <= 22) { - Shrinker->Say("I'm not level 23 yet."); + Shrinker->CastToBot()->BotGroupSay(Shrinker->CastToBot(), "I'm not level 23 yet."); } break; @@ -13123,103 +13124,103 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { switch(GaterClass) { case DRUID: if ((!strcasecmp(sep->arg[2], "karana")) && (c->GetLevel() >= 25) ) { - Gater->Say("Casting Circle of Karana..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Karana..."); Gater->CastSpell(550, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "commons")) && (c->GetLevel() >= 27)) { - Gater->Say("Casting Circle of Commons..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Commons..."); Gater->CastSpell(551, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "tox")) && (c->GetLevel() >= 25)) { - Gater->Say("Casting Circle of Toxxulia..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Toxxulia..."); Gater->CastSpell(552, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "butcher")) && (c->GetLevel() >= 25)) { - Gater->Say("Casting Circle of Butcherblock..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Butcherblock..."); Gater->CastSpell(553, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "lava")) && (c->GetLevel() >= 30)) { - Gater->Say("Casting Circle of Lavastorm..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Lavastorm..."); Gater->CastSpell(554, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "ro")) && (c->GetLevel() >= 32)) { - Gater->Say("Casting Circle of Ro..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Ro..."); Gater->CastSpell(555, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "feerrott")) && (c->GetLevel() >= 32)) { - Gater->Say("Casting Circle of feerrott..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of feerrott..."); Gater->CastSpell(556, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "steamfont")) && (c->GetLevel() >= 31)) { - Gater->Say("Casting Circle of Steamfont..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Steamfont..."); Gater->CastSpell(557, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "misty")) && (c->GetLevel() >= 36)) { - Gater->Say("Casting Circle of Misty..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Misty..."); Gater->CastSpell(558, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "wakening")) && (c->GetLevel() >= 40)) { - Gater->Say("Casting Circle of Wakening Lands..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Wakening Lands..."); Gater->CastSpell(1398, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "iceclad")) && (c->GetLevel() >= 32)) { - Gater->Say("Casting Circle of Iceclad Ocean..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Iceclad Ocean..."); Gater->CastSpell(1434, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "divide")) && (c->GetLevel() >= 36)) { - Gater->Say("Casting Circle of The Great Divide..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of The Great Divide..."); Gater->CastSpell(1438, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "cobalt")) && (c->GetLevel() >= 42)) { - Gater->Say("Casting Circle of Cobalt Scar..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Cobalt Scar..."); Gater->CastSpell(1440, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "combines")) && (c->GetLevel() >= 33)) { - Gater->Say("Casting Circle of The Combines..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of The Combines..."); Gater->CastSpell(1517, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "surefall")) && (c->GetLevel() >= 26)) { - Gater->Say("Casting Circle of Surefall Glade..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Surefall Glade..."); Gater->CastSpell(2020, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "grimling")) && (c->GetLevel() >= 29)) { - Gater->Say("Casting Circle of Grimling Forest..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Grimling Forest..."); Gater->CastSpell(2419, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "twilight")) && (c->GetLevel() >= 33)) { - Gater->Say("Casting Circle of Twilight..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Twilight..."); Gater->CastSpell(2424, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "dawnshroud")) && (c->GetLevel() >= 37)) { - Gater->Say("Casting Circle of Dawnshroud..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Dawnshroud..."); Gater->CastSpell(2429, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "nexus")) && (c->GetLevel() >= 26)) { - Gater->Say("Casting Circle of The Nexus..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of The Nexus..."); Gater->CastSpell(2432, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "pok")) && (c->GetLevel() >= 38)) { - Gater->Say("Casting Circle of Knowledge..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Knowledge..."); Gater->CastSpell(3184, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "stonebrunt")) && (c->GetLevel() >= 28)) { - Gater->Say("Casting Circle of Stonebrunt Mountains..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Stonebrunt Mountains..."); Gater->CastSpell(3792, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "bloodfields")) && (c->GetLevel() >= 55)) { - Gater->Say("Casting Circle of Bloodfields..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Bloodfields..."); Gater->CastSpell(6184, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "emerald")) && (c->GetLevel() >= 39)) { - Gater->Say("Casting Wind of the South..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Wind of the South..."); Gater->CastSpell(1737, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "skyfire")) && (c->GetLevel() >= 44)) { - Gater->Say("Casting Wind of the North..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Wind of the North..."); Gater->CastSpell(1736, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "slaughter")) && (c->GetLevel() >= 64)) { - Gater->Say("Casting Circle of Slaughter..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Slaughter..."); Gater->CastSpell(6179, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "karana") @@ -13247,109 +13248,109 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { || !strcasecmp(sep->arg[2], "emerald") && (c->GetLevel() <= 38) || !strcasecmp(sep->arg[2], "skyfire") && (c->GetLevel() <= 43) || !strcasecmp(sep->arg[2], "wos") && (c->GetLevel() <= 64)) { - Gater->Say("I don't have the needed level yet", sep->arg[2]); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "I don't have the needed level yet", sep->arg[2]); } else { - Gater->Say("With the proper level I can [gate] to [karana],[commons],[tox],[butcher],[lava],[ro],[feerrott],[steamfont],[misty],[wakening],[iceclad],[divide],[cobalt],[combines],[surefall],[grimling],[twilight],[dawnshroud],[nexus],[pok],[stonebrunt],[bloodfields],[emerald],[skyfire] or [wos].", c->GetName()); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "With the proper level I can [gate] to [karana],[commons],[tox],[butcher],[lava],[ro],[feerrott],[steamfont],[misty],[wakening],[iceclad],[divide],[cobalt],[combines],[surefall],[grimling],[twilight],[dawnshroud],[nexus],[pok],[stonebrunt],[bloodfields],[emerald],[skyfire] or [wos].", c->GetName()); } break; case WIZARD: if ((!strcasecmp(sep->arg[2], "commons")) && (c->GetLevel() >= 35) ) { - Gater->Say("Casting Common Portal..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Common Portal..."); Gater->CastSpell(566, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "fay")) && (c->GetLevel() >= 27)) { - Gater->Say("Casting Fay Portal..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Fay Portal..."); Gater->CastSpell(563, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "ro")) && (c->GetLevel() >= 37)) { - Gater->Say("Casting Ro Portal..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Ro Portal..."); Gater->CastSpell(567, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "tox")) && (c->GetLevel() >= 25)) { - Gater->Say("Casting Toxxula Portal..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Toxxula Portal..."); Gater->CastSpell(561, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "nk")) && (c->GetLevel() >= 27)) { - Gater->Say("Casting North Karana Portal..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting North Karana Portal..."); Gater->CastSpell(562, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "nek")) && (c->GetLevel() >= 32)) { - Gater->Say("Casting Nektulos Portal..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Nektulos Portal..."); Gater->CastSpell(564, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "wakening")) && (c->GetLevel() >= 43)) { - Gater->Say("Casting Wakening Lands Portal..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Wakening Lands Portal..."); Gater->CastSpell(1399, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "iceclad")) && (c->GetLevel() >= 33)) { - Gater->Say("Casting Iceclad Ocean Portal..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Iceclad Ocean Portal..."); Gater->CastSpell(1418, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "divide")) && (c->GetLevel() >= 36)) { - Gater->Say("Casting Great Divide Portal..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Great Divide Portal..."); Gater->CastSpell(1423, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "cobalt")) && (c->GetLevel() >= 43)) { - Gater->Say("Casting Cobalt Scar Portal..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Cobalt Scar Portal..."); Gater->CastSpell(1425, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "combines")) && (c->GetLevel() >= 34)) { - Gater->Say("Casting Combines Portal..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Combines Portal..."); Gater->CastSpell(1516, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "wk")) && (c->GetLevel() >= 27)) { - Gater->Say("Casting West Karana Portal..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting West Karana Portal..."); Gater->CastSpell(568, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "twilight")) && (c->GetLevel() >= 27)) { - Gater->Say("Casting Twilight Portal..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Twilight Portal..."); Gater->CastSpell(2425, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "dawnshroud")) && (c->GetLevel() >= 27)) { - Gater->Say("Casting Dawnshroud Portal..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Dawnshroud Portal..."); Gater->CastSpell(2430, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "nexus")) && (c->GetLevel() >= 29)) { - Gater->Say("Casting Nexus Portal..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Nexus Portal..."); Gater->CastSpell(2944, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "pok")) && (c->GetLevel() >= 27)) { - Gater->Say("Casting Plane of Knowledge Portal..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Plane of Knowledge Portal..."); Gater->CastSpell(3180, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "wos")) && (c->GetLevel() >= 27)) { - Gater->Say("Casting Wall of Slaughter Portal..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Wall of Slaughter Portal..."); Gater->CastSpell(6178, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "grimling")) && (c->GetLevel() >= 29)) { - Gater->Say("Casting Fay Portal..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Fay Portal..."); Gater->CastSpell(2420, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "emerald")) && (c->GetLevel() >= 37)) { - Gater->Say("Porting to Emerald Jungle..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Porting to Emerald Jungle..."); Gater->CastSpell(1739, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "hateplane")) && (c->GetLevel() >= 39)) { - Gater->Say("Porting to Hate Plane..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Porting to Hate Plane..."); Gater->CastSpell(666, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "airplane")) && (c->GetLevel() >= 39)) { - Gater->Say("Porting to airplane..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Porting to airplane..."); Gater->CastSpell(674, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "skyfire")) && (c->GetLevel() >= 36)) { - Gater->Say("Porting to Skyfire..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Porting to Skyfire..."); Gater->CastSpell(1738, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "bloodfields")) && (c->GetLevel() >= 55)) { - Gater->Say("Casting Bloodfields Portal..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Bloodfields Portal..."); Gater->CastSpell(6183, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "stonebrunt")) && (c->GetLevel() >= 27)) { - Gater->Say("Casting Stonebrunt Portal..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Stonebrunt Portal..."); Gater->CastSpell(3793, c->GetID(), 1, -1, -1); } else if ((!strcasecmp(sep->arg[2], "commons") && (c->GetLevel() <= 35)) @@ -13376,10 +13377,10 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { || !strcasecmp(sep->arg[2], "emerald") && (c->GetLevel() <= 36) || !strcasecmp(sep->arg[2], "skyfire") && (c->GetLevel() <= 36) || !strcasecmp(sep->arg[2], "wos") && (c->GetLevel() <= 64)) { - Gater->Say("I don't have the needed level yet", sep->arg[2]); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "I don't have the needed level yet", sep->arg[2]); } else { - Gater->Say("With the proper level I can [gate] to [commons],[fay],[ro],[tox],[nk],[wakening],[iceclad],[divide],[cobalt],[combines],[wk],[grimling],[twilight],[dawnshroud],[nexus],[pok],[stonebrunt],[bloodfields],[emerald],[skyfire],[hateplane],[airplane] or [wos].", c->GetName()); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "With the proper level I can [gate] to [commons],[fay],[ro],[tox],[nk],[wakening],[iceclad],[divide],[cobalt],[combines],[wk],[grimling],[twilight],[dawnshroud],[nexus],[pok],[stonebrunt],[bloodfields],[emerald],[skyfire],[hateplane],[airplane] or [wos].", c->GetName()); } break; default: @@ -13434,47 +13435,47 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { switch(EndurerClass) { case DRUID: if (c->GetLevel() < 6) { - Endurer->Say("I'm not level 6 yet."); + Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "I'm not level 6 yet."); } else { - Endurer->Say("Casting Enduring Breath..."); + Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "Casting Enduring Breath..."); Endurer->CastSpell(86, c->GetID(), 1, -1, -1); break; } break; case SHAMAN: if (c->GetLevel() < 12) { - Endurer->Say("I'm not level 12 yet."); + Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "I'm not level 12 yet."); } else { - Endurer->Say("Casting Enduring Breath..."); + Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "Casting Enduring Breath..."); Endurer->CastSpell(86, c->GetID(), 1, -1, -1); } break; case RANGER: if (c->GetLevel() < 20) { - Endurer->Say("I'm not level 20 yet."); + Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "I'm not level 20 yet."); } else { - Endurer->Say("Casting Enduring Breath..."); + Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "Casting Enduring Breath..."); Endurer->CastSpell(86, c->GetID(), 1, -1, -1); } break; case ENCHANTER: if (c->GetLevel() < 12) { - Endurer->Say("I'm not level 12 yet."); + Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "I'm not level 12 yet."); } else { - Endurer->Say("Casting Enduring Breath..."); + Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "Casting Enduring Breath..."); Endurer->CastSpell(86, c->GetID(), 1, -1, -1); } break; case BEASTLORD: if (c->GetLevel() < 25) { - Endurer->Say("I'm not level 25 yet."); + Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "I'm not level 25 yet."); } else { - Endurer->Say("Casting Enduring Breath..."); + Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "Casting Enduring Breath..."); Endurer->CastSpell(86, c->GetID(), 1, -1, -1); } break; @@ -13531,135 +13532,135 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { switch(InviserClass) { case ENCHANTER: if ((c->GetLevel() <= 14) && (!strcasecmp(sep->arg[2], "undead"))) { - Inviser->Say("I'm not level 14 yet."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I'm not level 14 yet."); } else if ((!c->IsInvisible(c)) && (!c->invisible_undead) && (c->GetLevel() >= 14) && (!strcasecmp(sep->arg[2], "undead"))) { - Inviser->Say("Casting invis undead..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting invis undead..."); Inviser->CastSpell(235, c->GetID(), 1, -1, -1); } else if ((c->GetLevel() <= 4) && (!strcasecmp(sep->arg[2], "live"))) { - Inviser->Say("I'm not level 4 yet."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I'm not level 4 yet."); } else if ((!c->IsInvisible(c))&& (!c->invisible_undead) && (c->GetLevel() >= 4) && (!strcasecmp(sep->arg[2], "live"))) { - Inviser->Say("Casting invisibilty..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting invisibilty..."); Inviser->CastSpell(42, c->GetID(), 1, -1, -1); } else if ((c->GetLevel() <= 6) && (!strcasecmp(sep->arg[2], "see"))) { - Inviser->Say("I'm not level 6 yet."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I'm not level 6 yet."); } else if ((c->GetLevel() >= 6) && (!strcasecmp(sep->arg[2], "see"))) { - Inviser->Say("Casting see invisible..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting see invisible..."); Inviser->CastSpell(80, c->GetID(), 1, -1, -1); } else if ((c->IsInvisible(c)) || (c->invisible_undead)) { - Inviser->Say("I can't cast this if you're already invis-buffed..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I can't cast this if you're already invis-buffed..."); } else { - Inviser->Say("Do you want [invis undead], [invis live] or [invis see] ?", c->GetName()); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Do you want [invis undead], [invis live] or [invis see] ?", c->GetName()); } break; case MAGICIAN: if (!strcasecmp(sep->arg[2], "undead")) { - Inviser->Say("I don't have that spell."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I don't have that spell."); } else if ((c->GetLevel() <= 8) && (!strcasecmp(sep->arg[2], "live"))) { - Inviser->Say("I'm not level 8 yet."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I'm not level 8 yet."); } else if ((!c->IsInvisible(c))&& (!c->invisible_undead) && (c->GetLevel() >= 8) && (!strcasecmp(sep->arg[2], "live"))) { - Inviser->Say("Casting invisibilty..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting invisibilty..."); Inviser->CastSpell(42, c->GetID(), 1, -1, -1); } else if ((c->GetLevel() <= 16) && (!strcasecmp(sep->arg[2], "see"))) { - Inviser->Say("I'm not level 16 yet."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I'm not level 16 yet."); } else if ((c->GetLevel() >= 16) && (!strcasecmp(sep->arg[2], "see"))) { - Inviser->Say("Casting see invisible..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting see invisible..."); Inviser->CastSpell(80, c->GetID(), 1, -1, -1); } else if ((c->IsInvisible(c)) || (c->invisible_undead)) { - Inviser->Say("I can't cast this if you're already invis-buffed..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I can't cast this if you're already invis-buffed..."); } else { - Inviser->Say("Do you want [invis live] or [invis see] ?", c->GetName()); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Do you want [invis live] or [invis see] ?", c->GetName()); } break; case WIZARD: if ((c->GetLevel() <= 39) && (!strcasecmp(sep->arg[2], "undead"))) { - Inviser->Say("I'm not level 39 yet."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I'm not level 39 yet."); } else if ((!c->IsInvisible(c))&& (!c->invisible_undead) && (c->GetLevel() >= 39) && (!strcasecmp(sep->arg[2], "undead"))) { - Inviser->Say("Casting invis undead..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting invis undead..."); Inviser->CastSpell(235, c->GetID(), 1, -1, -1); } else if ((c->GetLevel() <= 16) && (!strcasecmp(sep->arg[2], "live"))) { - Inviser->Say("I'm not level 16 yet."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I'm not level 16 yet."); } else if ((!c->IsInvisible(c))&& (!c->invisible_undead) && (c->GetLevel() >= 16) && (!strcasecmp(sep->arg[2], "live"))) { - Inviser->Say("Casting invisibilty..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting invisibilty..."); Inviser->CastSpell(42, c->GetID(), 1, -1, -1); } else if ((c->GetLevel() <= 4) && (!strcasecmp(sep->arg[2], "see"))) { - Inviser->Say("I'm not level 6 yet."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I'm not level 6 yet."); } else if ((c->GetLevel() >= 4) && (!strcasecmp(sep->arg[2], "see"))) { - Inviser->Say("Casting see invisible..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting see invisible..."); Inviser->CastSpell(80, c->GetID(), 1, -1, -1); } else if ((c->IsInvisible(c)) || (c->invisible_undead)) { - Inviser->Say("I can't cast this if you're already invis-buffed..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I can't cast this if you're already invis-buffed..."); } else { - Inviser->Say("Do you want [invis undead], [invis live] or [invis see] ?", c->GetName()); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Do you want [invis undead], [invis live] or [invis see] ?", c->GetName()); } break; case NECROMANCER: if ((!c->IsInvisible(c))&& (!c->invisible_undead) && (!strcasecmp(sep->arg[2], "undead"))) { - Inviser->Say("Casting invis undead..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting invis undead..."); Inviser->CastSpell(235, c->GetID(), 1, -1, -1); } else if (!strcasecmp(sep->arg[2], "see")) { - Inviser->Say("I don't have that spell..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I don't have that spell..."); } else if (!strcasecmp(sep->arg[2], "live")) { - Inviser->Say("I don't have that spell..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I don't have that spell..."); } else if ((c->IsInvisible(c))|| (c->invisible_undead)) { - Inviser->Say("I can't cast this if you're already invis-buffed..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I can't cast this if you're already invis-buffed..."); } else { - Inviser->Say("I only have [invis undead]", c->GetName()); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I only have [invis undead]", c->GetName()); } break; case DRUID: if (!strcasecmp(sep->arg[2], "undead")) { - Inviser->Say("I don't have that spell..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I don't have that spell..."); } else if ((c->GetLevel() <= 4) && (!strcasecmp(sep->arg[2], "live"))) { - Inviser->Say("I'm not level 4 yet."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I'm not level 4 yet."); } else if ((!c->IsInvisible(c))&& (!c->invisible_undead) && (c->GetLevel() >= 18) && (!strcasecmp(sep->arg[2], "live"))) { - Inviser->Say("Casting Superior Camouflage..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting Superior Camouflage..."); Inviser->CastSpell(34, c->GetID(), 1, -1, -1); } else if ((!c->IsInvisible(c))&& (!c->invisible_undead) && (c->GetLevel() >= 4) && (!strcasecmp(sep->arg[2], "live")) && (zone->CanCastOutdoor())) { - Inviser->Say("Casting Camouflage..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting Camouflage..."); Inviser->CastSpell(247, c->GetID(), 1, -1, -1); } else if ((c->GetLevel() >= 4) && (!strcasecmp(sep->arg[2], "live")) && (!zone->CanCastOutdoor())) { - Inviser->Say("I can't cast this spell indoors..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I can't cast this spell indoors..."); } else if ((c->GetLevel() <= 13) && (!strcasecmp(sep->arg[2], "see"))) { - Inviser->Say("I'm not level 13 yet."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I'm not level 13 yet."); } else if ((c->GetLevel() >= 13) && (!strcasecmp(sep->arg[2], "see"))) { - Inviser->Say("Casting see invisible..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting see invisible..."); Inviser->CastSpell(80, c->GetID(), 1, -1, -1); } else if ((c->IsInvisible(c)) || (c->invisible_undead)) { - Inviser->Say("I can't cast this if you're already invis-buffed..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I can't cast this if you're already invis-buffed..."); } else { - Inviser->Say("Do you want [invis live] or [invis see] ?", c->GetName()); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Do you want [invis live] or [invis see] ?", c->GetName()); } break; default: @@ -13708,57 +13709,57 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { switch(LeverClass) { case DRUID: if (c->GetLevel() <= 14) { - Lever->Say("I'm not level 14 yet."); + Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "I'm not level 14 yet."); } else if (zone->CanCastOutdoor()) { - Lever->Say("Casting Levitate..."); + Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "Casting Levitate..."); Lever->CastSpell(261, c->GetID(), 1, -1, -1); break; } else if (!zone->CanCastOutdoor()) { - Lever->Say("I can't cast this spell indoors", c->GetName()); + Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "I can't cast this spell indoors", c->GetName()); } break; case SHAMAN: if ((zone->CanCastOutdoor()) && (c->GetLevel() >= 10)) { - Lever->Say("Casting Levitate..."); + Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "Casting Levitate..."); Lever->CastToClient()->CastSpell(261, c->GetID(), 1, -1, -1); } else if (!zone->CanCastOutdoor()) { - Lever->Say("I can't cast this spell indoors", c->GetName()); + Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "I can't cast this spell indoors", c->GetName()); } else if (c->GetLevel() <= 10) { - Lever->Say("I'm not level 10 yet."); + Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "I'm not level 10 yet."); } break; case WIZARD: if((zone->CanCastOutdoor()) && (c->GetLevel() >= 22)){ - Lever->Say("Casting Levitate..."); + Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "Casting Levitate..."); Lever->CastToClient()->CastSpell(261, c->GetID(), 1, -1, -1); } else if (!zone->CanCastOutdoor()) { - Lever->Say("I can't cast this spell indoors", c->GetName()); + Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "I can't cast this spell indoors", c->GetName()); } else if (c->GetLevel() <= 22) { - Lever->Say("I'm not level 22 yet."); + Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "I'm not level 22 yet."); } break; case ENCHANTER: if((zone->CanCastOutdoor()) && (c->GetLevel() >= 15)) { - Lever->Say("Casting Levitate..."); + Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "Casting Levitate..."); Lever->CastToClient()->CastSpell(261, c->GetID(), 1, -1, -1); } else if (!zone->CanCastOutdoor()) { - Lever->Say("I can't cast this spell indoors", c->GetName()); + Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "I can't cast this spell indoors", c->GetName()); } else if (c->GetLevel() <= 15) { - Lever->Say("I'm not level 15 yet."); + Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "I'm not level 15 yet."); } break; @@ -13803,23 +13804,23 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { switch(ResisterClass) { case CLERIC: if (!strcasecmp(sep->arg[2], "poison") && (c->GetLevel() >= 6)) { - Resister->Say("Casting poison protection...", sep->arg[2]); + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting poison protection...", sep->arg[2]); Resister->CastToBot()->Bot_Command_Resist(1, Resister->GetLevel()); } else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 11)) { - Resister->Say("Casting disease protection...", sep->arg[2]); + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting disease protection...", sep->arg[2]); Resister->CastToBot()->Bot_Command_Resist(2, Resister->GetLevel()); } else if(!strcasecmp(sep->arg[2], "fire") && (c->GetLevel() >= 8)) { - Resister->Say("Casting fire protection...", sep->arg[2]); + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting fire protection...", sep->arg[2]); Resister->CastToBot()->Bot_Command_Resist(3, Resister->GetLevel()); } else if(!strcasecmp(sep->arg[2], "cold") && (c->GetLevel() >= 13)) { - Resister->Say("Casting cold protection...", sep->arg[2]); + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting cold protection...", sep->arg[2]); Resister->CastToBot()->Bot_Command_Resist(4, Resister->GetLevel()); } else if(!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() >= 16)) { - Resister->Say("Casting magic protection...", sep->arg[2]); + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting magic protection...", sep->arg[2]); Resister->CastToBot()->Bot_Command_Resist(5, Resister->GetLevel()); } else if (!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() <= 16) @@ -13827,32 +13828,32 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { || !strcasecmp(sep->arg[2], "fire") && (c->GetLevel() <= 8) || !strcasecmp(sep->arg[2], "disease") && (c->GetLevel() <= 11) || !strcasecmp(sep->arg[2], "poison") && (c->GetLevel() <= 6)) { - Resister->Say("I don't have the needed level yet", sep->arg[2]); + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "I don't have the needed level yet", sep->arg[2]); } else - Resister->Say("Do you want [resist poison], [resist disease], [resist fire], [resist cold], or [resist magic]?", c->GetName()); + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Do you want [resist poison], [resist disease], [resist fire], [resist cold], or [resist magic]?", c->GetName()); break; case SHAMAN: if (!strcasecmp(sep->arg[2], "poison") && (c->GetLevel() >= 20)) { - Resister->Say("Casting poison protection...", sep->arg[2]); + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting poison protection...", sep->arg[2]); Resister->CastToBot()->Bot_Command_Resist(12, Resister->GetLevel()); } else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 8)) { - Resister->Say("Casting disease protection...", sep->arg[2]); + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting disease protection...", sep->arg[2]); Resister->CastToBot()->Bot_Command_Resist(13, Resister->GetLevel()); } else if(!strcasecmp(sep->arg[2], "fire") && (c->GetLevel() >= 5)) { - Resister->Say("Casting fire protection...", sep->arg[2]); + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting fire protection...", sep->arg[2]); Resister->CastToBot()->Bot_Command_Resist(14, Resister->GetLevel()); } else if(!strcasecmp(sep->arg[2], "cold") && (c->GetLevel() >= 1)) { - Resister->Say("Casting cold protection...", sep->arg[2]); + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting cold protection...", sep->arg[2]); Resister->CastToBot()->Bot_Command_Resist(15, Resister->GetLevel()); } else if(!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() >= 19)) { - Resister->Say("Casting magic protection...", sep->arg[2]); + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting magic protection...", sep->arg[2]); Resister->CastToBot()->Bot_Command_Resist(16, Resister->GetLevel()); } else if (!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() <= 19) @@ -13860,43 +13861,43 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { || !strcasecmp(sep->arg[2], "fire") && (c->GetLevel() <= 5) || !strcasecmp(sep->arg[2], "disease") && (c->GetLevel() <= 8) || !strcasecmp(sep->arg[2], "poison") && (c->GetLevel() <= 20)) { - Resister->Say("I don't have the needed level yet", sep->arg[2]); + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "I don't have the needed level yet", sep->arg[2]); } else - Resister->Say("Do you want [resist poison], [resist disease], [resist fire], [resist cold], or [resist magic]?", c->GetName()); + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Do you want [resist poison], [resist disease], [resist fire], [resist cold], or [resist magic]?", c->GetName()); break; case DRUID: if (!strcasecmp(sep->arg[2], "poison") && (c->GetLevel() >= 19)) { - Resister->Say("Casting poison protection...", sep->arg[2]); + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting poison protection...", sep->arg[2]); Resister->CastToBot()->Bot_Command_Resist(7, Resister->GetLevel()); } else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 19)) { - Resister->Say("Casting disease protection...", sep->arg[2]); + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting disease protection...", sep->arg[2]); Resister->CastToBot()->Bot_Command_Resist(8, Resister->GetLevel()); } else if(!strcasecmp(sep->arg[2], "fire")) { // Fire level 1 - Resister->Say("Casting fire protection...", sep->arg[2]); + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting fire protection...", sep->arg[2]); Resister->CastToBot()->Bot_Command_Resist(9, Resister->GetLevel()); } else if(!strcasecmp(sep->arg[2], "cold") && (c->GetLevel() >= 13)) { - Resister->Say("Casting cold protection...", sep->arg[2]); + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting cold protection...", sep->arg[2]); Resister->CastToBot()->Bot_Command_Resist(10, Resister->GetLevel()); } else if(!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() >= 16)) { - Resister->Say("Casting magic protection...", sep->arg[2]); + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting magic protection...", sep->arg[2]); Resister->CastToBot()->Bot_Command_Resist(11, Resister->GetLevel()); } else if (!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() <= 16) || !strcasecmp(sep->arg[2], "cold") && (c->GetLevel() <= 9) || !strcasecmp(sep->arg[2], "disease") && (c->GetLevel() <= 19) || !strcasecmp(sep->arg[2], "poison") && (c->GetLevel() <= 19)) { - Resister->Say("I don't have the needed level yet", sep->arg[2]) ; + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "I don't have the needed level yet", sep->arg[2]) ; } else - Resister->Say("Do you want [resist poison], [resist disease], [resist fire], [resist cold], or [resist magic]?", c->GetName()); + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Do you want [resist poison], [resist disease], [resist fire], [resist cold], or [resist magic]?", c->GetName()); break; @@ -14022,9 +14023,9 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(botGroupLeader) { if(Bot::BotGroupCreate(botGroupLeader)) - botGroupLeader->Say("I am prepared to lead."); + botGroupLeader->BotGroupSay(botGroupLeader, "I am prepared to lead."); else - botGroupLeader->Say("I can not lead."); + botGroupLeader->BotGroupSay(botGroupLeader, "I cannot lead."); } else c->Message(13, "You must target a spawned bot first."); @@ -14088,17 +14089,17 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { // invite if(Bot::AddBotToGroup(botGroupMember, g)) { database.SetGroupID(botGroupMember->GetName(), g->GetID(), botGroupMember->GetBotID()); - botGroupMember->Say("I have joined %s\'s group.", botGroupLeader->GetName()); + botGroupMember->BotGroupSay(botGroupMember, "I have joined %s\'s group.", botGroupLeader->GetName()); } else { - botGroupMember->Say("I can not join %s\'s group.", botGroupLeader->GetName()); + botGroupMember->BotGroupSay(botGroupMember, "I can not join %s\'s group.", botGroupLeader->GetName()); } } else { // "I am already in a group." Group* tempGroup = botGroupMember->GetGroup(); if(tempGroup) - botGroupMember->Say("I can not join %s\'s group. I am already a member in %s\'s group.", botGroupLeader->GetName(), tempGroup->GetLeaderName()); + botGroupMember->BotGroupSay(botGroupMember, "I can not join %s\'s group. I am already a member in %s\'s group.", botGroupLeader->GetName(), tempGroup->GetLeaderName()); } } else { @@ -14108,14 +14109,14 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } else { // "My group is full." - botGroupLeader->Say("I have no more openings in my group, %s.", c->GetName()); + botGroupLeader->BotGroupSay(botGroupMember, "I have no more openings in my group, %s.", c->GetName()); } } else { // "I am not a group leader." Group* tempGroup = botGroupLeader->GetGroup(); if(tempGroup) - botGroupLeader->Say("I can not lead anyone because I am a member in %s\'s group.", tempGroup->GetLeaderName()); + botGroupLeader->BotGroupSay(botGroupLeader, "I can not lead anyone because I am a member in %s\'s group.", tempGroup->GetLeaderName()); } } } @@ -14141,12 +14142,12 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { Group* g = botGroupMember->GetGroup(); if(Bot::RemoveBotFromGroup(botGroupMember, g)) - botGroupMember->Say("I am no longer in a group."); + botGroupMember->BotGroupSay(botGroupMember, "I am no longer in a group."); else - botGroupMember->Say("I can not leave %s\'s group.", g->GetLeaderName()); + botGroupMember->BotGroupSay(botGroupMember, "I can not leave %s\'s group.", g->GetLeaderName()); } else - botGroupMember->Say("I am not in a group."); + botGroupMember->BotGroupSay(botGroupMember, "I am not in a group."); } else c->Message(13, "You must target a spawned bot first."); @@ -14173,16 +14174,16 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(g->IsLeader(botGroupLeader)) { if(Bot::RemoveBotFromGroup(botGroupLeader, g)) - botGroupLeader->Say("I have disbanded my group, %s.", c->GetName()); + botGroupLeader->BotGroupSay(botGroupLeader, "I have disbanded my group, %s.", c->GetName()); else - botGroupLeader->Say("I was not able to disband my group, %s.", c->GetName()); + botGroupLeader->BotGroupSay(botGroupLeader, "I was not able to disband my group, %s.", c->GetName()); } else { - botGroupLeader->Say("I can not disband my group, %s, because I am not the leader. %s is the leader of my group.", c->GetName(), g->GetLeaderName()); + botGroupLeader->BotGroupSay(botGroupLeader, "I can not disband my group, %s, because I am not the leader. %s is the leader of my group.", c->GetName(), g->GetLeaderName()); } } else - botGroupLeader->Say("I am not a group leader, %s.", c->GetName()); + botGroupLeader->BotGroupSay(botGroupLeader, "I am not a group leader, %s.", c->GetName()); } else c->Message(13, "You must target a spawned bot group leader first."); @@ -14633,7 +14634,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { c->Message(0, "Requires a value."); } } else { - c->Message(0,"A bot needs to be targetted."); + c->Message(0,"A bot needs to be targeted."); } return; } @@ -14667,11 +14668,11 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(taunt) { if(!targetedBot->taunting) - targetedBot->Say("I am now taunting."); + targetedBot->BotGroupSay(targetedBot, "I am now taunting."); } else { if(targetedBot->taunting) - targetedBot->Say("I am no longer taunting."); + targetedBot->BotGroupSay(targetedBot, "I am no longer taunting."); } targetedBot->SetTaunting(taunt); @@ -15944,7 +15945,7 @@ void EntityList::BotPickLock(Bot* rogue) if((skill+bonus1+bonus2) >= cdoor->GetLockpick()) cdoor->ForceOpen(rogue); else - rogue->Say("I am not skilled enough for this lock."); + rogue->BotGroupSay(rogue, "I am not skilled enough for this lock."); } } @@ -16369,9 +16370,7 @@ void Bot::SetDefaultBotStance() { _botStance = defaultStance; } -void Bot::BotGroupSay(Mob *speaker, const char *msg, ...) -{ - +void Bot::BotGroupSay(Mob *speaker, const char *msg, ...) { char buf[1000]; va_list ap; @@ -16384,6 +16383,8 @@ void Bot::BotGroupSay(Mob *speaker, const char *msg, ...) if(g) g->GroupMessage(speaker->CastToMob(), 0, 100, buf); + } else { + speaker->Say("%s", buf); } } @@ -16408,7 +16409,7 @@ bool Bot::UseDiscipline(uint32 spell_id, uint32 target) { //make sure we can use it.. if(!IsValidSpell(spell_id)) { - Say("Not a valid spell"); + BotGroupSay(this, "Not a valid spell"); return(false); } From 6ffe7a956319eadad56534963bccf8b06c203a26 Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Fri, 19 Jun 2015 05:25:48 -0400 Subject: [PATCH 126/129] More bot changes. - Added #bot showhelm [on|off] - Allows you to disable your bot's helmet showing up --- zone/bot.cpp | 35 +++++++++++++++++++++++++++++++---- zone/bot.h | 3 +++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index c27ae20ce..4d3c0e75e 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -67,6 +67,7 @@ Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, nullptr, glm SetHealRotationTimer(0); SetNumHealRotationMembers(0); SetBardUseOutOfCombatSongs(GetClass() == BARD); + SetShowHelm(true); CalcChanceToCast(); rest_timer.Disable(); @@ -4126,7 +4127,7 @@ void Bot::Spawn(Client* botCharacterOwner, std::string* errorMessage) { uint8 materialFromSlot = 0xFF; for(int i = EmuConstants::EQUIPMENT_BEGIN; i <= EmuConstants::EQUIPMENT_END; ++i) { itemID = GetBotItemBySlot(i); - if(itemID != 0) { + if(itemID != 0) { materialFromSlot = Inventory::CalcMaterialFromSlot(i); if(materialFromSlot != 0xFF) this->SendWearChange(materialFromSlot); @@ -4359,7 +4360,7 @@ void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { ns->spawn.is_npc = 0; // 0=no, 1=yes ns->spawn.is_pet = 0; ns->spawn.guildrank = 0; - ns->spawn.showhelm = 1; + ns->spawn.showhelm = GetShowHelm(); ns->spawn.flymode = 0; ns->spawn.size = 0; ns->spawn.NPC = 0; // 0=player,1=npc,2=pc corpse,3=npc corpse @@ -4367,7 +4368,7 @@ void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { UpdateActiveLight(); ns->spawn.light = m_Light.Type.Active; - ns->spawn.helm = helmtexture; //0xFF; + ns->spawn.helm = (GetShowHelm() ? helmtexture : 0); //0xFF; ns->spawn.equip_chest2 = texture; //0xFF; const Item_Struct* item = 0; @@ -11428,7 +11429,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if (!results.Success()) return; - for (int i = 0; i < 7; i++) { + for (int i = 0; i < 7; i++) { uint8 slotmaterial = Inventory::CalcMaterialFromSlot((uint8)slots[i]); c->GetTarget()->CastToBot()->SendWearChange(slotmaterial); } @@ -15616,6 +15617,32 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } return; } + + if(!strcasecmp(sep->arg[1], "showhelm")) { + bool showhelm = true; + if (sep->arg[2]) { + if (!strcasecmp(sep->arg[2], "on")) + showhelm = true; + else if (!strcasecmp(sep->arg[2], "off")) + showhelm = false; + else { + c->Message(0, "Usage #bot showhelm [on|off]"); + return; + } + + Mob *target = c->GetTarget(); + if (target && target->IsBot() && (c == target->GetOwner()->CastToClient())) { + Bot* b = target->CastToBot(); + if (b) { + b->SetShowHelm(showhelm); + c->Message(0, "Your bot will %s show their helmet.", (showhelm ? "now" : "no longer")); + } + } + } else + c->Message(0, "Usage #bot showhelm [on|off]"); + + return; + } } // franck: EQoffline diff --git a/zone/bot.h b/zone/bot.h index 25e0d1c52..19968c394 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -467,6 +467,7 @@ public: uint32 GetHealRotationNextHealTime() { return _healRotationNextHeal; } uint32 GetHealRotationTimer () { return _healRotationTimer; } bool GetBardUseOutOfCombatSongs() { return _bardUseOutOfCombatSongs;} + bool GetShowHelm() { return _showhelm; } inline virtual int32 GetAC() const { return AC; } inline virtual int32 GetSTR() const { return STR; } inline virtual int32 GetSTA() const { return STA; } @@ -550,6 +551,7 @@ public: void SetHealRotationTimer( uint32 timer ) { _healRotationTimer = timer; } void SetNumHealRotationMembers( uint8 numMembers ) { _numHealRotationMembers = numMembers; } void SetBardUseOutOfCombatSongs(bool useOutOfCombatSongs) { _bardUseOutOfCombatSongs = useOutOfCombatSongs;} + void SetShowHelm(bool showhelm) { _showhelm = showhelm; } // Class Destructors virtual ~Bot(); @@ -622,6 +624,7 @@ private: std::map botAAs; InspectMessage_Struct _botInspectMessage; bool _bardUseOutOfCombatSongs; + bool _showhelm; // Private "base stats" Members int32 _baseMR; From c5609db8d12bdb36b65565cf0708bf86dc309a31 Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Fri, 19 Jun 2015 15:53:20 -0400 Subject: [PATCH 127/129] Preferential bot formatting changes. --- zone/bot.cpp | 7785 ++++++++++++++++---------------------------------- 1 file changed, 2488 insertions(+), 5297 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 4d3c0e75e..9846173ba 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -13,19 +13,16 @@ Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, nullptr, glm if(botOwner) { this->SetBotOwner(botOwner); this->_botOwnerCharacterID = botOwner->CharacterID(); - } - else { + } else { this->SetBotOwner(0); this->_botOwnerCharacterID = 0; } _guildRank = 0; _guildId = 0; - _lastTotalPlayTime = 0; _startTotalPlayTime = time(&_startTotalPlayTime); _lastZoneId = 0; - _baseMR = npcTypeData.MR; _baseCR = npcTypeData.CR; _baseDR = npcTypeData.DR; @@ -46,7 +43,6 @@ Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, nullptr, glm RestRegenHP = 0; RestRegenMana = 0; RestRegenEndurance = 0; - SetBotID(0); SetBotSpellID(0); SetSpawnStatus(false); @@ -70,53 +66,40 @@ Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, nullptr, glm SetShowHelm(true); CalcChanceToCast(); rest_timer.Disable(); - SetFollowDistance(184); - // Do this once and only in this constructor GenerateAppearance(); - GenerateBaseStats(); GenerateArmorClass(); - // Calculate HitPoints Last As It Uses Base Stats cur_hp = GenerateBaseHitPoints(); cur_mana = GenerateBaseManaPoints(); cur_end = CalcBaseEndurance(); - hp_regen = CalcHPRegen(); mana_regen = CalcManaRegen(); end_regen = CalcEnduranceRegen(); - - for (int i = 0; i < MaxTimer; i++) { + for (int i = 0; i < MaxTimer; i++) timers[i] = 0; - } - for(int i = 0; i < MaxHealRotationTargets; i++) { + for(int i = 0; i < MaxHealRotationTargets; i++) _healRotationTargets[i] = 0; - } strcpy(this->name, this->GetCleanName()); - memset(&m_Light, 0, sizeof(LightProfile_Struct)); } // This constructor is used when the bot is loaded out of the database Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, NPCType npcTypeData) : NPC(&npcTypeData, nullptr, glm::vec4(), 0, false), rest_timer(1) { this->_botOwnerCharacterID = botOwnerCharacterID; - - if(this->_botOwnerCharacterID > 0) { + if(this->_botOwnerCharacterID > 0) this->SetBotOwner(entity_list.GetClientByCharID(this->_botOwnerCharacterID)); - } _guildRank = 0; _guildId = 0; - _lastTotalPlayTime = totalPlayTime; _startTotalPlayTime = time(&_startTotalPlayTime); _lastZoneId = lastZoneId; berserk = false; - _baseMR = npcTypeData.MR; _baseCR = npcTypeData.CR; _baseDR = npcTypeData.DR; @@ -136,11 +119,9 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to _baseGender = npcTypeData.gender; cur_hp = npcTypeData.cur_hp; cur_mana = npcTypeData.Mana; - RestRegenHP = 0; RestRegenMana = 0; RestRegenEndurance = 0; - SetBotID(botID); SetBotSpellID(botSpellsID); SetSpawnStatus(false); @@ -164,61 +145,49 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to SetNumHealRotationMembers(0); CalcChanceToCast(); rest_timer.Disable(); - SetFollowDistance(184); - strcpy(this->name, this->GetCleanName()); - database.GetBotInspectMessage(this->GetBotID(), &_botInspectMessage); - LoadGuildMembership(&_guildId, &_guildRank, &_guildName); - std::string TempErrorMessage; - EquipBot(&TempErrorMessage); - if(!TempErrorMessage.empty()) { - // TODO: log error message to zone error log if(GetBotOwner()) GetBotOwner()->Message(13, TempErrorMessage.c_str()); } - for (int i = 0; i < MaxTimer; i++) { + for (int i = 0; i < MaxTimer; i++) timers[i] = 0; - } - for(int i = 0; i < MaxHealRotationTargets; i++) { + for(int i = 0; i < MaxHealRotationTargets; i++) _healRotationTargets[i] = 0; - } GenerateBaseStats(); - LoadTimers(); LoadAAs(); LoadBuffs(); - CalcBotStats(false); - hp_regen = CalcHPRegen(); mana_regen = CalcManaRegen(); end_regen = CalcEnduranceRegen(); - if(cur_hp > max_hp) cur_hp = max_hp; + if(cur_hp <= 0) { SetHP(max_hp/5); SetMana(0); BuffFadeAll(); SpellOnTarget(756, this); // Rezz effects } + if(cur_mana > max_mana) cur_mana = max_mana; + cur_end = max_end; } Bot::~Bot() { AI_Stop(); - if(HasGroup()) Bot::RemoveBotFromGroup(this, GetGroup()); @@ -237,57 +206,39 @@ void Bot::SetBotSpellID(uint32 newSpellID) { this->npc_spells_id = newSpellID; } -uint32 Bot::GetBotArcheryRange() -{ +uint32 Bot::GetBotArcheryRange() { const ItemInst *range_inst = GetBotItem(MainRange); const ItemInst *ammo_inst = GetBotItem(MainAmmo); - - // empty slots if (!range_inst || !ammo_inst) return 0; const Item_Struct *range_item = range_inst->GetItem(); const Item_Struct *ammo_item = ammo_inst->GetItem(); - - // no item struct for whatever reason - if (!range_item || !ammo_item) - return 0; - - // bad item types - if (range_item->ItemType != ItemTypeBow || ammo_item->ItemType != ItemTypeArrow) + if (!range_item || !ammo_item || range_item->ItemType != ItemTypeBow || ammo_item->ItemType != ItemTypeArrow) return 0; // everything is good! - return range_item->Range + ammo_item->Range; + return (range_item->Range + ammo_item->Range); } void Bot::ChangeBotArcherWeapons(bool isArcher) { - if((GetClass()==WARRIOR) || (GetClass()==PALADIN) || (GetClass()==RANGER) - || (GetClass()==SHADOWKNIGHT) || (GetClass()==ROGUE)) - { + if((GetClass()==WARRIOR) || (GetClass()==PALADIN) || (GetClass()==RANGER) || (GetClass()==SHADOWKNIGHT) || (GetClass()==ROGUE)) { if(!isArcher) { BotAddEquipItem(MainPrimary, GetBotItemBySlot(MainPrimary)); BotAddEquipItem(MainSecondary, GetBotItemBySlot(MainSecondary)); - //archerbot->SendWearChange(MATERIAL_PRIMARY); - //archerbot->SendWearChange(MATERIAL_SECONDARY); SetAttackTimer(); BotGroupSay(this, "My blade is ready."); - } - else { - //archerbot->SendWearChange(MATERIAL_PRIMARY); - //archerbot->SendWearChange(MATERIAL_SECONDARY); + } else { BotRemoveEquipItem(MainPrimary); BotRemoveEquipItem(MainSecondary); - //archerbot->SendBotArcheryWearChange(MATERIAL_PRIMARY, archeryMaterial, archeryColor); BotAddEquipItem(MainAmmo, GetBotItemBySlot(MainAmmo)); BotAddEquipItem(MainSecondary, GetBotItemBySlot(MainRange)); SetAttackTimer(); BotGroupSay(this, "My bow is true and ready."); } } - else { + else BotGroupSay(this, "I don't know how to use a bow."); - } } void Bot::Sit() { @@ -306,7 +257,6 @@ void Bot::Stand() { bool Bot::IsSitting() { bool result = false; - if(GetAppearance() == eaSitting && !IsMoving()) result = true; @@ -315,7 +265,6 @@ bool Bot::IsSitting() { bool Bot::IsStanding() { bool result = false; - if(GetAppearance() == eaStanding) result = true; @@ -325,15 +274,12 @@ bool Bot::IsStanding() { NPCType Bot::FillNPCTypeStruct(uint32 botSpellsID, std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender, float size, uint32 face, uint32 hairStyle, uint32 hairColor, uint32 eyeColor, uint32 eyeColor2, uint32 beardColor, uint32 beard, uint32 drakkinHeritage, uint32 drakkinTattoo, uint32 drakkinDetails, int32 hp, int32 mana, int32 mr, int32 cr, int32 dr, int32 fr, int32 pr, int32 corrup, int32 ac, uint32 str, uint32 sta, uint32 dex, uint32 agi, uint32 _int, uint32 wis, uint32 cha, uint32 attack) { NPCType BotNPCType; int CopyLength = 0; - CopyLength = botName.copy(BotNPCType.name, 63); BotNPCType.name[CopyLength] = '\0'; CopyLength = 0; - CopyLength = botLastName.copy(BotNPCType.lastname, 69); BotNPCType.lastname[CopyLength] = '\0'; CopyLength = 0; - BotNPCType.npc_spells_id = botSpellsID; BotNPCType.level = botLevel; BotNPCType.race = botRace; @@ -367,7 +313,6 @@ NPCType Bot::FillNPCTypeStruct(uint32 botSpellsID, std::string botName, std::str BotNPCType.WIS = wis; BotNPCType.CHA = cha; BotNPCType.ATK = attack; - BotNPCType.npc_id = 0; BotNPCType.texture = 0; BotNPCType.d_melee_texture1 = 0; @@ -380,29 +325,23 @@ NPCType Bot::FillNPCTypeStruct(uint32 botSpellsID, std::string botName, std::str BotNPCType.hp_regen = 1; BotNPCType.mana_regen = 1; BotNPCType.maxlevel = botLevel; - BotNPCType.light = NOT_USED; // due to the way that bots are coded..this is sent post-spawn - return BotNPCType; } NPCType Bot::CreateDefaultNPCTypeStructForBot(std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender) { NPCType Result; int CopyLength = 0; - CopyLength = botName.copy(Result.name, 63); Result.name[CopyLength] = '\0'; CopyLength = 0; - CopyLength = botLastName.copy(Result.lastname, 69); Result.lastname[CopyLength] = '\0'; CopyLength = 0; - Result.level = botLevel; Result.race = botRace; Result.class_ = botClass; Result.gender = gender; - // default values Result.maxlevel = botLevel; Result.size = 6.0; @@ -437,13 +376,11 @@ NPCType Bot::CreateDefaultNPCTypeStructForBot(std::string botName, std::string b Result.CR = 25; Result.Corrup = 15; Result.AC = 12; - return Result; } void Bot::GenerateBaseStats() { int BotSpellID = 0; - // base stats uint32 Strength = _baseSTR; uint32 Stamina = _baseSTA; @@ -459,286 +396,284 @@ void Bot::GenerateBaseStats() { int32 PoisonResist = _basePR; int32 ColdResist = _baseCR; int32 CorruptionResist = _baseCorrup; - switch(this->GetClass()) { - case 1: // Warrior (why not just use 'case WARRIOR:'?) - Strength += 10; - Stamina += 20; - Agility += 10; - Dexterity += 10; - Attack += 12; - break; - case 2: // Cleric - BotSpellID = 701; - Strength += 5; - Stamina += 5; - Agility += 10; - Wisdom += 30; - Attack += 8; - break; - case 3: // Paladin - BotSpellID = 708; - Strength += 15; - Stamina += 5; - Wisdom += 15; - Charisma += 10; - Dexterity += 5; - Attack += 17; - break; - case 4: // Ranger - BotSpellID = 710; - Strength += 15; - Stamina += 10; - Agility += 10; - Wisdom += 15; - Attack += 17; - break; - case 5: // Shadowknight - BotSpellID = 709; - Strength += 10; - Stamina += 15; - Intelligence += 20; - Charisma += 5; - Attack += 17; - break; - case 6: // Druid - BotSpellID = 707; - Stamina += 15; - Wisdom += 35; - Attack += 5; - break; - case 7: // Monk - Strength += 5; - Stamina += 15; - Agility += 15; - Dexterity += 15; - Attack += 17; - break; - case 8: // Bard - BotSpellID = 711; - Strength += 15; - Dexterity += 10; - Charisma += 15; - Intelligence += 10; - Attack += 17; - break; - case 9: // Rogue - Strength += 10; - Stamina += 20; - Agility += 10; - Dexterity += 10; - Attack += 12; - break; - case 10: // Shaman - BotSpellID = 706; - Stamina += 10; - Wisdom += 30; - Charisma += 10; - Attack += 28; - break; - case 11: // Necromancer - BotSpellID = 703; - Dexterity += 10; - Agility += 10; - Intelligence += 30; - Attack += 5; - break; - case 12: // Wizard - BotSpellID = 702; - Stamina += 20; - Intelligence += 30; - Attack += 5; - break; - case 13: // Magician - BotSpellID = 704; - Stamina += 20; - Intelligence += 30; - Attack += 5; - break; - case 14: // Enchanter - BotSpellID = 705; - Intelligence += 25; - Charisma += 25; - Attack += 5; - break; - case 15: // Beastlord - BotSpellID = 712; - Stamina += 10; - Agility += 10; - Dexterity += 5; - Wisdom += 20; - Charisma += 5; - Attack += 31; - break; - case 16: // Berserker - Strength += 10; - Stamina += 15; - Dexterity += 15; - Agility += 10; - Attack += 25; - break; + case 1: // Warrior (why not just use 'case WARRIOR:'?) + Strength += 10; + Stamina += 20; + Agility += 10; + Dexterity += 10; + Attack += 12; + break; + case 2: // Cleric + BotSpellID = 701; + Strength += 5; + Stamina += 5; + Agility += 10; + Wisdom += 30; + Attack += 8; + break; + case 3: // Paladin + BotSpellID = 708; + Strength += 15; + Stamina += 5; + Wisdom += 15; + Charisma += 10; + Dexterity += 5; + Attack += 17; + break; + case 4: // Ranger + BotSpellID = 710; + Strength += 15; + Stamina += 10; + Agility += 10; + Wisdom += 15; + Attack += 17; + break; + case 5: // Shadowknight + BotSpellID = 709; + Strength += 10; + Stamina += 15; + Intelligence += 20; + Charisma += 5; + Attack += 17; + break; + case 6: // Druid + BotSpellID = 707; + Stamina += 15; + Wisdom += 35; + Attack += 5; + break; + case 7: // Monk + Strength += 5; + Stamina += 15; + Agility += 15; + Dexterity += 15; + Attack += 17; + break; + case 8: // Bard + BotSpellID = 711; + Strength += 15; + Dexterity += 10; + Charisma += 15; + Intelligence += 10; + Attack += 17; + break; + case 9: // Rogue + Strength += 10; + Stamina += 20; + Agility += 10; + Dexterity += 10; + Attack += 12; + break; + case 10: // Shaman + BotSpellID = 706; + Stamina += 10; + Wisdom += 30; + Charisma += 10; + Attack += 28; + break; + case 11: // Necromancer + BotSpellID = 703; + Dexterity += 10; + Agility += 10; + Intelligence += 30; + Attack += 5; + break; + case 12: // Wizard + BotSpellID = 702; + Stamina += 20; + Intelligence += 30; + Attack += 5; + break; + case 13: // Magician + BotSpellID = 704; + Stamina += 20; + Intelligence += 30; + Attack += 5; + break; + case 14: // Enchanter + BotSpellID = 705; + Intelligence += 25; + Charisma += 25; + Attack += 5; + break; + case 15: // Beastlord + BotSpellID = 712; + Stamina += 10; + Agility += 10; + Dexterity += 5; + Wisdom += 20; + Charisma += 5; + Attack += 31; + break; + case 16: // Berserker + Strength += 10; + Stamina += 15; + Dexterity += 15; + Agility += 10; + Attack += 25; + break; } float BotSize = GetSize(); switch(this->GetRace()) { - case 1: // Humans have no race bonus - break; - case 2: // Barbarian - Strength += 28; - Stamina += 20; - Agility += 7; - Dexterity -= 5; - Wisdom -= 5; - Intelligence -= 10; - Charisma -= 20; - BotSize = 7.0; - ColdResist += 10; - break; - case 3: // Erudite - Strength -= 15; - Stamina -= 5; - Agility -= 5; - Dexterity -= 5; - Wisdom += 8; - Intelligence += 32; - Charisma -= 5; - MagicResist += 5; - DiseaseResist -= 5; - break; - case 4: // Wood Elf - Strength -= 10; - Stamina -= 10; - Agility += 20; - Dexterity += 5; - Wisdom += 5; - BotSize = 5.0; - break; - case 5: // High Elf - Strength -= 20; - Stamina -= 10; - Agility += 10; - Dexterity -= 5; - Wisdom += 20; - Intelligence += 12; - Charisma += 5; - break; - case 6: // Dark Elf - Strength -= 15; - Stamina -= 10; - Agility += 15; - Wisdom += 8; - Intelligence += 24; - Charisma -= 15; - BotSize = 5.0; - break; - case 7: // Half Elf - Strength -= 5; - Stamina -= 5; - Agility += 15; - Dexterity += 10; - Wisdom -= 15; - BotSize = 5.5; - break; - case 8: // Dwarf - Strength += 15; - Stamina += 15; - Agility -= 5; - Dexterity += 15; - Wisdom += 8; - Intelligence -= 15; - Charisma -= 30; - BotSize = 4.0; - MagicResist -= 5; - PoisonResist += 5; - break; - case 9: // Troll - Strength += 33; - Stamina += 34; - Agility += 8; - Wisdom -= 15; - Intelligence -= 23; - Charisma -= 35; - BotSize = 8.0; - FireResist -= 20; - break; - case 10: // Ogre - Strength += 55; - Stamina += 77; - Agility -= 5; - Dexterity -= 5; - Wisdom -= 8; - Intelligence -= 15; - Charisma -= 38; - BotSize = 9.0; - break; - case 11: // Halfling - Strength -= 5; - Agility += 20; - Dexterity += 15; - Wisdom += 5; - Intelligence -= 8; - Charisma -= 25; - BotSize = 3.5; - PoisonResist += 5; - DiseaseResist += 5; - break; - case 12: // Gnome - Strength -= 15; - Stamina -= 5; - Agility += 10; - Dexterity += 10; - Wisdom -= 8; - Intelligence += 23; - Charisma -= 15; - BotSize = 3.0; - break; - case 128: // Iksar - Strength -= 5; - Stamina -= 5; - Agility += 15; - Dexterity += 10; - Wisdom += 5; - Charisma -= 20; - MagicResist -= 5; - FireResist -= 5; - break; - case 130: // Vah Shir - Strength += 15; - Agility += 15; - Dexterity -= 5; - Wisdom -= 5; - Intelligence -= 10; - Charisma -= 10; - BotSize = 7.0; - MagicResist -= 5; - FireResist -= 5; - break; - case 330: // Froglok - Strength -= 5; - Stamina += 5; - Agility += 25; - Dexterity += 25; - Charisma -= 25; - BotSize = 5.0; - MagicResist -= 5; - FireResist -= 5; - break; - case 522: // Drakkin - Strength -= 5; - Stamina += 5; - Agility += 10; - Intelligence += 10; - Wisdom += 5; - BotSize = 5.0; - PoisonResist += 2; - DiseaseResist += 2; - MagicResist += 2; - FireResist += 2; - ColdResist += 2; - break; + case 1: // Humans have no race bonus + break; + case 2: // Barbarian + Strength += 28; + Stamina += 20; + Agility += 7; + Dexterity -= 5; + Wisdom -= 5; + Intelligence -= 10; + Charisma -= 20; + BotSize = 7.0; + ColdResist += 10; + break; + case 3: // Erudite + Strength -= 15; + Stamina -= 5; + Agility -= 5; + Dexterity -= 5; + Wisdom += 8; + Intelligence += 32; + Charisma -= 5; + MagicResist += 5; + DiseaseResist -= 5; + break; + case 4: // Wood Elf + Strength -= 10; + Stamina -= 10; + Agility += 20; + Dexterity += 5; + Wisdom += 5; + BotSize = 5.0; + break; + case 5: // High Elf + Strength -= 20; + Stamina -= 10; + Agility += 10; + Dexterity -= 5; + Wisdom += 20; + Intelligence += 12; + Charisma += 5; + break; + case 6: // Dark Elf + Strength -= 15; + Stamina -= 10; + Agility += 15; + Wisdom += 8; + Intelligence += 24; + Charisma -= 15; + BotSize = 5.0; + break; + case 7: // Half Elf + Strength -= 5; + Stamina -= 5; + Agility += 15; + Dexterity += 10; + Wisdom -= 15; + BotSize = 5.5; + break; + case 8: // Dwarf + Strength += 15; + Stamina += 15; + Agility -= 5; + Dexterity += 15; + Wisdom += 8; + Intelligence -= 15; + Charisma -= 30; + BotSize = 4.0; + MagicResist -= 5; + PoisonResist += 5; + break; + case 9: // Troll + Strength += 33; + Stamina += 34; + Agility += 8; + Wisdom -= 15; + Intelligence -= 23; + Charisma -= 35; + BotSize = 8.0; + FireResist -= 20; + break; + case 10: // Ogre + Strength += 55; + Stamina += 77; + Agility -= 5; + Dexterity -= 5; + Wisdom -= 8; + Intelligence -= 15; + Charisma -= 38; + BotSize = 9.0; + break; + case 11: // Halfling + Strength -= 5; + Agility += 20; + Dexterity += 15; + Wisdom += 5; + Intelligence -= 8; + Charisma -= 25; + BotSize = 3.5; + PoisonResist += 5; + DiseaseResist += 5; + break; + case 12: // Gnome + Strength -= 15; + Stamina -= 5; + Agility += 10; + Dexterity += 10; + Wisdom -= 8; + Intelligence += 23; + Charisma -= 15; + BotSize = 3.0; + break; + case 128: // Iksar + Strength -= 5; + Stamina -= 5; + Agility += 15; + Dexterity += 10; + Wisdom += 5; + Charisma -= 20; + MagicResist -= 5; + FireResist -= 5; + break; + case 130: // Vah Shir + Strength += 15; + Agility += 15; + Dexterity -= 5; + Wisdom -= 5; + Intelligence -= 10; + Charisma -= 10; + BotSize = 7.0; + MagicResist -= 5; + FireResist -= 5; + break; + case 330: // Froglok + Strength -= 5; + Stamina += 5; + Agility += 25; + Dexterity += 25; + Charisma -= 25; + BotSize = 5.0; + MagicResist -= 5; + FireResist -= 5; + break; + case 522: // Drakkin + Strength -= 5; + Stamina += 5; + Agility += 10; + Intelligence += 10; + Wisdom += 5; + BotSize = 5.0; + PoisonResist += 2; + DiseaseResist += 2; + MagicResist += 2; + FireResist += 2; + ColdResist += 2; + break; } - this->STR = Strength; this->STA = Stamina; this->DEX = Dexterity; @@ -756,7 +691,6 @@ void Bot::GenerateBaseStats() { this->Corrup = CorruptionResist; SetBotSpellID(BotSpellID); this->size = BotSize; - this->pAggroRange = 0; this->pAssistRange = 0; this->raid_target = false; @@ -765,12 +699,10 @@ void Bot::GenerateBaseStats() { void Bot::GenerateAppearance() { // Randomize facial appearance int iFace = 0; - if(this->GetRace() == 2) { // Barbarian w/Tatoo + if(this->GetRace() == 2) // Barbarian w/Tatoo iFace = zone->random.Int(0, 79); - } - else { + else iFace = zone->random.Int(0, 7); - } int iHair = 0; int iBeard = 0; @@ -779,40 +711,32 @@ void Bot::GenerateAppearance() { iHair = zone->random.Int(0, 8); iBeard = zone->random.Int(0, 11); iBeardColor = zone->random.Int(0, 3); - } - else if(this->GetGender()) { + } else if(this->GetGender()) { iHair = zone->random.Int(0, 2); if(this->GetRace() == 8) { // Dwarven Females can have a beard - if(zone->random.Int(1, 100) < 50) { + if(zone->random.Int(1, 100) < 50) iFace += 10; - } } - } - else { + } else { iHair = zone->random.Int(0, 3); iBeard = zone->random.Int(0, 5); iBeardColor = zone->random.Int(0, 19); } int iHairColor = 0; - if(this->GetRace() == 522) { + if(this->GetRace() == 522) iHairColor = zone->random.Int(0, 3); - } - else { + else iHairColor = zone->random.Int(0, 19); - } uint8 iEyeColor1 = (uint8)zone->random.Int(0, 9); uint8 iEyeColor2 = 0; - if(this->GetRace() == 522) { + if(this->GetRace() == 522) iEyeColor1 = iEyeColor2 = (uint8)zone->random.Int(0, 11); - } - else if(zone->random.Int(1, 100) > 96) { + else if(zone->random.Int(1, 100) > 96) iEyeColor2 = zone->random.Int(0, 9); - } - else { + else iEyeColor2 = iEyeColor1; - } int iHeritage = 0; int iTattoo = 0; @@ -822,7 +746,6 @@ void Bot::GenerateAppearance() { iTattoo = zone->random.Int(0, 7); iDetails = zone->random.Int(0, 7); } - this->luclinface = iFace; this->hairstyle = iHair; this->beard = iBeard; @@ -833,77 +756,71 @@ void Bot::GenerateAppearance() { this->drakkin_heritage = iHeritage; this->drakkin_tattoo = iTattoo; this->drakkin_details = iDetails; - } -int32 Bot::acmod() -{ +int32 Bot::acmod() { int agility = GetAGI(); int level = GetLevel(); if(agility < 1 || level < 1) return 0; - if(agility <= 74) - { + if(agility <= 74) { if(agility == 1) return -24; - else if(agility <=3) + else if(agility <= 3) return -23; else if(agility == 4) return -22; - else if(agility <=6) + else if(agility <= 6) return -21; - else if(agility <=8) + else if(agility <= 8) return -20; else if(agility == 9) return -19; - else if(agility <=11) + else if(agility <= 11) return -18; else if(agility == 12) return -17; - else if(agility <=14) + else if(agility <= 14) return -16; - else if(agility <=16) + else if(agility <= 16) return -15; else if(agility == 17) return -14; - else if(agility <=19) + else if(agility <= 19) return -13; else if(agility == 20) return -12; - else if(agility <=22) + else if(agility <= 22) return -11; - else if(agility <=24) + else if(agility <= 24) return -10; else if(agility == 25) return -9; - else if(agility <=27) + else if(agility <= 27) return -8; else if(agility == 28) return -7; - else if(agility <=30) + else if(agility <= 30) return -6; - else if(agility <=32) + else if(agility <= 32) return -5; else if(agility == 33) return -4; - else if(agility <=35) + else if(agility <= 35) return -3; else if(agility == 36) return -2; - else if(agility <=38) + else if(agility <= 38) return -1; - else if(agility <=65) + else if(agility <= 65) return 0; - else if(agility <=70) + else if(agility <= 70) return 1; - else if(agility <=74) + else if(agility <= 74) return 5; - } - else if(agility <= 137) - { - if(agility == 75) - { + } else if(agility <= 137) { + if(agility == 75) { if(level <= 6) return 9; else if(level <= 19) @@ -912,9 +829,7 @@ int32 Bot::acmod() return 33; else return 39; - } - else if(agility >= 76 && agility <= 79) - { + } else if(agility >= 76 && agility <= 79) { if(level <= 6) return 10; else if(level <= 19) @@ -923,9 +838,7 @@ int32 Bot::acmod() return 33; else return 40; - } - else if(agility == 80) - { + } else if(agility == 80) { if(level <= 6) return 11; else if(level <= 19) @@ -934,9 +847,7 @@ int32 Bot::acmod() return 34; else return 41; - } - else if(agility >= 81 && agility <= 85) - { + } else if(agility >= 81 && agility <= 85) { if(level <= 6) return 12; else if(level <= 19) @@ -945,9 +856,7 @@ int32 Bot::acmod() return 35; else return 42; - } - else if(agility >= 86 && agility <= 90) - { + } else if(agility >= 86 && agility <= 90) { if(level <= 6) return 12; else if(level <= 19) @@ -956,9 +865,7 @@ int32 Bot::acmod() return 36; else return 42; - } - else if(agility >= 91 && agility <= 95) - { + } else if(agility >= 91 && agility <= 95) { if(level <= 6) return 13; else if(level <= 19) @@ -967,8 +874,7 @@ int32 Bot::acmod() return 36; else return 43; - } - else if(agility >= 96 && agility <= 99){ + } else if(agility >= 96 && agility <= 99) { if(level <= 6) return 14; else if(level <= 19) @@ -977,9 +883,7 @@ int32 Bot::acmod() return 37; else return 44; - } - else if(agility == 100 && level >= 7) - { + } else if(agility == 100 && level >= 7) { if(level <= 19) return 28; else if (level <= 39) @@ -988,39 +892,30 @@ int32 Bot::acmod() return 45; } else if(level <= 6) - { return 15; - } //level is >6 - else if(agility >= 101 && agility <= 105) - { + else if(agility >= 101 && agility <= 105) { if(level <= 19) return 29; else if(level <= 39) return 39;// not verified else return 45; - } - else if(agility >= 106 && agility <= 110) - { + } else if(agility >= 106 && agility <= 110) { if(level <= 19) return 29; else if(level <= 39) return 39;// not verified else return 46; - } - else if(agility >= 111 && agility <= 115) - { + } else if(agility >= 111 && agility <= 115) { if(level <= 19) return 30; else if(level <= 39) return 40;// not verified else return 47; - } - else if(agility >= 116 && agility <= 119) - { + } else if(agility >= 116 && agility <= 119) { if(level <= 19) return 31; else if(level <= 39) @@ -1029,307 +924,269 @@ int32 Bot::acmod() return 47; } else if(level <= 19) - { return 32; - } //level is > 19 - else if(agility == 120) - { + else if(agility == 120) { if(level <= 39) return 42; else return 48; - } - else if(agility <= 125) - { + } else if(agility <= 125) { if(level <= 39) return 42; else return 49; - } - else if(agility <= 135) - { + } else if(agility <= 135) { if(level <= 39) return 42; else return 50; - } - else { + } else { if(level <= 39) return 42; else return 51; } - } - else if(agility <= 300) - { + } else if(agility <= 300) { if(level <= 6) { if(agility <= 139) - return(21); + return 21; else if(agility == 140) - return(22); + return 22; else if(agility <= 145) - return(23); + return 23; else if(agility <= 150) - return(23); + return 23; else if(agility <= 155) - return(24); + return 24; else if(agility <= 159) - return(25); + return 25; else if(agility == 160) - return(26); + return 26; else if(agility <= 165) - return(26); + return 26; else if(agility <= 170) - return(27); + return 27; else if(agility <= 175) - return(28); + return 28; else if(agility <= 179) - return(28); + return 28; else if(agility == 180) - return(29); + return 29; else if(agility <= 185) - return(30); + return 30; else if(agility <= 190) - return(31); + return 31; else if(agility <= 195) - return(31); + return 31; else if(agility <= 199) - return(32); + return 32; else if(agility <= 219) - return(33); + return 33; else if(agility <= 239) - return(34); + return 34; else - return(35); - } - else if(level <= 19) - { + return 35; + } else if(level <= 19) { if(agility <= 139) - return(34); + return 34; else if(agility == 140) - return(35); + return 35; else if(agility <= 145) - return(36); + return 36; else if(agility <= 150) - return(37); + return 37; else if(agility <= 155) - return(37); + return 37; else if(agility <= 159) - return(38); + return 38; else if(agility == 160) - return(39); + return 39; else if(agility <= 165) - return(40); + return 40; else if(agility <= 170) - return(40); + return 40; else if(agility <= 175) - return(41); + return 41; else if(agility <= 179) - return(42); + return 42; else if(agility == 180) - return(43); + return 43; else if(agility <= 185) - return(43); + return 43; else if(agility <= 190) - return(44); + return 44; else if(agility <= 195) - return(45); + return 45; else if(agility <= 199) - return(45); + return 45; else if(agility <= 219) - return(46); + return 46; else if(agility <= 239) - return(47); + return 47; else - return(48); - } - else if(level <= 39) - { + return 48; + } else if(level <= 39) { if(agility <= 139) - return(44); + return 44; else if(agility == 140) - return(45); + return 45; else if(agility <= 145) - return(46); + return 46; else if(agility <= 150) - return(47); + return 47; else if(agility <= 155) - return(47); + return 47; else if(agility <= 159) - return(48); + return 48; else if(agility == 160) - return(49); + return 49; else if(agility <= 165) - return(50); + return 50; else if(agility <= 170) - return(50); + return 50; else if(agility <= 175) - return(51); + return 51; else if(agility <= 179) - return(52); + return 52; else if(agility == 180) - return(53); + return 53; else if(agility <= 185) - return(53); + return 53; else if(agility <= 190) - return(54); + return 54; else if(agility <= 195) - return(55); + return 55; else if(agility <= 199) - return(55); + return 55; else if(agility <= 219) - return(56); + return 56; else if(agility <= 239) - return(57); + return 57; else - return(58); - } - else - { //lvl >= 40 + return 58; + } else { //lvl >= 40 if(agility <= 139) - return(51); + return 51; else if(agility == 140) - return(52); + return 52; else if(agility <= 145) - return(53); + return 53; else if(agility <= 150) - return(53); + return 53; else if(agility <= 155) - return(54); + return 54; else if(agility <= 159) - return(55); + return 55; else if(agility == 160) - return(56); + return 56; else if(agility <= 165) - return(56); + return 56; else if(agility <= 170) - return(57); + return 57; else if(agility <= 175) - return(58); + return 58; else if(agility <= 179) - return(58); + return 58; else if(agility == 180) - return(59); + return 59; else if(agility <= 185) - return(60); + return 60; else if(agility <= 190) - return(61); + return 61; else if(agility <= 195) - return(61); + return 61; else if(agility <= 199) - return(62); + return 62; else if(agility <= 219) - return(63); + return 63; else if(agility <= 239) - return(64); + return 64; else - return(65); + return 65; } } else - { - //seems about 21 agil per extra AC pt over 300... - return (65 + ((agility-300) / 21)); - } + return (65 + ((agility - 300) / 21)); #if EQDEBUG >= 11 Log.Out(Logs::General, Logs::Error, "Error in Bot::acmod(): Agility: %i, Level: %i",agility,level); #endif return 0; } -void Bot::GenerateArmorClass() -{ +void Bot::GenerateArmorClass() { /// new formula int avoidance = 0; - avoidance = (acmod() + ((GetSkill(SkillDefense)*16)/9)); + avoidance = (acmod() + ((GetSkill(SkillDefense) * 16) / 9)); if(avoidance < 0) avoidance = 0; int mitigation = 0; - if(GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == NECROMANCER || GetClass() == ENCHANTER) - { - mitigation = GetSkill(SkillDefense)/4 + (itembonuses.AC+1); + if(GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == NECROMANCER || GetClass() == ENCHANTER) { + mitigation = (GetSkill(SkillDefense) / 4 + (itembonuses.AC + 1)); mitigation -= 4; - } - else - { - mitigation = GetSkill(SkillDefense)/3 + ((itembonuses.AC*4)/3); + } else { + mitigation = (GetSkill(SkillDefense) / 3 + ((itembonuses.AC * 4) / 3)); if(GetClass() == MONK) - mitigation += GetLevel() * 13/10; //the 13/10 might be wrong, but it is close... + mitigation += (GetLevel() * 13 / 10); //the 13/10 might be wrong, but it is close... } int displayed = 0; - displayed += ((avoidance+mitigation)*1000)/847; //natural AC + displayed += (((avoidance + mitigation) * 1000) / 847); //natural AC //Iksar AC, untested - if(GetRace() == IKSAR) - { + if(GetRace() == IKSAR) { displayed += 12; int iksarlevel = GetLevel(); iksarlevel -= 10; if(iksarlevel > 25) iksarlevel = 25; + if(iksarlevel > 0) - displayed += iksarlevel * 12 / 10; + displayed += (iksarlevel * 12 / 10); } //spell AC bonuses are added directly to natural total displayed += spellbonuses.AC; - this->AC = displayed; } -uint16 Bot::GetPrimarySkillValue() -{ +uint16 Bot::GetPrimarySkillValue() { SkillUseTypes skill = HIGHEST_SKILL; //because nullptr == 0, which is 1H Slashing, & we want it to return 0 from GetSkill bool equiped = m_inv.GetItem(MainPrimary); - if(!equiped) - { skill = SkillHandtoHand; - } - else - { + else { uint8 type = m_inv.GetItem(MainPrimary)->GetItem()->ItemType; //is this the best way to do this? - switch(type) - { - case ItemType1HSlash: // 1H Slashing - { + switch(type) { + case ItemType1HSlash: { skill = Skill1HSlashing; break; } - case ItemType2HSlash: // 2H Slashing - { + case ItemType2HSlash: { skill = Skill2HSlashing; break; } - case ItemType1HPiercing: // Piercing - { + case ItemType1HPiercing: { skill = Skill1HPiercing; break; } - case ItemType1HBlunt: // 1H Blunt - { + case ItemType1HBlunt: { skill = Skill1HBlunt; break; } - case ItemType2HBlunt: // 2H Blunt - { + case ItemType2HBlunt: { skill = Skill2HBlunt; break; } - case ItemType2HPiercing: // 2H Piercing - { + case ItemType2HPiercing: { skill = Skill1HPiercing; // change to Skill2HPiercing once activated break; } - case ItemTypeMartial: // Hand to Hand - { + case ItemTypeMartial: { skill = SkillHandtoHand; break; } - default: // All other types default to Hand to Hand - { + default: { skill = SkillHandtoHand; break; } @@ -1343,15 +1200,12 @@ uint16 Bot::MaxSkill(SkillUseTypes skillid, uint16 class_, uint16 level) const { return(database.GetSkillCap(class_, skillid, level)); } -uint32 Bot::GetTotalATK() -{ +uint32 Bot::GetTotalATK() { uint32 AttackRating = 0; uint32 WornCap = itembonuses.ATK; - if(IsBot()) { AttackRating = ((WornCap * 1.342) + (GetSkill(SkillOffense) * 1.345) + ((GetSTR() - 66) * 0.9) + (GetPrimarySkillValue() * 2.69)); AttackRating += aabonuses.ATK + GroupLeadershipAAOffenseEnhancement(); - if (AttackRating < 10) AttackRating = 10; } @@ -1359,34 +1213,27 @@ uint32 Bot::GetTotalATK() AttackRating = GetATK(); AttackRating += spellbonuses.ATK; - return AttackRating; } -uint32 Bot::GetATKRating() -{ +uint32 Bot::GetATKRating() { uint32 AttackRating = 0; if(IsBot()) { AttackRating = (GetSkill(SkillOffense) * 1.345) + ((GetSTR() - 66) * 0.9) + (GetPrimarySkillValue() * 2.69); - if (AttackRating < 10) AttackRating = 10; } return AttackRating; } -int32 Bot::GenerateBaseHitPoints() -{ +int32 Bot::GenerateBaseHitPoints() { // Calc Base Hit Points int new_base_hp = 0; uint32 lm = GetClassLevelFactor(); int32 Post255; int32 NormalSTA = GetSTA(); - - if(GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= ClientVersion::SoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) - { + if(GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= ClientVersion::SoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) { float SoDPost255; - if(((NormalSTA - 255) / 2) > 0) SoDPost255 = ((NormalSTA - 255) / 2); else @@ -1395,33 +1242,20 @@ int32 Bot::GenerateBaseHitPoints() int hp_factor = GetClassHPFactor(); if(level < 41) - { new_base_hp = (5 + (GetLevel() * hp_factor / 12) + ((NormalSTA - SoDPost255) * GetLevel() * hp_factor / 3600)); - } else if(level < 81) - { - new_base_hp = (5 + (40 * hp_factor / 12) + ((GetLevel() - 40) * hp_factor / 6) + - ((NormalSTA - SoDPost255) * hp_factor / 90) + - ((NormalSTA - SoDPost255) * (GetLevel() - 40) * hp_factor / 1800)); - } + new_base_hp = (5 + (40 * hp_factor / 12) + ((GetLevel() - 40) * hp_factor / 6) + ((NormalSTA - SoDPost255) * hp_factor / 90) + ((NormalSTA - SoDPost255) * (GetLevel() - 40) * hp_factor / 1800)); else - { - new_base_hp = (5 + (80 * hp_factor / 8) + ((GetLevel() - 80) * hp_factor / 10) + - ((NormalSTA - SoDPost255) * hp_factor / 90) + - ((NormalSTA - SoDPost255) * hp_factor / 45)); - } - } - else - { - if((NormalSTA-255)/2 > 0) - Post255 = (NormalSTA-255)/2; + new_base_hp = (5 + (80 * hp_factor / 8) + ((GetLevel() - 80) * hp_factor / 10) + ((NormalSTA - SoDPost255) * hp_factor / 90) + ((NormalSTA - SoDPost255) * hp_factor / 45)); + } else { + if(((NormalSTA - 255) / 2) > 0) + Post255 = ((NormalSTA - 255) / 2); else Post255 = 0; - new_base_hp = (5)+(GetLevel()*lm/10) + (((NormalSTA-Post255)*GetLevel()*lm/3000)) + ((Post255*1)*lm/6000); + new_base_hp = (5) + (GetLevel() * lm / 10) + (((NormalSTA - Post255) * GetLevel() * lm / 3000)) + ((Post255 * 1) * lm / 6000); } this->base_hp = new_base_hp; - return new_base_hp; } @@ -1429,12 +1263,9 @@ void Bot::GenerateAABonuses(StatBonuses* newbon) { // General AA bonus uint8 botClass = GetClass(); uint8 botLevel = GetLevel(); - memset(newbon, 0, sizeof(StatBonuses)); //start fresh - if(botLevel >= 51) { //level 51 = 1 AA level - int i; int totalAAs = database.CountAAs(); uint32 slots = 0; @@ -1449,7 +1280,7 @@ void Bot::GenerateAABonuses(StatBonuses* newbon) { //slots = database.GetTotalAALevels(aa_AA); //find out how many effects from aa_effects table slots = zone->GetTotalAALevels(aa_AA); //find out how many effects from aa_effects, which is loaded into memory if (slots > 0) //and does it have any effects? may be able to put this above, not sure if it runs on each iteration - ApplyAABonuses(aa_AA + aa_value -1, slots, newbon); //add the bonuses + ApplyAABonuses((aa_AA + aa_value - 1), slots, newbon); //add the bonuses } } } @@ -1461,25 +1292,21 @@ void Bot::LoadAAs() { botAAs.clear(); //start fresh std::string query; - if(GetClass() == BERSERKER) query = StringFormat("SELECT skill_id FROM altadv_vars WHERE berserker = 1 AND class_type > 1 AND class_type <= %i AND aa_expansion <= %i ORDER BY skill_id;", GetLevel(), maxAAExpansion); else query = StringFormat("SELECT skill_id FROM altadv_vars WHERE ((classes & ( 1 << %i )) >> %i) = 1 AND class_type > 1 AND class_type <= %i AND aa_expansion <= %i ORDER BY skill_id;", GetClass(), GetClass(), GetLevel(), maxAAExpansion); auto results = database.QueryDatabase(query); - if(!results.Success()) { Log.Out(Logs::General, Logs::Error, "Error in Bot::LoadAAs()"); return; } int totalAAs = database.CountAAs(); - for (auto row = results.begin(); row != results.end(); ++row) { uint32 skill_id = 0; skill_id = atoi(row[0]); - if(skill_id <= 0 || skill_id >= totalAAs) continue; @@ -1490,11 +1317,10 @@ void Bot::LoadAAs() { for(int i=0; imax_level; i++) { //Get AA info & add to list - uint32 aaid = sendAA->id + i; + uint32 aaid = (sendAA->id + i); uint8 total_levels = 0; uint8 req_level; std::map::iterator RequiredLevel = AARequiredLevelAndCost.find(aaid); - //Get level required for AA if(RequiredLevel != AARequiredLevelAndCost.end()) req_level = RequiredLevel->second.Level; @@ -1506,43 +1332,36 @@ void Bot::LoadAAs() { //Bot is high enough level for AA std::map::iterator foundAA = botAAs.find(aaid); - // AA is already in list if(foundAA != botAAs.end()) continue; if(sendAA->id == aaid) { BotAA newAA; - newAA.total_levels = 0; newAA.aa_id = aaid; newAA.req_level = req_level; newAA.total_levels += 1; - botAAs[aaid] = newAA; //add to list } else //update master AA record with number of levels a bot has in AA, based on level. - botAAs[sendAA->id].total_levels+=1; + botAAs[sendAA->id].total_levels += 1; } } } uint32 Bot::GetAA(uint32 aa_id) { - std::map::const_iterator find_iter = botAAs.find(aa_id); int aaLevel = 0; - - if(find_iter != botAAs.end()) { + if(find_iter != botAAs.end()) aaLevel = find_iter->second.total_levels; - } return aaLevel; } //current with Client::ApplyAABonuses 9/26/12 -void Bot::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) -{ +void Bot::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) { if(slots == 0) //sanity check. why bother if no slots to fill? return; @@ -1551,38 +1370,27 @@ void Bot::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) int32 base1 = 0; int32 base2 = 0; //only really used for SE_RaiseStatCap & SE_ReduceSkillTimer in aa_effects table uint32 slot = 0; - std::map >::const_iterator find_iter = aa_effects.find(aaid); if(find_iter == aa_effects.end()) - { return; - } for (std::map::const_iterator iter = aa_effects[aaid].begin(); iter != aa_effects[aaid].end(); ++iter) { effect = iter->second.skill_id; base1 = iter->second.base1; base2 = iter->second.base2; slot = iter->second.slot; - //we default to 0 (SE_CurrentHP) for the effect, so if there aren't any base1/2 values, we'll just skip it - if (effect == 0 && base1 == 0 && base2 == 0) - continue; - - //IsBlankSpellEffect() - if (effect == SE_Blank || (effect == SE_CHA && base1 == 0) || effect == SE_StackingCommand_Block || effect == SE_StackingCommand_Overwrite) + if ((effect == 0 && base1 == 0 && base2 == 0) || effect == SE_Blank || (effect == SE_CHA && base1 == 0) || effect == SE_StackingCommand_Block || effect == SE_StackingCommand_Overwrite) continue; Log.Out(Logs::Detail, Logs::AA, "Applying Effect %d from AA %u in slot %d (base1: %d, base2: %d) on %s", effect, aaid, slot, base1, base2, this->GetCleanName()); - - uint8 focus = IsFocusEffect(0, 0, true,effect); - if (focus) - { + uint8 focus = IsFocusEffect(0, 0, true, effect); + if (focus) { newbon->FocusEffects[focus] = effect; continue; } - switch (effect) - { + switch (effect) { //Note: AA effects that use accuracy are skill limited, while spell effect is not. case SE_Accuracy: if ((base2 == -1) && (newbon->Accuracy[HIGHEST_SKILL+1] < base1)) @@ -1597,9 +1405,7 @@ void Bot::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) newbon->EnduranceRegen += base1; break; case SE_MovementSpeed: - newbon->movementspeed += base1; //should we let these stack? - /*if (base1 > newbon->movementspeed) //or should we use a total value? - newbon->movementspeed = base1;*/ + newbon->movementspeed += base1; break; case SE_STR: newbon->STR += base1; @@ -1664,8 +1470,7 @@ void Bot::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) case SE_SetBreathLevel: break; case SE_RaiseStatCap: - switch(base2) - { + switch(base2) { //are these #define'd somewhere? case 0: //str newbon->STRCapMod += base1; @@ -1880,216 +1685,169 @@ void Bot::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) case SE_UnfailingDivinity: newbon->UnfailingDivinity += base1; break; - - case SE_ProcOnKillShot: - for(int i = 0; i < MAX_SPELL_TRIGGER*3; i+=3) - { - if(!newbon->SpellOnKill[i] || ((newbon->SpellOnKill[i] == base2) && (newbon->SpellOnKill[i+1] < base1))) - { + case SE_ProcOnKillShot: { + for(int i = 0; i < (MAX_SPELL_TRIGGER * 3); i += 3) { + if(!newbon->SpellOnKill[i] || ((newbon->SpellOnKill[i] == base2) && (newbon->SpellOnKill[i + 1] < base1))) { //base1 = chance, base2 = SpellID to be triggered, base3 = min npc level newbon->SpellOnKill[i] = base2; - newbon->SpellOnKill[i+1] = base1; + newbon->SpellOnKill[i + 1] = base1; if (GetLevel() > 15) - newbon->SpellOnKill[i+2] = GetLevel() - 15; //AA specifiy "non-trivial" + newbon->SpellOnKill[i + 2] = (GetLevel() - 15); //AA specifiy "non-trivial" else - newbon->SpellOnKill[i+2] = 0; + newbon->SpellOnKill[i + 2] = 0; break; } } - break; - - case SE_SpellOnDeath: - for(int i = 0; i < MAX_SPELL_TRIGGER*2; i+=2) - { - if(!newbon->SpellOnDeath[i]) - { + break; + } + case SE_SpellOnDeath: { + for(int i = 0; i < (MAX_SPELL_TRIGGER * 2); i += 2) { + if(!newbon->SpellOnDeath[i]) { // base1 = SpellID to be triggered, base2 = chance to fire newbon->SpellOnDeath[i] = base1; - newbon->SpellOnDeath[i+1] = base2; + newbon->SpellOnDeath[i + 1] = base2; break; } } - break; - - case SE_TriggerOnCast: - - for(int i = 0; i < MAX_SPELL_TRIGGER; i++) - { + break; + } + case SE_TriggerOnCast: { + for(int i = 0; i < MAX_SPELL_TRIGGER; i++) { if (newbon->SpellTriggers[i] == aaid) break; - if(!newbon->SpellTriggers[i]) - { + if(!newbon->SpellTriggers[i]) { //Save the 'aaid' of each triggerable effect to an array newbon->SpellTriggers[i] = aaid; break; } } - break; - - case SE_CriticalHitChance: - { + break; + } + case SE_CriticalHitChance: { if(base2 == -1) newbon->CriticalHitChance[HIGHEST_SKILL+1] += base1; else newbon->CriticalHitChance[base2] += base1; + break; } - break; - - case SE_CriticalDamageMob: - { + case SE_CriticalDamageMob: { // base1 = effect value, base2 = skill restrictions(-1 for all) if(base2 == -1) - newbon->CritDmgMob[HIGHEST_SKILL+1] += base1; + newbon->CritDmgMob[HIGHEST_SKILL + 1] += base1; else newbon->CritDmgMob[base2] += base1; break; } - - case SE_CriticalSpellChance: - { + case SE_CriticalSpellChance: { newbon->CriticalSpellChance += base1; - if (base2 > newbon->SpellCritDmgIncrease) newbon->SpellCritDmgIncrease = base2; break; } - - case SE_ResistFearChance: - { + case SE_ResistFearChance: { if(base1 == 100) // If we reach 100% in a single spell/item then we should be immune to negative fear resist effects until our immunity is over newbon->Fearless = true; newbon->ResistFearChance += base1; // these should stack break; } - - case SE_SkillDamageAmount: - { + case SE_SkillDamageAmount: { if(base2 == -1) - newbon->SkillDamageAmount[HIGHEST_SKILL+1] += base1; + newbon->SkillDamageAmount[HIGHEST_SKILL + 1] += base1; else newbon->SkillDamageAmount[base2] += base1; break; } - - case SE_SpecialAttackKBProc: - { + case SE_SpecialAttackKBProc: { //You can only have one of these per client. [AA Dragon Punch] newbon->SpecialAttackKBProc[0] = base1; //Chance base 100 = 25% proc rate newbon->SpecialAttackKBProc[1] = base2; //Skill to KB Proc Off break; } - - case SE_DamageModifier: - { + case SE_DamageModifier: { if(base2 == -1) - newbon->DamageModifier[HIGHEST_SKILL+1] += base1; + newbon->DamageModifier[HIGHEST_SKILL + 1] += base1; else newbon->DamageModifier[base2] += base1; break; } - - case SE_SlayUndead: - { + case SE_SlayUndead: { if(newbon->SlayUndead[1] < base1) newbon->SlayUndead[0] = base1; // Rate newbon->SlayUndead[1] = base2; // Damage Modifier break; } - - case SE_GiveDoubleRiposte: - { + case SE_GiveDoubleRiposte: { //0=Regular Riposte 1=Skill Attack Riposte 2=Skill - if(base2 == 0){ + if(base2 == 0) { if(newbon->GiveDoubleRiposte[0] < base1) newbon->GiveDoubleRiposte[0] = base1; } //Only for special attacks. - else if(base2 > 0 && (newbon->GiveDoubleRiposte[1] < base1)){ + else if(base2 > 0 && (newbon->GiveDoubleRiposte[1] < base1)) { newbon->GiveDoubleRiposte[1] = base1; newbon->GiveDoubleRiposte[2] = base2; } - break; } - //Kayen: Not sure best way to implement this yet. //Physically raises skill cap ie if 55/55 it will raise to 55/60 - case SE_RaiseSkillCap: - { - if(newbon->RaiseSkillCap[0] < base1){ + case SE_RaiseSkillCap: { + if(newbon->RaiseSkillCap[0] < base1) { newbon->RaiseSkillCap[0] = base1; //value newbon->RaiseSkillCap[1] = base2; //skill } break; } - - case SE_MasteryofPast: - { + case SE_MasteryofPast: { if(newbon->MasteryofPast < base1) newbon->MasteryofPast = base1; break; } - case SE_CastingLevel2: - case SE_CastingLevel: - { + case SE_CastingLevel: { newbon->effective_casting_level += base1; break; } - - - case SE_DivineSave: - { - if(newbon->DivineSaveChance[0] < base1) - { + case SE_DivineSave: { + if(newbon->DivineSaveChance[0] < base1) { newbon->DivineSaveChance[0] = base1; newbon->DivineSaveChance[1] = base2; } break; } - - case SE_SpellEffectResistChance: - { - for(int e = 0; e < MAX_RESISTABLE_EFFECTS*2; e+=2) - { - if(!newbon->SEResist[e] || ((newbon->SEResist[e] = base2) && (newbon->SEResist[e+1] < base1)) ){ + case SE_SpellEffectResistChance: { + for(int e = 0; e < (MAX_RESISTABLE_EFFECTS * 2); e += 2) { + if(!newbon->SEResist[e] || ((newbon->SEResist[e] = base2) && (newbon->SEResist[e + 1] < base1)) ){ newbon->SEResist[e] = base2; - newbon->SEResist[e+1] = base1; - break; + newbon->SEResist[e + 1] = base1; + break; } } break; } - - case SE_MitigateDamageShield: - { + case SE_MitigateDamageShield: { if (base1 < 0) - base1 = base1*(-1); + base1 = (base1 * -1); newbon->DSMitigationOffHand += base1; break; } - - case SE_FinishingBlow: - { - + case SE_FinishingBlow: { //base1 = chance, base2 = damage - if (newbon->FinishingBlow[1] < base2){ + if (newbon->FinishingBlow[1] < base2) { newbon->FinishingBlow[0] = base1; newbon->FinishingBlow[1] = base2; } break; } - - case SE_FinishingBlowLvl: - { + case SE_FinishingBlowLvl: { //base1 = level, base2 = ??? (Set to 200 in AA data, possible proc rate mod?) - if (newbon->FinishingBlowLvl[0] < base1){ + if (newbon->FinishingBlowLvl[0] < base1) { newbon->FinishingBlowLvl[0] = base1; newbon->FinishingBlowLvl[1] = base2; } @@ -2321,18 +2079,16 @@ bool Bot::IsValidName() { std::string TempBotName = std::string(this->GetCleanName()); for(int iCounter = 0; iCounter < TempBotName.length(); iCounter++) { - if(isalpha(TempBotName[iCounter]) || TempBotName[iCounter] == '_') { + if(isalpha(TempBotName[iCounter]) || TempBotName[iCounter] == '_') Result = true; - } } return Result; } bool Bot::IsBotNameAvailable(char *botName, std::string* errorMessage) { - if (botName == "" || strlen(botName) > 15 || !database.CheckNameFilter(botName) || !database.CheckUsedName(botName)) { - return false; //Check if Botname is Empty / Check if Botname larger than 15 char / Valid to Player standards / Not used by a player! - } + if (botName == "" || strlen(botName) > 15 || !database.CheckNameFilter(botName) || !database.CheckUsedName(botName)) + return false; std::string query = StringFormat("SELECT id FROM vwBotCharacterMobs WHERE name LIKE '%s'", botName); auto results = database.QueryDatabase(query); @@ -2340,15 +2096,14 @@ bool Bot::IsBotNameAvailable(char *botName, std::string* errorMessage) { *errorMessage = std::string(results.ErrorMessage()); return false; } - if (results.RowCount()) { //Name already in use! + + if (results.RowCount()) return false; - } return true; //We made it with a valid name! } bool Bot::Save() { - if(this->GetBotID() == 0) { // New bot record std::string query = StringFormat("INSERT INTO bots (BotOwnerCharacterID, BotSpellsID, Name, LastName, " @@ -2374,6 +2129,7 @@ bool Bot::Save() { auto botOwner = GetBotOwner(); if (botOwner) botOwner->Message(13, results.ErrorMessage().c_str()); + return false; } @@ -2412,36 +2168,28 @@ bool Bot::Save() { auto botOwner = GetBotOwner(); if (botOwner) botOwner->Message(13, results.ErrorMessage().c_str()); + return false; } - SaveBuffs(); SavePet(); SaveStance(); SaveTimers(); - return true; } // Returns the current total play time for the bot uint32 Bot::GetTotalPlayTime() { uint32 Result = 0; - double TempTotalPlayTime = 0; - time_t currentTime = time(¤tTime); - TempTotalPlayTime = difftime(currentTime, _startTotalPlayTime); - TempTotalPlayTime += _lastTotalPlayTime; - Result = (uint32)TempTotalPlayTime; - return Result; } void Bot::SaveBuffs() { - // Remove any existing buff saves std::string query = StringFormat("DELETE FROM botbuffs WHERE BotId = %u", GetBotID()); auto results = database.QueryDatabase(query); @@ -2458,7 +2206,7 @@ void Bot::SaveBuffs() { "CorruptionCounters, HitCount, MeleeRune, MagicRune, dot_rune, " "caston_x, Persistent, caston_y, caston_z, ExtraDIChance) " "VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %i, %u, " - "%i, %i, %i);", + "%i, %i, %i)", GetBotID(), buffs[buffIndex].spellid, buffs[buffIndex].casterlevel, spells[buffs[buffIndex].spellid].buffdurationformula, buffs[buffIndex].ticsremaining, @@ -2473,24 +2221,16 @@ void Bot::SaveBuffs() { auto results = database.QueryDatabase(query); if(!results.Success()) return; - } - } void Bot::LoadBuffs() { - - std::string query = StringFormat("SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, " - "PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, " - "HitCount, MeleeRune, MagicRune, dot_rune, caston_x, Persistent, " - "caston_y, caston_z, ExtraDIChance FROM botbuffs WHERE BotId = %u", - GetBotID()); + std::string query = StringFormat("SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, dot_rune, caston_x, Persistent, caston_y, caston_z, ExtraDIChance FROM botbuffs WHERE BotId = %u", GetBotID()); auto results = database.QueryDatabase(query); if(!results.Success()) return; int buffCount = 0; - for (auto row = results.begin(); row != results.end(); ++row) { if(buffCount == BUFF_COUNT) break; @@ -2498,7 +2238,6 @@ void Bot::LoadBuffs() { buffs[buffCount].spellid = atoi(row[0]); buffs[buffCount].casterlevel = atoi(row[1]); buffs[buffCount].ticsremaining = atoi(row[3]); - if(CalculatePoisonCounters(buffs[buffCount].spellid) > 0) buffs[buffCount].counters = atoi(row[4]); else if(CalculateDiseaseCounters(buffs[buffCount].spellid) > 0) @@ -2514,49 +2253,35 @@ void Bot::LoadBuffs() { buffs[buffCount].dot_rune = atoi(row[11]); buffs[buffCount].caston_x = atoi(row[12]); buffs[buffCount].casterid = 0; - buffs[buffCount].persistant_buff = atoi(row[13])? true: false; - buffs[buffCount].caston_y = atoi(row[14]); buffs[buffCount].caston_z = atoi(row[15]); buffs[buffCount].ExtraDIChance = atoi(row[16]); - buffCount++; } - query = StringFormat("DELETE FROM botbuffs WHERE BotId = %u", GetBotID()); results = database.QueryDatabase(query); - } uint32 Bot::GetPetSaveId() { - - std::string query = StringFormat("SELECT BotPetsId FROM botpets WHERE BotId = %u;", GetBotID()); + std::string query = StringFormat("SELECT BotPetsId FROM botpets WHERE BotId = %u", GetBotID()); auto results = database.QueryDatabase(query); - if(!results.Success()) + if(!results.Success() || results.RowCount() == 0) return 0; - if (results.RowCount() == 0) - return 0; - auto row = results.begin(); - return atoi(row[0]); } void Bot::LoadPet() { uint32 PetSaveId = GetPetSaveId(); - if(PetSaveId > 0 && !GetPet() && PetSaveId <= SPDAT_RECORDS) { std::string petName; uint32 petMana = 0; uint32 petHitPoints = 0; uint32 botPetId = 0; - LoadPetStats(&petName, &petMana, &petHitPoints, &botPetId, PetSaveId); - MakePet(botPetId, spells[botPetId].teleport_zone, petName.c_str()); - if(GetPet() && GetPet()->IsNPC()) { NPC *pet = GetPet()->CastToNPC(); SpellBuff_Struct petBuffs[BUFF_COUNT]; @@ -2571,7 +2296,6 @@ void Bot::LoadPet() { pet->SetHP(petHitPoints); pet->SetMana(petMana); } - DeletePetStats(PetSaveId); } } @@ -2580,18 +2304,12 @@ void Bot::LoadPetStats(std::string* petName, uint32* petMana, uint32* petHitPoin if(botPetSaveId == 0) return; - std::string query = StringFormat("SELECT PetId, Name, Mana, HitPoints " - "FROM botpets WHERE BotPetsId = %u;", - botPetSaveId); + std::string query = StringFormat("SELECT PetId, Name, Mana, HitPoints FROM botpets WHERE BotPetsId = %u", botPetSaveId); auto results = database.QueryDatabase(query); - if(!results.Success()) - return; - - if (results.RowCount() == 0) + if(!results.Success() || results.RowCount() == 0) return; auto row = results.begin(); - *botPetId = atoi(row[0]); *petName = std::string(row[1]); *petMana = atoi(row[2]); @@ -2602,15 +2320,12 @@ void Bot::LoadPetBuffs(SpellBuff_Struct* petBuffs, uint32 botPetSaveId) { if(!petBuffs || botPetSaveId == 0) return; - std::string query = StringFormat("SELECT SpellId, CasterLevel, Duration " - "FROM botpetbuffs WHERE BotPetsId = %u;", botPetSaveId); + std::string query = StringFormat("SELECT SpellId, CasterLevel, Duration FROM botpetbuffs WHERE BotPetsId = %u;", botPetSaveId); auto results = database.QueryDatabase(query); if(!results.Success()) return; - int buffIndex = 0; - for (auto row = results.begin();row != results.end(); ++row) { if(buffIndex == BUFF_COUNT) break; @@ -2618,39 +2333,31 @@ void Bot::LoadPetBuffs(SpellBuff_Struct* petBuffs, uint32 botPetSaveId) { petBuffs[buffIndex].spellid = atoi(row[0]); petBuffs[buffIndex].level = atoi(row[1]); petBuffs[buffIndex].duration = atoi(row[2]); - buffIndex++; } - query = StringFormat("DELETE FROM botpetbuffs WHERE BotPetsId = %u;", botPetSaveId); results = database.QueryDatabase(query); - } void Bot::LoadPetItems(uint32* petItems, uint32 botPetSaveId) { if(!petItems || botPetSaveId == 0) return; - std::string query = StringFormat("SELECT ItemId FROM botpetinventory " - "WHERE BotPetsId = %u;", botPetSaveId); + std::string query = StringFormat("SELECT ItemId FROM botpetinventory WHERE BotPetsId = %u;", botPetSaveId); auto results = database.QueryDatabase(query); if(!results.Success()) return; int itemIndex = 0; - for(auto row = results.begin(); row != results.end(); ++row) { if(itemIndex == EmuConstants::EQUIPMENT_SIZE) break; petItems[itemIndex] = atoi(row[0]); - itemIndex++; } - - query = StringFormat("DELETE FROM botpetinventory WHERE BotPetsId = %u;", botPetSaveId); + query = StringFormat("DELETE FROM botpetinventory WHERE BotPetsId = %u", botPetSaveId); results = database.QueryDatabase(query); - } void Bot::SavePet() { @@ -2662,40 +2369,28 @@ void Bot::SavePet() { char* tempPetName = new char[64]; SpellBuff_Struct petBuffs[BUFF_COUNT]; uint32 petItems[EmuConstants::EQUIPMENT_SIZE]; - pet->GetPetState(petBuffs, petItems, tempPetName); - uint32 existingBotPetSaveId = GetPetSaveId(); - if(existingBotPetSaveId > 0) { // Remove any existing pet buffs DeletePetBuffs(existingBotPetSaveId); - // Remove any existing pet items DeletePetItems(existingBotPetSaveId); } - // Save pet stats and get a new bot pet save id uint32 botPetSaveId = SavePetStats(std::string(tempPetName), petMana, petHitPoints, botPetId); - // Save pet buffs SavePetBuffs(petBuffs, botPetSaveId); - // Save pet items SavePetItems(petItems, botPetSaveId); - if(tempPetName) safe_delete_array(tempPetName); } } uint32 Bot::SavePetStats(std::string petName, uint32 petMana, uint32 petHitPoints, uint32 botPetId) { - - std::string query = StringFormat("REPLACE INTO botpets SET PetId = %u, BotId = %u, Name = '%s', " - "Mana = %u, HitPoints = %u;", botPetId, GetBotID(), petName.c_str(), - petMana, petHitPoints); + std::string query = StringFormat("REPLACE INTO botpets SET PetId = %u, BotId = %u, Name = '%s', Mana = %u, HitPoints = %u", botPetId, GetBotID(), petName.c_str(), petMana, petHitPoints); auto results = database.QueryDatabase(query); - return 0; } @@ -2704,21 +2399,14 @@ void Bot::SavePetBuffs(SpellBuff_Struct* petBuffs, uint32 botPetSaveId) { return; int buffIndex = 0; - while(buffIndex < BUFF_COUNT) { if(petBuffs[buffIndex].spellid > 0 && petBuffs[buffIndex].spellid != SPELL_UNKNOWN) { - std::string query = StringFormat("INSERT INTO botpetbuffs " - "(BotPetsId, SpellId, CasterLevel, Duration) " - "VALUES(%u, %u, %u, %u);", - botPetSaveId, petBuffs[buffIndex].spellid, - petBuffs[buffIndex].level, petBuffs[buffIndex].duration); + std::string query = StringFormat("INSERT INTO botpetbuffs (BotPetsId, SpellId, CasterLevel, Duration) VALUES(%u, %u, %u, %u)", botPetSaveId, petBuffs[buffIndex].spellid, petBuffs[buffIndex].level, petBuffs[buffIndex].duration); auto results = database.QueryDatabase(query); if(!results.Success()) break; - } - buffIndex++; } @@ -2732,55 +2420,46 @@ void Bot::SavePetItems(uint32* petItems, uint32 botPetSaveId) { if(petItems[itemIndex] == 0) continue; - std::string query = StringFormat("INSERT INTO botpetinventory " - "(BotPetsId, ItemId) VALUES(%u, %u);", - botPetSaveId, petItems[itemIndex]); + std::string query = StringFormat("INSERT INTO botpetinventory (BotPetsId, ItemId) VALUES(%u, %u)", botPetSaveId, petItems[itemIndex]); auto results = database.QueryDatabase(query); if(!results.Success()) break; } - } void Bot::DeletePetBuffs(uint32 botPetSaveId) { if(botPetSaveId == 0) return; - std::string query = StringFormat("DELETE FROM botpetbuffs WHERE BotPetsId = %u;", botPetSaveId); + std::string query = StringFormat("DELETE FROM botpetbuffs WHERE BotPetsId = %u", botPetSaveId); auto results = database.QueryDatabase(query); - } void Bot::DeletePetItems(uint32 botPetSaveId) { if(botPetSaveId == 0) return; - std::string query = StringFormat("DELETE FROM botpetinventory WHERE BotPetsId = %u;", botPetSaveId); + std::string query = StringFormat("DELETE FROM botpetinventory WHERE BotPetsId = %u", botPetSaveId); auto results = database.QueryDatabase(query); - } void Bot::DeletePetStats(uint32 botPetSaveId) { if(botPetSaveId == 0) return; - std::string query = StringFormat("DELETE from botpets where BotPetsId = %u;", botPetSaveId); + std::string query = StringFormat("DELETE from botpets where BotPetsId = %u", botPetSaveId); auto results = database.QueryDatabase(query); - } void Bot::LoadStance() { - - std::string query = StringFormat("SELECT StanceID FROM botstances WHERE BotID = %u;", GetBotID()); + std::string query = StringFormat("SELECT StanceID FROM botstances WHERE BotID = %u", GetBotID()); auto results = database.QueryDatabase(query); if(!results.Success() || results.RowCount() == 0) { Log.Out(Logs::General, Logs::Error, "Error in Bot::LoadStance()"); SetDefaultBotStance(); return; } - auto row = results.begin(); - SetBotStance((BotStanceType)atoi(row[0])); } @@ -2788,22 +2467,19 @@ void Bot::SaveStance() { if(_baseBotStance == _botStance) return; - std::string query = StringFormat("REPLACE INTO botstances (BotID, StanceId) " - "VALUES(%u, %u);", GetBotID(), GetBotStance()); + std::string query = StringFormat("REPLACE INTO botstances (BotID, StanceId) VALUES(%u, %u)", GetBotID(), GetBotStance()); auto results = database.QueryDatabase(query); if(!results.Success()) Log.Out(Logs::General, Logs::Error, "Error in Bot::SaveStance()"); - } void Bot::LoadTimers() { - std::string query = StringFormat("SELECT IfNull(bt.TimerID, 0) As TimerID, IfNull(bt.Value, 0) As Value, " "IfNull(MAX(sn.recast_time), 0) AS MaxTimer FROM bottimers bt, spells_new sn " "WHERE bt.BotID = %u AND sn.EndurTimerIndex = " "(SELECT case WHEN TimerID > %i THEN TimerID - %i ELSE TimerID END AS TimerID " "FROM bottimers WHERE TimerID = bt.TimerID AND BotID = bt.BotID ) " - "AND sn.classes%i <= %i;", + "AND sn.classes%i <= %i", GetBotID(), DisciplineReuseStart-1, DisciplineReuseStart-1, GetClass(), GetLevel()); auto results = database.QueryDatabase(query); if(!results.Success()) { @@ -2814,12 +2490,10 @@ void Bot::LoadTimers() { int timerID = 0; uint32 value = 0; uint32 maxValue = 0; - for (auto row = results.begin(); row != results.end(); ++row) { timerID = atoi(row[0]) - 1; value = atoi(row[1]); maxValue = atoi(row[2]); - if(timerID >= 0 && timerID < MaxTimer && value < (Timer::GetCurrentTime() + maxValue)) timers[timerID] = value; } @@ -2828,8 +2502,7 @@ void Bot::LoadTimers() { void Bot::SaveTimers() { bool hadError = false; - - std::string query = StringFormat("DELETE FROM bottimers WHERE BotID = %u;", GetBotID()); + std::string query = StringFormat("DELETE FROM bottimers WHERE BotID = %u", GetBotID()); auto results = database.QueryDatabase(query); if(!results.Success()) hadError = true; @@ -2838,10 +2511,8 @@ void Bot::SaveTimers() { if(timers[timerIndex] <= Timer::GetCurrentTime()) continue; - query = StringFormat("REPLACE INTO bottimers (BotID, TimerID, Value) VALUES(%u, %u, %u);", - GetBotID(), timerIndex+1, timers[timerIndex]); + query = StringFormat("REPLACE INTO bottimers (BotID, TimerID, Value) VALUES(%u, %u, %u)", GetBotID(), timerIndex + 1, timers[timerIndex]); results = database.QueryDatabase(query); - if(!results.Success()) hadError = true; } @@ -2851,16 +2522,14 @@ void Bot::SaveTimers() { } -bool Bot::Process() -{ +bool Bot::Process() { if(IsStunned() && stunned_timer.Check()) Mob::UnStun(); if(!GetBotOwner()) return false; - if (GetDepop()) - { + if (GetDepop()) { _botOwner = 0; _botOwnerCharacterID = 0; _previousTarget = 0; @@ -2869,25 +2538,18 @@ bool Bot::Process() SpellProcess(); - if(tic_timer.Check()) - { + if(tic_timer.Check()) { //6 seconds, or whatever the rule is set to has passed, send this position to everyone to avoid ghosting - if(!IsMoving() && !IsEngaged()) - { + if(!IsMoving() && !IsEngaged()) { SendPosition(); - if(IsSitting()) - { + if(IsSitting()) { if(!rest_timer.Enabled()) - { rest_timer.Start(RuleI(Character, RestRegenTimeToActivate) * 1000); - } } } BuffProcess(); - CalcRestState(); - if(curfp) ProcessFlee(); @@ -2904,7 +2566,6 @@ bool Bot::Process() if (sendhpupdate_timer.Check(false)) { SendHPUpdate(); - if(HasPet()) GetPet()->SendHPUpdate(); } @@ -2915,24 +2576,14 @@ bool Bot::Process() if (IsStunned() || IsMezzed()) return true; - //Handle assists... - /*if(assist_timer.Check() && !Charmed() && GetTarget() != nullptr) { - entity_list.AIYellForHelp(this, GetTarget()); - }*/ - // Bot AI AI_Process(); - return true; } -void Bot::SpellProcess() -{ - // check the rapid recast prevention timer - if(spellend_timer.Check(false)) - { +void Bot::SpellProcess() { + if(spellend_timer.Check(false)) { NPC::SpellProcess(); - if(GetClass() == BARD) { if (casting_spell_id != 0) casting_spell_id = 0; @@ -2942,34 +2593,24 @@ void Bot::SpellProcess() void Bot::BotMeditate(bool isSitting) { if(isSitting) { - // If the bot is a caster has less than 99% mana while its not engaged, he needs to sit to meditate - if(GetManaRatio() < 99.0f || GetHPRatio() < 99.0f) - { + if(GetManaRatio() < 99.0f || GetHPRatio() < 99.0f) { if (!IsEngaged() && !IsSitting()) Sit(); - } - else - { + } else { if(IsSitting()) Stand(); } - } - else - { + } else { if(IsSitting()) Stand(); } - if(IsSitting()) - { + + if(IsSitting()) { if(!rest_timer.Enabled()) - { rest_timer.Start(RuleI(Character, RestRegenTimeToActivate) * 1000); - } } else - { rest_timer.Disable(); - } } void Bot::BotRangedAttack(Mob* other) { @@ -2995,21 +2636,11 @@ void Bot::BotRangedAttack(Mob* other) { return; Log.Out(Logs::Detail, Logs::Combat, "Shooting %s with bow %s (%d) and arrow %s (%d)", other->GetCleanName(), RangeWeapon->Name, RangeWeapon->ID, Ammo->Name, Ammo->ID); - - if(!IsAttackAllowed(other) || - IsCasting() || - DivineAura() || - IsStunned() || - IsMezzed() || - (GetAppearance() == eaDead)) - { + if(!IsAttackAllowed(other) || IsCasting() || DivineAura() || IsStunned() || IsMezzed() || (GetAppearance() == eaDead)) return; - } SendItemAnimation(other, Ammo, SkillArchery); - DoArcheryAttackDmg(GetTarget(), rangedItem, ammoItem); - //break invis when you attack if(invisible) { Log.Out(Logs::Detail, Logs::Combat, "Removing invisibility due to melee attack."); @@ -3017,13 +2648,15 @@ void Bot::BotRangedAttack(Mob* other) { BuffFadeByEffect(SE_Invisibility2); invisible = false; } + if(invisible_undead) { Log.Out(Logs::Detail, Logs::Combat, "Removing invisibility vs. undead due to melee attack."); BuffFadeByEffect(SE_InvisVsUndead); BuffFadeByEffect(SE_InvisVsUndead2); invisible_undead = false; } - if(invisible_animals){ + + if(invisible_animals) { Log.Out(Logs::Detail, Logs::Combat, "Removing invisibility vs. animals due to melee attack."); BuffFadeByEffect(SE_InvisVsAnimals); invisible_animals = false; @@ -3046,26 +2679,21 @@ void Bot::BotRangedAttack(Mob* other) { } bool Bot::CheckBotDoubleAttack(bool tripleAttack) { - //Check for bonuses that give you a double attack chance regardless of skill (ie Bestial Frenzy/Harmonious Attack AA) - uint32 bonusGiveDA = aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack; - + uint32 bonusGiveDA = (aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack); // If you don't have the double attack skill, return if(!GetSkill(SkillDoubleAttack) && !(GetClass() == BARD || GetClass() == BEASTLORD)) return false; // You start with no chance of double attacking float chance = 0.0f; - uint16 skill = GetSkill(SkillDoubleAttack); - - int32 bonusDA = aabonuses.DoubleAttackChance + spellbonuses.DoubleAttackChance + itembonuses.DoubleAttackChance; - + int32 bonusDA = (aabonuses.DoubleAttackChance + spellbonuses.DoubleAttackChance + itembonuses.DoubleAttackChance); //Use skill calculations otherwise, if you only have AA applied GiveDoubleAttack chance then use that value as the base. if (skill) - chance = (float(skill+GetLevel()) * (float(100.0f+bonusDA+bonusGiveDA) /100.0f)) /500.0f; + chance = ((float(skill + GetLevel()) * (float(100.0f + bonusDA + bonusGiveDA) / 100.0f)) / 500.0f); else - chance = (float(bonusGiveDA) * (float(100.0f+bonusDA)/100.0f) ) /100.0f; + chance = ((float(bonusGiveDA) * (float(100.0f + bonusDA) / 100.0f)) / 100.0f); //Live now uses a static Triple Attack skill (lv 46 = 2% lv 60 = 20%) - We do not have this skill on EMU ATM. //A reasonable forumla would then be TA = 20% * chance @@ -3073,9 +2701,9 @@ bool Bot::CheckBotDoubleAttack(bool tripleAttack) { //Kayen: Need to decide if we can implement triple attack skill before working in over the cap effect. if(tripleAttack) { // Only some Double Attack classes get Triple Attack [This is already checked in client_processes.cpp] - int32 triple_bonus = spellbonuses.TripleAttackChance + itembonuses.TripleAttackChance; + int32 triple_bonus = (spellbonuses.TripleAttackChance + itembonuses.TripleAttackChance); chance *= 0.2f; //Baseline chance is 20% of your double attack chance. - chance *= float(100.0f+triple_bonus)/100.0f; //Apply modifiers. + chance *= (float(100.0f + triple_bonus) / 100.0f); //Apply modifiers. } if((zone->random.Real(0, 1) < chance)) @@ -3084,44 +2712,37 @@ bool Bot::CheckBotDoubleAttack(bool tripleAttack) { return false; } -void Bot::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes skillinuse, int16 chance_mod, int16 focus, bool CanRiposte, int ReuseTime) -{ +void Bot::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes skillinuse, int16 chance_mod, int16 focus, bool CanRiposte, int ReuseTime) { if (!CanDoSpecialAttack(other)) return; //For spells using skill value 98 (feral swipe ect) server sets this to 67 automatically. - //Kayen: This is unlikely to be completely accurate but use OFFENSE skill value for these effects. if (skillinuse == SkillBegging) skillinuse = SkillOffense; int damage = 0; uint32 hate = 0; int Hand = MainPrimary; - if (hate == 0 && weapon_damage > 1) hate = weapon_damage; + if (hate == 0 && weapon_damage > 1) + hate = weapon_damage; - if(weapon_damage > 0){ - - if(GetClass() == BERSERKER){ - int bonus = 3 + GetLevel()/10; - weapon_damage = weapon_damage * (100+bonus) / 100; + if(weapon_damage > 0) { + if(GetClass() == BERSERKER) { + int bonus = (3 + GetLevel( )/ 10); + weapon_damage = (weapon_damage * (100 + bonus) / 100); } int32 min_hit = 1; - int32 max_hit = (2*weapon_damage*GetDamageTable(skillinuse)) / 100; - - if(GetLevel() >= 28 && IsWarriorClass() ) - { - int ucDamageBonus = GetWeaponDamageBonus((const Item_Struct*) nullptr ); - + int32 max_hit = ((2 * weapon_damage * GetDamageTable(skillinuse)) / 100); + if(GetLevel() >= 28 && IsWarriorClass()) { + int ucDamageBonus = GetWeaponDamageBonus((const Item_Struct*) nullptr); min_hit += (int) ucDamageBonus; max_hit += (int) ucDamageBonus; hate += ucDamageBonus; } ApplySpecialAttackMod(skillinuse, max_hit, min_hit); - - min_hit += min_hit * GetMeleeMinDamageMod_SE(skillinuse) / 100; - + min_hit += (min_hit * GetMeleeMinDamageMod_SE(skillinuse) / 100); if(max_hit < min_hit) max_hit = min_hit; @@ -3130,16 +2751,16 @@ void Bot::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes else damage = zone->random.Int(min_hit, max_hit); - if(!other->CheckHitChance(this, skillinuse, Hand, chance_mod)) { + if(!other->CheckHitChance(this, skillinuse, Hand, chance_mod)) damage = 0; - } else { + else { other->AvoidDamage(this, damage, CanRiposte); other->MeleeMitigation(this, damage, min_hit); if(damage > 0) { damage += damage*focus/100; ApplyMeleeDamageBonus(skillinuse, damage); damage += other->GetFcDamageAmtIncoming(this, 0, true, skillinuse); - damage += (itembonuses.HeroicSTR / 10) + (damage * other->GetSkillDmgTaken(skillinuse) / 100) + GetSkillDmgAmt(skillinuse); + damage += ((itembonuses.HeroicSTR / 10) + (damage * other->GetSkillDmgTaken(skillinuse) / 100) + GetSkillDmgAmt(skillinuse)); TryCriticalHit(other, skillinuse, damage, nullptr); } } @@ -3150,7 +2771,6 @@ void Bot::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes return; } } - else damage = -5; @@ -3159,11 +2779,12 @@ void Bot::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes const Item_Struct* botweapon = 0; if(inst) botweapon = inst->GetItem(); + if(botweapon) { - if(botweapon->ItemType == ItemTypeShield) { + if(botweapon->ItemType == ItemTypeShield) hate += botweapon->AC; - } - hate = hate * (100 + GetFuriousBash(botweapon->Focus.Effect)) / 100; + + hate = (hate * (100 + GetFuriousBash(botweapon->Focus.Effect)) / 100); } } @@ -3176,7 +2797,6 @@ void Bot::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes } other->Damage(this, damage, SPELL_UNKNOWN, skillinuse); - if (HasDied()) return; @@ -3196,27 +2816,22 @@ void Bot::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes } void Bot::ApplySpecialAttackMod(SkillUseTypes skill, int32 &dmg, int32 &mindmg) { - int item_slot = -1; //1: Apply bonus from AC (BOOT/SHIELD/HANDS) est. 40AC=6dmg - - switch (skill){ - + switch (skill) { case SkillFlyingKick: case SkillRoundKick: case SkillKick: item_slot = MainFeet; - break; - + break; case SkillBash: item_slot = MainSecondary; - break; - + break; case SkillDragonPunch: case SkillEagleStrike: case SkillTigerClaw: item_slot = MainHands; - break; + break; } if (item_slot >= EmuConstants::EQUIPMENT_BEGIN){ @@ -3224,13 +2839,13 @@ void Bot::ApplySpecialAttackMod(SkillUseTypes skill, int32 &dmg, int32 &mindmg) const Item_Struct* botweapon = 0; if(inst) botweapon = inst->GetItem(); + if(botweapon) dmg += botweapon->AC * (RuleI(Combat, SpecialAttackACBonus))/100; } } -bool Bot::CanDoSpecialAttack(Mob *other) -{ +bool Bot::CanDoSpecialAttack(Mob *other) { //Make sure everything is valid before doing any attacks. if (!other) { SetTarget(nullptr); @@ -3240,12 +2855,7 @@ bool Bot::CanDoSpecialAttack(Mob *other) if(!GetTarget()) SetTarget(other); - if ((other == nullptr || ((GetAppearance() == eaDead) || (other->IsClient() && other->CastToClient()->IsDead())) - || HasDied() || (!IsAttackAllowed(other)))) { - return false; - } - - if(other->GetInvul() || other->GetSpecialAbility(IMMUNE_MELEE)) + if ((other == nullptr || ((GetAppearance() == eaDead) || (other->IsClient() && other->CastToClient()->IsDead())) || HasDied() || (!IsAttackAllowed(other))) || other->GetInvul() || other->GetSpecialAbility(IMMUNE_MELEE)) return false; return true; @@ -3262,7 +2872,6 @@ void Bot::SetTarget(Mob* mob) { float Bot::GetMaxMeleeRangeToTarget(Mob* target) { float result = 0; - if(target) { float size_mod = GetSize(); float other_size_mod = target->GetSize(); @@ -3277,22 +2886,19 @@ float Bot::GetMaxMeleeRangeToTarget(Mob* target) { else if (other_size_mod < 6.0) other_size_mod = 8.0f; - if (other_size_mod > size_mod) { + if (other_size_mod > size_mod) size_mod = other_size_mod; - } - - // this could still use some work, but for now it's an improvement.... if (size_mod > 29) size_mod *= size_mod; else if (size_mod > 19) - size_mod *= size_mod * 2; + size_mod *= (size_mod * 2); else - size_mod *= size_mod * 4; + size_mod *= (size_mod * 4); // prevention of ridiculously sized hit boxes if (size_mod > 10000) - size_mod = size_mod / 7; + size_mod = (size_mod / 7); result = size_mod; } @@ -3307,21 +2913,14 @@ void Bot::AI_Process() { uint8 botClass = GetClass(); uint8 botLevel = GetLevel(); - if(IsCasting() && (botClass != BARD)) return; // A bot wont start its AI if not grouped - if(!GetBotOwner() || !IsGrouped()) { - return; - } - - if(GetAppearance() == eaDead) + if(!GetBotOwner() || !IsGrouped() || GetAppearance() == eaDead) return; Mob* BotOwner = GetBotOwner(); - - // The bots need an owner if(!BotOwner) return; @@ -3343,9 +2942,8 @@ void Bot::AI_Process() { SetHasHealedThisCycle(true); NotifyNextHealRotationMember(); } - else { + else NotifyNextHealRotationMember(true); - } } if(GetHasBeenSummoned()) { @@ -3354,11 +2952,10 @@ void Bot::AI_Process() { if(!GetTarget() || (IsBotCaster() && !IsBotCasterCombatRange(GetTarget())) || (IsBotArcher() && IsArcheryRange(GetTarget())) || (DistanceSquaredNoZ(static_cast(m_Position), m_PreSummonLocation) < 10)) { if(GetTarget()) FaceTarget(GetTarget()); + SetHasBeenSummoned(false); - } - else if(!IsRooted()) { - if(GetTarget() && GetTarget()->GetHateTop() && GetTarget()->GetHateTop() != this) - { + } else if(!IsRooted()) { + if(GetTarget() && GetTarget()->GetHateTop() && GetTarget()->GetHateTop() != this) { Log.Out(Logs::Detail, Logs::AI, "Returning to location prior to being summoned."); CalculateNewPosition2(m_PreSummonLocation.x, m_PreSummonLocation.y, m_PreSummonLocation.z, GetRunspeed()); SetHeading(CalculateHeadingToTarget(m_PreSummonLocation.x, m_PreSummonLocation.y)); @@ -3371,35 +2968,29 @@ void Bot::AI_Process() { else SendPosition(); } - } - else { + } else { if(GetTarget()) FaceTarget(GetTarget()); + SetHasBeenSummoned(false); } - return; } if(!IsEngaged()) { if(GetFollowID()) { - if(BotOwner && BotOwner->GetTarget() && BotOwner->GetTarget()->IsNPC() && (BotOwner->GetTarget()->GetHateAmount(BotOwner) - || BotOwner->CastToClient()->AutoAttackEnabled()) && IsAttackAllowed(BotOwner->GetTarget())) { + if(BotOwner && BotOwner->GetTarget() && BotOwner->GetTarget()->IsNPC() && (BotOwner->GetTarget()->GetHateAmount(BotOwner) || BotOwner->CastToClient()->AutoAttackEnabled()) && IsAttackAllowed(BotOwner->GetTarget())) { AddToHateList(BotOwner->GetTarget(), 1); - if(HasPet()) GetPet()->AddToHateList(BotOwner->GetTarget(), 1); - } - else { + } else { Group* g = GetGroup(); - if(g) { for(int counter = 0; counter < g->GroupCount(); counter++) { if(g->members[counter]) { Mob* tar = g->members[counter]->GetTarget(); if(tar && tar->IsNPC() && tar->GetHateAmount(g->members[counter]) && IsAttackAllowed(g->members[counter]->GetTarget())) { AddToHateList(tar, 1); - if(HasPet()) GetPet()->AddToHateList(tar, 1); @@ -3412,9 +3003,7 @@ void Bot::AI_Process() { } } - if(IsEngaged()) - { - + if(IsEngaged()) { if(rest_timer.Enabled()) rest_timer.Disable(); @@ -3440,17 +3029,13 @@ void Bot::AI_Process() { // Else, it was causing the bot to aggro behind wall etc... causing massive trains. if(!CheckLosFN(GetTarget()) || GetTarget()->IsMezzed() || !IsAttackAllowed(GetTarget())) { WipeHateList(); - if(IsMoving()) { SetHeading(0); SetRunAnimSpeed(0); SetCurrentSpeed(GetRunspeed()); - - if(moved) { + if(moved) SetCurrentSpeed(0); - } } - return; } @@ -3458,15 +3043,11 @@ void Bot::AI_Process() { SendAddPlayerState(PlayerState::Aggressive); bool atCombatRange = false; - float meleeDistance = GetMaxMeleeRangeToTarget(GetTarget()); - - if(botClass == SHADOWKNIGHT || botClass == PALADIN || botClass == WARRIOR) { - meleeDistance = meleeDistance * .30; - } - else { + if(botClass == SHADOWKNIGHT || botClass == PALADIN || botClass == WARRIOR) + meleeDistance = (meleeDistance * .30); + else meleeDistance *= (float)zone->random.Real(.50, .85); - } bool atArcheryRange = IsArcheryRange(GetTarget()); @@ -3476,8 +3057,7 @@ void Bot::AI_Process() { if(atArcheryRange && !IsBotArcher()) { SetBotArcher(true); changeWeapons = true; - } - else if(!atArcheryRange && IsBotArcher()) { + } else if(!atArcheryRange && IsBotArcher()) { SetBotArcher(false); changeWeapons = true; } @@ -3496,16 +3076,13 @@ void Bot::AI_Process() { SetCurrentSpeed(0); } } - atCombatRange = true; - } - else if(IsBotCaster() && GetLevel() > 12) { + } else if(IsBotCaster() && GetLevel() > 12) { if(IsBotCasterCombatRange(GetTarget())) atCombatRange = true; } - else if(DistanceSquared(m_Position, GetTarget()->GetPosition()) <= meleeDistance) { + else if(DistanceSquared(m_Position, GetTarget()->GetPosition()) <= meleeDistance) atCombatRange = true; - } if(atCombatRange) { if(IsMoving()) { @@ -3523,7 +3100,6 @@ void Bot::AI_Process() { float newX = 0; float newY = 0; float newZ = 0; - if(PlotPositionAroundTarget(GetTarget(), newX, newY, newZ)) { CalculateNewPosition2(newX, newY, newZ, GetRunspeed()); return; @@ -3534,7 +3110,6 @@ void Bot::AI_Process() { float newX = 0; float newY = 0; float newZ = 0; - if(PlotPositionAroundTarget(GetTarget(), newX, newY, newZ, false) && GetArchetype() != ARCHETYPE_CASTER) { CalculateNewPosition2(newX, newY, newZ, GetRunspeed()); return; @@ -3549,35 +3124,24 @@ void Bot::AI_Process() { if(IsBotArcher() && ranged_timer.Check(false)) { if(GetTarget()->GetHPRatio() <= 99.0f) - // Mob::DoArcheryAttackDmg() takes care of Bot Range and Ammo procs BotRangedAttack(GetTarget()); } else if(!IsBotArcher() && (!(IsBotCaster() && GetLevel() > 12)) && GetTarget() && !IsStunned() && !IsMezzed() && (GetAppearance() != eaDead)) { // we can't fight if we don't have a target, are stun/mezzed or dead.. // Stop attacking if the target is enraged - if(IsEngaged() && !BehindMob(GetTarget(), GetX(), GetY()) && GetTarget()->IsEnraged()) - return; - - if(GetBotStance() == BotStancePassive) + if((IsEngaged() && !BehindMob(GetTarget(), GetX(), GetY()) && GetTarget()->IsEnraged()) || GetBotStance() == BotStancePassive) return; // First, special attack per class (kick, backstab etc..) DoClassAttacks(GetTarget()); - - //try main hand first if(attack_timer.Check()) { Attack(GetTarget(), MainPrimary); - ItemInst *wpn = GetBotItem(MainPrimary); TryWeaponProc(wpn, GetTarget(), MainPrimary); - bool tripleSuccess = false; - if(BotOwner && GetTarget() && CanThisClassDoubleAttack()) { - - if(BotOwner && CheckBotDoubleAttack()) { + if(BotOwner && CheckBotDoubleAttack()) Attack(GetTarget(), MainPrimary, true); - } if(BotOwner && GetTarget() && GetSpecialAbility(SPECATK_TRIPLE) && CheckBotDoubleAttack(true)) { tripleSuccess = true; @@ -3585,37 +3149,27 @@ void Bot::AI_Process() { } //quad attack, does this belong here?? - if(BotOwner && GetTarget() && GetSpecialAbility(SPECATK_QUAD) && CheckBotDoubleAttack(true)) { + if(BotOwner && GetTarget() && GetSpecialAbility(SPECATK_QUAD) && CheckBotDoubleAttack(true)) Attack(GetTarget(), MainPrimary, true); - } } //Live AA - Flurry, Rapid Strikes ect (Flurry does not require Triple Attack). - int32 flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance; - - if (GetTarget() && flurrychance) - { - if(zone->random.Int(0, 100) < flurrychance) - { + int32 flurrychance = (aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance); + if (GetTarget() && flurrychance) { + if(zone->random.Int(0, 100) < flurrychance) { Message_StringID(MT_NPCFlurry, YOU_FLURRY); Attack(GetTarget(), MainPrimary, false); Attack(GetTarget(), MainPrimary, false); } } - int32 ExtraAttackChanceBonus = spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance + aabonuses.ExtraAttackChance; - + int32 ExtraAttackChanceBonus = (spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance + aabonuses.ExtraAttackChance); if (GetTarget() && ExtraAttackChanceBonus) { ItemInst *wpn = GetBotItem(MainPrimary); - if(wpn){ - if(wpn->GetItem()->ItemType == ItemType2HSlash || - wpn->GetItem()->ItemType == ItemType2HBlunt || - wpn->GetItem()->ItemType == ItemType2HPiercing ) - { + if(wpn) { + if(wpn->GetItem()->ItemType == ItemType2HSlash || wpn->GetItem()->ItemType == ItemType2HBlunt || wpn->GetItem()->ItemType == ItemType2HPiercing) { if(zone->random.Int(0, 100) < ExtraAttackChanceBonus) - { Attack(GetTarget(), MainPrimary, false); - } } } } @@ -3626,6 +3180,7 @@ void Bot::AI_Process() { entity_list.MessageClose_StringID(this, false, 200, 0, BERSERK_START, GetName()); this->berserk = true; } + if (berserk && this->GetHPRatio() > 30) { entity_list.MessageClose_StringID(this, false, 200, 0, BERSERK_END, GetName()); this->berserk = false; @@ -3643,7 +3198,6 @@ void Bot::AI_Process() { int weapontype = 0; // No weapon type. bool bIsFist = true; - if(weapon) { weapontype = weapon->ItemType; bIsFist = false; @@ -3651,21 +3205,15 @@ void Bot::AI_Process() { if(bIsFist || ((weapontype != ItemType2HSlash) && (weapontype != ItemType2HPiercing) && (weapontype != ItemType2HBlunt))) { float DualWieldProbability = 0.0f; - - int32 Ambidexterity = aabonuses.Ambidexterity + spellbonuses.Ambidexterity + itembonuses.Ambidexterity; - DualWieldProbability = (GetSkill(SkillDualWield) + GetLevel() + Ambidexterity) / 400.0f; // 78.0 max - int32 DWBonus = spellbonuses.DualWieldChance + itembonuses.DualWieldChance; - DualWieldProbability += DualWieldProbability*float(DWBonus)/ 100.0f; - + int32 Ambidexterity = (aabonuses.Ambidexterity + spellbonuses.Ambidexterity + itembonuses.Ambidexterity); + DualWieldProbability = ((GetSkill(SkillDualWield) + GetLevel() + Ambidexterity) / 400.0f); // 78.0 max + int32 DWBonus = (spellbonuses.DualWieldChance + itembonuses.DualWieldChance); + DualWieldProbability += (DualWieldProbability * float(DWBonus) / 100.0f); float random = zone->random.Real(0, 1); - if (random < DualWieldProbability){ // Max 78% of DW - Attack(GetTarget(), MainSecondary); // Single attack with offhand - ItemInst *wpn = GetBotItem(MainSecondary); TryWeaponProc(wpn, GetTarget(), MainSecondary); - if( CanThisClassDoubleAttack() && CheckBotDoubleAttack()) { if(GetTarget() && GetTarget()->GetHP() > -10) Attack(GetTarget(), MainSecondary); // Single attack with offhand @@ -3675,8 +3223,7 @@ void Bot::AI_Process() { } } } - } // end in combat range - else { + } else { if(GetTarget()->IsFeared() && !spellend_timer.Enabled()){ // This is a mob that is fleeing either because it has been feared or is low on hitpoints if(GetBotStance() != BotStancePassive) @@ -3701,17 +3248,13 @@ void Bot::AI_Process() { if(GetBotStance() == BotStancePassive) return; - if(AI_EngagedCastCheck()) { + if(AI_EngagedCastCheck()) BotMeditate(false); - } else if(GetArchetype() == ARCHETYPE_CASTER) BotMeditate(true); } - } // end IsEngaged() - else { - // Not engaged in combat + } else { SetTarget(0); - if (m_PlayerState & static_cast(PlayerState::Aggressive)) SendRemovePlayerState(PlayerState::Aggressive); @@ -3720,20 +3263,16 @@ void Bot::AI_Process() { if(!AI_IdleCastCheck() && !IsCasting()) BotMeditate(true); } - else { + else BotMeditate(true); - } - } if(AImovement_timer->Check()) { if(GetFollowID()) { Mob* follow = entity_list.GetMob(GetFollowID()); - if(follow) { float dist = DistanceSquared(m_Position, follow->GetPosition()); int speed = follow->GetRunspeed(); - if(dist < GetFollowDistance() + 1000) speed = follow->GetWalkspeed(); @@ -3742,11 +3281,8 @@ void Bot::AI_Process() { if(rest_timer.Enabled()) rest_timer.Disable(); return; - } - else - { - if(moved) - { + } else { + if(moved) { moved = false; SetCurrentSpeed(0); } @@ -3759,33 +3295,20 @@ void Bot::AI_Process() { // AI Processing for a Bot object's pet void Bot::PetAIProcess() { - if( !HasPet() || !GetPet() || !GetPet()->IsNPC()) return; Mob* BotOwner = this->GetBotOwner(); NPC* botPet = this->GetPet()->CastToNPC(); - if(!botPet->GetOwner() || !botPet->GetID() || !botPet->GetOwnerID()) { Kill(); return; } - if (!botPet->IsAIControlled()) - return; - - if(botPet->GetAttackTimer().Check(false)) - return; - - if (botPet->IsCasting()) - return; - - // Return if the owner of the bot pet isnt a bot. - if (!botPet->GetOwner()->IsBot()) + if (!botPet->IsAIControlled() || botPet->GetAttackTimer().Check(false) || botPet->IsCasting() || !botPet->GetOwner()->IsBot()) return; if (IsEngaged()) { - if (botPet->IsRooted()) botPet->SetTarget(hate_list.GetClosestEntOnHateList(botPet)); else @@ -3797,40 +3320,22 @@ void Bot::PetAIProcess() { if(!botPet->CheckLosFN(botPet->GetTarget()) || botPet->GetTarget()->IsMezzed() || !botPet->IsAttackAllowed(GetTarget())) { botPet->WipeHateList(); botPet->SetTarget(botPet->GetOwner()); - return; } botPet->FaceTarget(botPet->GetTarget()); - - // Lets see if we can let the main tank build a little aggro - /*if(GetBotRaidID()) { - BotRaids *br = entity_list.GetBotRaidByMob(GetOwner()); - if(br) { - if(br->GetBotMainTank() && (br->GetBotMainTank() != this)) { - if(br->GetBotMainTarget() && (br->GetBotMainTarget()->GetHateAmount(br->GetBotMainTank()) < 5000)) { - if(GetTarget() == br->GetBotMainTarget()) { - return; - } - } - } - } - }*/ - bool is_combat_range = botPet->CombatRange(botPet->GetTarget()); - // Ok, we're engaged, each class type has a special AI // Only melee class will go to melee. Casters and healers will stay behind, following the leader by default. // I should probably make the casters staying in place so they can cast.. // Ok, we 're a melee or any other class lvl<12. Yes, because after it becomes hard to go in melee for casters.. even for bots.. - if( is_combat_range ) { + if(is_combat_range) { botPet->GetAIMovementTimer()->Check(); - if(botPet->IsMoving()) { botPet->SetHeading(botPet->GetTarget()->GetHeading()); if(moved) { - moved=false; + moved = false; botPet->SetRunAnimSpeed(0); } } @@ -3840,10 +3345,8 @@ void Bot::PetAIProcess() { float newY = 0; float newZ = 0; bool petHasAggro = false; - - if(botPet->GetTarget() && botPet->GetTarget()->GetHateTop() && botPet->GetTarget()->GetHateTop() == botPet) { + if(botPet->GetTarget() && botPet->GetTarget()->GetHateTop() && botPet->GetTarget()->GetHateTop() == botPet) petHasAggro = true; - } if(botPet->GetClass() == ROGUE && !petHasAggro && !botPet->BehindMob(botPet->GetTarget(), botPet->GetX(), botPet->GetY())) { // Move the rogue to behind the mob @@ -3863,7 +3366,6 @@ void Bot::PetAIProcess() { // Let's try to adjust our melee range so we don't appear to be bunched up bool isBehindMob = false; bool moveBehindMob = false; - if(botPet->BehindMob(botPet->GetTarget(), botPet->GetX(), botPet->GetY())) isBehindMob = true; @@ -3886,14 +3388,11 @@ void Bot::PetAIProcess() { return; if(botPet->Attack(GetTarget(), MainPrimary)) // try the main hand - if (botPet->GetTarget()) // Do we still have a target? - { + if (botPet->GetTarget()) { // We're a pet so we re able to dual attack int32 RandRoll = zone->random.Int(0, 99); - if (botPet->CanThisClassDoubleAttack() && (RandRoll < (botPet->GetLevel() + NPCDualAttackModifier))) - { - if(botPet->Attack(botPet->GetTarget(), MainPrimary)) - {} + if (botPet->CanThisClassDoubleAttack() && (RandRoll < (botPet->GetLevel() + NPCDualAttackModifier))) { + if(botPet->Attack(botPet->GetTarget(), MainPrimary)) {} } } @@ -3906,19 +3405,18 @@ void Bot::PetAIProcess() { aa_skill += botPet->GetOwner()->GetAA(aaQuickeningofDeath); // Beastlord AA aa_skill += botPet->GetOwner()->GetAA(aaWardersAlacrity); - - if(aa_skill >= 1) { - aa_chance += (aa_skill > 5 ? 5 : aa_skill) * 4; - } - if(aa_skill >= 6) { - aa_chance += (aa_skill-5 > 3 ? 3 : aa_skill-5) * 7; - } - if(aa_skill >= 9) { - aa_chance += (aa_skill-8 > 3 ? 3 : aa_skill-8) * 3; - } - if(aa_skill >= 12) { - aa_chance += (aa_skill - 11) * 1; - } + if(aa_skill >= 1) + aa_chance += ((aa_skill > 5 ? 5 : aa_skill) * 4); + + if(aa_skill >= 6) + aa_chance += ((aa_skill - 5 > 3 ? 3 : aa_skill - 5) * 7); + + if(aa_skill >= 9) + aa_chance += ((aa_skill - 8 > 3 ? 3 : aa_skill - 8) * 3); + + if(aa_skill >= 12) + aa_chance += ((aa_skill - 11) * 1); + //aa_chance += botPet->GetOwner()->GetAA(aaCompanionsAlacrity) * 3; @@ -3927,21 +3425,16 @@ void Bot::PetAIProcess() { } // Ok now, let's check pet's offhand. - if (botPet->GetAttackDWTimer().Check() && botPet->GetOwnerID() && botPet->GetOwner() && ((botPet->GetOwner()->GetClass() == MAGICIAN) || (botPet->GetOwner()->GetClass() == NECROMANCER) || (botPet->GetOwner()->GetClass() == SHADOWKNIGHT) || (botPet->GetOwner()->GetClass() == BEASTLORD))) - { - if(botPet->GetOwner()->GetLevel() >= 24) - { - float DualWieldProbability = (botPet->GetSkill(SkillDualWield) + botPet->GetLevel()) / 400.0f; + if (botPet->GetAttackDWTimer().Check() && botPet->GetOwnerID() && botPet->GetOwner() && ((botPet->GetOwner()->GetClass() == MAGICIAN) || (botPet->GetOwner()->GetClass() == NECROMANCER) || (botPet->GetOwner()->GetClass() == SHADOWKNIGHT) || (botPet->GetOwner()->GetClass() == BEASTLORD))) { + if(botPet->GetOwner()->GetLevel() >= 24) { + float DualWieldProbability = ((botPet->GetSkill(SkillDualWield) + botPet->GetLevel()) / 400.0f); DualWieldProbability -= zone->random.Real(0, 1); - if(DualWieldProbability < 0){ + if(DualWieldProbability < 0) { botPet->Attack(botPet->GetTarget(), MainSecondary); - if (botPet->CanThisClassDoubleAttack()) - { + if (botPet->CanThisClassDoubleAttack()) { int32 RandRoll = zone->random.Int(0, 99); if (RandRoll < (botPet->GetLevel() + 20)) - { botPet->Attack(botPet->GetTarget(), MainSecondary); - } } } } @@ -3955,25 +3448,20 @@ void Bot::PetAIProcess() { // See if the pet can cast any spell botPet->AI_EngagedCastCheck(); } - }// end of the combat in range - else{ + } else { // Now, if we cannot reach our target - if (!botPet->HateSummon()) - { - if(botPet->GetTarget() && botPet->AI_PursueCastCheck()) - {} - else if (botPet->GetTarget() && botPet->GetAIMovementTimer()->Check()) - { + if (!botPet->HateSummon()) { + if(botPet->GetTarget() && botPet->AI_PursueCastCheck()) {} + else if (botPet->GetTarget() && botPet->GetAIMovementTimer()->Check()) { botPet->SetRunAnimSpeed(0); if(!botPet->IsRooted()) { Log.Out(Logs::Detail, Logs::AI, "Pursuing %s while engaged.", botPet->GetTarget()->GetCleanName()); botPet->CalculateNewPosition2(botPet->GetTarget()->GetX(), botPet->GetTarget()->GetY(), botPet->GetTarget()->GetZ(), botPet->GetOwner()->GetRunspeed()); return; - } - else { + } else { botPet->SetHeading(botPet->GetTarget()->GetHeading()); if(moved) { - moved=false; + moved = false; SetCurrentSpeed(0); botPet->SendPosition(); botPet->SetMoving(false); @@ -3982,39 +3470,33 @@ void Bot::PetAIProcess() { } } } - } - else{ - // Franck: EQoffline + } else { // Ok if we're not engaged, what's happening.. - if(botPet->GetTarget() != botPet->GetOwner()) { + if(botPet->GetTarget() != botPet->GetOwner()) botPet->SetTarget(botPet->GetOwner()); - } - if(!IsMoving()) { + if(!IsMoving()) botPet->AI_IdleCastCheck(); - } if(botPet->GetAIMovementTimer()->Check()) { switch(pStandingPetOrder) { - case SPO_Follow: - { - float dist = DistanceSquared(botPet->GetPosition(), botPet->GetTarget()->GetPosition()); - botPet->SetRunAnimSpeed(0); - if(dist > 184) { - botPet->CalculateNewPosition2(botPet->GetTarget()->GetX(), botPet->GetTarget()->GetY(), botPet->GetTarget()->GetZ(), botPet->GetTarget()->GetRunspeed()); - return; - } - else { - botPet->SetHeading(botPet->GetTarget()->GetHeading()); - if(moved) { - moved=false; - SetCurrentSpeed(0); - botPet->SendPosition(); - botPet->SetMoving(false); - } + case SPO_Follow: { + float dist = DistanceSquared(botPet->GetPosition(), botPet->GetTarget()->GetPosition()); + botPet->SetRunAnimSpeed(0); + if(dist > 184) { + botPet->CalculateNewPosition2(botPet->GetTarget()->GetX(), botPet->GetTarget()->GetY(), botPet->GetTarget()->GetZ(), botPet->GetTarget()->GetRunspeed()); + return; + } else { + botPet->SetHeading(botPet->GetTarget()->GetHeading()); + if(moved) { + moved = false; + SetCurrentSpeed(0); + botPet->SendPosition(); + botPet->SetMoving(false); } } break; + } case SPO_Sit: botPet->SetAppearance(eaSitting); break; @@ -4028,26 +3510,21 @@ void Bot::PetAIProcess() { void Bot::Depop() { WipeHateList(); - entity_list.RemoveFromHateLists(this); - if(HasGroup()) Bot::RemoveBotFromGroup(this, GetGroup()); - if(HasPet()) { + if(HasPet()) GetPet()->Depop(); - } _botOwner = 0; _botOwnerCharacterID = 0; _previousTarget = 0; - NPC::Depop(false); } bool Bot::DeleteBot(std::string* errorMessage) { bool hadError = false; - if(this->GetBotID() == 0) return false; @@ -4107,20 +3584,12 @@ void Bot::Spawn(Client* botCharacterOwner, std::string* errorMessage) { // Make the bot look at the bot owner FaceTarget(botCharacterOwner); - - // Level the bot to the same level as the bot owner - //this->SetLevel(botCharacterOwner->GetLevel()); - UpdateEquipmentLight(); UpdateActiveLight(); - entity_list.AddBot(this, true, true); - // Load pet LoadPet(); - this->SendPosition(); - // there is something askew with spawn struct appearance fields... // I re-enabled this until I can sort it out uint32 itemID = 0; @@ -4138,9 +3607,7 @@ void Bot::Spawn(Client* botCharacterOwner, std::string* errorMessage) { // Saves the specified item as an inventory record in the database for this bot. void Bot::SetBotItemInSlot(uint32 slotID, uint32 itemID, const ItemInst* inst, std::string *errorMessage) { - uint32 augslot[EmuConstants::ITEM_COMMON_SIZE] = { NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM }; - if (this->GetBotID() == 0 || slotID < EmuConstants::EQUIPMENT_BEGIN || itemID <= NO_ITEM) return; @@ -4165,13 +3632,10 @@ void Bot::SetBotItemInSlot(uint32 slotID, uint32 itemID, const ItemInst* inst, s // Deletes the inventory record for the specified item from the database for this bot. void Bot::RemoveBotItemBySlot(uint32 slotID, std::string *errorMessage) { - if(this->GetBotID() == 0) return; - std::string query = StringFormat("DELETE FROM botinventory " - "WHERE botid = %i AND slotid = %i", - this->GetBotID(), slotID); + std::string query = StringFormat("DELETE FROM botinventory WHERE botid = %i AND slotid = %i", this->GetBotID(), slotID); auto results = database.QueryDatabase(query); if(!results.Success()) *errorMessage = std::string(results.ErrorMessage()); @@ -4182,7 +3646,6 @@ void Bot::RemoveBotItemBySlot(uint32 slotID, std::string *errorMessage) { // Retrieves all the inventory records from the database for this bot. void Bot::GetBotItems(std::string* errorMessage, Inventory &inv) { - if(this->GetBotID() == 0) return; @@ -4208,7 +3671,6 @@ void Bot::GetBotItems(std::string* errorMessage, Inventory &inv) { aug[3] = (uint32)atoul(row[7]); aug[4] = (uint32)atoul(row[8]); bool instnodrop = (row[9] && (uint16)atoi(row[9])) ? true : false; - ItemInst* inst = database.CreateItem(item_id, charges, aug[0], aug[1], aug[2], aug[3], aug[4]); if (!inst) { Log.Out(Logs::General, Logs::Error, "Warning: botid %i has an invalid item_id %i in inventory slot %i", this->GetBotID(), item_id, slot_id); @@ -4236,7 +3698,6 @@ void Bot::GetBotItems(std::string* errorMessage, Inventory &inv) { // Save ptr to item in inventory if (put_slot_id == INVALID_INDEX) Log.Out(Logs::General, Logs::Error, "Warning: Invalid slot_id for item in inventory: botid=%i, item_id=%i, slot_id=%i",this->GetBotID(), item_id, slot_id); - } UpdateEquipmentLight(); @@ -4244,26 +3705,20 @@ void Bot::GetBotItems(std::string* errorMessage, Inventory &inv) { // Returns the inventory record for this bot from the database for the specified equipment slot. uint32 Bot::GetBotItemBySlot(uint32 slotID) { - if(this->GetBotID() == 0 || slotID < EmuConstants::EQUIPMENT_BEGIN) return 0; - std::string query = StringFormat("SELECT itemid FROM botinventory WHERE botid=%i AND slotid = %i", GetBotID(), slotID); + std::string query = StringFormat("SELECT itemid FROM botinventory WHERE botid = %i AND slotid = %i", GetBotID(), slotID); auto results = database.QueryDatabase(query); - if(!results.Success()) - return 0; - - if(results.RowCount() != 1) + if(!results.Success() || results.RowCount() != 1) return 0; auto row = results.begin(); - return atoi(row[0]); } // Returns the number of inventory records the bot has in the database. uint32 Bot::GetBotItemsCount(std::string *errorMessage) { - if(this->GetBotID() == 0) return 0; @@ -4283,50 +3738,35 @@ uint32 Bot::GetBotItemsCount(std::string *errorMessage) { bool Bot::MesmerizeTarget(Mob* target) { bool Result = false; - if(target) { int mezid = 0; int mezlevel = GetLevel(); - - if(mezlevel >= 69) { + if(mezlevel >= 69) mezid = 5520; - } - else if(mezlevel == 68) { + else if(mezlevel == 68) mezid = 8035; - } - else if(mezlevel == 67) { + else if(mezlevel == 67) mezid = 5503; - } - else if(mezlevel >= 64) { + else if(mezlevel >= 64) mezid = 3358; - } - else if(mezlevel == 63) { + else if(mezlevel == 63) mezid = 3354; - } - else if(mezlevel >= 61) { + else if(mezlevel >= 61) mezid = 3341; - } - else if(mezlevel == 60) { + else if(mezlevel == 60) mezid = 2120; - } - else if(mezlevel == 59) { + else if(mezlevel == 59) mezid = 1692; - } - else if(mezlevel >= 54) { + else if(mezlevel >= 54) mezid = 1691; - } - else if(mezlevel >= 47) { + else if(mezlevel >= 47) mezid = 190; - } - else if(mezlevel >= 30) { + else if(mezlevel >= 30) mezid = 188; - } - else if(mezlevel >= 13) { + else if(mezlevel >= 13) mezid = 187; - } - else if(mezlevel >= 2) { + else if(mezlevel >= 2) mezid = 292; - } if(mezid > 0) { uint32 DontRootMeBeforeTime = 0; CastSpell(mezid, target->GetID(), 1, -1, -1, &DontRootMeBeforeTime); @@ -4339,16 +3779,13 @@ bool Bot::MesmerizeTarget(Mob* target) { } void Bot::SetLevel(uint8 in_level, bool command) { - if(in_level > 0) { + if(in_level > 0) Mob::SetLevel(in_level, command); - } } void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { - if(ns) - { + if(ns) { Mob::FillSpawnStruct(ns, ForWho); - ns->spawn.afk = 0; ns->spawn.lfg = 0; ns->spawn.anon = 0; @@ -4364,74 +3801,51 @@ void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { ns->spawn.flymode = 0; ns->spawn.size = 0; ns->spawn.NPC = 0; // 0=player,1=npc,2=pc corpse,3=npc corpse - UpdateActiveLight(); ns->spawn.light = m_Light.Type.Active; - ns->spawn.helm = (GetShowHelm() ? helmtexture : 0); //0xFF; ns->spawn.equip_chest2 = texture; //0xFF; - const Item_Struct* item = 0; const ItemInst* inst = 0; - uint32 spawnedbotid = 0; spawnedbotid = this->GetBotID(); - - for (int i = 0; i < MaterialPrimary; i++) - { + for (int i = 0; i < MaterialPrimary; i++) { inst = GetBotItem(i); - if (inst) - { + if (inst) { item = inst->GetItem(); - if (item != 0) - { + if (item != 0) { ns->spawn.equipment[i].Material = item->Material; ns->spawn.equipment[i].EliteMaterial = item->EliteMaterial; ns->spawn.equipment[i].HeroForgeModel = item->HerosForgeModel; if (armor_tint[i]) - { ns->spawn.colors[i].Color = armor_tint[i]; - - } else - { ns->spawn.colors[i].Color = item->Color; - } - } - else - { + } else { if (armor_tint[i]) - { ns->spawn.colors[i].Color = armor_tint[i]; - } } } } inst = GetBotItem(MainPrimary); - if(inst) - { + if(inst) { item = inst->GetItem(); - if(item) - { + if(item) { if(strlen(item->IDFile) > 2) - { ns->spawn.equipment[MaterialPrimary].Material = atoi(&item->IDFile[2]); - } + ns->spawn.colors[MaterialPrimary].Color = GetEquipmentColor(MaterialPrimary); } } inst = GetBotItem(MainSecondary); - if(inst) - { + if(inst) { item = inst->GetItem(); - if(item) - { + if(item) { if(strlen(item->IDFile) > 2) - { ns->spawn.equipment[MaterialSecondary].Material = atoi(&item->IDFile[2]); - } + ns->spawn.colors[MaterialSecondary].Color = GetEquipmentColor(MaterialSecondary); } } @@ -4444,10 +3858,7 @@ uint32 Bot::GetBotIDByBotName(std::string botName) { std::string query = StringFormat("SELECT BotID FROM bots WHERE Name = '%s'", botName.c_str()); auto results = database.QueryDatabase(query); - if(!results.Success()) - return 0; - - if (results.RowCount() == 0) + if(!results.Success() || results.RowCount() == 0) return 0; auto row = results.begin(); @@ -4456,7 +3867,6 @@ uint32 Bot::GetBotIDByBotName(std::string botName) { Bot* Bot::LoadBot(uint32 botID, std::string* errorMessage) { Bot* loadedBot = nullptr; - if(botID == 0) return nullptr; @@ -4477,9 +3887,7 @@ Bot* Bot::LoadBot(uint32 botID, std::string* errorMessage) { return nullptr; auto row = results.begin(); - NPCType defaultNPCTypeStruct = CreateDefaultNPCTypeStructForBot(std::string(row[2]), std::string(row[3]), - atoi(row[4]), atoi(row[5]), atoi(row[6]), atoi(row[7])); - + NPCType defaultNPCTypeStruct = CreateDefaultNPCTypeStructForBot(std::string(row[2]), std::string(row[3]), atoi(row[4]), atoi(row[5]), atoi(row[6]), atoi(row[7])); NPCType tempNPCStruct = FillNPCTypeStruct(atoi(row[1]), std::string(row[2]), std::string(row[3]), atoi(row[4]), atoi(row[5]), atoi(row[6]), atoi(row[7]), atof(row[8]), atoi(row[9]), atoi(row[10]), atoi(row[11]), atoi(row[12]), atoi(row[13]), atoi(row[14]), @@ -4490,21 +3898,16 @@ Bot* Bot::LoadBot(uint32 botID, std::string* errorMessage) { defaultNPCTypeStruct.STA, defaultNPCTypeStruct.DEX, defaultNPCTypeStruct.AGI, defaultNPCTypeStruct.INT, defaultNPCTypeStruct.WIS, defaultNPCTypeStruct.CHA, defaultNPCTypeStruct.ATK); - loadedBot = new Bot(botID, atoi(row[0]), atoi(row[1]), atof(row[38]), atoi(row[39]), tempNPCStruct); - return loadedBot; } std::list Bot::GetGroupedBotsByGroupId(uint32 groupId, std::string* errorMessage) { std::list groupedBots; - if(groupId == 0) return groupedBots; - std::string query = StringFormat("SELECT g.mobid AS BotID FROM vwGroups AS g " - "JOIN bots AS b ON g.mobid = b.BotId AND g.mobtype = 'B' " - "WHERE g.groupid = %u", groupId); + std::string query = StringFormat("SELECT g.mobid AS BotID FROM vwGroups AS g JOIN bots AS b ON g.mobid = b.BotId AND g.mobtype = 'B' WHERE g.groupid = %u", groupId); auto results = database.QueryDatabase(query); if(!results.Success()) { *errorMessage = std::string(results.ErrorMessage()); @@ -4526,11 +3929,9 @@ void Bot::LoadAndSpawnAllZonedBots(Client* botOwner) { uint32 TempGroupId = g->GetID(); std::string errorMessage; std::list ActiveBots = Bot::GetGroupedBotsByGroupId(botOwner->GetGroup()->GetID(), &errorMessage); - if(errorMessage.empty() && !ActiveBots.empty()) { for(std::list::iterator itr = ActiveBots.begin(); itr != ActiveBots.end(); ++itr) { Bot* activeBot = Bot::LoadBot(*itr, &errorMessage); - if(!errorMessage.empty()) { safe_delete(activeBot); break; @@ -4538,9 +3939,7 @@ void Bot::LoadAndSpawnAllZonedBots(Client* botOwner) { if(activeBot) { activeBot->Spawn(botOwner, &errorMessage); - g->UpdatePlayer(activeBot); - if(g->GetLeader()) activeBot->SetFollowID(g->GetLeader()->GetID()); } @@ -4549,11 +3948,6 @@ void Bot::LoadAndSpawnAllZonedBots(Client* botOwner) { database.SetGroupID(activeBot->GetCleanName(), 0, activeBot->GetBotID()); } } - - // Catch all condition for error messages destined for the zone error log - if(!errorMessage.empty()) { - // TODO: Log this error message to zone error log - } } } } @@ -4562,12 +3956,10 @@ void Bot::LoadAndSpawnAllZonedBots(Client* botOwner) { // Returns TRUE if there is atleast 1 bot in the specified group bool Bot::GroupHasBot(Group* group) { bool Result = false; - if(group) { for(int Counter = 0; Counter < MAX_GROUP_MEMBERS; Counter++) { - if (group->members[Counter] == nullptr) { + if (group->members[Counter] == nullptr) continue; - } if(group->members[Counter]->IsBot()) { Result = true; @@ -4575,18 +3967,15 @@ bool Bot::GroupHasBot(Group* group) { } } } - return Result; } std::list Bot::GetBotList(uint32 botOwnerCharacterID, std::string* errorMessage) { std::list ownersBots; - if(botOwnerCharacterID == 0) return ownersBots; - std::string query = StringFormat("SELECT BotID, Name, Class, BotLevel, Race " - "FROM bots WHERE BotOwnerCharacterID = '%u'", botOwnerCharacterID); + std::string query = StringFormat("SELECT BotID, Name, Class, BotLevel, Race FROM bots WHERE BotOwnerCharacterID = '%u'", botOwnerCharacterID); auto results = database.QueryDatabase(query); if(!results.Success()) { *errorMessage = std::string(results.ErrorMessage()); @@ -4600,20 +3989,17 @@ std::list Bot::GetBotList(uint32 botOwnerCharacterID, std::st availableBot.BotClass = atoi(row[2]); availableBot.BotLevel = atoi(row[3]); availableBot.BotRace = atoi(row[4]); - ownersBots.push_back(availableBot); } - return ownersBots; } std::list Bot::ListSpawnedBots(uint32 characterID, std::string* errorMessage) { std::list spawnedBots; - if(characterID == 0) return spawnedBots; - std::string query = StringFormat("SELECT bot_name, zone_name FROM botleader WHERE leaderid=%i", characterID); + std::string query = StringFormat("SELECT bot_name, zone_name FROM botleader WHERE leaderid = %i", characterID); auto results = database.QueryDatabase(query); if(!results.Success()) { *errorMessage = std::string(results.ErrorMessage()); @@ -4625,7 +4011,6 @@ std::list Bot::ListSpawnedBots(uint32 characterID, std::string* spawnedBotsList.BotLeaderCharID = characterID; strcpy(spawnedBotsList.BotName, row[0]); strcpy(spawnedBotsList.ZoneName, row[1]); - spawnedBots.push_back(spawnedBotsList); } @@ -4637,15 +4022,12 @@ void Bot::SaveBotGroup(Group* botGroup, std::string botGroupName, std::string* e return; Mob* tempGroupLeader = botGroup->GetLeader(); - if(!tempGroupLeader->IsBot()) return; uint32 botGroupId = 0; uint32 botGroupLeaderBotId = tempGroupLeader->CastToBot()->GetBotID(); - - std::string query = StringFormat("INSERT INTO botgroup (BotGroupLeaderBotId, BotGroupName) " - "VALUES (%u, '%s')", botGroupLeaderBotId, botGroupName.c_str()); + std::string query = StringFormat("INSERT INTO botgroup (BotGroupLeaderBotId, BotGroupName) VALUES (%u, '%s')", botGroupLeaderBotId, botGroupName.c_str()); auto results = database.QueryDatabase(query); if(!results.Success()) { *errorMessage = std::string(results.ErrorMessage()); @@ -4657,14 +4039,11 @@ void Bot::SaveBotGroup(Group* botGroup, std::string botGroupName, std::string* e for(int groupMemberIndex = 0; groupMemberIndex < botGroup->GroupCount(); groupMemberIndex++) { Mob* tempBot = botGroup->members[groupMemberIndex]; - if(!tempBot || !tempBot->IsBot()) continue; uint32 botGroupMemberBotId = tempBot->CastToBot()->GetBotID(); - - query = StringFormat("INSERT INTO botgroupmembers (BotGroupId, BotId) " - "VALUES (%u, %u)", botGroupId, botGroupMemberBotId); + query = StringFormat("INSERT INTO botgroupmembers (BotGroupId, BotId) VALUES (%u, %u)", botGroupId, botGroupMemberBotId); results = database.QueryDatabase(query); if(!results.Success()) *errorMessage = std::string(results.ErrorMessage()); @@ -4673,12 +4052,10 @@ void Bot::SaveBotGroup(Group* botGroup, std::string botGroupName, std::string* e } void Bot::DeleteBotGroup(std::string botGroupName, std::string* errorMessage) { - if(botGroupName.empty()) return; uint32 botGroupId = GetBotGroupIdByBotGroupName(botGroupName, errorMessage); - if(!errorMessage->empty() || botGroupId== 0) return; @@ -4697,12 +4074,10 @@ void Bot::DeleteBotGroup(std::string botGroupName, std::string* errorMessage) { std::list Bot::LoadBotGroup(std::string botGroupName, std::string* errorMessage) { std::list botGroup; - if(botGroupName.empty()) return botGroup; uint32 botGroupId = GetBotGroupIdByBotGroupName(botGroupName, errorMessage); - if(botGroupId == 0) return botGroup; @@ -4717,21 +4092,17 @@ std::list Bot::LoadBotGroup(std::string botGroupName, std::string* err BotGroup tempBotGroup; tempBotGroup.BotGroupID = botGroupId; tempBotGroup.BotID = atoi(row[0]); - botGroup.push_back(tempBotGroup); } - return botGroup; } std::list Bot::GetBotGroupListByBotOwnerCharacterId(uint32 botOwnerCharacterId, std::string* errorMessage) { std::list botGroups; - if(botOwnerCharacterId == 0) return botGroups; - std::string query = StringFormat("SELECT BotGroupName, BotGroupLeaderName FROM vwBotGroups " - "WHERE BotOwnerCharacterId = %u", botOwnerCharacterId); + std::string query = StringFormat("SELECT BotGroupName, BotGroupLeaderName FROM vwBotGroups WHERE BotOwnerCharacterId = %u", botOwnerCharacterId); auto results = database.QueryDatabase(query); if(!results.Success()) { *errorMessage = std::string(results.ErrorMessage()); @@ -4742,20 +4113,16 @@ std::list Bot::GetBotGroupListByBotOwnerCharacterId(uint32 botOwne BotGroupList botGroupList; botGroupList.BotGroupName = std::string(row[0]); botGroupList.BotGroupLeaderName = std::string(row[1]); - botGroups.push_back(botGroupList); } - return botGroups; } bool Bot::DoesBotGroupNameExist(std::string botGroupName) { - if(botGroupName.empty()) return false; - std::string query = StringFormat("SELECT BotGroupId FROM vwBotGroups " - "WHERE BotGroupName = '%s'", botGroupName.c_str()); + std::string query = StringFormat("SELECT BotGroupId FROM vwBotGroups WHERE BotGroupName = '%s'", botGroupName.c_str()); auto results = database.QueryDatabase(query); if (!results.Success() || results.RowCount() == 0) return false; @@ -4763,7 +4130,6 @@ bool Bot::DoesBotGroupNameExist(std::string botGroupName) { for(auto row = results.begin(); row != results.end(); ++row) { uint32 tempBotGroupId = atoi(row[0]); std::string tempBotGroupName = std::string(row[1]); - if (botGroupName == tempBotGroupName && tempBotGroupId != 0) return true; } @@ -4772,12 +4138,10 @@ bool Bot::DoesBotGroupNameExist(std::string botGroupName) { } uint32 Bot::CanLoadBotGroup(uint32 botOwnerCharacterId, std::string botGroupName, std::string* errorMessage) { - if(botOwnerCharacterId == 0 || botGroupName.empty()) return 0; - std::string query = StringFormat("SELECT BotGroupId, BotGroupName FROM vwBotGroups " - "WHERE BotOwnerCharacterId = %u", botOwnerCharacterId); + std::string query = StringFormat("SELECT BotGroupId, BotGroupName FROM vwBotGroups WHERE BotOwnerCharacterId = %u", botOwnerCharacterId); auto results = database.QueryDatabase(query); if(!results.Success()) { *errorMessage = std::string(results.ErrorMessage()); @@ -4788,10 +4152,8 @@ uint32 Bot::CanLoadBotGroup(uint32 botOwnerCharacterId, std::string botGroupName return 0; for(auto row = results.begin(); row != results.end(); ++row) { - uint32 tempBotGroupId = atoi(row[0]); - std::string tempBotGroupName = std::string(row[1]); - + std::string tempBotGroupName = std::string(row[1]); if(botGroupName == tempBotGroupName) return tempBotGroupId; } @@ -4800,12 +4162,10 @@ uint32 Bot::CanLoadBotGroup(uint32 botOwnerCharacterId, std::string botGroupName } uint32 Bot::GetBotGroupIdByBotGroupName(std::string botGroupName, std::string* errorMessage) { - if(botGroupName.empty()) return 0; - std::string query = StringFormat("SELECT BotGroupId FROM vwBotGroups " - "WHERE BotGroupName = '%s'", botGroupName.c_str()); + std::string query = StringFormat("SELECT BotGroupId FROM vwBotGroups WHERE BotGroupName = '%s'", botGroupName.c_str()); auto results = database.QueryDatabase(query); if(!results.Success()) { *errorMessage = std::string(results.ErrorMessage()); @@ -4820,7 +4180,6 @@ uint32 Bot::GetBotGroupIdByBotGroupName(std::string botGroupName, std::string* e } uint32 Bot::GetBotGroupLeaderIdByBotGroupName(std::string botGroupName) { - if(botGroupName.empty()) return 0; @@ -4834,13 +4193,10 @@ uint32 Bot::GetBotGroupLeaderIdByBotGroupName(std::string botGroupName) { } uint32 Bot::AllowedBotSpawns(uint32 botOwnerCharacterID, std::string* errorMessage) { - if(botOwnerCharacterID == 0) return 0; - std::string query = StringFormat("SELECT value FROM quest_globals " - "WHERE name = 'bot_spawn_limit' AND charid = %i", - botOwnerCharacterID); + std::string query = StringFormat("SELECT value FROM quest_globals WHERE name = 'bot_spawn_limit' AND charid = %i", botOwnerCharacterID); auto results = database.QueryDatabase(query); if (!results.Success()) { *errorMessage = std::string(results.ErrorMessage()); @@ -4856,23 +4212,18 @@ uint32 Bot::AllowedBotSpawns(uint32 botOwnerCharacterID, std::string* errorMessa uint32 Bot::SpawnedBotCount(uint32 botOwnerCharacterID, std::string* errorMessage) { uint32 Result = 0; - if(botOwnerCharacterID > 0) { std::list SpawnedBots = entity_list.GetBotsByBotOwnerCharacterID(botOwnerCharacterID); - Result = SpawnedBots.size(); } - return Result; } uint32 Bot::CreatedBotCount(uint32 botOwnerCharacterID, std::string* errorMessage) { - if(botOwnerCharacterID == 0) return 0; - std::string query = StringFormat("SELECT COUNT(BotID) FROM bots " - "WHERE BotOwnerCharacterID=%i", botOwnerCharacterID); + std::string query = StringFormat("SELECT COUNT(BotID) FROM bots WHERE BotOwnerCharacterID=%i", botOwnerCharacterID); auto results = database.QueryDatabase(query); if (!results.Success()) { *errorMessage = std::string(results.ErrorMessage()); @@ -5061,9 +4412,8 @@ void Bot::SendBotArcheryWearChange(uint8 material_slot, uint32 material, uint32 // Returns the item id that is in the bot inventory collection for the specified slot. ItemInst* Bot::GetBotItem(uint32 slotID) { ItemInst* item = m_inv.GetItem(slotID); - if(item){ + if(item) return item; - } return nullptr; } @@ -5140,190 +4490,148 @@ bool Bot::Bot_Command_Resist(int resisttype, int level) { int resistid = 0; switch(resisttype) { case 1: // Poison Cleric - if(level >= 30) { + if(level >= 30) resistid = 62; - } - else if(level >= 6) { + else if(level >= 6) resistid = 227; - } break; case 2: // Disease Cleric - if(level >= 36) { + if(level >= 36) resistid = 63; - } - else if(level >= 11) { + else if(level >= 11) resistid = 226; - } break; case 3: // Fire Cleric - if(level >= 33) { + if(level >= 33) resistid = 60; - } - else if(level >= 8) { + else if(level >= 8) resistid = 224; - } break; case 4: // Cold Cleric - if(level >= 38) { + if(level >= 38) resistid = 61; - } - else if(level >= 13) { + else if(level >= 13) resistid = 225; - } break; case 5: // Magic Cleric - if(level >= 43) { + if(level >= 43) resistid = 64; - } - else if(level >= 16) { + else if(level >= 16) resistid = 228; - } break; case 6: // Magic Enchanter - if(level >= 37) { + if(level >= 37) resistid = 64; - } - else if(level >= 17) { + else if(level >= 17) resistid = 228; - } break; case 7: // Poison Druid - if(level >= 44) { + if(level >= 44) resistid = 62; - } - else if(level >= 19) { + else if(level >= 19) resistid = 227; - } break; case 8: // Disease Druid - if(level >= 44) { + if(level >= 44) resistid = 63; - } - else if(level >= 19) { + else if(level >= 19) resistid = 226; - } break; case 9: // Fire Druid - if(level >= 20) { + if(level >= 20) resistid = 60; - } - else if(level >= 1) { + else if(level >= 1) resistid = 224; - } break; case 10: // Cold Druid - if(level >= 30) { + if(level >= 30) resistid = 61; - } - else if(level >= 9) { + else if(level >= 9) resistid = 225; - } break; case 11: // Magic Druid - if(level >= 49) { + if(level >= 49) resistid = 64; - } - else if(level >= 34) { + else if(level >= 34) resistid = 228; - } break; case 12: // Poison Shaman - if(level >= 35) { + if(level >= 35) resistid = 62; - } - else if(level >= 20) { + else if(level >= 20) resistid = 227; - } break; case 13: // Disease Shaman - if(level >= 30) { + if(level >= 30) resistid = 63; - } - else if(level >= 8) { + else if(level >= 8) resistid = 226; - } break; case 14: // Fire Shaman - if(level >= 27) { + if(level >= 27) resistid = 60; - } - else if(level >= 5) { + else if(level >= 5) resistid = 224; - } break; case 15: // Cold Shaman - if(level >= 24) { + if(level >= 24) resistid = 61; - } - else if(level >= 1) { + else if(level >= 1) resistid = 225; - } break; case 16: // Magic Shaman - if(level >= 43) { + if(level >= 43) resistid = 64; - } - else if(level >= 19) { + else if(level >= 19) resistid = 228; - } break; } if(resistid > 0) { Group* g = GetGroup(); if(g) { - for(int k=0; kmembers[k]) { + for(int k = 0; k < MAX_GROUP_MEMBERS; k++) { + if(g->members[k]) SpellOnTarget(resistid, g->members[k]); - } } return true; } } - return false; } bool Bot::RemoveBotFromGroup(Bot* bot, Group* group) { bool Result = false; - if(bot && group) { if(bot->HasGroup()) { if(!group->IsLeader(bot)) { bot->SetFollowID(0); - if(group->DelMember(bot)) database.SetGroupID(bot->GetCleanName(), 0, bot->GetBotID()); - } - else { + } else { for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { if(!group->members[i]) continue; group->members[i]->SetFollowID(0); } - group->DisbandGroup(); database.SetGroupID(bot->GetCleanName(), 0, bot->GetBotID()); } - Result = true; } } - return Result; } bool Bot::AddBotToGroup(Bot* bot, Group* group) { bool Result = false; - if(bot && group) { if(!bot->HasGroup()) { // Add bot to this group if(group->AddMember(bot)) { if(group->GetLeader()) { bot->SetFollowID(group->GetLeader()->GetID()); - // Need to send this only once when a group is formed with a bot so the client knows it is also the group leader if(group->GroupCount() == 2 && group->GetLeader()->IsClient()) { group->UpdateGroupAAs(); @@ -5331,45 +4639,35 @@ bool Bot::AddBotToGroup(Bot* bot, Group* group) { group->SendUpdate(groupActUpdate, TempLeader); } } - Result = true; } } } - return Result; } bool Bot::BotGroupCreate(std::string botGroupLeaderName) { bool Result = false; - if(!botGroupLeaderName.empty()) { Bot* botGroupLeader = entity_list.GetBotByBotName(botGroupLeaderName); - if(botGroupLeader) Result = BotGroupCreate(botGroupLeader); } - return Result; } bool Bot::BotGroupCreate(Bot* botGroupLeader) { bool Result = false; - if(botGroupLeader && !botGroupLeader->HasGroup()) { Group* newGroup = new Group(botGroupLeader); - if(newGroup) { entity_list.AddGroup(newGroup); database.SetGroupID(botGroupLeader->GetName(), newGroup->GetID(), botGroupLeader->GetBotID()); database.SetGroupLeaderName(newGroup->GetID(), botGroupLeader->GetName()); - botGroupLeader->SetFollowID(botGroupLeader->GetBotOwner()->GetID()); - Result = true; } } - return Result; } @@ -5379,64 +4677,46 @@ bool Bot::Bot_Command_CharmTarget(int charmtype, Mob *target) { if(target) { switch(charmtype) { case 1: // Enchanter - if((charmlevel >= 64) && (charmlevel <= 75)) { + if((charmlevel >= 64) && (charmlevel <= 75)) charmid = 3355; - } - else if((charmlevel >= 62) && (charmlevel <= 63)) { + else if((charmlevel >= 62) && (charmlevel <= 63)) charmid = 3347; - } - else if((charmlevel >= 60) && (charmlevel <= 61)) { + else if((charmlevel >= 60) && (charmlevel <= 61)) charmid = 1707; - } - else if((charmlevel >= 53) && (charmlevel <= 59)) { + else if((charmlevel >= 53) && (charmlevel <= 59)) charmid = 1705; - } - else if((charmlevel >= 37) && (charmlevel <= 52)) { + else if((charmlevel >= 37) && (charmlevel <= 52)) charmid = 183; - } - else if((charmlevel >= 23) && (charmlevel <= 36)) { + else if((charmlevel >= 23) && (charmlevel <= 36)) charmid = 182; - } - else if((charmlevel >= 11) && (charmlevel <= 22)) { + else if((charmlevel >= 11) && (charmlevel <= 22)) charmid = 300; - } break; case 2: // Necromancer - if((charmlevel >= 60) && (charmlevel <= 75)) { + if((charmlevel >= 60) && (charmlevel <= 75)) charmid = 1629; - } - else if((charmlevel >=47) && (charmlevel <= 59)) { + else if((charmlevel >=47) && (charmlevel <= 59)) charmid = 198; - } - else if((charmlevel >= 31) && (charmlevel <= 46)) { + else if((charmlevel >= 31) && (charmlevel <= 46)) charmid = 197; - } - else if((charmlevel >= 18) && (charmlevel <= 30)) { + else if((charmlevel >= 18) && (charmlevel <= 30)) charmid = 196; - } break; case 3: // Druid - if((charmlevel >= 63) && (charmlevel <= 75)) { + if((charmlevel >= 63) && (charmlevel <= 75)) charmid = 3445; - } - else if((charmlevel >= 55) && (charmlevel <= 62)) { + else if((charmlevel >= 55) && (charmlevel <= 62)) charmid = 1556; - } - else if((charmlevel >= 52) && (charmlevel <= 54)) { + else if((charmlevel >= 52) && (charmlevel <= 54)) charmid = 1553; - } - else if((charmlevel >= 43) && (charmlevel <= 51)) { + else if((charmlevel >= 43) && (charmlevel <= 51)) charmid = 142; - } - else if((charmlevel >= 33) && (charmlevel <= 42)) { + else if((charmlevel >= 33) && (charmlevel <= 42)) charmid = 141; - } - else if((charmlevel >= 23) && (charmlevel <= 32)) { + else if((charmlevel >= 23) && (charmlevel <= 32)) charmid = 260; - } - else if((charmlevel >= 13) && (charmlevel <= 22)) { + else if((charmlevel >= 13) && (charmlevel <= 22)) charmid = 242; - } break; } if(charmid > 0) { @@ -5455,28 +4735,22 @@ bool Bot::Bot_Command_DireTarget(int diretype, Mob *target) { if(target) { switch(diretype) { case 1: // Enchanter - if(direlevel >= 65) { + if(direlevel >= 65) direid = 5874; - } - else if(direlevel >= 55) { + else if(direlevel >= 55) direid = 2761; - } break; case 2: // Necromancer - if(direlevel >= 65) { + if(direlevel >= 65) direid = 5876; - } - else if(direlevel >= 55) { + else if(direlevel >= 55) direid = 2759; - } break; case 3: // Druid - if(direlevel >= 65) { + if(direlevel >= 65) direid = 5875; - } - else if(direlevel >= 55) { + else if(direlevel >= 55) direid = 2760; - } break; } if(direid > 0) { @@ -5493,24 +4767,18 @@ bool Bot::Bot_Command_CalmTarget(Mob *target) { if(target) { int calmid = 0; int calmlevel = GetLevel(); - if((calmlevel >= 67) && (calmlevel <= 75)) { + if((calmlevel >= 67) && (calmlevel <= 75)) calmid = 5274; - } - else if((calmlevel >= 62) && (calmlevel <= 66)) { + else if((calmlevel >= 62) && (calmlevel <= 66)) calmid = 3197; - } - else if((calmlevel >= 35) && (calmlevel <= 61)) { + else if((calmlevel >= 35) && (calmlevel <= 61)) calmid = 45; - } - else if((calmlevel >= 18) && (calmlevel <= 34)) { + else if((calmlevel >= 18) && (calmlevel <= 34)) calmid = 47; - } - else if((calmlevel >= 6) && (calmlevel <= 17)) { + else if((calmlevel >= 6) && (calmlevel <= 17)) calmid = 501; - } - else if((calmlevel >= 1) && (calmlevel <= 5)) { + else if((calmlevel >= 1) && (calmlevel <= 5)) calmid = 208; - } if(calmid > 0) { uint32 DontRootMeBeforeTime = 0; CastSpell(calmid, target->GetID(), 1, -1, -1, &DontRootMeBeforeTime); @@ -5525,30 +4793,22 @@ bool Bot::Bot_Command_RezzTarget(Mob *target) { if(target) { int rezid = 0; int rezlevel = GetLevel(); - if(rezlevel >= 56) { + if(rezlevel >= 56) rezid = 1524; - } - else if(rezlevel >= 47) { + else if(rezlevel >= 47) rezid = 392; - } - else if(rezlevel >= 42) { + else if(rezlevel >= 42) rezid = 2172; - } - else if(rezlevel >= 37) { + else if(rezlevel >= 37) rezid = 388; - } - else if(rezlevel >= 32) { + else if(rezlevel >= 32) rezid = 2171; - } - else if(rezlevel >= 27) { + else if(rezlevel >= 27) rezid = 391; - } - else if(rezlevel >= 22) { + else if(rezlevel >= 22) rezid = 2170; - } - else if(rezlevel >= 18) { + else if(rezlevel >= 18) rezid = 2169; - } if(rezid > 0) { uint32 DontRootMeBeforeTime = 0; CastSpell(rezid, target->GetID(), 1, -1, -1, &DontRootMeBeforeTime); @@ -5563,58 +4823,45 @@ bool Bot::Bot_Command_Cure(int curetype, int level) { int cureid = 0; switch(curetype) { case 1: // Poison - if(level >= 58) { + if(level >= 58) cureid = 1525; - } - else if(level >= 48) { + else if(level >= 48) cureid = 97; - } - else if(level >= 22) { + else if(level >= 22) cureid = 95; - } - else if(level >= 1) { + else if(level >= 1) cureid = 203; - } break; case 2: // Disease - if(level >= 51) { + if(level >= 51) cureid = 3693; - } - else if(level >= 28) { + else if(level >= 28) cureid = 96; - } - else if(level >= 4) { + else if(level >= 4) cureid = 213; - } break; case 3: // Curse - if(level >= 54) { + if(level >= 54) cureid = 2880; - } - else if(level >= 38) { + else if(level >= 38) cureid = 2946; - } - else if(level >= 23) { + else if(level >= 23) cureid = 4057; - } - else if(level >= 8) { + else if(level >= 8) cureid = 4056; - } break; case 4: // Blindness - if(level >= 3) { + if(level >= 3) cureid = 212; - } break; } if(cureid > 0) { Group* g = GetGroup(); if(g) { - for(int k=0; kmembers[k]) { + for(int k = 0; k < MAX_GROUP_MEMBERS; k++) { + if(g->members[k]) SpellOnTarget(cureid, g->members[k]); - } } return true; } @@ -5868,40 +5115,35 @@ bool Bot::Death(Mob *killerMob, int32 damage, uint16 spell_id, SkillUseTypes att return false; Save(); - Mob *give_exp = hate_list.GetDamageTopOnHateList(this); Client *give_exp_client = nullptr; - if(give_exp && give_exp->IsClient()) give_exp_client = give_exp->CastToClient(); bool IsLdonTreasure = (this->GetClass() == LDON_TREASURE); - if(entity_list.GetCorpseByID(GetID())) entity_list.GetCorpseByID(GetID())->Depop(); Group *g = GetGroup(); if(g) { - for(int i=0; imembers[i]) { if(g->members[i] == this) { // If the leader dies, make the next bot the leader // and reset all bots followid if(g->IsLeader(g->members[i])) { - if(g->members[i+1]) { - g->SetLeader(g->members[i+1]); - g->members[i+1]->SetFollowID(g->members[i]->GetFollowID()); - for(int j=0; jmembers[j] && (g->members[j] != g->members[i+1])) { - g->members[j]->SetFollowID(g->members[i+1]->GetID()); - } + if(g->members[i + 1]) { + g->SetLeader(g->members[i + 1]); + g->members[i + 1]->SetFollowID(g->members[i]->GetFollowID()); + for(int j = 0; j < MAX_GROUP_MEMBERS; j++) { + if(g->members[j] && (g->members[j] != g->members[i + 1])) + g->members[j]->SetFollowID(g->members[i + 1]->GetID()); } } } // delete from group data RemoveBotFromGroup(this, g); - //Make sure group still exists if it doesnt they were already updated in RemoveBotFromGroup g = GetGroup(); if (!g) @@ -5909,8 +5151,8 @@ bool Bot::Death(Mob *killerMob, int32 damage, uint16 spell_id, SkillUseTypes att // if group members exist below this one, move // them all up one slot in the group list - int j = i+1; - for(; jmembers[j]) { g->members[j-1] = g->members[j]; strcpy(g->membername[j-1], g->members[j]->GetCleanName()); @@ -5926,55 +5168,26 @@ bool Bot::Death(Mob *killerMob, int32 damage, uint16 spell_id, SkillUseTypes att gu->action = groupActLeave; strcpy(gu->membername, GetCleanName()); if(g) { - for(int k=0; kmembers[k] && g->members[k]->IsClient()) g->members[k]->CastToClient()->QueuePacket(outapp); } } safe_delete(outapp); - - // now that's done, lets see if all we have left is the client - // and we can clean up the clients raid group and group - /*if(GetBotRaidID()) { - BotRaids* br = entity_list.GetBotRaidByMob(this); - if(br) { - if(this == br->botmaintank) { - br->botmaintank = nullptr; - } - if(this == br->botsecondtank) { - br->botsecondtank = nullptr; - } - } - if(g->GroupCount() == 0) { - uint32 gid = g->GetID(); - if(br) { - br->RemoveEmptyBotGroup(); - } - entity_list.RemoveGroup(gid); - } - if(br && (br->RaidBotGroupsCount() == 1)) { - br->RemoveClientGroup(br->GetRaidBotLeader()); - } - if(br && (br->RaidBotGroupsCount() == 0)) { - br->DisbandBotRaid(); - } - }*/ } } } } - if(GetInHealRotation()) { + if(GetInHealRotation()) GetHealRotationLeader()->RemoveHealRotationMember(this); - } entity_list.RemoveBot(this->GetID()); - return true; } void Bot::Damage(Mob *from, int32 damage, uint16 spell_id, SkillUseTypes attack_skill, bool avoidable, int8 buffslot, bool iBuffTic) { - if(spell_id==0) + if(spell_id == 0) spell_id = SPELL_UNKNOWN; //handle EVENT_ATTACK. Resets after we have not been attacked for 12 seconds @@ -5982,13 +5195,8 @@ void Bot::Damage(Mob *from, int32 damage, uint16 spell_id, SkillUseTypes attack_ Log.Out(Logs::Detail, Logs::Combat, "Triggering EVENT_ATTACK due to attack by %s", from->GetName()); parse->EventNPC(EVENT_ATTACK, this, from, "", 0); } - + attacked_timer.Start(CombatEventTimer_expire); - - // TODO: A bot doesnt call this, right? - /*if (!IsEngaged()) - zone->AddAggroMob();*/ - // if spell is lifetap add hp to the caster if (spell_id != SPELL_UNKNOWN && IsLifetapSpell(spell_id)) { int healed = GetActSpellHealing(spell_id, damage); @@ -5998,7 +5206,6 @@ void Bot::Damage(Mob *from, int32 damage, uint16 spell_id, SkillUseTypes attack_ } CommonDamage(from, damage, spell_id, attack_skill, avoidable, buffslot, iBuffTic); - if(GetHP() < 0) { if(IsCasting()) InterruptSpell(); @@ -6006,35 +5213,26 @@ void Bot::Damage(Mob *from, int32 damage, uint16 spell_id, SkillUseTypes attack_ } SendHPUpdate(); - - if(this == from) { + if(this == from) return; - } // Aggro the bot's group members - if(IsGrouped()) - { + if(IsGrouped()) { Group *g = GetGroup(); - if(g) - { - for(int i=0; imembers[i] && g->members[i]->IsBot() && from && !g->members[i]->CheckAggro(from) && g->members[i]->IsAttackAllowed(from)) - { g->members[i]->AddToHateList(from, 1); - } } } } } -void Bot::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, bool iYellForHelp /*= true*/, bool bFrenzy /*= false*/, bool iBuffTic /*= false*/) -{ +void Bot::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, bool iYellForHelp /*= true*/, bool bFrenzy /*= false*/, bool iBuffTic /*= false*/) { Mob::AddToHateList(other, hate, damage, iYellForHelp, bFrenzy, iBuffTic); } -bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts) -{ +bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts) { if (!other) { SetTarget(nullptr); Log.Out(Logs::General, Logs::Error, "A null Mob object was passed to Bot::Attack for evaluation!"); @@ -6045,15 +5243,10 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b SetTarget(other); Log.Out(Logs::Detail, Logs::Combat, "Attacking %s with hand %d %s", other?other->GetCleanName():"(nullptr)", Hand, FromRiposte?"(this is a riposte)":""); - - if ((IsCasting() && (GetClass() != BARD) && !IsFromSpell) || - other == nullptr || - (GetHP() < 0) || - (GetAppearance() == eaDead) || - (!IsAttackAllowed(other))) - { + if ((IsCasting() && (GetClass() != BARD) && !IsFromSpell) || other == nullptr || (GetHP() < 0) || (GetAppearance() == eaDead) || (!IsAttackAllowed(other))) { if(this->GetOwnerID()) entity_list.MessageClose(this, 1, 200, 10, "%s says, '%s is not a legal target master.'", this->GetCleanName(), this->GetTarget()->GetCleanName()); + if(other) { RemoveFromHateList(other); Log.Out(Logs::Detail, Logs::Combat, "I am not allowed to attack %s", other->GetCleanName()); @@ -6065,22 +5258,14 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b Log.Out(Logs::Detail, Logs::Combat, "Attack canceled, Divine Aura is in effect."); return false; } - - // TODO: Uncomment this block after solved the bug that is assigning a null value to GetTarget() for bots while in combat. Appears to happen at random, but frequently. - /*if(HasGroup() && _previousTarget != GetTarget()) { - std::ostringstream attackMessage; - attackMessage << "Attacking " << other->GetCleanName() << "."; - - GetGroup()->GroupMessage(this, 0, 100, attackMessage.str().c_str()); - }*/ - + FaceTarget(GetTarget()); - ItemInst* weapon = nullptr; if(Hand == MainPrimary) { weapon = GetBotItem(MainPrimary); OffHandAtk(false); } + if(Hand == MainSecondary) { weapon = GetBotItem(MainSecondary); OffHandAtk(true); @@ -6089,46 +5274,46 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b if(weapon != nullptr) { if (!weapon->IsWeapon()) { Log.Out(Logs::Detail, Logs::Combat, "Attack canceled, Item %s (%d) is not a weapon.", weapon->GetItem()->Name, weapon->GetID()); - return(false); + return false; } Log.Out(Logs::Detail, Logs::Combat, "Attacking with weapon: %s (%d)", weapon->GetItem()->Name, weapon->GetID()); - } else { - Log.Out(Logs::Detail, Logs::Combat, "Attacking without a weapon."); } + else + Log.Out(Logs::Detail, Logs::Combat, "Attacking without a weapon."); // calculate attack_skill and skillinuse depending on hand and weapon // also send Packet to near clients SkillUseTypes skillinuse; AttackAnimation(skillinuse, Hand, weapon); Log.Out(Logs::Detail, Logs::Combat, "Attacking with %s in slot %d using skill %d", weapon?weapon->GetItem()->Name:"Fist", Hand, skillinuse); - /// Now figure out damage int damage = 0; uint8 mylevel = GetLevel() ? GetLevel() : 1; uint32 hate = 0; - if (weapon) hate = weapon->GetItem()->Damage + weapon->GetItem()->ElemDmgAmt; + if (weapon) + hate = (weapon->GetItem()->Damage + weapon->GetItem()->ElemDmgAmt); + int weapon_damage = GetWeaponDamage(other, weapon, &hate); - if (hate == 0 && weapon_damage > 1) hate = weapon_damage; + if (hate == 0 && weapon_damage > 1) + hate = weapon_damage; //if weapon damage > 0 then we know we can hit the target with this weapon //otherwise we cannot and we set the damage to -5 later on - if(weapon_damage > 0){ - + if(weapon_damage > 0) { //Berserker Berserk damage bonus if(berserk && (GetClass() == BERSERKER)){ - int bonus = 3 + GetLevel()/10; //unverified - weapon_damage = weapon_damage * (100+bonus) / 100; + int bonus = (3 + GetLevel() / 10); //unverified + weapon_damage = (weapon_damage * (100 + bonus) / 100); Log.Out(Logs::Detail, Logs::Combat, "Berserker damage bonus increases DMG to %d", weapon_damage); } //try a finishing blow.. if successful end the attack - if(TryFinishingBlow(other, skillinuse)) { - return (true); - } + if(TryFinishingBlow(other, skillinuse)) + return true; //damage formula needs some work int min_hit = 1; - int max_hit = (2*weapon_damage*GetDamageTable(skillinuse)) / 100; + int max_hit = ((2 * weapon_damage * GetDamageTable(skillinuse)) / 100); if(GetLevel() < 10 && max_hit > RuleI(Combat, HitCapPre10)) max_hit = (RuleI(Combat, HitCapPre10)); @@ -6146,16 +5331,11 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b // // This is not recommended for normal usage, as the damage bonus represents a non-trivial component of the DPS output // of weapons wielded by higher-level melee characters (especially for two-handed weapons). - int ucDamageBonus = 0; - - if( Hand == MainPrimary && GetLevel() >= 28 && IsWarriorClass() ) - { + if(Hand == MainPrimary && GetLevel() >= 28 && IsWarriorClass()) { // Damage bonuses apply only to hits from the main hand (Hand == MainPrimary) by characters level 28 and above // who belong to a melee class. If we're here, then all of these conditions apply. - - ucDamageBonus = GetWeaponDamageBonus( weapon ? weapon->GetItem() : (const Item_Struct*) nullptr ); - + ucDamageBonus = GetWeaponDamageBonus(weapon ? weapon->GetItem() : (const Item_Struct*) nullptr); min_hit += (int) ucDamageBonus; max_hit += (int) ucDamageBonus; hate += ucDamageBonus; @@ -6164,16 +5344,14 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b //Live AA - Sinister Strikes *Adds weapon damage bonus to offhand weapon. if (Hand==MainSecondary) { if (aabonuses.SecondaryDmgInc || itembonuses.SecondaryDmgInc || spellbonuses.SecondaryDmgInc){ - - ucDamageBonus = GetWeaponDamageBonus( weapon ? weapon->GetItem() : (const Item_Struct*) nullptr ); - + ucDamageBonus = GetWeaponDamageBonus(weapon ? weapon->GetItem() : (const Item_Struct*) nullptr); min_hit += (int) ucDamageBonus; max_hit += (int) ucDamageBonus; hate += ucDamageBonus; } } - min_hit = min_hit * GetMeleeMinDamageMod_SE(skillinuse) / 100; + min_hit = (min_hit * GetMeleeMinDamageMod_SE(skillinuse) / 100); if(max_hit < min_hit) max_hit = min_hit; @@ -6203,7 +5381,7 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b other->MeleeMitigation(this, damage, min_hit, opts); if(damage > 0) { ApplyMeleeDamageBonus(skillinuse, damage); - damage += (itembonuses.HeroicSTR / 10) + (damage * other->GetSkillDmgTaken(skillinuse) / 100) + GetSkillDmgAmt(skillinuse); + damage += ((itembonuses.HeroicSTR / 10) + (damage * other->GetSkillDmgTaken(skillinuse) / 100) + GetSkillDmgAmt(skillinuse)); TryCriticalHit(other, skillinuse, damage, opts); Log.Out(Logs::Detail, Logs::Combat, "Generating hate %d towards %s", hate, GetCleanName()); // now add done damage to the hate list @@ -6217,12 +5395,13 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b //riposte bool slippery_attack = false; // Part of hack to allow riposte to become a miss, but still allow a Strikethrough chance (like on Live) if (damage == -3) { - if (FromRiposte) return false; + if (FromRiposte) + return false; else { if (Hand == MainSecondary) {// Do we even have it & was attack with mainhand? If not, don't bother with other calculations //Live AA - SlipperyAttacks //This spell effect most likely directly modifies the actual riposte chance when using offhand attack. - int32 OffhandRiposteFail = aabonuses.OffhandRiposteFail + itembonuses.OffhandRiposteFail + spellbonuses.OffhandRiposteFail; + int32 OffhandRiposteFail = (aabonuses.OffhandRiposteFail + itembonuses.OffhandRiposteFail + spellbonuses.OffhandRiposteFail); OffhandRiposteFail *= -1; //Live uses a negative value for this. if (OffhandRiposteFail && @@ -6231,17 +5410,18 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b slippery_attack = true; } else DoRiposte(other); - if (GetHP() < 0) return false; + if (GetHP() < 0) + return false; } else DoRiposte(other); - if (GetHP() < 0) return false; + if (GetHP() < 0) + return false; } } if (((damage < 0) || slippery_attack) && !FromRiposte && !IsStrikethrough) { // Hack to still allow Strikethrough chance w/ Slippery Attacks AA - int32 bonusStrikeThrough = itembonuses.StrikeThrough + spellbonuses.StrikeThrough + aabonuses.StrikeThrough; - + int32 bonusStrikeThrough = (itembonuses.StrikeThrough + spellbonuses.StrikeThrough + aabonuses.StrikeThrough); if(bonusStrikeThrough && (zone->random.Int(0, 100) < bonusStrikeThrough)) { Message_StringID(MT_StrikeThrough, STRIKETHROUGH_STRING); // You strike through your opponents defenses! Attack(other, Hand, false, true); // Strikethrough only gives another attempted hit @@ -6249,22 +5429,21 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b } } } - else{ + else damage = -5; - } // Hate Generation is on a per swing basis, regardless of a hit, miss, or block, its always the same. // If we are this far, this means we are atleast making a swing. - if (!FromRiposte) {// Ripostes never generate any aggro. + if (!FromRiposte) other->AddToHateList(this, hate); - } /////////////////////////////////////////////////////////// ////// Send Attack Damage /////////////////////////////////////////////////////////// other->Damage(this, damage, SPELL_UNKNOWN, skillinuse); - if (GetHP() < 0) return false; + if (GetHP() < 0) + return false; MeleeLifeTap(damage); @@ -6278,12 +5457,14 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b BuffFadeByEffect(SE_Invisibility2); invisible = false; } + if(invisible_undead) { Log.Out(Logs::Detail, Logs::Combat, "Removing invisibility vs. undead due to melee attack."); BuffFadeByEffect(SE_InvisVsUndead); BuffFadeByEffect(SE_InvisVsUndead2); invisible_undead = false; } + if(invisible_animals){ Log.Out(Logs::Detail, Logs::Combat, "Removing invisibility vs. animals due to melee attack."); BuffFadeByEffect(SE_InvisVsAnimals); @@ -6310,15 +5491,12 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b if (damage > 0) return true; - else return false; } -int32 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint16 spell_id) -{ +int32 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint16 spell_id) { const SPDat_Spell_Struct &spell = spells[spell_id]; - int32 value = 0; int lvlModifier = 100; int spell_level = 0; @@ -6329,116 +5507,94 @@ int32 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint16 spell_id) int32 base1 = 0; int32 base2 = 0; uint32 slot = 0; - bool LimitFound = false; int FocusCount = 0; - std::map >::const_iterator find_iter = aa_effects.find(aa_ID); if(find_iter == aa_effects.end()) - { return 0; - } - - for (std::map::const_iterator iter = aa_effects[aa_ID].begin(); iter != aa_effects[aa_ID].end(); ++iter) - { + + for (std::map::const_iterator iter = aa_effects[aa_ID].begin(); iter != aa_effects[aa_ID].end(); ++iter) { effect = iter->second.skill_id; base1 = iter->second.base1; base2 = iter->second.base2; slot = iter->second.slot; - //AA Foci's can contain multiple focus effects within the same AA. //To handle this we will not automatically return zero if a limit is found. //Instead if limit is found and multiple effects, we will reset the limit check //when the next valid focus effect is found. - if (IsFocusEffect(0, 0, true,effect) || (effect == SE_TriggerOnCast)){ + if (IsFocusEffect(0, 0, true,effect) || (effect == SE_TriggerOnCast)) { FocusCount++; //If limit found on prior check next, else end loop. - if (FocusCount > 1){ - if (LimitFound){ + if (FocusCount > 1) { + if (LimitFound) { value = 0; LimitFound = false; } - - else{ + else break; - } } } - switch (effect) - { + switch (effect) { case SE_Blank: break; - - //Handle Focus Limits case SE_LimitResist: - if(base1) - { + if(base1) { if(spell.resisttype != base1) LimitFound = true; } - break; + break; case SE_LimitInstant: if(spell.buffduration) LimitFound = true; - break; + break; case SE_LimitMaxLevel: - spell_level = spell.classes[(GetClass()%16) - 1]; + spell_level = spell.classes[(GetClass() % 16) - 1]; lvldiff = spell_level - base1; //every level over cap reduces the effect by base2 percent unless from a clicky when ItemCastsUseFocus is true - if(lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) || RuleB(Character, ItemCastsUseFocus) == false)) - { - if(base2 > 0) - { - lvlModifier -= base2*lvldiff; + if(lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) || RuleB(Character, ItemCastsUseFocus) == false)) { + if(base2 > 0) { + lvlModifier -= (base2 * lvldiff); if(lvlModifier < 1) LimitFound = true; } - else { + else LimitFound = true; - } } - break; + break; case SE_LimitMinLevel: - if((spell.classes[(GetClass()%16) - 1]) < base1) + if((spell.classes[(GetClass() % 16) - 1]) < base1) LimitFound = true; - break; + break; case SE_LimitCastTimeMin: if (spell.cast_time < base1) LimitFound = true; - break; + break; case SE_LimitSpell: - // Exclude spell(any but this) if(base1 < 0) { if (spell_id == (base1*-1)) LimitFound = true; - } - else { - // Include Spell(only this) + } else { if (spell_id != base1) LimitFound = true; } - break; + break; case SE_LimitMinDur: if (base1 > CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) LimitFound = true; - break; + break; case SE_LimitEffect: - // Exclude effect(any but this) if(base1 < 0) { if(IsEffectInSpell(spell_id,(base1*-1))) LimitFound = true; - } - else { - // Include effect(only this) + } else { if(!IsEffectInSpell(spell_id,base1)) LimitFound = true; } - break; + break; case SE_LimitSpellType: - switch(base1) - { + switch(base1) { case 0: if (!IsDetrimentalSpell(spell_id)) LimitFound = true; @@ -6448,130 +5604,93 @@ int32 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint16 spell_id) LimitFound = true; break; } - break; + break; case SE_LimitManaMin: if(spell.mana < base1) LimitFound = true; - break; - + break; case SE_LimitTarget: - // Exclude - if(base1 < 0){ - if(-base1 == spell.targettype) - LimitFound = true; - } - // Include - else { - if(base1 != spell.targettype) - LimitFound = true; - } - break; - + if(base1 < 0) { + if(-base1 == spell.targettype) + LimitFound = true; + } else { + if(base1 != spell.targettype) + LimitFound = true; + } + break; case SE_LimitCombatSkills: - // 1 is for disciplines only - if(base1 == 1 && !IsDiscipline(spell_id)) - LimitFound = true; - // 0 is spells only - else if(base1 == 0 && IsDiscipline(spell_id)) + if((base1 == 1 && !IsDiscipline(spell_id)) || (base1 == 0 && IsDiscipline(spell_id))) LimitFound = true; break; - case SE_LimitSpellGroup: - if(base1 > 0 && base1 != spell.spellgroup) + if((base1 > 0 && base1 != spell.spellgroup) || (base1 < 0 && base1 == spell.spellgroup)) LimitFound = true; - else if(base1 < 0 && base1 == spell.spellgroup) - LimitFound = true; - break; - - + break; case SE_LimitCastingSkill: LimitSpellSkill = true; if(base1 == spell.skill) SpellSkill_Found = true; - break; - + break; case SE_LimitClass: //Do not use this limit more then once per spell. If multiple class, treat value like items would. - if (!PassLimitClass(base1, GetClass())) - LimitFound = true; - break; - - + if (!PassLimitClass(base1, GetClass())) + LimitFound = true; + break; //Handle Focus Effects case SE_ImprovedDamage: if (type == focusImprovedDamage && base1 > value) value = base1; - break; - + break; case SE_ImprovedHeal: if (type == focusImprovedHeal && base1 > value) value = base1; - break; - + break; case SE_ReduceManaCost: - if (type == focusManaCost ) + if (type == focusManaCost) value = base1; - break; - + break; case SE_IncreaseSpellHaste: if (type == focusSpellHaste && base1 > value) value = base1; break; - case SE_IncreaseSpellDuration: if (type == focusSpellDuration && base1 > value) value = base1; break; - case SE_SpellDurationIncByTic: if (type == focusSpellDurByTic && base1 > value) value = base1; break; - case SE_SwarmPetDuration: if (type == focusSwarmPetDuration && base1 > value) value = base1; break; - case SE_IncreaseRange: if (type == focusRange && base1 > value) value = base1; break; - case SE_ReduceReagentCost: if (type == focusReagentCost && base1 > value) value = base1; break; - case SE_PetPowerIncrease: if (type == focusPetPower && base1 > value) value = base1; break; - case SE_SpellResistReduction: if (type == focusResistRate && base1 > value) value = base1; break; - case SE_SpellHateMod: - if (type == focusSpellHateMod) - { - if(value != 0) - { - if(value > 0) - { + if (type == focusSpellHateMod) { + if(value != 0) { + if(value > 0) { if(base1 > value) - { value = base1; - } - } - else - { + } else { if(base1 < value) - { value = base1; - } } } else @@ -6579,158 +5698,100 @@ int32 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint16 spell_id) } break; - case SE_ReduceReuseTimer: - { + case SE_ReduceReuseTimer: { if(type == focusReduceRecastTime) - value = base1 / 1000; - + value = (base1 / 1000); break; } - - case SE_TriggerOnCast: - { - if(type == focusTriggerOnCast) - { - if(zone->random.Int(0, 100) <= base1){ + case SE_TriggerOnCast: { + if(type == focusTriggerOnCast) { + if(zone->random.Int(0, 100) <= base1) value = base2; - } - - else{ + else { value = 0; LimitFound = true; } } break; } - case SE_FcSpellVulnerability: - { + case SE_FcSpellVulnerability: { if(type == focusSpellVulnerability) - { value = base1; - } break; } - case SE_BlockNextSpellFocus: - { - if(type == focusBlockNextSpell) - { + case SE_BlockNextSpellFocus: { + if(type == focusBlockNextSpell) { if(zone->random.Int(1, 100) <= base1) value = 1; } break; } - case SE_FcTwincast: - { + case SE_FcTwincast: { if(type == focusTwincast) - { value = base1; - } break; } - - /* - case SE_SympatheticProc: - { - if(type == focusSympatheticProc) - { - float ProcChance, ProcBonus; - int16 ProcRateMod = base1; //Baseline is 100 for most Sympathetic foci - int32 cast_time = GetActSpellCasttime(spell_id, spells[spell_id].cast_time); - GetSympatheticProcChances(ProcBonus, ProcChance, cast_time, ProcRateMod); - - if(zone->random.Real(0, 1) <= ProcChance) - value = focus_id; - - else - value = 0; - } - break; - } - */ - case SE_FcDamageAmt: - { + case SE_FcDamageAmt: { if(type == focusFcDamageAmt) value = base1; - break; } - - case SE_FcDamageAmtCrit: - { + case SE_FcDamageAmtCrit: { if(type == focusFcDamageAmtCrit) value = base1; - break; } - - case SE_FcDamageAmtIncoming: - { + case SE_FcDamageAmtIncoming: { if(type == focusFcDamageAmtIncoming) value = base1; - break; } - case SE_FcHealAmtIncoming: if(type == focusFcHealAmtIncoming) value = base1; break; - case SE_FcHealPctCritIncoming: if (type == focusFcHealPctCritIncoming) value = base1; break; - case SE_FcHealAmtCrit: if(type == focusFcHealAmtCrit) value = base1; break; - case SE_FcHealAmt: if(type == focusFcHealAmt) value = base1; break; - case SE_FcHealPctIncoming: if(type == focusFcHealPctIncoming) value = base1; break; - - case SE_FcBaseEffects: - { + case SE_FcBaseEffects: { if (type == focusFcBaseEffects) value = base1; - break; } - case SE_FcDamagePctCrit: - { + case SE_FcDamagePctCrit: { if(type == focusFcDamagePctCrit) value = base1; - break; } - - case SE_FcIncreaseNumHits: - { + case SE_FcIncreaseNumHits: { if(type == focusIncreaseNumHits) value = base1; - break; } //Check for spell skill limits. - if ((LimitSpellSkill) && (!SpellSkill_Found)) - return 0; - + if ((LimitSpellSkill) && (!SpellSkill_Found)) + return 0; } } - if (LimitFound){ + if (LimitFound) return 0; - } - return(value*lvlModifier/100); + return (value * lvlModifier / 100); } int32 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { @@ -6741,18 +5802,13 @@ int32 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { int32 realTotal2 = 0; int32 realTotal3 = 0; bool rand_effectiveness = false; - //Improved Healing, Damage & Mana Reduction are handled differently in that some are random percentages //In these cases we need to find the most powerful effect, so that each piece of gear wont get its own chance - if((bottype == BotfocusManaCost || bottype == BotfocusImprovedHeal || bottype == BotfocusImprovedDamage) - && RuleB(Spells, LiveLikeFocusEffects)) - { + if((bottype == BotfocusManaCost || bottype == BotfocusImprovedHeal || bottype == BotfocusImprovedDamage) && RuleB(Spells, LiveLikeFocusEffects)) rand_effectiveness = true; - } //Check if item focus effect exists for the client. - if (itembonuses.FocusEffects[bottype]){ - + if (itembonuses.FocusEffects[bottype]) { const Item_Struct* TempItem = 0; const Item_Struct* UsedItem = 0; const ItemInst* TempInst = 0; @@ -6760,35 +5816,25 @@ int32 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { int32 Total = 0; int32 focus_max = 0; int32 focus_max_real = 0; - //item focus - for(int x = EmuConstants::EQUIPMENT_BEGIN; x <= EmuConstants::EQUIPMENT_END; x++) - { + for(int x = EmuConstants::EQUIPMENT_BEGIN; x <= EmuConstants::EQUIPMENT_END; x++) { TempItem = nullptr; ItemInst* ins = GetBotItem(x); if (!ins) continue; + TempItem = ins->GetItem(); if (TempItem && TempItem->Focus.Effect > 0 && TempItem->Focus.Effect != SPELL_UNKNOWN) { if(rand_effectiveness) { focus_max = CalcBotFocusEffect(bottype, TempItem->Focus.Effect, spell_id, true); - if (focus_max > 0 && focus_max_real >= 0 && focus_max > focus_max_real) { - focus_max_real = focus_max; - UsedItem = TempItem; - UsedFocusID = TempItem->Focus.Effect; - } else if (focus_max < 0 && focus_max < focus_max_real) { + if ((focus_max > 0 && focus_max_real >= 0 && focus_max > focus_max_real) || (focus_max < 0 && focus_max < focus_max_real)) { focus_max_real = focus_max; UsedItem = TempItem; UsedFocusID = TempItem->Focus.Effect; } - } - else { + } else { Total = CalcBotFocusEffect(bottype, TempItem->Focus.Effect, spell_id); - if (Total > 0 && realTotal >= 0 && Total > realTotal) { - realTotal = Total; - UsedItem = TempItem; - UsedFocusID = TempItem->Focus.Effect; - } else if (Total < 0 && Total < realTotal) { + if ((Total > 0 && realTotal >= 0 && Total > realTotal) || (Total < 0 && Total < realTotal)) { realTotal = Total; UsedItem = TempItem; UsedFocusID = TempItem->Focus.Effect; @@ -6796,33 +5842,22 @@ int32 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { } } - for (int y = AUG_BEGIN; y < EmuConstants::ITEM_COMMON_SIZE; ++y) - { + for (int y = AUG_BEGIN; y < EmuConstants::ITEM_COMMON_SIZE; ++y) { ItemInst *aug = nullptr; aug = ins->GetAugment(y); - if(aug) - { + if(aug) { const Item_Struct* TempItemAug = aug->GetItem(); if (TempItemAug && TempItemAug->Focus.Effect > 0 && TempItemAug->Focus.Effect != SPELL_UNKNOWN) { if(rand_effectiveness) { focus_max = CalcBotFocusEffect(bottype, TempItemAug->Focus.Effect, spell_id, true); - if (focus_max > 0 && focus_max_real >= 0 && focus_max > focus_max_real) { - focus_max_real = focus_max; - UsedItem = TempItem; - UsedFocusID = TempItemAug->Focus.Effect; - } else if (focus_max < 0 && focus_max < focus_max_real) { + if ((focus_max > 0 && focus_max_real >= 0 && focus_max > focus_max_real) || (focus_max < 0 && focus_max < focus_max_real)) { focus_max_real = focus_max; UsedItem = TempItem; UsedFocusID = TempItemAug->Focus.Effect; } - } - else { + } else { Total = CalcBotFocusEffect(bottype, TempItemAug->Focus.Effect, spell_id); - if (Total > 0 && realTotal >= 0 && Total > realTotal) { - realTotal = Total; - UsedItem = TempItem; - UsedFocusID = TempItemAug->Focus.Effect; - } else if (Total < 0 && Total < realTotal) { + if ((Total > 0 && realTotal >= 0 && Total > realTotal) || (Total < 0 && Total < realTotal)) { realTotal = Total; UsedItem = TempItem; UsedFocusID = TempItemAug->Focus.Effect; @@ -6838,13 +5873,11 @@ int32 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { } //Check if spell focus effect exists for the client. - if (spellbonuses.FocusEffects[bottype]){ - + if (spellbonuses.FocusEffects[bottype]) { //Spell Focus int32 Total2 = 0; int32 focus_max2 = 0; int32 focus_max_real2 = 0; - int buff_tracker = -1; int buff_slot = 0; uint32 focusspellid = 0; @@ -6857,23 +5890,14 @@ int32 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { if(rand_effectiveness) { focus_max2 = CalcBotFocusEffect(bottype, focusspellid, spell_id, true); - if (focus_max2 > 0 && focus_max_real2 >= 0 && focus_max2 > focus_max_real2) { - focus_max_real2 = focus_max2; - buff_tracker = buff_slot; - focusspell_tracker = focusspellid; - } else if (focus_max2 < 0 && focus_max2 < focus_max_real2) { + if ((focus_max2 > 0 && focus_max_real2 >= 0 && focus_max2 > focus_max_real2) || (focus_max2 < 0 && focus_max2 < focus_max_real2)) { focus_max_real2 = focus_max2; buff_tracker = buff_slot; focusspell_tracker = focusspellid; } - } - else { + } else { Total2 = CalcBotFocusEffect(bottype, focusspellid, spell_id); - if (Total2 > 0 && realTotal2 >= 0 && Total2 > realTotal2) { - realTotal2 = Total2; - buff_tracker = buff_slot; - focusspell_tracker = focusspellid; - } else if (Total2 < 0 && Total2 < realTotal2) { + if ((Total2 > 0 && realTotal2 >= 0 && Total2 > realTotal2) || (Total2 < 0 && Total2 < realTotal2)) { realTotal2 = Total2; buff_tracker = buff_slot; focusspell_tracker = focusspellid; @@ -6885,20 +5909,17 @@ int32 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { realTotal2 = CalcBotFocusEffect(bottype, focusspell_tracker, spell_id); // For effects like gift of mana that only fire once, save the spellid into an array that consists of all available buff slots. - if(buff_tracker >= 0 && buffs[buff_tracker].numhits > 0) { + if(buff_tracker >= 0 && buffs[buff_tracker].numhits > 0) m_spellHitsLeft[buff_tracker] = focusspell_tracker; - } } // AA Focus - if (aabonuses.FocusEffects[bottype]){ - + if (aabonuses.FocusEffects[bottype]) { int totalAAs = database.CountAAs(); int32 Total3 = 0; uint32 slots = 0; uint32 aa_AA = 0; uint32 aa_value = 0; - for (int i = 0; i < totalAAs; i++) { //iterate through all of the client's AAs std::map::iterator aa = botAAs.find(i); if(aa != botAAs.end()) { // make sure aa exists or we'll crash zone @@ -6908,12 +5929,8 @@ int32 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { continue; Total3 = CalcBotAAFocus(bottype, aa_AA, spell_id); - if (Total3 > 0 && realTotal3 >= 0 && Total3 > realTotal3) { + if ((Total3 > 0 && realTotal3 >= 0 && Total3 > realTotal3) || (Total3 < 0 && Total3 < realTotal3)) realTotal3 = Total3; - } - else if (Total3 < 0 && Total3 < realTotal3) { - realTotal3 = Total3; - } } } } @@ -6921,13 +5938,10 @@ int32 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { if(bottype == BotfocusReagentCost && IsSummonPetSpell(spell_id) && GetAA(aaElementalPact)) return 100; - if(bottype == BotfocusReagentCost && (IsEffectInSpell(spell_id, SE_SummonItem) || IsSacrificeSpell(spell_id))){ + if(bottype == BotfocusReagentCost && (IsEffectInSpell(spell_id, SE_SummonItem) || IsSacrificeSpell(spell_id))) return 0; - //Summon Spells that require reagents are typically imbue type spells, enchant metal, sacrifice and shouldn't be affected - //by reagent conservation for obvious reasons. - } - return realTotal + realTotal2; + return (realTotal + realTotal2); } int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spell_id, bool best_focus) { @@ -6936,433 +5950,317 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel const SPDat_Spell_Struct &focus_spell = spells[focus_id]; const SPDat_Spell_Struct &spell = spells[spell_id]; - int32 value = 0; int lvlModifier = 100; int spell_level = 0; int lvldiff = 0; bool LimitSpellSkill = false; bool SpellSkill_Found = false; - for (int i = 0; i < EFFECT_COUNT; i++) { switch (focus_spell.effectid[i]) { - case SE_Blank: - break; - //check limits - - case SE_LimitResist:{ - if(focus_spell.base[i]){ - if(spell.resisttype != focus_spell.base[i]) - return(0); - } - break; - } - case SE_LimitInstant:{ - if(spell.buffduration) - return(0); - break; - } - - case SE_LimitMaxLevel:{ - if (IsNPC()) + case SE_Blank: break; - spell_level = spell.classes[(GetClass()%16) - 1]; - lvldiff = spell_level - focus_spell.base[i]; - //every level over cap reduces the effect by focus_spell.base2[i] percent unless from a clicky when ItemCastsUseFocus is true - if(lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) || RuleB(Character, ItemCastsUseFocus) == false)) - { - if(focus_spell.base2[i] > 0) - { - lvlModifier -= focus_spell.base2[i]*lvldiff; - if(lvlModifier < 1) + case SE_LimitResist:{ + if(focus_spell.base[i]) { + if(spell.resisttype != focus_spell.base[i]) return 0; } - else - { - return 0; - } - } - break; - } - - case SE_LimitMinLevel: - if (IsNPC()) break; - if (spell.classes[(GetClass()%16) - 1] < focus_spell.base[i]) - return(0); - break; - - case SE_LimitCastTimeMin: - if (spells[spell_id].cast_time < (uint32)focus_spell.base[i]) - return(0); - break; - - case SE_LimitSpell: - if(focus_spell.base[i] < 0) { //exclude spell - if (spell_id == (focus_spell.base[i]*-1)) - return(0); - } else { - //this makes the assumption that only one spell can be explicitly included... - if (spell_id != focus_spell.base[i]) - return(0); } - break; - - case SE_LimitMinDur: - if (focus_spell.base[i] > CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) - return(0); - break; - - case SE_LimitEffect: - if(focus_spell.base[i] < 0){ - if(IsEffectInSpell(spell_id,focus_spell.base[i])){ //we limit this effect, can't have + case SE_LimitInstant: { + if(spell.buffduration) return 0; - } + break; } - else{ - if(focus_spell.base[i] == SE_SummonPet) //summoning haste special case - { //must have one of the three pet effects to qualify - if(!IsEffectInSpell(spell_id, SE_SummonPet) && - !IsEffectInSpell(spell_id, SE_NecPet) && - !IsEffectInSpell(spell_id, SE_SummonBSTPet)) - { - return 0; + case SE_LimitMaxLevel:{ + if (IsNPC()) + break; + spell_level = spell.classes[(GetClass() % 16) - 1]; + lvldiff = (spell_level - focus_spell.base[i]); + if(lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) || RuleB(Character, ItemCastsUseFocus) == false)) { + if(focus_spell.base2[i] > 0) { + lvlModifier -= (focus_spell.base2[i] * lvldiff); + if(lvlModifier < 1) + return 0; } + else + return 0; } - else if(!IsEffectInSpell(spell_id,focus_spell.base[i])){ //we limit this effect, must have + break; + } + case SE_LimitMinLevel: + if (IsNPC()) + break; + if (spell.classes[(GetClass() % 16) - 1] < focus_spell.base[i]) return 0; + break; + + case SE_LimitCastTimeMin: + if (spells[spell_id].cast_time < (uint32)focus_spell.base[i]) + return 0; + break; + case SE_LimitSpell: + if(focus_spell.base[i] < 0) { + if (spell_id == (focus_spell.base[i] * -1)) + return 0; + } else { + if (spell_id != focus_spell.base[i]) + return 0; } - } - break; - - - case SE_LimitSpellType: - switch( focus_spell.base[i] ) - { - case 0: - if (!IsDetrimentalSpell(spell_id)) + break; + case SE_LimitMinDur: + if (focus_spell.base[i] > CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) + return 0; + break; + case SE_LimitEffect: + if(focus_spell.base[i] < 0) { + if(IsEffectInSpell(spell_id,focus_spell.base[i])) return 0; - break; - case 1: - if (!IsBeneficialSpell(spell_id)) + } else { + if(focus_spell.base[i] == SE_SummonPet) { + if(!IsEffectInSpell(spell_id, SE_SummonPet) && !IsEffectInSpell(spell_id, SE_NecPet) && !IsEffectInSpell(spell_id, SE_SummonBSTPet)) { + return 0; + } + } else if(!IsEffectInSpell(spell_id,focus_spell.base[i])) return 0; - break; - default: - Log.Out(Logs::General, Logs::Normal, "CalcFocusEffect: unknown limit spelltype %d", focus_spell.base[i]); - } - break; - - case SE_LimitManaMin: + } + break; + + + case SE_LimitSpellType: + switch(focus_spell.base[i]) { + case 0: + if (!IsDetrimentalSpell(spell_id)) + return 0; + break; + case 1: + if (!IsBeneficialSpell(spell_id)) + return 0; + break; + default: + Log.Out(Logs::General, Logs::Normal, "CalcFocusEffect: unknown limit spelltype %d", focus_spell.base[i]); + } + break; + + case SE_LimitManaMin: if(spell.mana < focus_spell.base[i]) return 0; - break; - - case SE_LimitTarget: - // Exclude - if((focus_spell.base[i] < 0) && -focus_spell.base[i] == spell.targettype) - return 0; - // Include - else if (focus_spell.base[i] > 0 && focus_spell.base[i] != spell.targettype) - return 0; - - break; - - case SE_LimitCombatSkills: - // 1 is for disciplines only + break; + case SE_LimitTarget: + if((focus_spell.base[i] < 0) && -focus_spell.base[i] == spell.targettype) + return 0; + else if (focus_spell.base[i] > 0 && focus_spell.base[i] != spell.targettype) + return 0; + break; + case SE_LimitCombatSkills: if(focus_spell.base[i] == 1 && !IsDiscipline(spell_id)) return 0; - // 0 is for spells only else if(focus_spell.base[i] == 0 && IsDiscipline(spell_id)) return 0; - break; - - case SE_LimitSpellGroup: + break; + case SE_LimitSpellGroup: if(focus_spell.base[i] > 0 && focus_spell.base[i] != spell.spellgroup) return 0; else if(focus_spell.base[i] < 0 && focus_spell.base[i] == spell.spellgroup) return 0; - break; - - case SE_LimitCastingSkill: + break; + case SE_LimitCastingSkill: LimitSpellSkill = true; if(focus_spell.base[i] == spell.skill) SpellSkill_Found = true; - break; - - case SE_LimitClass: - //Do not use this limit more then once per spell. If multiple class, treat value like items would. - if (!PassLimitClass(focus_spell.base[i], GetClass())) - return 0; - break; - - //handle effects - case SE_ImprovedDamage: - // No Spell used this, its handled by different spell effect IDs. - if (bottype == BotfocusImprovedDamage) { - // This is used to determine which focus should be used for the random calculation - if(best_focus) { - // If the spell contains a value in the base2 field then that is the max value - if (focus_spell.base2[i] != 0) { - value = focus_spell.base2[i]; - } - // If the spell does not contain a base2 value, then its a straight non random value - else { - value = focus_spell.base[i]; - } - } - // Actual focus calculation starts here - else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; - } - else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); - } - } - break; - case SE_ImprovedHeal: - if (bottype == BotfocusImprovedHeal) { - if(best_focus) { - if (focus_spell.base2[i] != 0) { - value = focus_spell.base2[i]; - } - else { - value = focus_spell.base[i]; - } - } - else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; - } - else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); - } - } - break; - case SE_ReduceManaCost: - if (bottype == BotfocusManaCost) { - if(best_focus) { - if (focus_spell.base2[i] != 0) { - value = focus_spell.base2[i]; - } - else { - value = focus_spell.base[i]; - } - } - else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; - } - else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); - } - } - break; - - case SE_IncreaseSpellHaste: - if (bottype == BotfocusSpellHaste && focus_spell.base[i] > value) - { - value = focus_spell.base[i]; - } - break; - case SE_IncreaseSpellDuration: - if (bottype == BotfocusSpellDuration && focus_spell.base[i] > value) - { - value = focus_spell.base[i]; - } - break; - case SE_SpellDurationIncByTic: - if (bottype == BotfocusSpellDurByTic && focus_spell.base[i] > value) - { - value = focus_spell.base[i]; - } - break; - case SE_SwarmPetDuration: - if (bottype == BotfocusSwarmPetDuration && focus_spell.base[i] > value) - { - value = focus_spell.base[i]; - } - break; - case SE_IncreaseRange: - if (bottype == BotfocusRange && focus_spell.base[i] > value) - { - value = focus_spell.base[i]; - } - break; - case SE_ReduceReagentCost: - if (bottype == BotfocusReagentCost && focus_spell.base[i] > value) - { - value = focus_spell.base[i]; - } - break; - case SE_PetPowerIncrease: - if (bottype == BotfocusPetPower && focus_spell.base[i] > value) - { - value = focus_spell.base[i]; - } - break; - case SE_SpellResistReduction: - if (bottype == BotfocusResistRate && focus_spell.base[i] > value) - { - value = focus_spell.base[i]; - } - break; - case SE_SpellHateMod: - if (bottype == BotfocusSpellHateMod) - { - if(value != 0) - { - if(value > 0) - { - if(focus_spell.base[i] > value) - { + break; + case SE_LimitClass: + if (!PassLimitClass(focus_spell.base[i], GetClass())) + return 0; + break; + case SE_ImprovedDamage: + if (bottype == BotfocusImprovedDamage) { + if(best_focus) { + if (focus_spell.base2[i] != 0) + value = focus_spell.base2[i]; + else value = focus_spell.base[i]; - } } + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) + value = focus_spell.base[i]; else - { - if(focus_spell.base[i] < value) - { - value = focus_spell.base[i]; - } - } + value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); } - else + break; + case SE_ImprovedHeal: + if (bottype == BotfocusImprovedHeal) { + if(best_focus) { + if (focus_spell.base2[i] != 0) + value = focus_spell.base2[i]; + else + value = focus_spell.base[i]; + } + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) + value = focus_spell.base[i]; + else + value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); + } + break; + case SE_ReduceManaCost: + if (bottype == BotfocusManaCost) { + if(best_focus) { + if (focus_spell.base2[i] != 0) + value = focus_spell.base2[i]; + else + value = focus_spell.base[i]; + } + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) + value = focus_spell.base[i]; + else + value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); + } + break; + case SE_IncreaseSpellHaste: + if (bottype == BotfocusSpellHaste && focus_spell.base[i] > value) value = focus_spell.base[i]; + break; + case SE_IncreaseSpellDuration: + if (bottype == BotfocusSpellDuration && focus_spell.base[i] > value) + value = focus_spell.base[i]; + break; + case SE_SpellDurationIncByTic: + if (bottype == BotfocusSpellDurByTic && focus_spell.base[i] > value) + value = focus_spell.base[i]; + break; + case SE_SwarmPetDuration: + if (bottype == BotfocusSwarmPetDuration && focus_spell.base[i] > value) + value = focus_spell.base[i]; + break; + case SE_IncreaseRange: + if (bottype == BotfocusRange && focus_spell.base[i] > value) + value = focus_spell.base[i]; + break; + case SE_ReduceReagentCost: + if (bottype == BotfocusReagentCost && focus_spell.base[i] > value) + value = focus_spell.base[i]; + break; + case SE_PetPowerIncrease: + if (bottype == BotfocusPetPower && focus_spell.base[i] > value) + value = focus_spell.base[i]; + break; + case SE_SpellResistReduction: + if (bottype == BotfocusResistRate && focus_spell.base[i] > value) + value = focus_spell.base[i]; + break; + case SE_SpellHateMod: + if (bottype == BotfocusSpellHateMod) { + if(value != 0) { + if(value > 0) { + if(focus_spell.base[i] > value) + value = focus_spell.base[i]; + } + else { + if(focus_spell.base[i] < value) + value = focus_spell.base[i]; + } + } else + value = focus_spell.base[i]; + } + break; + case SE_ReduceReuseTimer: { + if(bottype == BotfocusReduceRecastTime) + value = (focus_spell.base[i] / 1000); + break; } - break; - - case SE_ReduceReuseTimer: - { - if(bottype == BotfocusReduceRecastTime) - value = focus_spell.base[i] / 1000; - - break; - } - - case SE_TriggerOnCast: - { - if(bottype == BotfocusTriggerOnCast) - - if(zone->random.Int(0, 100) <= focus_spell.base[i]) - value = focus_spell.base2[i]; - - else - value = 0; - - break; - } - case SE_FcSpellVulnerability: - { - if(bottype == BotfocusSpellVulnerability) - { - value = focus_spell.base[i]; + case SE_TriggerOnCast: { + if(bottype == BotfocusTriggerOnCast) { + if(zone->random.Int(0, 100) <= focus_spell.base[i]) + value = focus_spell.base2[i]; + else + value = 0; + } + break; } - break; - } - case SE_BlockNextSpellFocus: - { - if(bottype == BotfocusBlockNextSpell) - { - if(zone->random.Int(1, 100) <= focus_spell.base[i]) - value = 1; + case SE_FcSpellVulnerability: { + if(bottype == BotfocusSpellVulnerability) + value = focus_spell.base[i]; + break; } - break; - } - case SE_FcTwincast: - { - if(bottype == BotfocusTwincast) - { - value = focus_spell.base[i]; + case SE_BlockNextSpellFocus: { + if(bottype == BotfocusBlockNextSpell) { + if(zone->random.Int(1, 100) <= focus_spell.base[i]) + value = 1; + } + break; } - break; - } - case SE_SympatheticProc: - { - if(bottype == BotfocusSympatheticProc) - { - - float ProcChance = GetSympatheticProcChances(spell_id, focus_spell.base[i]); - - if(zone->random.Real(0, 1) <= ProcChance) - value = focus_id; - - else - value = 0; + case SE_FcTwincast: { + if(bottype == BotfocusTwincast) + value = focus_spell.base[i]; + break; } - break; - } - case SE_FcDamageAmt: - { - if(bottype == BotfocusFcDamageAmt) - value = focus_spell.base[i]; - - break; - } - - case SE_FcDamageAmtCrit: - { - if(bottype == BotfocusFcDamageAmtCrit) - value = focus_spell.base[i]; - - break; - } - - case SE_FcHealAmtIncoming: - if(bottype == BotfocusFcHealAmtIncoming) - value = focus_spell.base[i]; - break; - - case SE_FcHealPctCritIncoming: - if (bottype == BotfocusFcHealPctCritIncoming) - value = focus_spell.base[i]; - break; - - case SE_FcHealAmtCrit: - if(bottype == BotfocusFcHealAmtCrit) - value = focus_spell.base[i]; - break; - - case SE_FcHealAmt: - if(bottype == BotfocusFcHealAmt) - value = focus_spell.base[i]; - break; - - case SE_FcHealPctIncoming: - if(bottype == BotfocusFcHealPctIncoming) - value = focus_spell.base[i]; - break; - - case SE_FcBaseEffects: - { - if (bottype == BotfocusFcBaseEffects) - value = focus_spell.base[i]; - - break; - } - case SE_FcDamagePctCrit: - { - if(bottype == BotfocusFcDamagePctCrit) - value = focus_spell.base[i]; - - break; - } - - case SE_FcIncreaseNumHits: - { - if(bottype == BotfocusIncreaseNumHits) - value = focus_spell.base[i]; - - break; - } - //this spits up a lot of garbage when calculating spell focuses - //since they have all kinds of extra effects on them. - default: - Log.Out(Logs::General, Logs::Spells, "CalcFocusEffect: unknown effectid %d", focus_spell.effectid[i]); + case SE_SympatheticProc: { + if(bottype == BotfocusSympatheticProc) { + float ProcChance = GetSympatheticProcChances(spell_id, focus_spell.base[i]); + if(zone->random.Real(0, 1) <= ProcChance) + value = focus_id; + else + value = 0; + } + break; + } + case SE_FcDamageAmt: { + if(bottype == BotfocusFcDamageAmt) + value = focus_spell.base[i]; + break; + } + case SE_FcDamageAmtCrit: { + if(bottype == BotfocusFcDamageAmtCrit) + value = focus_spell.base[i]; + break; + } + case SE_FcHealAmtIncoming: + if(bottype == BotfocusFcHealAmtIncoming) + value = focus_spell.base[i]; + break; + case SE_FcHealPctCritIncoming: + if (bottype == BotfocusFcHealPctCritIncoming) + value = focus_spell.base[i]; + break; + case SE_FcHealAmtCrit: + if(bottype == BotfocusFcHealAmtCrit) + value = focus_spell.base[i]; + break; + case SE_FcHealAmt: + if(bottype == BotfocusFcHealAmt) + value = focus_spell.base[i]; + break; + case SE_FcHealPctIncoming: + if(bottype == BotfocusFcHealPctIncoming) + value = focus_spell.base[i]; + break; + case SE_FcBaseEffects: { + if (bottype == BotfocusFcBaseEffects) + value = focus_spell.base[i]; + + break; + } + case SE_FcDamagePctCrit: { + if(bottype == BotfocusFcDamagePctCrit) + value = focus_spell.base[i]; + + break; + } + case SE_FcIncreaseNumHits: { + if(bottype == BotfocusIncreaseNumHits) + value = focus_spell.base[i]; + + break; + } + default: + Log.Out(Logs::General, Logs::Spells, "CalcFocusEffect: unknown effectid %d", focus_spell.effectid[i]); + break; } } //Check for spell skill limits. if ((LimitSpellSkill) && (!SpellSkill_Found)) return 0; - return(value*lvlModifier/100); + return(value * lvlModifier / 100); } //proc chance includes proc bonus @@ -7382,38 +6280,23 @@ float Bot::GetProcChances(float ProcBonus, uint16 hand) { break; } - //calculate the weapon speed in ms, so we can use the rule to compare against. - // fast as a client can swing, so should be the floor of the proc chance if (weapon_speed < RuleI(Combat, MinHastedDelay)) weapon_speed = RuleI(Combat, MinHastedDelay); if (RuleB(Combat, AdjustProcPerMinute)) { - ProcChance = (static_cast(weapon_speed) * - RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms + ProcChance = (static_cast(weapon_speed) * RuleR(Combat, AvgProcsPerMinute) / 60000.0f); ProcBonus += static_cast(mydex) * RuleR(Combat, ProcPerMinDexContrib); - ProcChance += ProcChance * ProcBonus / 100.0f; + ProcChance += (ProcChance * ProcBonus / 100.0f); } else { - ProcChance = RuleR(Combat, BaseProcChance) + - static_cast(mydex) / RuleR(Combat, ProcDexDivideBy); - ProcChance += ProcChance*ProcBonus / 100.0f; + ProcChance = (RuleR(Combat, BaseProcChance) + static_cast(mydex) / RuleR(Combat, ProcDexDivideBy)); + ProcChance += (ProcChance * ProcBonus / 100.0f); } Log.Out(Logs::Detail, Logs::Combat, "Proc chance %.2f (%.2f from bonuses)", ProcChance, ProcBonus); return ProcChance; } -bool Bot::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte) -{ - /* called when a mob is attacked, does the checks to see if it's a hit - * and does other mitigation checks. 'this' is the mob being attacked. - * - * special return values: - * -1 - block - * -2 - parry - * -3 - riposte - * -4 - dodge - * - */ +bool Bot::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte) { if(GetAppearance() == eaDead) return false; @@ -7421,76 +6304,52 @@ bool Bot::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte) float bonus = 0; float RollTable[4] = {0,0,0,0}; float roll = 0; - Mob *attacker=other; - Mob *defender=this; + Mob *attacker = other; + Mob *defender = this; - //garunteed hit bool ghit = false; if((attacker->GetSpellBonuses().MeleeSkillCheck + attacker->GetItemBonuses().MeleeSkillCheck) > 500) ghit = true; - ////////////////////////////////////////////////////////// - // make enrage same as riposte - ///////////////////////////////////////////////////////// if (IsEnraged() && !other->BehindMob(this, other->GetX(), other->GetY())) { damage = -3; Log.Out(Logs::Detail, Logs::Combat, "I am enraged, riposting frontal attack."); } - - ///////////////////////////////////////////////////////// - // riposte - ///////////////////////////////////////////////////////// + float riposte_chance = 0.0f; - if (CanRiposte && damage > 0 && CanThisClassRiposte() && !other->BehindMob(this, other->GetX(), other->GetY())) - { - riposte_chance = (100.0f + (float)defender->GetAABonuses().RiposteChance + (float)defender->GetSpellBonuses().RiposteChance + (float)defender->GetItemBonuses().RiposteChance) / 100.0f; + if (CanRiposte && damage > 0 && CanThisClassRiposte() && !other->BehindMob(this, other->GetX(), other->GetY())) { + riposte_chance = ((100.0f + (float)defender->GetAABonuses().RiposteChance + (float)defender->GetSpellBonuses().RiposteChance + (float)defender->GetItemBonuses().RiposteChance) / 100.0f); skill = GetSkill(SkillRiposte); - - if (!ghit) { //if they are not using a garunteed hit discipline - bonus = 2.0 + skill/60.0 + (GetDEX()/200); + if (!ghit) { + bonus = (2.0 + skill / 60.0 + (GetDEX() / 200)); bonus *= riposte_chance; - RollTable[0] = bonus + (itembonuses.HeroicDEX / 25); // 25 heroic = 1%, applies to ripo, parry, block + RollTable[0] = (bonus + (itembonuses.HeroicDEX / 25)); } } - - /////////////////////////////////////////////////////// - // block - /////////////////////////////////////////////////////// - + bool bBlockFromRear = false; bool bShieldBlockFromRear = false; - if (this->IsBot()) { int aaChance = 0; - - // a successful roll on this does not mean a successful block is forthcoming. only that a chance to block - // from a direction other than the rear is granted. - - //Live AA - HightenedAwareness - int BlockBehindChance = aabonuses.BlockBehind + spellbonuses.BlockBehind + itembonuses.BlockBehind; - + int BlockBehindChance = (aabonuses.BlockBehind + spellbonuses.BlockBehind + itembonuses.BlockBehind); if (BlockBehindChance && (BlockBehindChance > zone->random.Int(1, 100))){ bBlockFromRear = true; - if (spellbonuses.BlockBehind || itembonuses.BlockBehind) - bShieldBlockFromRear = true; //This bonus should allow a chance to Shield Block from behind. + bShieldBlockFromRear = true; } } float block_chance = 0.0f; if (damage > 0 && CanThisClassBlock() && (!other->BehindMob(this, other->GetX(), other->GetY()) || bBlockFromRear)) { - block_chance = (100.0f + (float)spellbonuses.IncreaseBlockChance + (float)itembonuses.IncreaseBlockChance) / 100.0f; + block_chance = ((100.0f + (float)spellbonuses.IncreaseBlockChance + (float)itembonuses.IncreaseBlockChance) / 100.0f); skill = GetSkill(SkillBlock); - - if (!ghit) { //if they are not using a garunteed hit discipline - bonus = 2.0 + skill/35.0 + (GetDEX()/200); - RollTable[1] = RollTable[0] + (bonus * block_chance) - riposte_chance; - block_chance *= bonus; // set this so we can remove it from the parry calcs + if (!ghit) { + bonus = (2.0 + skill / 35.0 + (GetDEX() / 200)); + RollTable[1] = (RollTable[0] + (bonus * block_chance) - riposte_chance); + block_chance *= bonus; } - } - else{ + } else RollTable[1] = RollTable[0]; - } if(damage > 0 && (aabonuses.ShieldBlock || spellbonuses.ShieldBlock || itembonuses.ShieldBlock) && (!other->BehindMob(this, other->GetX(), other->GetY()) || bShieldBlockFromRear)) { @@ -7499,10 +6358,8 @@ bool Bot::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte) uint8 shield = GetBotItem(MainSecondary)->GetItem()->ItemType; float bonusShieldBlock = 0.0f; if(shield == ItemTypeShield) { - - //Live AA - Shield Block - bonusShieldBlock = aabonuses.ShieldBlock + spellbonuses.ShieldBlock + itembonuses.ShieldBlock; - RollTable[1] = RollTable[0] + bonusShieldBlock; + bonusShieldBlock = (aabonuses.ShieldBlock + spellbonuses.ShieldBlock + itembonuses.ShieldBlock); + RollTable[1] = (RollTable[0] + bonusShieldBlock); } } } @@ -7514,79 +6371,58 @@ bool Bot::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte) uint8 TwoHandBlunt = GetBotItem(MainPrimary)->GetItem()->ItemType; float bonusStaffBlock = 0.0f; if(TwoHandBlunt == ItemType2HBlunt) { - - bonusStaffBlock = aabonuses.TwoHandBluntBlock + spellbonuses.TwoHandBluntBlock + itembonuses.TwoHandBluntBlock; - RollTable[1] = RollTable[0] + bonusStaffBlock; + bonusStaffBlock = (aabonuses.TwoHandBluntBlock + spellbonuses.TwoHandBluntBlock + itembonuses.TwoHandBluntBlock); + RollTable[1] = (RollTable[0] + bonusStaffBlock); } } } - ////////////////////////////////////////////////////// - // parry - ////////////////////////////////////////////////////// float parry_chance = 0.0f; - if (damage > 0 && CanThisClassParry() && !other->BehindMob(this, other->GetX(), other->GetY())) - { - parry_chance = (100.0f + (float)defender->GetSpellBonuses().ParryChance + (float)defender->GetItemBonuses().ParryChance) / 100.0f; + if (damage > 0 && CanThisClassParry() && !other->BehindMob(this, other->GetX(), other->GetY())) { + parry_chance = ((100.0f + (float)defender->GetSpellBonuses().ParryChance + (float)defender->GetItemBonuses().ParryChance) / 100.0f); skill = GetSkill(SkillParry); - - if (!ghit) { //if they are not using a garunteed hit discipline - bonus = 2.0 + skill/60.0 + (GetDEX()/200); + if (!ghit) { + bonus = (2.0 + skill / 60.0 + (GetDEX() / 200)); bonus *= parry_chance; - RollTable[2] = RollTable[1] + bonus - block_chance; + RollTable[2] = (RollTable[1] + bonus - block_chance); } - } - else{ - RollTable[2] = RollTable[1] - block_chance; - } + } else + RollTable[2] = (RollTable[1] - block_chance); - //////////////////////////////////////////////////////// - // dodge - //////////////////////////////////////////////////////// float dodge_chance = 0.0f; - if (damage > 0 && CanThisClassDodge() && !other->BehindMob(this, other->GetX(), other->GetY())) - { - dodge_chance = (100.0f + (float)defender->GetSpellBonuses().DodgeChance + (float)defender->GetItemBonuses().DodgeChance) / 100.0f; + if (damage > 0 && CanThisClassDodge() && !other->BehindMob(this, other->GetX(), other->GetY())) { + dodge_chance = ((100.0f + (float)defender->GetSpellBonuses().DodgeChance + (float)defender->GetItemBonuses().DodgeChance) / 100.0f); skill = GetSkill(SkillDodge); - - if (!ghit) { //if they are not using a garunteed hit discipline - bonus = 2.0 + skill/60.0 + (GetAGI()/200); + if (!ghit) { + bonus = (2.0 + skill / 60.0 + (GetAGI() / 200)); bonus *= dodge_chance; - RollTable[3] = RollTable[2] + bonus - (itembonuses.HeroicDEX / 25) + (itembonuses.HeroicAGI / 25) - parry_chance; // Remove the dex as it doesnt count for dodge + RollTable[3] = (RollTable[2] + bonus - (itembonuses.HeroicDEX / 25) + (itembonuses.HeroicAGI / 25) - parry_chance); } - } - else{ - RollTable[3] = RollTable[2] - (itembonuses.HeroicDEX / 25) + (itembonuses.HeroicAGI / 25) - parry_chance; + } else { + RollTable[3] = (RollTable[2] - (itembonuses.HeroicDEX / 25) + (itembonuses.HeroicAGI / 25) - parry_chance); } - if(damage > 0) - { + if(damage > 0) { roll = zone->random.Real(0,100); - if(roll <= RollTable[0]){ + if(roll <= RollTable[0]) damage = -3; - } - else if(roll <= RollTable[1]){ + else if(roll <= RollTable[1]) damage = -1; - } - else if(roll <= RollTable[2]){ + else if(roll <= RollTable[2]) damage = -2; - } - else if(roll <= RollTable[3]){ + else if(roll <= RollTable[3]) damage = -4; - } } Log.Out(Logs::Detail, Logs::Combat, "Final damage after all avoidances: %d", damage); if (damage < 0) return true; + return false; } -int Bot::GetMonkHandToHandDamage(void) -{ - // Kaiyodo - Determine a monk's fist damage. Table data from www.monkly-business.com - // saved as static array - this should speed this function up considerably +int Bot::GetMonkHandToHandDamage(void) { static int damage[66] = { // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 99, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, @@ -7594,15 +6430,11 @@ int Bot::GetMonkHandToHandDamage(void) 12,12,12,12,12,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14, 14,14,15,15,15,15 }; - // Have a look to see if we have epic fists on - uint32 botWeaponId = INVALID_ID; botWeaponId = CastToNPC()->GetEquipment(MaterialHands); - if(botWeaponId == 10652) { //Monk Epic ID + if(botWeaponId == 10652) return 9; - } - else - { + else { int Level = GetLevel(); if(Level > 65) return 19; @@ -7612,30 +6444,25 @@ int Bot::GetMonkHandToHandDamage(void) int Level = GetLevel(); if (Level > 65) - return(19); + return 19; else return damage[Level]; } -bool Bot::TryFinishingBlow(Mob *defender, SkillUseTypes skillinuse) -{ +bool Bot::TryFinishingBlow(Mob *defender, SkillUseTypes skillinuse) { if (!defender) return false; - if (aabonuses.FinishingBlow[1] && !defender->IsClient() && defender->GetHPRatio() < 10){ - - uint32 chance = aabonuses.FinishingBlow[0]/10; //500 = 5% chance. + if (aabonuses.FinishingBlow[1] && !defender->IsClient() && defender->GetHPRatio() < 10) { + uint32 chance = (aabonuses.FinishingBlow[0] / 10); uint32 damage = aabonuses.FinishingBlow[1]; uint16 levelreq = aabonuses.FinishingBlowLvl[0]; - if(defender->GetLevel() <= levelreq && (chance >= zone->random.Int(0, 1000))){ Log.Out(Logs::Detail, Logs::Combat, "Landed a finishing blow: levelreq at %d, other level %d", levelreq , defender->GetLevel()); entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, FINISHING_BLOW, GetName()); defender->Damage(this, damage, SPELL_UNKNOWN, skillinuse); return true; - } - else - { + } else { Log.Out(Logs::Detail, Logs::Combat, "FAILED a finishing blow: levelreq at %d, other level %d", levelreq , defender->GetLevel()); return false; } @@ -7645,27 +6472,17 @@ bool Bot::TryFinishingBlow(Mob *defender, SkillUseTypes skillinuse) void Bot::DoRiposte(Mob* defender) { Log.Out(Logs::Detail, Logs::Combat, "Preforming a riposte"); - if (!defender) return; defender->Attack(this, MainPrimary, true); - - //double riposte - int32 DoubleRipChance = defender->GetAABonuses().GiveDoubleRiposte[0] + - defender->GetSpellBonuses().GiveDoubleRiposte[0] + - defender->GetItemBonuses().GiveDoubleRiposte[0]; - + int32 DoubleRipChance = (defender->GetAABonuses().GiveDoubleRiposte[0] + defender->GetSpellBonuses().GiveDoubleRiposte[0] + defender->GetItemBonuses().GiveDoubleRiposte[0]); if(DoubleRipChance && (DoubleRipChance >= zone->random.Int(0, 100))) { Log.Out(Logs::Detail, Logs::Combat, "Preforming a double riposte (%d percent chance)", DoubleRipChance); - defender->Attack(this, MainPrimary, true); } - //Double Riposte effect, allows for a chance to do RIPOSTE with a skill specfic special attack (ie Return Kick). - //Coded narrowly: Limit to one per client. Limit AA only. [1 = Skill Attack Chance, 2 = Skill] DoubleRipChance = defender->GetAABonuses().GiveDoubleRiposte[1]; - if(DoubleRipChance && (DoubleRipChance >= zone->random.Int(0, 100))) { if (defender->GetClass() == MONK) defender->MonkSpecialAttack(this, defender->GetAABonuses().GiveDoubleRiposte[2]); @@ -7675,9 +6492,6 @@ void Bot::DoRiposte(Mob* defender) { } void Bot::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, int32 min_damage, int32 hate_override,int ReuseTime, bool HitChance) { - //this really should go through the same code as normal melee damage to - //pick up all the special behavior there - int32 hate = max_damage; if(hate_override > -1) hate = hate_override; @@ -7687,22 +6501,21 @@ void Bot::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, const Item_Struct* botweapon = 0; if(inst) botweapon = inst->GetItem(); + if(botweapon) { - if(botweapon->ItemType == ItemTypeShield) { + if(botweapon->ItemType == ItemTypeShield) hate += botweapon->AC; - } - hate = hate * (100 + GetFuriousBash(botweapon->Focus.Effect)) / 100; + + hate = (hate * (100 + GetFuriousBash(botweapon->Focus.Effect)) / 100); } } - min_damage += min_damage * GetMeleeMinDamageMod_SE(skill) / 100; - + min_damage += (min_damage * GetMeleeMinDamageMod_SE(skill) / 100); if(HitChance && !who->CheckHitChance(this, skill, MainPrimary)) max_damage = 0; - - else{ + else { bool CanRiposte = true; - if(skill == SkillThrowing || skill == SkillArchery) // changed from '&&' + if(skill == SkillThrowing || skill == SkillArchery) CanRiposte = false; who->AvoidDamage(this, max_damage, CanRiposte); @@ -7711,37 +6524,35 @@ void Bot::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, if(max_damage > 0) { ApplyMeleeDamageBonus(skill, max_damage); max_damage += who->GetFcDamageAmtIncoming(this, 0, true, skill); - max_damage += (itembonuses.HeroicSTR / 10) + (max_damage * who->GetSkillDmgTaken(skill) / 100) + GetSkillDmgAmt(skill); + max_damage += ((itembonuses.HeroicSTR / 10) + (max_damage * who->GetSkillDmgTaken(skill) / 100) + GetSkillDmgAmt(skill)); TryCriticalHit(who, skill, max_damage); } } - if(max_damage >= 0) //You should probably get aggro no matter what, but unclear why it was set like this. + if(max_damage >= 0) who->AddToHateList(this, hate); who->Damage(this, max_damage, SPELL_UNKNOWN, skill, false); - if(!GetTarget())return; - if (HasDied()) return; + if(!GetTarget() || HasDied()) + return; if (max_damage > 0) CheckNumHitsRemaining(NumHit::OutgoingHitSuccess); - //[AA Dragon Punch] value[0] = 100 for 25%, chance value[1] = skill if(aabonuses.SpecialAttackKBProc[0] && aabonuses.SpecialAttackKBProc[1] == skill){ int kb_chance = 25; - kb_chance += kb_chance*(100-aabonuses.SpecialAttackKBProc[0])/100; + kb_chance += (kb_chance * (100 - aabonuses.SpecialAttackKBProc[0]) / 100); if (zone->random.Int(0, 99) < kb_chance) SpellFinished(904, who, 10, 0, -1, spells[904].ResistDiff); - //who->Stun(100); Kayen: This effect does not stun on live, it only moves the NPC. } if (HasSkillProcs()) - TrySkillProc(who, skill, ReuseTime*1000); + TrySkillProc(who, skill, (ReuseTime * 1000)); if (max_damage > 0 && HasSkillProcSuccess()) - TrySkillProc(who, skill, ReuseTime*1000, true); + TrySkillProc(who, skill, (ReuseTime * 1000), true); if(max_damage == -3 && !(who->GetHP() <= 0)) DoRiposte(who); @@ -7753,53 +6564,35 @@ void Bot::TryBackstab(Mob *other, int ReuseTime) { bool bIsBehind = false; bool bCanFrontalBS = false; - const ItemInst* inst = GetBotItem(MainPrimary); const Item_Struct* botpiercer = nullptr; if(inst) botpiercer = inst->GetItem(); + if(!botpiercer || (botpiercer->ItemType != ItemType1HPiercing)) { BotGroupSay(this, "I can't backstab with this weapon!"); return; } - //Live AA - Triple Backstab - int tripleChance = itembonuses.TripleBackstab + spellbonuses.TripleBackstab + aabonuses.TripleBackstab; - - if (BehindMob(other, GetX(), GetY())) { + int tripleChance = (itembonuses.TripleBackstab + spellbonuses.TripleBackstab + aabonuses.TripleBackstab); + if (BehindMob(other, GetX(), GetY())) bIsBehind = true; - } else { - //Live AA - Seized Opportunity - int FrontalBSChance = itembonuses.FrontalBackstabChance + spellbonuses.FrontalBackstabChance + aabonuses.FrontalBackstabChance; - + int FrontalBSChance = (itembonuses.FrontalBackstabChance + spellbonuses.FrontalBackstabChance + aabonuses.FrontalBackstabChance); if (FrontalBSChance && (FrontalBSChance > zone->random.Int(0, 100))) bCanFrontalBS = true; } - if (bIsBehind || bCanFrontalBS){ // Bot is behind other OR can do Frontal Backstab - - // chance to assassinate - int chance = 10 + (GetDEX()/10) + (itembonuses.HeroicDEX/10); //18.5% chance at 85 dex 40% chance at 300 dex - if( - level >= 60 && // bot is 60 or higher - other->GetLevel() <= 45 && // mob 45 or under - !other->CastToNPC()->IsEngaged() && // not aggro - other->GetHP()<=32000 - && other->IsNPC() - && zone->random.Real(0, 99) < chance // chance - ) { + if (bIsBehind || bCanFrontalBS) { + int chance = (10 + (GetDEX() / 10) + (itembonuses.HeroicDEX / 10)); + if(level >= 60 && other->GetLevel() <= 45 && !other->CastToNPC()->IsEngaged() && other->GetHP()<= 32000 && other->IsNPC() && zone->random.Real(0, 99) < chance) { entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, ASSASSINATES, GetName()); RogueAssassinate(other); - } - else { + } else { RogueBackstab(other); if (level > 54) { - float DoubleAttackProbability = (GetSkill(SkillDoubleAttack) + GetLevel()) / 500.0f; // 62.4 max - // Check for double attack with main hand assuming maxed DA Skill (MS) - - if(zone->random.Real(0, 1) < DoubleAttackProbability) // Max 62.4 % chance of DA - { + float DoubleAttackProbability = ((GetSkill(SkillDoubleAttack) + GetLevel()) / 500.0f); + if(zone->random.Real(0, 1) < DoubleAttackProbability) { if(other->GetHP() > 0) RogueBackstab(other,false,ReuseTime); @@ -7808,167 +6601,123 @@ void Bot::TryBackstab(Mob *other, int ReuseTime) { } } } - } - //Live AA - Chaotic Backstab - else if(aabonuses.FrontalBackstabMinDmg || itembonuses.FrontalBackstabMinDmg || spellbonuses.FrontalBackstabMinDmg) { - - //we can stab from any angle, we do min damage though. + } else if(aabonuses.FrontalBackstabMinDmg || itembonuses.FrontalBackstabMinDmg || spellbonuses.FrontalBackstabMinDmg) { RogueBackstab(other, true); if (level > 54) { - float DoubleAttackProbability = (GetSkill(SkillDoubleAttack) + GetLevel()) / 500.0f; // 62.4 max - // Check for double attack with main hand assuming maxed DA Skill (MS) - if(zone->random.Real(0, 1) < DoubleAttackProbability) // Max 62.4 % chance of DA + float DoubleAttackProbability = ((GetSkill(SkillDoubleAttack) + GetLevel()) / 500.0f); + if(zone->random.Real(0, 1) < DoubleAttackProbability) if(other->GetHP() > 0) RogueBackstab(other,true, ReuseTime); if (tripleChance && other->GetHP() > 0 && tripleChance > zone->random.Int(0, 100)) - RogueBackstab(other,false,ReuseTime); + RogueBackstab(other,false,ReuseTime); } } - else { //We do a single regular attack if we attack from the front without chaotic stab + else Attack(other, MainPrimary); - } } -//heko: backstab -void Bot::RogueBackstab(Mob* other, bool min_damage, int ReuseTime) -{ +void Bot::RogueBackstab(Mob* other, bool min_damage, int ReuseTime) { int32 ndamage = 0; int32 max_hit = 0; int32 min_hit = 0; int32 hate = 0; int32 primaryweapondamage = 0; int32 backstab_dmg = 0; - ItemInst* botweaponInst = GetBotItem(MainPrimary); if(botweaponInst) { primaryweapondamage = GetWeaponDamage(other, botweaponInst); backstab_dmg = botweaponInst->GetItem()->BackstabDmg; - for (int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; ++i) - { + for (int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; ++i) { ItemInst *aug = botweaponInst->GetAugment(i); if(aug) - { backstab_dmg += aug->GetItem()->BackstabDmg; - } } - } - else - { - primaryweapondamage = (GetLevel()/7)+1; // fallback incase it's a npc without a weapon, 2 dmg at 10, 10 dmg at 65 + } else { + primaryweapondamage = ((GetLevel() / 7) + 1); backstab_dmg = primaryweapondamage; } - if(primaryweapondamage > 0){ - if(level > 25){ - max_hit = (((2*backstab_dmg) * GetDamageTable(SkillBackstab) / 100) * 10 * GetSkill(SkillBackstab) / 355) + ((level-25)/3) + 1; - hate = 20 * backstab_dmg * GetSkill(SkillBackstab) / 355; - } - else{ - max_hit = (((2*backstab_dmg) * GetDamageTable(SkillBackstab) / 100) * 10 * GetSkill(SkillBackstab) / 355) + 1; - hate = 20 * backstab_dmg * GetSkill(SkillBackstab) / 355; + if(primaryweapondamage > 0) { + if(level > 25) { + max_hit = ((((2 * backstab_dmg) * GetDamageTable(SkillBackstab) / 100) * 10 * GetSkill(SkillBackstab) / 355) + ((level - 25) / 3) + 1); + hate = (20 * backstab_dmg * GetSkill(SkillBackstab) / 355); + } else { + max_hit = ((((2 * backstab_dmg) * GetDamageTable(SkillBackstab) / 100) * 10 * GetSkill(SkillBackstab) / 355) + 1); + hate = (20 * backstab_dmg * GetSkill(SkillBackstab) / 355); } - // determine minimum hits if (level < 51) - { - min_hit = (level*15/10); - } + min_hit = (level * 15 / 10); else - { - // Trumpcard: Replaced switch statement with formula calc. This will give minhit increases all the way to 65. - min_hit = (level * ( level*5 - 105)) / 100; - } + min_hit = ((level * ( level * 5 - 105)) / 100); - if(!other->CheckHitChance(this, SkillBackstab, 0)) { + if(!other->CheckHitChance(this, SkillBackstab, 0)) ndamage = 0; - } - else{ - if(min_damage){ + else { + if (min_damage) { ndamage = min_hit; - } - else - { + } else { if (max_hit < min_hit) max_hit = min_hit; - if(RuleB(Combat, UseIntervalAC)) - ndamage = max_hit; - else - ndamage = zone->random.Int(min_hit, max_hit); - + ndamage = (RuleB(Combat, UseIntervalAC) ? max_hit : zone->random.Int(min_hit, max_hit)); } } - } - else{ + } else ndamage = -5; - } DoSpecialAttackDamage(other, SkillBackstab, ndamage, min_hit, hate, ReuseTime); DoAnim(animPiercing); } -void Bot::RogueAssassinate(Mob* other) -{ +void Bot::RogueAssassinate(Mob* other) { ItemInst* botweaponInst = GetBotItem(MainPrimary); if(botweaponInst) { - if(GetWeaponDamage(other, botweaponInst)) { + if(GetWeaponDamage(other, botweaponInst)) other->Damage(this, 32000, SPELL_UNKNOWN, SkillBackstab); - } - else { + else other->Damage(this, -5, SPELL_UNKNOWN, SkillBackstab); - } } - DoAnim(animPiercing); //piercing animation + DoAnim(animPiercing); } void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { - if(!target) - return; - - if(spellend_timer.Enabled() || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || GetHP() < 0) - return; - - if(!IsAttackAllowed(target)) + if(!target || spellend_timer.Enabled() || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || GetHP() < 0 || !IsAttackAllowed(target)) return; bool taunt_time = taunt_timer.Check(); bool ca_time = classattack_timer.Check(false); bool ka_time = knightattack_timer.Check(false); - - //only check attack allowed if we are going to do something if((taunt_time || ca_time || ka_time) && !IsAttackAllowed(target)) return; if(ka_time){ - int knightreuse = 1000; //lets give it a small cooldown actually. + int knightreuse = 1000; switch(GetClass()){ - case SHADOWKNIGHT: case SHADOWKNIGHTGM:{ + case SHADOWKNIGHT: + case SHADOWKNIGHTGM: { CastSpell(SPELL_NPC_HARM_TOUCH, target->GetID()); - knightreuse = HarmTouchReuseTime * 1000; + knightreuse = (HarmTouchReuseTime * 1000); break; } - case PALADIN: case PALADINGM:{ + case PALADIN: + case PALADINGM: { if(GetHPRatio() < 20) { CastSpell(SPELL_LAY_ON_HANDS, GetID()); - knightreuse = LayOnHandsReuseTime * 1000; - } else { - knightreuse = 2000; //Check again in two seconds. + knightreuse = (LayOnHandsReuseTime * 1000); } + else + knightreuse = 2000; + break; } } knightattack_timer.Start(knightreuse); } - //general stuff, for all classes.... - //only gets used when their primary ability get used too - - //franck-add: EQoffline. Warrior bots must taunt the target. - if(taunting && target && target->IsNPC() && taunt_time ) { - //Only taunt if we are not top on target's hate list - //This ensures we have taunt available to regain aggro if needed + if(taunting && target && target->IsNPC() && taunt_time) { if(GetTarget() && GetTarget()->GetHateTop() && GetTarget()->GetHateTop() != this) { BotGroupSay(this, "Taunting %s", target->GetCleanName()); Taunt(target->CastToNPC(), false); @@ -7979,287 +6728,193 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { if(!ca_time) return; - float HasteModifier = GetHaste() * 0.01f; + float HasteModifier = (GetHaste() * 0.01f); int32 dmg = 0; - uint16 skill_to_use = -1; - int level = GetLevel(); - int reuse = TauntReuseTime * 1000; //make this very long since if they dont use it once, they prolly never will + int reuse = (TauntReuseTime * 1000); bool did_attack = false; - - switch(GetClass()) - { - case WARRIOR: - if(level >= RuleI(Combat, NPCBashKickLevel)){ - bool canBash = false; - if((GetRace() == OGRE || GetRace() == TROLL || GetRace() == BARBARIAN) // Racial Slam - || (m_inv.GetItem(MainSecondary) && m_inv.GetItem(MainSecondary)->GetItem()->ItemType == ItemTypeShield) //Using Shield - || (m_inv.GetItem(MainPrimary) && (m_inv.GetItem(MainPrimary)->GetItem()->ItemType == ItemType2HSlash - || m_inv.GetItem(MainPrimary)->GetItem()->ItemType == ItemType2HBlunt - || m_inv.GetItem(MainPrimary)->GetItem()->ItemType == ItemType2HPiercing) - && GetAA(aa2HandBash) >= 1)) { //Using 2 hand weapon, but has AA 2 Hand Bash - canBash = true; + switch(GetClass()) { + case WARRIOR: + if(level >= RuleI(Combat, NPCBashKickLevel)){ + bool canBash = false; + if((GetRace() == OGRE || GetRace() == TROLL || GetRace() == BARBARIAN) || (m_inv.GetItem(MainSecondary) && m_inv.GetItem(MainSecondary)->GetItem()->ItemType == ItemTypeShield) || (m_inv.GetItem(MainPrimary) && (m_inv.GetItem(MainPrimary)->GetItem()->ItemType == ItemType2HSlash || m_inv.GetItem(MainPrimary)->GetItem()->ItemType == ItemType2HBlunt || m_inv.GetItem(MainPrimary)->GetItem()->ItemType == ItemType2HPiercing) && GetAA(aa2HandBash) >= 1)) + canBash = true; + + if(!canBash || zone->random.Int(0, 100) > 25) + skill_to_use = SkillKick; + else + skill_to_use = SkillBash; } - - if(!canBash || zone->random.Int(0, 100) > 25) { //tested on live, warrior mobs both kick and bash, kick about 75% of the time, casting doesn't seem to make a difference. - skill_to_use = SkillKick; - } - else { - skill_to_use = SkillBash; - } - } - case RANGER: - case BEASTLORD: - skill_to_use = SkillKick; - break; - case BERSERKER: - skill_to_use = SkillFrenzy; - break; - case CLERIC: - case SHADOWKNIGHT: - case PALADIN: - if(level >= RuleI(Combat, NPCBashKickLevel)){ - if((GetRace() == OGRE || GetRace() == TROLL || GetRace() == BARBARIAN) // Racial Slam - || (m_inv.GetItem(MainSecondary) && m_inv.GetItem(MainSecondary)->GetItem()->ItemType == ItemTypeShield) //Using Shield - || (m_inv.GetItem(MainPrimary) && (m_inv.GetItem(MainPrimary)->GetItem()->ItemType == ItemType2HSlash - || m_inv.GetItem(MainPrimary)->GetItem()->ItemType == ItemType2HBlunt - || m_inv.GetItem(MainPrimary)->GetItem()->ItemType == ItemType2HPiercing) - && GetAA(aa2HandBash) >= 1)) { //Using 2 hand weapon, but has AA 2 Hand Bash - skill_to_use = SkillBash; - } - } - break; - case MONK: - if(GetLevel() >= 30) - { - skill_to_use = SkillFlyingKick; - } - else if(GetLevel() >= 25) - { - skill_to_use = SkillDragonPunch; - } - else if(GetLevel() >= 20) - { - skill_to_use = SkillEagleStrike; - } - else if(GetLevel() >= 10) - { - skill_to_use = SkillTigerClaw; - } - else if(GetLevel() >= 5) - { - skill_to_use = SkillRoundKick; - } - else - { + case RANGER: + case BEASTLORD: skill_to_use = SkillKick; - } - break; - case ROGUE: - skill_to_use = SkillBackstab; - break; + break; + case BERSERKER: + skill_to_use = SkillFrenzy; + break; + case CLERIC: + case SHADOWKNIGHT: + case PALADIN: + if(level >= RuleI(Combat, NPCBashKickLevel)){ + if((GetRace() == OGRE || GetRace() == TROLL || GetRace() == BARBARIAN) || (m_inv.GetItem(MainSecondary) && m_inv.GetItem(MainSecondary)->GetItem()->ItemType == ItemTypeShield) || (m_inv.GetItem(MainPrimary) && (m_inv.GetItem(MainPrimary)->GetItem()->ItemType == ItemType2HSlash || m_inv.GetItem(MainPrimary)->GetItem()->ItemType == ItemType2HBlunt || m_inv.GetItem(MainPrimary)->GetItem()->ItemType == ItemType2HPiercing) && GetAA(aa2HandBash) >= 1)) + skill_to_use = SkillBash; + } + break; + case MONK: + if(GetLevel() >= 30) + skill_to_use = SkillFlyingKick; + else if(GetLevel() >= 25) + skill_to_use = SkillDragonPunch; + else if(GetLevel() >= 20) + skill_to_use = SkillEagleStrike; + else if(GetLevel() >= 10) + skill_to_use = SkillTigerClaw; + else if(GetLevel() >= 5) + skill_to_use = SkillRoundKick; + else + skill_to_use = SkillKick; + break; + case ROGUE: + skill_to_use = SkillBackstab; + break; } if(skill_to_use == -1) return; - - if(skill_to_use == SkillBash) - { - if (target!=this) - { + if(skill_to_use == SkillBash) { + if (target != this) { DoAnim(animTailRake); - - if(GetWeaponDamage(target, GetBotItem(MainSecondary)) <= 0 && - GetWeaponDamage(target, GetBotItem(MainShoulders)) <= 0){ + if(GetWeaponDamage(target, GetBotItem(MainSecondary)) <= 0 && GetWeaponDamage(target, GetBotItem(MainShoulders)) <= 0) dmg = -5; - } - else{ - if(!target->CheckHitChance(this, SkillBash, 0)) { + else { + if(!target->CheckHitChance(this, SkillBash, 0)) dmg = 0; - } - else{ + else { if(RuleB(Combat, UseIntervalAC)) dmg = GetBashDamage(); else dmg = zone->random.Int(1, GetBashDamage()); - } } - - reuse = BashReuseTime * 1000; - //reuse = (reuse*HasteModifier)/100; - - DoSpecialAttackDamage(target, SkillBash, dmg, 1,-1,reuse); - + reuse = (BashReuseTime * 1000); + DoSpecialAttackDamage(target, SkillBash, dmg, 1, -1,reuse); did_attack = true; - - if(reuse > 0 && !IsRiposte) - { - //p_timers.Start(pTimerCombatAbility, reuse); - } } } - if(skill_to_use == SkillFrenzy) - { + if(skill_to_use == SkillFrenzy) { int AtkRounds = 3; int skillmod = 0; - if(MaxSkill(SkillFrenzy) > 0) - skillmod = 100*GetSkill(SkillFrenzy)/MaxSkill(SkillFrenzy); + skillmod = (100 * GetSkill(SkillFrenzy)/MaxSkill(SkillFrenzy)); - int32 max_dmg = (26 + ((((GetLevel()-6) * 2)*skillmod)/100)) * ((100+RuleI(Combat, FrenzyBonus))/100); + int32 max_dmg = (26 + ((((GetLevel() - 6) * 2) * skillmod) / 100)) * ((100 + RuleI(Combat, FrenzyBonus)) / 100); int32 min_dmg = 0; DoAnim(anim2HSlashing); if (GetLevel() < 51) min_dmg = 1; else - min_dmg = GetLevel()*8/10; + min_dmg = (GetLevel() * 8 / 10); if (min_dmg > max_dmg) max_dmg = min_dmg; - reuse = FrenzyReuseTime * 1000; - //reuse = (reuse * HasteModifier)/100; - + reuse = (FrenzyReuseTime * 1000); did_attack = true; - - //Live parses show around 55% Triple 35% Double 10% Single, you will always get first hit. while(AtkRounds > 0) { - - if (GetTarget() && (AtkRounds == 1 || zone->random.Int(0,100) < 75)){ - DoSpecialAttackDamage(GetTarget(), SkillFrenzy, max_dmg, min_dmg, max_dmg , reuse, true); + if (GetTarget() && (AtkRounds == 1 || zone->random.Int(0, 100) < 75)) { + DoSpecialAttackDamage(GetTarget(), SkillFrenzy, max_dmg, min_dmg, max_dmg, reuse, true); } - AtkRounds--; - } - if(reuse > 0 && !IsRiposte) { - //p_timers.Start(pTimerCombatAbility, reuse); + AtkRounds--; } } - if(skill_to_use == SkillKick) - { - if(target!=this) - { + if(skill_to_use == SkillKick) { + if(target != this) { DoAnim(animKick); - - if(GetWeaponDamage(target, GetBotItem(MainFeet)) <= 0){ + if(GetWeaponDamage(target, GetBotItem(MainFeet)) <= 0) dmg = -5; - } - else{ - if(!target->CheckHitChance(this, SkillKick, 0)) { + else { + if(!target->CheckHitChance(this, SkillKick, 0)) dmg = 0; - } - else{ + else { if(RuleB(Combat, UseIntervalAC)) dmg = GetKickDamage(); else dmg = zone->random.Int(1, GetKickDamage()); } } - - reuse = KickReuseTime * 1000; - - DoSpecialAttackDamage(target, SkillKick, dmg, 1,-1, reuse); - + reuse = (KickReuseTime * 1000); + DoSpecialAttackDamage(target, SkillKick, dmg, 1, -1, reuse); did_attack = true; } } - if(skill_to_use == SkillFlyingKick || - skill_to_use == SkillDragonPunch || - skill_to_use == SkillEagleStrike || - skill_to_use == SkillTigerClaw || - skill_to_use == SkillRoundKick) - { - reuse = MonkSpecialAttack(target, skill_to_use) - 1; + if(skill_to_use == SkillFlyingKick || skill_to_use == SkillDragonPunch || skill_to_use == SkillEagleStrike || skill_to_use == SkillTigerClaw || skill_to_use == SkillRoundKick) { + reuse = (MonkSpecialAttack(target, skill_to_use) - 1); MonkSpecialAttack(target, skill_to_use); - - //Live AA - Technique of Master Wu - uint32 bDoubleSpecialAttack = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; - if( bDoubleSpecialAttack && (bDoubleSpecialAttack >= 100 || bDoubleSpecialAttack > zone->random.Int(0,100))) { - + uint32 bDoubleSpecialAttack = (itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack); + if(bDoubleSpecialAttack && (bDoubleSpecialAttack >= 100 || bDoubleSpecialAttack > zone->random.Int(0, 100))) { int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick }; - MonkSpecialAttack(target, MonkSPA[zone->random.Int(0,4)]); - + MonkSpecialAttack(target, MonkSPA[zone->random.Int(0, 4)]); int TripleChance = 25; - if (bDoubleSpecialAttack > 100) - TripleChance += TripleChance*(100-bDoubleSpecialAttack)/100; + TripleChance += (TripleChance * (100 - bDoubleSpecialAttack) / 100); - if(TripleChance > zone->random.Int(0,100)) { - MonkSpecialAttack(target, MonkSPA[zone->random.Int(0,4)]); - } + if(TripleChance > zone->random.Int(0,100)) + MonkSpecialAttack(target, MonkSPA[zone->random.Int(0, 4)]); } reuse *= 1000; did_attack = true; } - if(skill_to_use == SkillBackstab) - { - reuse = BackstabReuseTime * 1000; + if(skill_to_use == SkillBackstab) { + reuse = (BackstabReuseTime * 1000); did_attack = true; - if (IsRiposte) - reuse=0; + reuse = 0; TryBackstab(target,reuse); } - classattack_timer.Start(reuse / HasteModifier); } bool Bot::TryHeadShot(Mob* defender, SkillUseTypes skillInUse) { bool Result = false; - if(defender && (defender->GetBodyType() == BT_Humanoid) && (skillInUse == SkillArchery) && (GetClass() == RANGER) && (GetLevel() >= 62)) { int defenderLevel = defender->GetLevel(); int rangerLevel = GetLevel(); - // Bot Ranger Headshot AA through level 85(Underfoot) - if( GetAA(aaHeadshot) && ((defenderLevel - 46) <= GetAA(aaHeadshot) * 2) ) { - // WildcardX: These chance formula's below are arbitrary. If someone has a better formula that is more - // consistent with live, feel free to update these. + if(GetAA(aaHeadshot) && ((defenderLevel - 46) <= GetAA(aaHeadshot) * 2)) { float AttackerChance = 0.20f + ((float)(rangerLevel - 51) * 0.005f); float DefenderChance = (float)zone->random.Real(0.00f, 1.00f); if(AttackerChance > DefenderChance) { Log.Out(Logs::Detail, Logs::Combat, "Landed a headshot: Attacker chance was %f and Defender chance was %f.", AttackerChance, DefenderChance); - // WildcardX: At the time I wrote this, there wasnt a string id for something like HEADSHOT_BLOW - //entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, FINISHING_BLOW, GetName()); entity_list.MessageClose(this, false, 200, MT_CritMelee, "%s has scored a leathal HEADSHOT!", GetName()); defender->Damage(this, (defender->GetMaxHP()+50), SPELL_UNKNOWN, skillInUse); Result = true; - } - else { + } else Log.Out(Logs::Detail, Logs::Combat, "FAILED a headshot: Attacker chance was %f and Defender chance was %f.", AttackerChance, DefenderChance); - } } } - return Result; } -//offensive spell aggro int32 Bot::CheckAggroAmount(uint16 spellid) { int32 AggroAmount = Mob::CheckAggroAmount(spellid); - int32 focusAggro = GetBotFocusEffect(BotfocusSpellHateMod, spellid); - AggroAmount = (AggroAmount * (100+focusAggro) / 100); - + AggroAmount = (AggroAmount * (100 + focusAggro) / 100); return AggroAmount; } int32 Bot::CheckHealAggroAmount(uint16 spellid, uint32 heal_possible) { int32 AggroAmount = Mob::CheckHealAggroAmount(spellid, heal_possible); - int32 focusAggro = GetBotFocusEffect(BotfocusSpellHateMod, spellid); - AggroAmount = (AggroAmount * (100 + focusAggro) / 100); - return AggroAmount; } @@ -8272,20 +6927,15 @@ void Bot::AI_Stop() { Mob::AI_Stop(); } -//this is called with 'this' as the mob being looked at, and -//iOther the mob who is doing the looking. It should figure out -//what iOther thinks about 'this' FACTION_VALUE Bot::GetReverseFactionCon(Mob* iOther) { - if(iOther->IsBot()) { + if(iOther->IsBot()) return FACTION_ALLY; - } return NPC::GetReverseFactionCon(iOther); } Mob* Bot::GetOwnerOrSelf() { Mob* Result = 0; - if(this->GetBotOwner()) Result = GetBotOwner(); else @@ -8296,103 +6946,61 @@ Mob* Bot::GetOwnerOrSelf() { Mob* Bot::GetOwner() { Mob* Result = 0; - Result = GetBotOwner(); - - if(!Result) { + if(!Result) this->SetBotOwner(0); - } return Result; } -bool Bot::IsBotAttackAllowed(Mob* attacker, Mob* target, bool& hasRuleDefined) -{ +bool Bot::IsBotAttackAllowed(Mob* attacker, Mob* target, bool& hasRuleDefined) { bool Result = false; - - if(attacker && target) - { - if(attacker == target) - { + if(attacker && target) { + if(attacker == target) { hasRuleDefined = true; Result = false; - } - else if(attacker->IsClient() && target->IsBot() && attacker->CastToClient()->GetPVP() && target->CastToBot()->GetBotOwner()->CastToClient()->GetPVP()) - { + } else if(attacker->IsClient() && target->IsBot() && attacker->CastToClient()->GetPVP() && target->CastToBot()->GetBotOwner()->CastToClient()->GetPVP()) { hasRuleDefined = true; Result = true; - } - else if(attacker->IsClient() && target->IsBot()) - { + } else if(attacker->IsClient() && target->IsBot()) { hasRuleDefined = true; Result = false; - } - else if(attacker->IsBot() && target->IsNPC()) - { + } else if(attacker->IsBot() && target->IsNPC()) { hasRuleDefined = true; Result = true; - } - else if(attacker->IsBot() && !target->IsNPC()) - { + } else if(attacker->IsBot() && !target->IsNPC()) { hasRuleDefined = true; Result = false; - } - else if(attacker->IsPet() && attacker->IsFamiliar()) - { + } else if(attacker->IsPet() && attacker->IsFamiliar()) { hasRuleDefined = true; Result = false; - } - else if(attacker->IsBot() && attacker->CastToBot()->GetBotOwner() && attacker->CastToBot()->GetBotOwner()->CastToClient()->GetPVP()) - { - if(target->IsBot() && target->GetOwner() && target->GetOwner()->CastToClient()->GetPVP()) - { - // my target is a bot and it's owner is pvp + } else if(attacker->IsBot() && attacker->CastToBot()->GetBotOwner() && attacker->CastToBot()->GetBotOwner()->CastToClient()->GetPVP()) { + if(target->IsBot() && target->GetOwner() && target->GetOwner()->CastToClient()->GetPVP()) { hasRuleDefined = true; - if(target->GetOwner() == attacker->GetOwner()) - { - // no attacking if my target's owner is my owner Result = false; - } else - { Result = true; - } - } - else if(target->IsClient() && target->CastToClient()->GetPVP()) - { - // my target is a player and it's pvp + } else if(target->IsClient() && target->CastToClient()->GetPVP()) { hasRuleDefined = true; - if(target == attacker->GetOwner()) - { - // my target cannot be my owner Result = false; - } else - { Result = true; - } - } - else if(target->IsNPC()) - { + } else if(target->IsNPC()) { hasRuleDefined = true; Result = true; - } - else if(!target->IsNPC()) - { + } else if(!target->IsNPC()) { hasRuleDefined = true; Result = false; } } } - return Result; } void Bot::EquipBot(std::string* errorMessage) { GetBotItems(errorMessage, m_inv); - const ItemInst* inst = 0; const Item_Struct* item = 0; for(int i = EmuConstants::EQUIPMENT_BEGIN; i <= EmuConstants::EQUIPMENT_END; ++i) { @@ -8404,33 +7012,14 @@ void Bot::EquipBot(std::string* errorMessage) { return; } } - UpdateEquipmentLight(); } -//// This method is meant to be called by zone or client methods to clean up objects when a client camps, goes LD, zones out or something like that. -//void Bot::DestroyBotRaidObjects(Client* client) { -// if(client) { -// if(client->GetBotRaidID() > 0) { -// BotRaids* br = entity_list.GetBotRaidByMob(client); -// if(br) { -// br->RemoveRaidBots(); -// br = nullptr; -// } -// } -// -// //BotOrderCampAll(client); -// } -//} - -// Orders all the bots owned by the specified client bot owner to camp out of the game void Bot::BotOrderCampAll(Client* c) { if(c) { std::list BotList = entity_list.GetBotsByBotOwnerCharacterID(c->CharacterID()); - - for(std::list::iterator botListItr = BotList.begin(); botListItr != BotList.end(); ++botListItr) { + for(std::list::iterator botListItr = BotList.begin(); botListItr != BotList.end(); ++botListItr) (*botListItr)->Camp(); - } } } @@ -8438,11 +7027,9 @@ void Bot::ProcessBotOwnerRefDelete(Mob* botOwner) { if(botOwner) { if(botOwner->IsClient()) { std::list BotList = entity_list.GetBotsByBotOwnerCharacterID(botOwner->CastToClient()->CharacterID()); - if(!BotList.empty()) { for(std::list::iterator botListItr = BotList.begin(); botListItr != BotList.end(); ++botListItr) { Bot* tempBot = *botListItr; - if(tempBot) { tempBot->SetTarget(0); tempBot->SetBotOwner(0); @@ -8455,20 +7042,12 @@ void Bot::ProcessBotOwnerRefDelete(Mob* botOwner) { void Bot::ProcessGuildInvite(Client* guildOfficer, Bot* botToGuild) { if(guildOfficer && botToGuild) { - // Bots can be only guild member rank if(!botToGuild->IsInAGuild()) { - //they are not in this or any other guild, this is an invite if (!guild_mgr.CheckPermission(guildOfficer->GuildID(), guildOfficer->GuildRank(), GUILD_INVITE)) { guildOfficer->Message(13, "You dont have permission to invite."); return; } - - // Log.Out(Logs::Detail, Logs::Guilds, "Inviting %s (%d) into guild %s (%d)", botToGuild->GetName(), botToGuild->GetBotID(), guild_mgr.GetGuildName(client->GuildID()), client->GuildID()); - SetBotGuildMembership(botToGuild->GetBotID(), guildOfficer->GuildID(), GUILD_MEMBER); - - //Log.LogDebugType(Logs::Detail, Logs::Guilds, "Sending char refresh for BOT %s from guild %d to world", botToGuild->GetName(), guildOfficer->GuildID(); - ServerPacket* pack = new ServerPacket(ServerOP_GuildCharRefresh, sizeof(ServerGuildCharRefresh_Struct)); ServerGuildCharRefresh_Struct *s = (ServerGuildCharRefresh_Struct *) pack->pBuffer; s->guild_id = guildOfficer->GuildID(); @@ -8476,9 +7055,7 @@ void Bot::ProcessGuildInvite(Client* guildOfficer, Bot* botToGuild) { s->char_id = botToGuild->GetBotID(); worldserver.SendPacket(pack); safe_delete(pack); - } else { - //they are in some other guild guildOfficer->Message(13, "Player is in a guild."); return; } @@ -8487,18 +7064,14 @@ void Bot::ProcessGuildInvite(Client* guildOfficer, Bot* botToGuild) { bool Bot::ProcessGuildRemoval(Client* guildOfficer, std::string botName) { bool Result = false; - if(guildOfficer && !botName.empty()) { Bot* botToUnGuild = entity_list.GetBotByBotName(botName); if(botToUnGuild) { SetBotGuildMembership(botToUnGuild->GetBotID(), 0, 0); Result = true; - } - else { + } else { uint32 botId = GetBotIDByBotName(botName); - if(botId > 0) { - // Bot is camped or in another zone SetBotGuildMembership(botId, 0, 0); Result = true; } @@ -8514,7 +7087,6 @@ bool Bot::ProcessGuildRemoval(Client* guildOfficer, std::string botName) { safe_delete(outapp); } } - return Result; } @@ -8523,27 +7095,20 @@ void Bot::SetBotGuildMembership(uint32 botId, uint32 guildid, uint8 rank) { return; if(guildid > 0) { - std::string query = StringFormat("REPLACE INTO botguildmembers " - "SET char_id = %u, guild_id = %u, rank = %u;", - botId, guildid, rank); + std::string query = StringFormat("REPLACE INTO botguildmembers SET char_id = %u, guild_id = %u, rank = %u", botId, guildid, rank); auto results = database.QueryDatabase(query); return; } - std::string query = StringFormat("DELETE FROM botguildmembers WHERE char_id = %u;", botId); + std::string query = StringFormat("DELETE FROM botguildmembers WHERE char_id = %u", botId); auto results = database.QueryDatabase(query); } void Bot::LoadGuildMembership(uint32* guildId, uint8* guildRank, std::string* guildName) { - if(guildId == nullptr || guildRank == nullptr || guildName == nullptr) return; - std::string query = StringFormat("SELECT gm.guild_id, gm.rank, g.name " - "FROM vwGuildMembers AS gm JOIN guilds AS g " - "ON gm.guild_id = g.id " - "WHERE gm.char_id = %u AND gm.mobtype = 'B';", - GetBotID()); + std::string query = StringFormat("SELECT gm.guild_id, gm.rank, g.name FROM vwGuildMembers AS gm JOIN guilds AS g ON gm.guild_id = g.id WHERE gm.char_id = %u AND gm.mobtype = 'B'", GetBotID()); auto results = database.QueryDatabase(query); if(!results.Success() || results.RowCount() == 0) return; @@ -8555,56 +7120,44 @@ void Bot::LoadGuildMembership(uint32* guildId, uint8* guildRank, std::string* gu } int32 Bot::CalcMaxMana() { - switch(GetCasterClass()) - { + switch(GetCasterClass()) { case 'I': - case 'W': - { + case 'W': { max_mana = (GenerateBaseManaPoints() + itembonuses.Mana + spellbonuses.Mana + GroupLeadershipAAManaEnhancement()); break; } - case 'N': - { + case 'N': { max_mana = 0; break; } - default: - { + default: { Log.Out(Logs::General, Logs::None, "Invalid Class '%c' in CalcMaxMana", GetCasterClass()); max_mana = 0; break; } } - if(cur_mana > max_mana) { + if(cur_mana > max_mana) cur_mana = max_mana; - } - else if(max_mana < 0) { + else if(max_mana < 0) max_mana = 0; - } return max_mana; } void Bot::SetAttackTimer() { - float haste_mod = GetHaste() * 0.01f; - - //default value for attack timer in case they have - //an invalid weapon equipped: + float haste_mod = (GetHaste() * 0.01f); attack_timer.SetAtTrigger(4000, true); - Timer* TimerToUse = nullptr; const Item_Struct* PrimaryWeapon = nullptr; - for (int i = MainRange; i <= MainSecondary; i++) { - //pick a timer if (i == MainPrimary) TimerToUse = &attack_timer; else if (i == MainRange) TimerToUse = &ranged_timer; else if (i == MainSecondary) TimerToUse = &attack_dw_timer; - else //invalid slot (hands will always hit this) + else continue; const Item_Struct* ItemToUse = nullptr; @@ -8612,60 +7165,36 @@ void Bot::SetAttackTimer() { if (ci) ItemToUse = ci->GetItem(); - //special offhand stuff if (i == MainSecondary) { - //if we have a 2H weapon in our main hand, no dual if (PrimaryWeapon != nullptr) { - if (PrimaryWeapon->ItemClass == ItemClassCommon - && (PrimaryWeapon->ItemType == ItemType2HSlash - || PrimaryWeapon->ItemType == ItemType2HBlunt - || PrimaryWeapon->ItemType == ItemType2HPiercing)) { + if (PrimaryWeapon->ItemClass == ItemClassCommon && (PrimaryWeapon->ItemType == ItemType2HSlash || PrimaryWeapon->ItemType == ItemType2HBlunt || PrimaryWeapon->ItemType == ItemType2HPiercing)) { attack_dw_timer.Disable(); continue; } } - //clients must have the skill to use it... if (!GetSkill(SkillDualWield)) { attack_dw_timer.Disable(); continue; } } - //see if we have a valid weapon if (ItemToUse != nullptr) { - //check type and damage/delay - if (ItemToUse->ItemClass != ItemClassCommon - || ItemToUse->Damage == 0 - || ItemToUse->Delay == 0) { - //no weapon + if (ItemToUse->ItemClass != ItemClassCommon || ItemToUse->Damage == 0 || ItemToUse->Delay == 0 || ((ItemToUse->ItemType > ItemTypeLargeThrowing) && (ItemToUse->ItemType != ItemTypeMartial) && (ItemToUse->ItemType != ItemType2HPiercing))) ItemToUse = nullptr; - } - // Check to see if skill is valid - else if ((ItemToUse->ItemType > ItemTypeLargeThrowing) && (ItemToUse->ItemType != ItemTypeMartial) && (ItemToUse->ItemType != ItemType2HPiercing)) { - //no weapon - ItemToUse = nullptr; - } } - int hhe = itembonuses.HundredHands + spellbonuses.HundredHands; + int hhe = (itembonuses.HundredHands + spellbonuses.HundredHands); int speed = 0; int delay = 36; - - //if we have no weapon.. if (ItemToUse == nullptr) { - //above checks ensure ranged weapons do not fall into here - // Work out if we're a monk if ((GetClass() == MONK) || (GetClass() == BEASTLORD)) delay = GetMonkHandToHandDelay(); } else { - //we have a weapon, use its delay delay = ItemToUse->Delay; } - if (RuleB(Spells, Jun182014HundredHandsRevamp)) - speed = static_cast(((delay / haste_mod) + ((hhe / 1000.0f) * (delay / haste_mod))) * 100); - else - speed = static_cast(((delay / haste_mod) + ((hhe / 100.0f) * delay)) * 100); + + speed = (RuleB(Spells, Jun182014HundredHandsRevamp) ? static_cast(((delay / haste_mod) + ((hhe / 1000.0f) * (delay / haste_mod))) * 100) : static_cast(((delay / haste_mod) + ((hhe / 100.0f) * delay)) * 100)); TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true); if(i == MainPrimary) @@ -8674,66 +7203,52 @@ void Bot::SetAttackTimer() { } int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { - if (spells[spell_id].targettype == ST_Self) return value; bool Critical = false; int32 value_BaseEffect = 0; - - value_BaseEffect = value + (value*GetBotFocusEffect(BotfocusFcBaseEffects, spell_id)/100); - + value_BaseEffect = (value + (value*GetBotFocusEffect(BotfocusFcBaseEffects, spell_id) / 100)); // Need to scale HT damage differently after level 40! It no longer scales by the constant value in the spell file. It scales differently, instead of 10 more damage per level, it does 30 more damage per level. So we multiply the level minus 40 times 20 if they are over level 40. if ( (spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2 || spell_id == SPELL_IMP_HARM_TOUCH ) && GetLevel() > 40) - value -= (GetLevel() - 40) * 20; + value -= ((GetLevel() - 40) * 20); //This adds the extra damage from the AA Unholy Touch, 450 per level to the AA Improved Harm TOuch. if (spell_id == SPELL_IMP_HARM_TOUCH) //Improved Harm Touch - value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch + value -= (GetAA(aaUnholyTouch) * 450); //Unholy Touch int chance = RuleI(Spells, BaseCritChance); - chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance; + chance += (itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance); - if (chance > 0){ + if (chance > 0) { + int32 ratio = RuleI(Spells, BaseCritRatio); + if (spell_id == SPELL_IMP_HARM_TOUCH && (GetAA(aaSpellCastingFury) > 0) && (GetAA(aaUnholyTouch) > 0)) + chance = 100; - int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals. - - //Improved Harm Touch is a guaranteed crit if you have at least one level of SCF. - if (spell_id == SPELL_IMP_HARM_TOUCH && (GetAA(aaSpellCastingFury) > 0) && (GetAA(aaUnholyTouch) > 0)) - chance = 100; - - if (zone->random.Int(1,100) <= chance){ + if (zone->random.Int(1, 100) <= chance){ Critical = true; - ratio += itembonuses.SpellCritDmgIncrease + spellbonuses.SpellCritDmgIncrease + aabonuses.SpellCritDmgIncrease; - ratio += itembonuses.SpellCritDmgIncNoStack + spellbonuses.SpellCritDmgIncNoStack + aabonuses.SpellCritDmgIncNoStack; - } - - else if (GetClass() == WIZARD && (GetLevel() >= RuleI(Spells, WizCritLevel)) && (zone->random.Int(1,100) <= RuleI(Spells, WizCritChance))) { - ratio = zone->random.Int(1,100); //Wizard innate critical chance is calculated seperately from spell effect and is not a set ratio. + ratio += (itembonuses.SpellCritDmgIncrease + spellbonuses.SpellCritDmgIncrease + aabonuses.SpellCritDmgIncrease); + ratio += (itembonuses.SpellCritDmgIncNoStack + spellbonuses.SpellCritDmgIncNoStack + aabonuses.SpellCritDmgIncNoStack); + } else if (GetClass() == WIZARD && (GetLevel() >= RuleI(Spells, WizCritLevel)) && (zone->random.Int(1, 100) <= RuleI(Spells, WizCritChance))) { + ratio = zone->random.Int(1, 100); Critical = true; } - - ratio += RuleI(Spells, WizCritRatio); //Default is zero - - if (Critical){ - - value = value_BaseEffect*ratio/100; - - value += value_BaseEffect*GetBotFocusEffect(BotfocusImprovedDamage, spell_id)/100; - - value += int(value_BaseEffect*GetBotFocusEffect(BotfocusFcDamagePctCrit, spell_id)/100)*ratio/100; - + ratio += RuleI(Spells, WizCritRatio); + if (Critical) { + value = (value_BaseEffect * ratio / 100); + value += (value_BaseEffect * GetBotFocusEffect(BotfocusImprovedDamage, spell_id) / 100); + value += (int(value_BaseEffect * GetBotFocusEffect(BotfocusFcDamagePctCrit, spell_id) / 100) * ratio / 100); if (target) { - value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100; + value += (int(value_BaseEffect * target->GetVulnerability(this, spell_id, 0) / 100) * ratio / 100); value -= target->GetFcDamageAmtIncoming(this, spell_id); } - value -= GetBotFocusEffect(BotfocusFcDamageAmtCrit, spell_id)*ratio/100; + value -= (GetBotFocusEffect(BotfocusFcDamageAmtCrit, spell_id) * ratio / 100); value -= GetBotFocusEffect(BotfocusFcDamageAmt, spell_id); - if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) - value += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio/100; + if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 16) - 1] >= GetLevel() - 5) + value += (GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value) * ratio / 100); entity_list.MessageClose(this, false, 100, MT_SpellCrits, "%s delivers a critical blast! (%d)", GetName(), -value); @@ -8741,29 +7256,23 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { } } - value = value_BaseEffect; - - value += value_BaseEffect*GetBotFocusEffect(BotfocusImprovedDamage, spell_id)/100; - - value += value_BaseEffect*GetBotFocusEffect(BotfocusFcDamagePctCrit, spell_id)/100; - - if (target) { - value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100; + value = value_BaseEffect; + value += (value_BaseEffect * GetBotFocusEffect(BotfocusImprovedDamage, spell_id) / 100); + value += (value_BaseEffect * GetBotFocusEffect(BotfocusFcDamagePctCrit, spell_id) / 100); + if (target) { + value += (value_BaseEffect * target->GetVulnerability(this, spell_id, 0) / 100); value -= target->GetFcDamageAmtIncoming(this, spell_id); - } + } - value -= GetBotFocusEffect(BotfocusFcDamageAmtCrit, spell_id); - - value -= GetBotFocusEffect(BotfocusFcDamageAmt, spell_id); - - if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) - value += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); + value -= GetBotFocusEffect(BotfocusFcDamageAmtCrit, spell_id); + value -= GetBotFocusEffect(BotfocusFcDamageAmt, spell_id); + if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 16) - 1] >= GetLevel() - 5) + value += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); return value; } int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { - if (target == nullptr) target = this; @@ -8771,72 +7280,52 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { int32 chance = 0; int8 modifier = 1; bool Critical = false; - - value_BaseEffect = value + (value*GetBotFocusEffect(BotfocusFcBaseEffects, spell_id)/100); - + value_BaseEffect = (value + (value*GetBotFocusEffect(BotfocusFcBaseEffects, spell_id) / 100)); value = value_BaseEffect; - - value += int(value_BaseEffect*GetBotFocusEffect(BotfocusImprovedHeal, spell_id)/100); - - // Instant Heals + value += int(value_BaseEffect*GetBotFocusEffect(BotfocusImprovedHeal, spell_id) / 100); if(spells[spell_id].buffduration < 1) { - - chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance; - + chance += (itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance); chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); - if (spellbonuses.CriticalHealDecay) chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay); - if(chance && (zone->random.Int(0,99) < chance)) { + if(chance && (zone->random.Int(0, 99) < chance)) { Critical = true; - modifier = 2; //At present time no critical heal amount modifier SPA exists. + modifier = 2; } value *= modifier; - value += GetBotFocusEffect(BotfocusFcHealAmtCrit, spell_id) * modifier; + value += (GetBotFocusEffect(BotfocusFcHealAmtCrit, spell_id) * modifier); value += GetBotFocusEffect(BotfocusFcHealAmt, spell_id); value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); - if(itembonuses.HealAmt && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) - value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value) * modifier; - - value += value*target->GetHealRate(spell_id, this)/100; + if(itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 16) - 1] >= GetLevel() - 5) + value += (GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value) * modifier); + value += (value * target->GetHealRate(spell_id, this) / 100); if (Critical) entity_list.MessageClose(this, false, 100, MT_SpellCrits, "%s performs an exceptional heal! (%d)", GetName(), value); return value; - } - - //Heal over time spells. [Heal Rate and Additional Healing effects do not increase this value] - else { - - chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime; - + } else { + chance = (itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime); chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); - if (spellbonuses.CriticalRegenDecay) chance += GetDecayEffectValue(spell_id, SE_CriticalRegenDecay); if(chance && (zone->random.Int(0,99) < chance)) return (value * 2); } - return value; } int32 Bot::GetActSpellCasttime(uint16 spell_id, int32 casttime) { int32 cast_reducer = 0; cast_reducer += GetBotFocusEffect(BotfocusSpellHaste, spell_id); - uint8 botlevel = GetLevel(); uint8 botclass = GetClass(); - - if (botlevel >= 51 && casttime >= 3000 && !BeneficialSpell(spell_id) - && (botclass == SHADOWKNIGHT || botclass == RANGER - || botclass == PALADIN || botclass == BEASTLORD )) - cast_reducer += (GetLevel()-50)*3; + if (botlevel >= 51 && casttime >= 3000 && !BeneficialSpell(spell_id) && (botclass == SHADOWKNIGHT || botclass == RANGER || botclass == PALADIN || botclass == BEASTLORD )) + cast_reducer += ((GetLevel() - 50) * 3); if((casttime >= 4000) && BeneficialSpell(spell_id) && IsBuffSpell(spell_id)) { switch (GetAA(aaSpellCastingDeftness)) { @@ -8909,86 +7398,70 @@ int32 Bot::GetActSpellCasttime(uint16 spell_id, int32 casttime) { if (cast_reducer > RuleI(Spells, MaxCastTimeReduction)) cast_reducer = RuleI(Spells, MaxCastTimeReduction); - casttime = (casttime*(100 - cast_reducer)/100); - + casttime = (casttime * (100 - cast_reducer) / 100); return casttime; } int32 Bot::GetActSpellCost(uint16 spell_id, int32 cost) { - // Formula = Unknown exact, based off a random percent chance up to mana cost(after focuses) of the cast spell - if(this->itembonuses.Clairvoyance && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) - { - int32 mana_back = this->itembonuses.Clairvoyance * zone->random.Int(1, 100) / 100; - // Doesnt generate mana, so best case is a free spell + if(this->itembonuses.Clairvoyance && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) { + int32 mana_back = (this->itembonuses.Clairvoyance * zone->random.Int(1, 100) / 100); if(mana_back > cost) mana_back = cost; cost -= mana_back; } - // This formula was derived from the following resource: - // http://www.eqsummoners.com/eq1/specialization-library.html - // WildcardX float PercentManaReduction = 0; float SpecializeSkill = GetSpecializeSkillValue(spell_id); int SuccessChance = zone->random.Int(0, 100); - float bonus = 1.0; - switch(GetAA(aaSpellCastingMastery)) - { - case 1: - bonus += 0.05; - break; - case 2: - bonus += 0.15; - break; - case 3: - bonus += 0.30; - break; + switch(GetAA(aaSpellCastingMastery)) { + case 1: + bonus += 0.05; + break; + case 2: + bonus += 0.15; + break; + case 3: + bonus += 0.30; + break; } - bonus += 0.05 * GetAA(aaAdvancedSpellCastingMastery); + bonus += (0.05 * GetAA(aaAdvancedSpellCastingMastery)); - if(SuccessChance <= (SpecializeSkill * 0.3 * bonus)) - { - PercentManaReduction = 1 + 0.05 * SpecializeSkill; - switch(GetAA(aaSpellCastingMastery)) - { - case 1: - PercentManaReduction += 2.5; - break; - case 2: - PercentManaReduction += 5.0; - break; - case 3: - PercentManaReduction += 10.0; - break; + if(SuccessChance <= (SpecializeSkill * 0.3 * bonus)) { + PercentManaReduction = (1 + 0.05 * SpecializeSkill); + switch(GetAA(aaSpellCastingMastery)) { + case 1: + PercentManaReduction += 2.5; + break; + case 2: + PercentManaReduction += 5.0; + break; + case 3: + PercentManaReduction += 10.0; + break; } - switch(GetAA(aaAdvancedSpellCastingMastery)) - { - case 1: - PercentManaReduction += 2.5; - break; - case 2: - PercentManaReduction += 5.0; - break; - case 3: - PercentManaReduction += 10.0; - break; + switch(GetAA(aaAdvancedSpellCastingMastery)) { + case 1: + PercentManaReduction += 2.5; + break; + case 2: + PercentManaReduction += 5.0; + break; + case 3: + PercentManaReduction += 10.0; + break; } } int32 focus_redux = GetBotFocusEffect(BotfocusManaCost, spell_id); if(focus_redux > 0) - { PercentManaReduction += zone->random.Real(1, (double)focus_redux); - } cost -= (cost * (PercentManaReduction / 100)); - - // Gift of Mana - reduces spell cost to 1 mana if(focus_redux >= 100) { uint32 buff_max = GetMaxTotalSlots(); for (int buffSlot = 0; buffSlot < buff_max; buffSlot++) { @@ -9011,17 +7484,15 @@ int32 Bot::GetActSpellCost(uint16 spell_id, int32 cost) { float Bot::GetActSpellRange(uint16 spell_id, float range) { float extrange = 100; extrange += GetBotFocusEffect(BotfocusRange, spell_id); - return (range * extrange) / 100; + return ((range * extrange) / 100); } int32 Bot::GetActSpellDuration(uint16 spell_id, int32 duration) { int increase = 100; increase += GetBotFocusEffect(BotfocusSpellDuration, spell_id); - int tic_inc = 0; - tic_inc = GetBotFocusEffect(BotfocusSpellDurByTic, spell_id); + int tic_inc = 0; tic_inc = GetBotFocusEffect(BotfocusSpellDurByTic, spell_id); - if(IsBeneficialSpell(spell_id)) - { + if(IsBeneficialSpell(spell_id)) { switch (GetAA(aaSpellCastingReinforcement)) { case 1: increase += 5; @@ -9033,6 +7504,7 @@ int32 Bot::GetActSpellDuration(uint16 spell_id, int32 duration) { increase += 30; if (GetAA(aaSpellCastingReinforcementMastery) == 1) increase += 20; + break; } @@ -9040,52 +7512,43 @@ int32 Bot::GetActSpellDuration(uint16 spell_id, int32 duration) { increase += 20; } - if(IsMezSpell(spell_id)) { + if(IsMezSpell(spell_id)) tic_inc += GetAA(aaMesmerizationMastery); - } return (((duration * increase) / 100) + tic_inc); } float Bot::GetAOERange(uint16 spell_id) { float range; - range = spells[spell_id].aoerange; - if(range == 0) //for TGB spells, they prolly do not have an aoe range - range = spells[spell_id].range; if(range == 0) - range = 10; //something.... + range = spells[spell_id].range; + + if(range == 0) + range = 10; if(IsBardSong(spell_id) && IsBeneficialSpell(spell_id)) { - //Live AA - Extended Notes, SionachiesCrescendo - float song_bonus = aabonuses.SongRange + spellbonuses.SongRange + itembonuses.SongRange; - range += range*song_bonus /100.0f; + float song_bonus = (aabonuses.SongRange + spellbonuses.SongRange + itembonuses.SongRange); + range += (range * song_bonus / 100.0f); } - range = GetActSpellRange(spell_id, range); - return range; } bool Bot::SpellEffect(Mob* caster, uint16 spell_id, float partial) { bool Result = false; - Result = Mob::SpellEffect(caster, spell_id, partial); - - // Franck-add: If healed/doted, a bot must show its new HP to its leader if(IsGrouped()) { Group *g = GetGroup(); if(g) { EQApplicationPacket hp_app; CreateHPPacket(&hp_app); for(int i=0; imembers[i] && g->members[i]->IsClient()) { + if(g->members[i] && g->members[i]->IsClient()) g->members[i]->CastToClient()->QueuePacket(&hp_app); - } } } } - return Result; } @@ -9095,26 +7558,26 @@ void Bot::DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster) { bool Bot::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish, uint32 item_slot, int16 *resist_adjust) { bool Result = false; - if(zone && !zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))) { - - Log.Out(Logs::Detail, Logs::Spells, "CastSpell called for spell %s (%d) on entity %d, slot %d, time %d, mana %d, from item slot %d", - spells[spell_id].name, spell_id, target_id, slot, cast_time, mana_cost, (item_slot==0xFFFFFFFF)?999:item_slot); + Log.Out(Logs::Detail, Logs::Spells, "CastSpell called for spell %s (%d) on entity %d, slot %d, time %d, mana %d, from item slot %d", spells[spell_id].name, spell_id, target_id, slot, cast_time, mana_cost, (item_slot==0xFFFFFFFF)?999:item_slot); if(casting_spell_id == spell_id) ZeroCastingVars(); if(GetClass() != BARD) { if(!IsValidSpell(spell_id) || casting_spell_id || delaytimer || spellend_timer.Enabled() || IsStunned() || IsFeared() || IsMezzed() || (IsSilenced() && !IsDiscipline(spell_id)) || (IsAmnesiad() && IsDiscipline(spell_id))) { - Log.Out(Logs::Detail, Logs::Spells, "Spell casting canceled: not able to cast now. Valid? %d, casting %d, waiting? %d, spellend? %d, stunned? %d, feared? %d, mezed? %d, silenced? %d", - IsValidSpell(spell_id), casting_spell_id, delaytimer, spellend_timer.Enabled(), IsStunned(), IsFeared(), IsMezzed(), IsSilenced() ); + Log.Out(Logs::Detail, Logs::Spells, "Spell casting canceled: not able to cast now. Valid? %d, casting %d, waiting? %d, spellend? %d, stunned? %d, feared? %d, mezed? %d, silenced? %d", IsValidSpell(spell_id), casting_spell_id, delaytimer, spellend_timer.Enabled(), IsStunned(), IsFeared(), IsMezzed(), IsSilenced() ); if(IsSilenced() && !IsDiscipline(spell_id)) Message_StringID(13, SILENCED_STRING); + if(IsAmnesiad() && IsDiscipline(spell_id)) + Message_StringID(13, MELEE_SILENCE); + if(casting_spell_id) AI_Event_SpellCastFinished(false, casting_spell_slot); - return(false); + + return false; } } @@ -9122,60 +7585,45 @@ bool Bot::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, int32 cast_t Message_StringID(13, SPELL_WOULDNT_HOLD); if(casting_spell_id) AI_Event_SpellCastFinished(false, casting_spell_slot); - return(false); + + return false; } - //cannot cast under deivne aura if(DivineAura()) { Log.Out(Logs::Detail, Logs::Spells, "Spell casting canceled: cannot cast while Divine Aura is in effect."); InterruptSpell(173, 0x121, false); - return(false); + return false; } - - // check for fizzle - // note that CheckFizzle itself doesn't let NPCs fizzle, - // but this code allows for it. - if(slot < MAX_PP_MEMSPELL && !CheckFizzle(spell_id)) - { + + if(slot < MAX_PP_MEMSPELL && !CheckFizzle(spell_id)) { int fizzle_msg = IsBardSong(spell_id) ? MISS_NOTE : SPELL_FIZZLE; InterruptSpell(fizzle_msg, 0x121, spell_id); uint32 use_mana = ((spells[spell_id].mana) / 4); Log.Out(Logs::Detail, Logs::Spells, "Spell casting canceled: fizzled. %d mana has been consumed", use_mana); - - // fizzle 1/4 the mana away SetMana(GetMana() - use_mana); - return(false); + return false; } if (HasActiveSong()) { Log.Out(Logs::Detail, Logs::Spells, "Casting a new spell/song while singing a song. Killing old song %d.", bardsong); - //Note: this does NOT tell the client - //_StopSong(); bardsong = 0; bardsong_target_id = 0; bardsong_slot = 0; bardsong_timer.Disable(); } - Result = DoCastSpell(spell_id, target_id, slot, cast_time, mana_cost, oSpellWillFinish, item_slot); } - return Result; } bool Bot::SpellOnTarget(uint16 spell_id, Mob* spelltar) { bool Result = false; - if(!IsValidSpell(spell_id)) return false; if(spelltar) { if(spelltar->IsBot() && (spells[spell_id].targettype == ST_GroupTeleport)) { - // So I made this check because teleporting a group of bots tended to crash the zone - // It seems several group spells also show up as ST_GroupTeleport for some - // reason so I now have to check by spell id. These appear to be Group v1 spells and - // Heal over Time spells. switch(spell_id) { // Paladin case 3577: // Wave of Life @@ -9246,50 +7694,39 @@ bool Bot::SpellOnTarget(uint16 spell_id, Mob* spelltar) { } } - //Franck-add: can't detrimental spell on bots and bots can't detriment on you or the others bots if(((IsDetrimentalSpell(spell_id) && spelltar->IsBot()) || (IsDetrimentalSpell(spell_id) && spelltar->IsClient())) && !IsResurrectionEffects(spell_id)) return false; if(spelltar->IsPet()) { - for(int i=0; iGetZoneID() == 202) && !(this == caster)) { Result = Mob::IsImmuneToSpell(spell_id, caster); - if(!Result) { if(caster->IsBot()) { if(spells[spell_id].targettype == ST_Undead) { if((GetBodyType() != BT_SummonedUndead) && (GetBodyType() != BT_Undead) && (GetBodyType() != BT_Vampire)) { - Log.Out(Logs::Detail, Logs::Spells, "Bot's target is not an undead."); - return true; + Log.Out(Logs::Detail, Logs::Spells, "Bot's target is not an undead."); + return true; } } if(spells[spell_id].targettype == ST_Summoned) { - if((GetBodyType() != BT_SummonedUndead) - && (GetBodyType() != BT_Summoned) - && (GetBodyType() != BT_Summoned2) - && (GetBodyType() != BT_Summoned3) - ) { - Log.Out(Logs::Detail, Logs::Spells, "Bot's target is not a summoned creature."); - return true; + if((GetBodyType() != BT_SummonedUndead) && (GetBodyType() != BT_Summoned) && (GetBodyType() != BT_Summoned2) && (GetBodyType() != BT_Summoned3)) { + Log.Out(Logs::Detail, Logs::Spells, "Bot's target is not a summoned creature."); + return true; } } } @@ -9303,175 +7740,135 @@ bool Bot::IsImmuneToSpell(uint16 spell_id, Mob *caster) { bool Bot::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction) { bool Result = false; - SpellTargetType targetType = spells[spell_id].targettype; - - - // This is so PoK NPC Necro/Shd can create essence emeralds for pc's from perl scripts if(targetType == ST_GroupClientAndPet) { - if(((spell_id == 1768) && (zone->GetZoneID() == 202)) || (!IsDetrimentalSpell(spell_id))) { + if((spell_id == 1768 && zone->GetZoneID() == 202) || (!IsDetrimentalSpell(spell_id))) { CastAction = SingleTarget; return true; } } - Result = Mob::DetermineSpellTargets(spell_id, spell_target, ae_center, CastAction); - return Result; } bool Bot::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot, int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish, uint32 item_slot) { bool Result = false; - - if(GetClass() == BARD) { - // Bard bots casting time is interrupting thier melee + if(GetClass() == BARD) cast_time = 0; - } Result = Mob::DoCastSpell(spell_id, target_id, slot, cast_time, mana_cost, oSpellWillFinish, item_slot); - if(oSpellWillFinish) { const SPDat_Spell_Struct &spell = spells[spell_id]; *oSpellWillFinish = Timer::GetCurrentTime() + ((spell.recast_time > 20000) ? 10000 : spell.recast_time); } - return Result; } -int32 Bot::GenerateBaseManaPoints() -{ - // Now, we need to calc the base mana. +int32 Bot::GenerateBaseManaPoints() { int32 bot_mana = 0; int32 WisInt = 0; int32 MindLesserFactor, MindFactor; int wisint_mana = 0; int base_mana = 0; int ConvertedWisInt = 0; - - switch(GetCasterClass()) - { + switch(GetCasterClass()) { case 'I': WisInt = INT; if(GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= ClientVersion::SoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) { if(WisInt > 100) { ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100); - if(WisInt > 201) { + if(WisInt > 201) ConvertedWisInt -= ((WisInt - 201) * 5 / 4); - } } - else { + else ConvertedWisInt = WisInt; - } + if(GetLevel() < 41) { wisint_mana = (GetLevel() * 75 * ConvertedWisInt / 1000); base_mana = (GetLevel() * 15); - } - else if(GetLevel() < 81) { + } else if(GetLevel() < 81) { wisint_mana = ((3 * ConvertedWisInt) + ((GetLevel() - 40) * 15 * ConvertedWisInt / 100)); base_mana = (600 + ((GetLevel() - 40) * 30)); - } - else { + } else { wisint_mana = (9 * ConvertedWisInt); base_mana = (1800 + ((GetLevel() - 80) * 18)); } - bot_mana = base_mana + wisint_mana; - } - else { - if((( WisInt - 199 ) / 2) > 0) { - MindLesserFactor = ( WisInt - 199 ) / 2; - } - else { + bot_mana = (base_mana + wisint_mana); + } else { + if(((WisInt - 199) / 2) > 0) + MindLesserFactor = ((WisInt - 199) / 2); + else MindLesserFactor = 0; - } + MindFactor = WisInt - MindLesserFactor; - if(WisInt > 100) { + if(WisInt > 100) bot_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40); - } - else { + else bot_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100); - } } break; - case 'W': WisInt = WIS; if(GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= ClientVersion::SoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) { if(WisInt > 100) { ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100); - if(WisInt > 201) { + if(WisInt > 201) ConvertedWisInt -= ((WisInt - 201) * 5 / 4); - } - } - else { + } else ConvertedWisInt = WisInt; - } + if(GetLevel() < 41) { wisint_mana = (GetLevel() * 75 * ConvertedWisInt / 1000); base_mana = (GetLevel() * 15); - } - else if(GetLevel() < 81) { + } else if(GetLevel() < 81) { wisint_mana = ((3 * ConvertedWisInt) + ((GetLevel() - 40) * 15 * ConvertedWisInt / 100)); base_mana = (600 + ((GetLevel() - 40) * 30)); - } - else { + } else { wisint_mana = (9 * ConvertedWisInt); base_mana = (1800 + ((GetLevel() - 80) * 18)); } - bot_mana = base_mana + wisint_mana; - } - else { - if((( WisInt - 199 ) / 2) > 0) { - MindLesserFactor = ( WisInt - 199 ) / 2; - } - else { + bot_mana = (base_mana + wisint_mana); + } else { + if(((WisInt - 199) / 2) > 0) + MindLesserFactor = ((WisInt - 199) / 2); + else MindLesserFactor = 0; - } - MindFactor = WisInt - MindLesserFactor; - if(WisInt > 100) { + + MindFactor = (WisInt - MindLesserFactor); + if(WisInt > 100) bot_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40); - } - else { + else bot_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100); - } } break; - default: bot_mana = 0; break; } - max_mana = bot_mana; - return bot_mana; } -void Bot::GenerateSpecialAttacks() -{ - // Special Attacks - if(((GetClass() == MONK) || (GetClass() == WARRIOR) || (GetClass() == RANGER) || (GetClass() == BERSERKER)) && (GetLevel() >= 60)) { +void Bot::GenerateSpecialAttacks() { + if(((GetClass() == MONK) || (GetClass() == WARRIOR) || (GetClass() == RANGER) || (GetClass() == BERSERKER)) && (GetLevel() >= 60)) SetSpecialAbility(SPECATK_TRIPLE, 1); - } } bool Bot::DoFinishedSpellAETarget(uint16 spell_id, Mob* spellTarget, uint16 slot, bool& stopLogic) { if(GetClass() == BARD) { - if(!ApplyNextBardPulse(bardsong, this, bardsong_slot)) { + if(!ApplyNextBardPulse(bardsong, this, bardsong_slot)) InterruptSpell(SONG_ENDS_ABRUPTLY, 0x121, bardsong); - } + stopLogic = true; } - return true; } bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, uint16 slot, bool& stopLogic) { if(spellTarget) { if(IsGrouped() && (spellTarget->IsBot() || spellTarget->IsClient()) && RuleB(Bots, BotGroupBuffing)) { - //NPC *bot = this->CastToNPC(); bool noGroupSpell = false; uint16 thespell = spell_id; - for(int i=0; i < AIspells.size(); i++) { int j = BotGetSpells(i); int spelltype = BotGetSpellType(i); @@ -9480,11 +7877,8 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, uint16 bool spelltypetargetequal = ((spelltype == 8) && (spells[thespell].targettype == ST_Self)); bool spelltypeclassequal = ((spelltype == 1024) && (GetClass() == SHAMAN)); bool slotequal = (slot == USE_ITEM_SPELL_SLOT); - - // if it's a targeted heal or escape spell or pet spell or it's self only buff or self buff weapon proc, we only want to cast it once if(spellequal || slotequal) { if((spelltypeequal || spelltypetargetequal) || spelltypeclassequal || slotequal) { - // Don't let the Shaman canni themselves to death if(((spells[thespell].effectid[0] == 0) && (spells[thespell].base[0] < 0)) && (spellTarget->GetHP() < ((spells[thespell].base[0] * (-1)) + 100))) { return false; @@ -9500,71 +7894,45 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, uint16 if(!noGroupSpell) { Group *g = GetGroup(); if(g) { - for(int i=0; imembers[i]) { - if((g->members[i]->GetClass() == NECROMANCER) && - (IsEffectInSpell(thespell, SE_AbsorbMagicAtt) || IsEffectInSpell(thespell, SE_Rune))) { - // don't cast this on necro's, their health to mana - // spell eats up the rune spell and it just keeps - // getting recast over and over + if((g->members[i]->GetClass() == NECROMANCER) && (IsEffectInSpell(thespell, SE_AbsorbMagicAtt) || IsEffectInSpell(thespell, SE_Rune))) { } else - { SpellOnTarget(thespell, g->members[i]); - } - if(g->members[i] && g->members[i]->GetPetID()) { + + if(g->members[i] && g->members[i]->GetPetID()) SpellOnTarget(thespell, g->members[i]->GetPet()); - } } } SetMana(GetMana() - (GetActSpellCost(thespell, spells[thespell].mana) * (g->GroupCount() - 1))); } } - stopLogic = true; } } - return true; } bool Bot::DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, uint16 slot, bool& stopLogic) { bool isMainGroupMGB = false; - - //if(GetBotRaidID() > 0) { - // BotRaids *br = entity_list.GetBotRaidByMob(this); - // if(br) { - // for(int n=0; nBotRaidGroups[0] && (br->BotRaidGroups[0]->members[n] == this)) { - // if(GetLevel() >= 59) // MGB AA - // isMainGroupMGB = true; - // break; - // } - // } - // } - //} - if(isMainGroupMGB && (GetClass() != BARD)) { BotGroupSay(this, "MGB %s", spells[spell_id].name); SpellOnTarget(spell_id, this); entity_list.AESpell(this, this, spell_id, true); - } - else { + } else { Group *g = GetGroup(); if(g) { - for(int i=0; imembers[i]) { SpellOnTarget(spell_id, g->members[i]); - if(g->members[i] && g->members[i]->GetPetID()) { + if(g->members[i] && g->members[i]->GetPetID()) SpellOnTarget(spell_id, g->members[i]->GetPet()); - } } } } } - stopLogic = true; - return true; } @@ -9575,7 +7943,6 @@ void Bot::CalcBonuses() { CalcSpellBonuses(&spellbonuses); GenerateAABonuses(&aabonuses); SetAttackTimer(); - CalcATK(); CalcSTR(); CalcSTA(); @@ -9584,16 +7951,13 @@ void Bot::CalcBonuses() { CalcINT(); CalcWIS(); CalcCHA(); - CalcMR(); CalcFR(); CalcDR(); CalcPR(); CalcCR(); CalcCorrup(); - GenerateArmorClass(); - CalcMaxHP(); CalcMaxMana(); CalcMaxEndurance(); @@ -9605,17 +7969,14 @@ void Bot::CalcBonuses() { int32 Bot::CalcHPRegenCap(){ int level = GetLevel(); int32 hpregen_cap = 0; - hpregen_cap = RuleI(Character, ItemHealthRegenCap) + itembonuses.HeroicSTA/25; - - hpregen_cap += aabonuses.ItemHPRegenCap + spellbonuses.ItemHPRegenCap + itembonuses.ItemHPRegenCap; - + hpregen_cap = (RuleI(Character, ItemHealthRegenCap) + itembonuses.HeroicSTA / 25); + hpregen_cap += (aabonuses.ItemHPRegenCap + spellbonuses.ItemHPRegenCap + itembonuses.ItemHPRegenCap); return (hpregen_cap * RuleI(Character, HPRegenMultiplier) / 100); } int32 Bot::CalcManaRegenCap(){ int32 cap = RuleI(Character, ItemManaRegenCap) + aabonuses.ItemManaRegenCap; - switch(GetCasterClass()) - { + switch(GetCasterClass()) { case 'I': cap += (itembonuses.HeroicINT / 25); break; @@ -9623,36 +7984,27 @@ int32 Bot::CalcManaRegenCap(){ cap += (itembonuses.HeroicWIS / 25); break; } - return (cap * RuleI(Character, ManaRegenMultiplier) / 100); } -// Return max stat value for level int32 Bot::GetMaxStat() { int level = GetLevel(); int32 base = 0; - - if (level < 61) { + if (level < 61) base = 255; - } - else if (GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= ClientVersion::SoF) { - base = 255 + 5 * (level - 60); - } - else if (level < 71) { - base = 255 + 5 * (level - 60); - } - else { + else if (GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= ClientVersion::SoF) + base = (255 + 5 * (level - 60)); + else if (level < 71) + base = (255 + 5 * (level - 60)); + else base = 330; - } - return(base); + return base; } int32 Bot::GetMaxResist() { int level = GetLevel(); - int32 base = 500; - if(level > 60) base += ((level - 60) * 5); @@ -9660,93 +8012,63 @@ int32 Bot::GetMaxResist() { } int32 Bot::GetMaxSTR() { - return GetMaxStat() - + itembonuses.STRCapMod - + spellbonuses.STRCapMod - + aabonuses.STRCapMod; + return (GetMaxStat() + itembonuses.STRCapMod + spellbonuses.STRCapMod + aabonuses.STRCapMod); } + int32 Bot::GetMaxSTA() { - return GetMaxStat() - + itembonuses.STACapMod - + spellbonuses.STACapMod - + aabonuses.STACapMod; + return (GetMaxStat() + itembonuses.STACapMod + spellbonuses.STACapMod + aabonuses.STACapMod); } + int32 Bot::GetMaxDEX() { - return GetMaxStat() - + itembonuses.DEXCapMod - + spellbonuses.DEXCapMod - + aabonuses.DEXCapMod; + return (GetMaxStat() + itembonuses.DEXCapMod + spellbonuses.DEXCapMod + aabonuses.DEXCapMod); } + int32 Bot::GetMaxAGI() { - return GetMaxStat() - + itembonuses.AGICapMod - + spellbonuses.AGICapMod - + aabonuses.AGICapMod; + return (GetMaxStat() + itembonuses.AGICapMod + spellbonuses.AGICapMod + aabonuses.AGICapMod); } + int32 Bot::GetMaxINT() { - return GetMaxStat() - + itembonuses.INTCapMod - + spellbonuses.INTCapMod - + aabonuses.INTCapMod; + return (GetMaxStat() + itembonuses.INTCapMod + spellbonuses.INTCapMod + aabonuses.INTCapMod); } + int32 Bot::GetMaxWIS() { - return GetMaxStat() - + itembonuses.WISCapMod - + spellbonuses.WISCapMod - + aabonuses.WISCapMod; + return (GetMaxStat() + itembonuses.WISCapMod + spellbonuses.WISCapMod + aabonuses.WISCapMod); } int32 Bot::GetMaxCHA() { - return GetMaxStat() - + itembonuses.CHACapMod - + spellbonuses.CHACapMod - + aabonuses.CHACapMod; + return (GetMaxStat() + itembonuses.CHACapMod + spellbonuses.CHACapMod + aabonuses.CHACapMod); } + int32 Bot::GetMaxMR() { - return GetMaxResist() - + itembonuses.MRCapMod - + spellbonuses.MRCapMod - + aabonuses.MRCapMod; + return (GetMaxResist() + itembonuses.MRCapMod + spellbonuses.MRCapMod + aabonuses.MRCapMod); } + int32 Bot::GetMaxPR() { - return GetMaxResist() - + itembonuses.PRCapMod - + spellbonuses.PRCapMod - + aabonuses.PRCapMod; + return (GetMaxResist() + itembonuses.PRCapMod + spellbonuses.PRCapMod + aabonuses.PRCapMod); } + int32 Bot::GetMaxDR() { - return GetMaxResist() - + itembonuses.DRCapMod - + spellbonuses.DRCapMod - + aabonuses.DRCapMod; + return (GetMaxResist() + itembonuses.DRCapMod + spellbonuses.DRCapMod + aabonuses.DRCapMod); } + int32 Bot::GetMaxCR() { - return GetMaxResist() - + itembonuses.CRCapMod - + spellbonuses.CRCapMod - + aabonuses.CRCapMod; + return (GetMaxResist() + itembonuses.CRCapMod + spellbonuses.CRCapMod + aabonuses.CRCapMod); } + int32 Bot::GetMaxFR() { - return GetMaxResist() - + itembonuses.FRCapMod - + spellbonuses.FRCapMod - + aabonuses.FRCapMod; + return (GetMaxResist() + itembonuses.FRCapMod + spellbonuses.FRCapMod + aabonuses.FRCapMod); } + int32 Bot::GetMaxCorrup() { - return GetMaxResist() - + itembonuses.CorrupCapMod - + spellbonuses.CorrupCapMod - + aabonuses.CorrupCapMod; + return (GetMaxResist() + itembonuses.CorrupCapMod + spellbonuses.CorrupCapMod + aabonuses.CorrupCapMod); } int32 Bot::CalcSTR() { - int32 val = STR + itembonuses.STR + spellbonuses.STR; - + int32 val = (STR + itembonuses.STR + spellbonuses.STR); int32 mod = aabonuses.STR; - - if(val>255 && GetLevel() <= 60) + if(val > 255 && GetLevel() <= 60) val = 255; - STR = val + mod; - + + STR = (val + mod); if(STR < 1) STR = 1; @@ -9754,18 +8076,16 @@ int32 Bot::CalcSTR() { if(STR > m) STR = m; - return(STR); + return STR; } int32 Bot::CalcSTA() { - int32 val = STA + itembonuses.STA + spellbonuses.STA; - + int32 val = (STA + itembonuses.STA + spellbonuses.STA); int32 mod = aabonuses.STA; - - if(val>255 && GetLevel() <= 60) + if(val > 255 && GetLevel() <= 60) val = 255; - STA = val + mod; - + + STA = (val + mod); if(STA < 1) STA = 1; @@ -9773,18 +8093,16 @@ int32 Bot::CalcSTA() { if(STA > m) STA = m; - return(STA); + return STA; } int32 Bot::CalcAGI() { - int32 val = AGI + itembonuses.AGI + spellbonuses.AGI; + int32 val = (AGI + itembonuses.AGI + spellbonuses.AGI); int32 mod = aabonuses.AGI; - - if(val>255 && GetLevel() <= 60) + if(val > 255 && GetLevel() <= 60) val = 255; - AGI = val + mod; - + AGI = (val + mod); if(AGI < 1) AGI = 1; @@ -9792,18 +8110,16 @@ int32 Bot::CalcAGI() { if(AGI > m) AGI = m; - return(AGI); + return AGI; } int32 Bot::CalcDEX() { - int32 val = DEX + itembonuses.DEX + spellbonuses.DEX; - + int32 val = (DEX + itembonuses.DEX + spellbonuses.DEX); int32 mod = aabonuses.DEX; - - if(val>255 && GetLevel() <= 60) + if(val > 255 && GetLevel() <= 60) val = 255; - DEX = val + mod; - + + DEX = (val + mod); if(DEX < 1) DEX = 1; @@ -9811,35 +8127,34 @@ int32 Bot::CalcDEX() { if(DEX > m) DEX = m; - return(DEX); + return DEX; } int32 Bot::CalcINT() { - int32 val = INT + itembonuses.INT + spellbonuses.INT; - + int32 val = (INT + itembonuses.INT + spellbonuses.INT); int32 mod = aabonuses.INT; - - if(val>255 && GetLevel() <= 60) + if(val > 255 && GetLevel() <= 60) val = 255; - INT = val + mod; + + INT = (val + mod); if(INT < 1) INT = 1; + int m = GetMaxINT(); if(INT > m) INT = m; - return(INT); + return INT; } int32 Bot::CalcWIS() { - int32 val = WIS + itembonuses.WIS + spellbonuses.WIS; - + int32 val = (WIS + itembonuses.WIS + spellbonuses.WIS); int32 mod = aabonuses.WIS; - - if(val>255 && GetLevel() <= 60) + if(val > 255 && GetLevel() <= 60) val = 255; - WIS = val + mod; + + WIS = (val + mod); if(WIS < 1) WIS = 1; @@ -9848,17 +8163,16 @@ int32 Bot::CalcWIS() { if(WIS > m) WIS = m; - return(WIS); + return WIS; } int32 Bot::CalcCHA() { - int32 val = CHA + itembonuses.CHA + spellbonuses.CHA; - + int32 val = (CHA + itembonuses.CHA + spellbonuses.CHA); int32 mod = aabonuses.CHA; - - if(val>255 && GetLevel() <= 60) + if(val > 255 && GetLevel() <= 60) val = 255; - CHA = val + mod; + + CHA = (val + mod); if(CHA < 1) CHA = 1; @@ -9867,18 +8181,13 @@ int32 Bot::CalcCHA() { if(CHA > m) CHA = m; - return(CHA); + return CHA; } -//The AA multipliers are set to be 5, but were 2 on WR -//The resistant discipline which I think should be here is implemented -//in Mob::ResistSpell -int32 Bot::CalcMR() -{ - MR += itembonuses.MR + spellbonuses.MR + aabonuses.MR; - +int32 Bot::CalcMR() { + MR += (itembonuses.MR + spellbonuses.MR + aabonuses.MR); if(GetClass() == WARRIOR) - MR += GetLevel() / 2; + MR += (GetLevel() / 2); if(MR < 1) MR = 1; @@ -9886,21 +8195,19 @@ int32 Bot::CalcMR() if(MR > GetMaxMR()) MR = GetMaxMR(); - return(MR); + return MR; } -int32 Bot::CalcFR() -{ +int32 Bot::CalcFR() { int c = GetClass(); if(c == RANGER) { FR += 4; - int l = GetLevel(); if(l > 49) - FR += l - 49; + FR += (l - 49); } - FR += itembonuses.FR + spellbonuses.FR + aabonuses.FR; + FR += (itembonuses.FR + spellbonuses.FR + aabonuses.FR); if(FR < 1) FR = 1; @@ -9908,57 +8215,48 @@ int32 Bot::CalcFR() if(FR > GetMaxFR()) FR = GetMaxFR(); - return(FR); + return FR; } -int32 Bot::CalcDR() -{ +int32 Bot::CalcDR() { int c = GetClass(); if(c == PALADIN) { DR += 8; - int l = GetLevel(); if(l > 49) - DR += l - 49; - + DR += (l - 49); } else if(c == SHADOWKNIGHT) { DR += 4; - int l = GetLevel(); if(l > 49) - DR += l - 49; + DR += (l - 49); } - DR += itembonuses.DR + spellbonuses.DR + aabonuses.DR; - + DR += (itembonuses.DR + spellbonuses.DR + aabonuses.DR); if(DR < 1) DR = 1; if(DR > GetMaxDR()) DR = GetMaxDR(); - return(DR); + return DR; } -int32 Bot::CalcPR() -{ +int32 Bot::CalcPR() { int c = GetClass(); if(c == ROGUE) { PR += 8; - int l = GetLevel(); if(l > 49) - PR += l - 49; - + PR += (l - 49); } else if(c == SHADOWKNIGHT) { PR += 4; - int l = GetLevel(); if(l > 49) - PR += l - 49; + PR += (l - 49); } - PR += itembonuses.PR + spellbonuses.PR + aabonuses.PR; + PR += (itembonuses.PR + spellbonuses.PR + aabonuses.PR); if(PR < 1) PR = 1; @@ -9966,21 +8264,19 @@ int32 Bot::CalcPR() if(PR > GetMaxPR()) PR = GetMaxPR(); - return(PR); + return PR; } -int32 Bot::CalcCR() -{ +int32 Bot::CalcCR() { int c = GetClass(); if(c == RANGER) { CR += 4; - int l = GetLevel(); if(l > 49) - CR += l - 49; + CR += (l - 49); } - CR += itembonuses.CR + spellbonuses.CR + aabonuses.CR; + CR += (itembonuses.CR + spellbonuses.CR + aabonuses.CR); if(CR < 1) CR = 1; @@ -9988,39 +8284,28 @@ int32 Bot::CalcCR() if(CR > GetMaxCR()) CR = GetMaxCR(); - return(CR); + return CR; } -int32 Bot::CalcCorrup() -{ - Corrup = Corrup + itembonuses.Corrup + spellbonuses.Corrup + aabonuses.Corrup; - +int32 Bot::CalcCorrup() { + Corrup = (Corrup + itembonuses.Corrup + spellbonuses.Corrup + aabonuses.Corrup); if(Corrup > GetMaxCorrup()) Corrup = GetMaxCorrup(); - return(Corrup); + return Corrup; } int32 Bot::CalcATK() { - ATK = itembonuses.ATK + spellbonuses.ATK + aabonuses.ATK + GroupLeadershipAAOffenseEnhancement(); - return(ATK); + ATK = (itembonuses.ATK + spellbonuses.ATK + aabonuses.ATK + GroupLeadershipAAOffenseEnhancement()); + return ATK; } void Bot::CalcRestState() { - - // This method calculates rest state HP and mana regeneration. - // The bot must have been out of combat for RuleI(Character, RestRegenTimeToActivate) seconds, - // must be sitting down, and must not have any detrimental spells affecting them. - // if(!RuleI(Character, RestRegenPercent)) return; RestRegenHP = RestRegenMana = RestRegenEndurance = 0; - - if(IsEngaged() || !IsSitting()) - return; - - if(!rest_timer.Check(false)) + if(IsEngaged() || !IsSitting() || !rest_timer.Check(false)) return; uint32 buff_count = GetMaxTotalSlots(); @@ -10033,44 +8318,35 @@ void Bot::CalcRestState() { } RestRegenHP = (GetMaxHP() * RuleI(Character, RestRegenPercent) / 100); - RestRegenMana = (GetMaxMana() * RuleI(Character, RestRegenPercent) / 100); - if(RuleB(Character, RestRegenEndurance)) RestRegenEndurance = (GetMaxEndurance() * RuleI(Character, RestRegenPercent) / 100); } -int32 Bot::LevelRegen() -{ +int32 Bot::LevelRegen() { int level = GetLevel(); bool bonus = GetRaceBitmask(_baseRace) & RuleI(Character, BaseHPRegenBonusRaces); uint8 multiplier1 = bonus ? 2 : 1; int32 hp = 0; - - //these calculations should match up with the info from Monkly Business, which was last updated ~05/2008: http://www.monkly-business.net/index.php?pageid=abilities if (level < 51) { if (IsSitting()) { if (level < 20) - hp += 2 * multiplier1; + hp += (2 * multiplier1); else if (level < 50) - hp += 3 * multiplier1; - else //level == 50 - hp += 4 * multiplier1; - } - else //feigned or standing - hp += 1 * multiplier1; - } - //there may be an easier way to calculate this next part, but I don't know what it is - else { //level >= 51 + hp += (3 * multiplier1); + else + hp += (4 * multiplier1); + } else + hp += (1 * multiplier1); + } else { int32 tmp = 0; float multiplier2 = 1; if (level < 56) { tmp = 2; if (bonus) multiplier2 = 3; - } - else if (level < 60) { - tmp = 3; + } else if (level < 60) { + tmp = 3; if (bonus) multiplier2 = 3.34; } @@ -10078,56 +8354,44 @@ int32 Bot::LevelRegen() tmp = 4; if (bonus) multiplier2 = 3; - } - else if (level < 63) { + } else if (level < 63) { tmp = 5; if (bonus) multiplier2 = 2.8; - } - else if (level < 65) { + } else if (level < 65) { tmp = 6; if (bonus) multiplier2 = 2.67; - } - else { //level >= 65 + } else { tmp = 7; if (bonus) multiplier2 = 2.58; } - - hp += int32(float(tmp) * multiplier2); + hp += (int32(float(tmp) * multiplier2)); } - return hp; } int32 Bot::CalcHPRegen() { - int32 regen = LevelRegen() + itembonuses.HPRegen + spellbonuses.HPRegen; - regen += aabonuses.HPRegen + GroupLeadershipAAHealthRegeneration(); - - regen = (regen * RuleI(Character, HPRegenMultiplier)) / 100; + int32 regen = (LevelRegen() + itembonuses.HPRegen + spellbonuses.HPRegen); + regen += (aabonuses.HPRegen + GroupLeadershipAAHealthRegeneration()); + regen = ((regen * RuleI(Character, HPRegenMultiplier)) / 100); return regen; } -int32 Bot::CalcManaRegen() -{ +int32 Bot::CalcManaRegen() { uint8 level = GetLevel(); uint8 botclass = GetClass(); int32 regen = 0; - //this should be changed so we dont med while camping, etc... - if (IsSitting()) - { + if (IsSitting()) { BuffFadeBySitModifier(); if(botclass != WARRIOR && botclass != MONK && botclass != ROGUE && botclass != BERSERKER) { - regen = (((GetSkill(SkillMeditate) / 10) + (level - (level / 4))) / 4) + 4; - regen += spellbonuses.ManaRegen + itembonuses.ManaRegen; - } - else - regen = 2 + spellbonuses.ManaRegen + itembonuses.ManaRegen; - } - else { - regen = 2 + spellbonuses.ManaRegen + itembonuses.ManaRegen; - } + regen = ((((GetSkill(SkillMeditate) / 10) + (level - (level / 4))) / 4) + 4); + regen += (spellbonuses.ManaRegen + itembonuses.ManaRegen); + } else + regen = (2 + spellbonuses.ManaRegen + itembonuses.ManaRegen); + } else + regen = (2 + spellbonuses.ManaRegen + itembonuses.ManaRegen); if(GetCasterClass() == 'I') regen += (itembonuses.HeroicINT / 25); @@ -10137,37 +8401,18 @@ int32 Bot::CalcManaRegen() regen = 0; regen += aabonuses.ManaRegen; - - regen = (regen * RuleI(Character, ManaRegenMultiplier)) / 100; - + regen = ((regen * RuleI(Character, ManaRegenMultiplier)) / 100); float mana_regen_rate = RuleR(Bots, BotManaRegen); if(mana_regen_rate < 0.0f) mana_regen_rate = 0.0f; - regen = regen * mana_regen_rate; // 90% of people wouldnt guess that manaregen would decrease the larger the number they input, this makes more sense - + regen = (regen * mana_regen_rate); return regen; } -// This is for calculating Base HPs + STA bonus for SoD or later clients. uint32 Bot::GetClassHPFactor() { - - int factor; - - // Note: Base HP factor under level 41 is equal to factor / 12, and from level 41 to 80 is factor / 6. - // Base HP over level 80 is factor / 10 - // HP per STA point per level is factor / 30 for level 80+ - // HP per STA under level 40 is the level 80 HP Per STA / 120, and for over 40 it is / 60. - - switch(GetClass()) - { - case DRUID: - case ENCHANTER: - case NECROMANCER: - case MAGICIAN: - case WIZARD: - factor = 240; - break; + uint32 factor; + switch(GetClass()) { case BEASTLORD: case BERSERKER: case MONK: @@ -10199,46 +8444,36 @@ uint32 Bot::GetClassHPFactor() { int32 Bot::CalcMaxHP() { int32 bot_hp = 0; uint32 nd = 10000; - - bot_hp += GenerateBaseHitPoints() + itembonuses.HP; - - nd += aabonuses.MaxHP; //Natural Durability, Physical Enhancement, Planar Durability - bot_hp = (float)bot_hp * (float)nd / (float)10000; //this is to fix the HP-above-495k issue - bot_hp += spellbonuses.HP + aabonuses.HP; - + bot_hp += (GenerateBaseHitPoints() + itembonuses.HP); + nd += aabonuses.MaxHP; + bot_hp = ((float)bot_hp * (float)nd / (float)10000); + bot_hp += (spellbonuses.HP + aabonuses.HP); bot_hp += GroupLeadershipAAHealthEnhancement(); - - bot_hp += bot_hp * ((spellbonuses.MaxHPChange + itembonuses.MaxHPChange) / 10000.0f); + bot_hp += (bot_hp * ((spellbonuses.MaxHPChange + itembonuses.MaxHPChange) / 10000.0f)); max_hp = bot_hp; - if (cur_hp > max_hp) cur_hp = max_hp; int hp_perc_cap = spellbonuses.HPPercCap[0]; if(hp_perc_cap) { - int curHP_cap = (max_hp * hp_perc_cap) / 100; + int curHP_cap = ((max_hp * hp_perc_cap) / 100); if (cur_hp > curHP_cap || (spellbonuses.HPPercCap[1] && cur_hp > spellbonuses.HPPercCap[1])) cur_hp = curHP_cap; } - return max_hp; } -int32 Bot::CalcMaxEndurance() -{ - max_end = CalcBaseEndurance() + spellbonuses.Endurance + itembonuses.Endurance; - - if (max_end < 0) { +int32 Bot::CalcMaxEndurance() { + max_end = (CalcBaseEndurance() + spellbonuses.Endurance + itembonuses.Endurance); + if (max_end < 0) max_end = 0; - } - if (cur_end > max_end) { + if (cur_end > max_end) cur_end = max_end; - } int end_perc_cap = spellbonuses.EndPercCap[0]; if(end_perc_cap) { - int curEnd_cap = (max_end * end_perc_cap) / 100; + int curEnd_cap = ((max_end * end_perc_cap) / 100); if (cur_end > curEnd_cap || (spellbonuses.EndPercCap[1] && cur_end > spellbonuses.EndPercCap[1])) cur_end = curEnd_cap; } @@ -10246,49 +8481,37 @@ int32 Bot::CalcMaxEndurance() return max_end; } -int32 Bot::CalcBaseEndurance() -{ +int32 Bot::CalcBaseEndurance() { int32 base_end = 0; int32 base_endurance = 0; int32 ConvertedStats = 0; int32 sta_end = 0; int Stats = 0; - if(GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= ClientVersion::SoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) { int HeroicStats = 0; - Stats = ((GetSTR() + GetSTA() + GetDEX() + GetAGI()) / 4); HeroicStats = ((GetHeroicSTR() + GetHeroicSTA() + GetHeroicDEX() + GetHeroicAGI()) / 4); - if (Stats > 100) { ConvertedStats = (((Stats - 100) * 5 / 2) + 100); - if (Stats > 201) { + if (Stats > 201) ConvertedStats -= ((Stats - 201) * 5 / 4); - } - } - else { + } else ConvertedStats = Stats; - } if (GetLevel() < 41) { sta_end = (GetLevel() * 75 * ConvertedStats / 1000); base_endurance = (GetLevel() * 15); - } - else if (GetLevel() < 81) { + } else if (GetLevel() < 81) { sta_end = ((3 * ConvertedStats) + ((GetLevel() - 40) * 15 * ConvertedStats / 100)); base_endurance = (600 + ((GetLevel() - 40) * 30)); - } - else { + } else { sta_end = (9 * ConvertedStats); base_endurance = (1800 + ((GetLevel() - 80) * 18)); } base_end = (base_endurance + sta_end + (HeroicStats * 10)); - } - else - { - Stats = GetSTR()+GetSTA()+GetDEX()+GetAGI(); - int LevelBase = GetLevel() * 15; - + } else { + Stats = (GetSTR()+GetSTA()+GetDEX()+GetAGI()); + int LevelBase = (GetLevel() * 15); int at_most_800 = Stats; if(at_most_800 > 800) at_most_800 = 800; @@ -10297,57 +8520,45 @@ int32 Bot::CalcBaseEndurance() int HalfBonus400to800 = 0; int Bonus800plus = 0; int HalfBonus800plus = 0; - - int BonusUpto800 = int( at_most_800 / 4 ) ; + int BonusUpto800 = int(at_most_800 / 4) ; if(Stats > 400) { - Bonus400to800 = int( (at_most_800 - 400) / 4 ); - HalfBonus400to800 = int( std::max( ( at_most_800 - 400 ), 0 ) / 8 ); - + Bonus400to800 = int((at_most_800 - 400) / 4); + HalfBonus400to800 = int(std::max((at_most_800 - 400), 0) / 8); if(Stats > 800) { - Bonus800plus = int( (Stats - 800) / 8 ) * 2; - HalfBonus800plus = int( (Stats - 800) / 16 ); + Bonus800plus = (int((Stats - 800) / 8) * 2); + HalfBonus800plus = int((Stats - 800) / 16); } } - int bonus_sum = BonusUpto800 + Bonus400to800 + HalfBonus400to800 + Bonus800plus + HalfBonus800plus; - + int bonus_sum = (BonusUpto800 + Bonus400to800 + HalfBonus400to800 + Bonus800plus + HalfBonus800plus); base_end = LevelBase; - - //take all of the sums from above, then multiply by level*0.075 - base_end += ( bonus_sum * 3 * GetLevel() ) / 40; + base_end += ((bonus_sum * 3 * GetLevel()) / 40); } return base_end; } int32 Bot::CalcEnduranceRegen() { - int32 regen = int32(GetLevel() * 4 / 10) + 2; - regen += spellbonuses.EnduranceRegen + itembonuses.EnduranceRegen; - + int32 regen = (int32(GetLevel() * 4 / 10) + 2); + regen += (spellbonuses.EnduranceRegen + itembonuses.EnduranceRegen); return (regen * RuleI(Character, EnduranceRegenMultiplier) / 100); } int32 Bot::CalcEnduranceRegenCap() { - int cap = (RuleI(Character, ItemEnduranceRegenCap) + itembonuses.HeroicSTR/25 + itembonuses.HeroicDEX/25 + itembonuses.HeroicAGI/25 + itembonuses.HeroicSTA/25); - + int cap = (RuleI(Character, ItemEnduranceRegenCap) + itembonuses.HeroicSTR / 25 + itembonuses.HeroicDEX / 25 + itembonuses.HeroicAGI / 25 + itembonuses.HeroicSTA / 25); return (cap * RuleI(Character, EnduranceRegenMultiplier) / 100); } -void Bot::SetEndurance(int32 newEnd) -{ - /*Endurance can't be less than 0 or greater than max*/ +void Bot::SetEndurance(int32 newEnd) { if(newEnd < 0) newEnd = 0; - else if(newEnd > GetMaxEndurance()){ + else if(newEnd > GetMaxEndurance()) newEnd = GetMaxEndurance(); - } cur_end = newEnd; } void Bot::DoEnduranceUpkeep() { int upkeep_sum = 0; - - int cost_redux = spellbonuses.EnduranceReduction + itembonuses.EnduranceReduction; - + int cost_redux = (spellbonuses.EnduranceReduction + itembonuses.EnduranceReduction); uint32 buffs_i; uint32 buff_count = GetMaxTotalSlots(); for (buffs_i = 0; buffs_i < buff_count; buffs_i++) { @@ -10356,15 +8567,15 @@ void Bot::DoEnduranceUpkeep() { if(upkeep > 0) { if(cost_redux > 0) { if(upkeep <= cost_redux) - continue; //reduced to 0 + continue; + upkeep -= cost_redux; } - if((upkeep+upkeep_sum) > GetEndurance()) { - //they do not have enough to keep this one going. + + if((upkeep+upkeep_sum) > GetEndurance()) BuffFadeBySlot(buffs_i); - } else { + else upkeep_sum += upkeep; - } } } } @@ -10376,13 +8587,11 @@ void Bot::DoEnduranceUpkeep() { void Bot::Camp(bool databaseSave) { Sit(); - if(IsGrouped()) { + if(IsGrouped()) RemoveBotFromGroup(this, GetGroup()); - } - if(GetInHealRotation()) { + if(GetInHealRotation()) GetHealRotationLeader()->RemoveHealRotationMember(this); - } if(databaseSave) Save(); @@ -10391,9 +8600,8 @@ void Bot::Camp(bool databaseSave) { } void Bot::Zone() { - if(HasGroup()) { + if(HasGroup()) GetGroup()->MemberZoned(this); - } Save(); Depop(); @@ -10401,38 +8609,26 @@ void Bot::Zone() { bool Bot::IsArcheryRange(Mob *target) { bool result = false; - if(target) { - float range = GetBotArcheryRange() + 5.0; //Fudge it a little, client will let you hit something at 0 0 0 when you are at 205 0 0 - + float range = (GetBotArcheryRange() + 5.0); range *= range; - float targetDistance = DistanceSquaredNoZ(m_Position, target->GetPosition()); - - float minRuleDistance = RuleI(Combat, MinRangedAttackDist) * RuleI(Combat, MinRangedAttackDist); - + float minRuleDistance = (RuleI(Combat, MinRangedAttackDist) * RuleI(Combat, MinRangedAttackDist)); if((targetDistance > range) || (targetDistance < minRuleDistance)) result = false; else result = true; } - return result; } bool Bot::IsBotCasterCombatRange(Mob *target) { bool result = false; - if(target) { float range = BotAISpellRange; - range *= range; - - // half the max so the bot doesn't always stop at max range to allow combat movement range *= .5; - float targetDistance = DistanceSquaredNoZ(m_Position, target->GetPosition()); - if(targetDistance > range) result = false; else @@ -10445,36 +8641,28 @@ bool Bot::IsBotCasterCombatRange(Mob *target) { bool Bot::IsGroupPrimaryHealer() { bool result = false; uint8 botclass = GetClass(); - if(HasGroup()) { Group *g = GetGroup(); - - switch(botclass) - { - case CLERIC: - { + switch(botclass) { + case CLERIC: { result = true; break; } - case DRUID: - { + case DRUID: { result = GroupHasClericClass(g) ? false : true; break; } - case SHAMAN: - { + case SHAMAN: { result = (GroupHasClericClass(g) || GroupHasDruidClass(g)) ? false : true; break; } case PALADIN: case RANGER: - case BEASTLORD: - { + case BEASTLORD: { result = GroupHasPriestClass(g) ? false : true; break; } - default: - { + default: { result = false; break; } @@ -10487,29 +8675,22 @@ bool Bot::IsGroupPrimaryHealer() { bool Bot::IsGroupPrimarySlower() { bool result = false; uint8 botclass = GetClass(); - if(HasGroup()) { Group *g = GetGroup(); - - switch(botclass) - { - case SHAMAN: - { + switch(botclass) { + case SHAMAN: { result = true; break; } - case ENCHANTER: - { + case ENCHANTER: { result = GroupHasShamanClass(g) ? false : true; break; } - case BEASTLORD: - { + case BEASTLORD: { result = (GroupHasShamanClass(g) || GroupHasEnchanterClass(g)) ? false : true; break; } - default: - { + default: { result = false; break; } @@ -10532,14 +8713,9 @@ bool Bot::CanHeal() { botSpell = GetFirstBotSpellBySpellType(this, SpellType_Heal); - if(botSpell.SpellId != 0){ + if(botSpell.SpellId != 0) result = true; - } - - /*if(GetFirstBotSpellBySpellType(this, SpellType_Heal)){ - result = true; - }*/ - + return result; } @@ -10547,32 +8723,26 @@ bool Bot::CalculateNewPosition2(float x, float y, float z, float speed, bool che return MakeNewPositionAndSendUpdate(x, y, z, speed, checkZ); } -// Orders all bots in the specified group to follow their group leader. void Bot::BotGroupOrderFollow(Group* group, Client* client) { if(group && client) { Mob* groupLeader = group->GetLeader(); - if(groupLeader) { for(int i = 0; i< MAX_GROUP_MEMBERS; i++) { if(group->members[i] && group->members[i]->IsBot()) { Bot* botGroupMember = group->members[i]->CastToBot(); - if(botGroupMember && botGroupMember->GetBotOwnerCharacterID() == client->CharacterID()) { if(group->IsLeader(botGroupMember) && botGroupMember->GetBotOwner()) { botGroupMember->SetFollowID(botGroupMember->GetBotOwner()->GetID()); if(botGroupMember->GetBotOwner()) botGroupMember->BotGroupSay(botGroupMember, "Following %s.", botGroupMember->GetBotOwner()->GetName()); - } - else { + } else { botGroupMember->SetFollowID(groupLeader->GetID()); botGroupMember->BotGroupSay(botGroupMember, "Following %s.", groupLeader->GetCleanName()); } botGroupMember->WipeHateList(); - - if(botGroupMember->HasPet() && botGroupMember->GetPet()) { + if(botGroupMember->HasPet() && botGroupMember->GetPet()) botGroupMember->GetPet()->WipeHateList(); - } } } } @@ -10580,42 +8750,33 @@ void Bot::BotGroupOrderFollow(Group* group, Client* client) { } } -// Orders all bots in the specified group to guard their current location. void Bot::BotGroupOrderGuard(Group* group, Client* client) { if(group && client) { for(int i = 0; i< MAX_GROUP_MEMBERS; i++) { if(group->members[i] && group->members[i]->IsBot()) { Bot* botGroupMember = group->members[i]->CastToBot(); - if(botGroupMember && botGroupMember->GetBotOwnerCharacterID() == client->CharacterID()) { botGroupMember->SetFollowID(0); botGroupMember->BotGroupSay(botGroupMember, "Guarding here."); - botGroupMember->WipeHateList(); - - if(botGroupMember->HasPet() && botGroupMember->GetPet()) { + if(botGroupMember->HasPet() && botGroupMember->GetPet()) botGroupMember->GetPet()->WipeHateList(); - } } } } } } -// Orders all bots in the specified group to attack their group leader's target. void Bot::BotGroupOrderAttack(Group* group, Mob* target, Client* client) { if(group && target) { Mob* groupLeader = group->GetLeader(); - if(groupLeader) { for(int i=0; i < MAX_GROUP_MEMBERS; i++) { if(group->members[i] && group->members[i]->IsBot()) { Bot* botGroupMember = group->members[i]->CastToBot(); - if(botGroupMember->GetBotOwnerCharacterID() == client->CharacterID()) { botGroupMember->WipeHateList(); botGroupMember->AddToHateList(target, 1); - if(botGroupMember->HasPet() && botGroupMember->GetPet()) { botGroupMember->GetPet()->WipeHateList(); botGroupMember->GetPet()->AddToHateList(target, 1); @@ -10627,18 +8788,15 @@ void Bot::BotGroupOrderAttack(Group* group, Mob* target, Client* client) { } } -// Summons all bot group members to ther owners location. void Bot::BotGroupSummon(Group* group, Client* client) { if(group) { for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { if(group->members[i] && group->members[i]->IsBot()) { Bot* botMember = group->members[i]->CastToBot(); - if(botMember->GetBotOwnerCharacterID() == client->CharacterID()) { botMember->SetTarget(botMember->GetBotOwner()); botMember->WipeHateList(); botMember->Warp(glm::vec3(botMember->GetBotOwner()->GetPosition())); - if(botMember->HasPet() && botMember->GetPet()) { botMember->GetPet()->SetTarget(botMember); botMember->GetPet()->WipeHateList(); @@ -10650,13 +8808,10 @@ void Bot::BotGroupSummon(Group* group, Client* client) { } } -// Finds a bot in the entitity list by bot owner character id and the bot first name Bot* Bot::GetBotByBotClientOwnerAndBotName(Client* c, std::string botName) { Bot* Result = 0; - if(c) { std::list BotList = entity_list.GetBotsByBotOwnerCharacterID(c->CharacterID()); - if(!BotList.empty()) { for(std::list::iterator botListItr = BotList.begin(); botListItr != BotList.end(); ++botListItr) { if(std::string((*botListItr)->GetCleanName()) == botName) { @@ -10666,11 +8821,9 @@ Bot* Bot::GetBotByBotClientOwnerAndBotName(Client* c, std::string botName) { } } } - return Result; } -// Processes a group invite from a Client for a Bot character. void Bot::ProcessBotGroupInvite(Client* c, std::string botName) { if(c) { Bot* invitedBot = GetBotByBotClientOwnerAndBotName(c, botName); @@ -10685,16 +8838,11 @@ void Bot::ProcessBotGroupInvite(Client* c, std::string botName) { database.SetGroupID(c->GetName(), g->GetID(), c->CharacterID()); database.SetGroupID(invitedBot->GetCleanName(), g->GetID(), invitedBot->GetBotID()); } - } - else { + } else { AddBotToGroup(invitedBot, c->GetGroup()); database.SetGroupID(invitedBot->GetCleanName(), c->GetGroup()->GetID(), invitedBot->GetBotID()); } - - /*if(c->GetBotRaidID() > 0) - invitedBot->SetBotRaidID(c->GetBotRaidID());*/ } - // TODO: if there is a bot but the bot is already in a group, do we send an group invitation cancel message back to the client? } } @@ -11128,52 +9276,6 @@ void Bot::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug, } } - // Add Item Faction Mods - //if (item->FactionMod1) - //{ - // if (item->FactionAmt1 > 0 && item->FactionAmt1 > GetItemFactionBonus(item->FactionMod1)) - // { - // AddItemFactionBonus(item->FactionMod1, item->FactionAmt1); - // } - // else if (item->FactionAmt1 < 0 && item->FactionAmt1 < GetItemFactionBonus(item->FactionMod1)) - // { - // AddItemFactionBonus(item->FactionMod1, item->FactionAmt1); - // } - //} - //if (item->FactionMod2) - //{ - // if (item->FactionAmt2 > 0 && item->FactionAmt2 > GetItemFactionBonus(item->FactionMod2)) - // { - // AddItemFactionBonus(item->FactionMod2, item->FactionAmt2); - // } - // else if (item->FactionAmt2 < 0 && item->FactionAmt2 < GetItemFactionBonus(item->FactionMod2)) - // { - // AddItemFactionBonus(item->FactionMod2, item->FactionAmt2); - // } - //} - //if (item->FactionMod3) - //{ - // if (item->FactionAmt3 > 0 && item->FactionAmt3 > GetItemFactionBonus(item->FactionMod3)) - // { - // AddItemFactionBonus(item->FactionMod3, item->FactionAmt3); - // } - // else if (item->FactionAmt3 < 0 && item->FactionAmt3 < GetItemFactionBonus(item->FactionMod3)) - // { - // AddItemFactionBonus(item->FactionMod3, item->FactionAmt3); - // } - //} - //if (item->FactionMod4) - //{ - // if (item->FactionAmt4 > 0 && item->FactionAmt4 > GetItemFactionBonus(item->FactionMod4)) - // { - // AddItemFactionBonus(item->FactionMod4, item->FactionAmt4); - // } - // else if (item->FactionAmt4 < 0 && item->FactionAmt4 < GetItemFactionBonus(item->FactionMod4)) - // { - // AddItemFactionBonus(item->FactionMod4, item->FactionAmt4); - // } - //} - if (item->ExtraDmgSkill != 0 && item->ExtraDmgSkill <= HIGHEST_SKILL) { if((newbon->SkillDamageAmount[item->ExtraDmgSkill] + item->ExtraDmgAmt) > RuleI(Character, ItemExtraDmgCap)) newbon->SkillDamageAmount[item->ExtraDmgSkill] = RuleI(Character, ItemExtraDmgCap); @@ -11353,8 +9455,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { c->Message(0, "#bot groupmessages [on|off] [bot name|all] - Turns group messages on/off for named bot/all bots."); c->Message(0, "#bot defensive [bot name] - Causes warrior or knight bot to use defensive discipline / buff."); c->Message(0, "#bot healrotation help - Displays the commands available to manage BOT heal rotations."); - // TODO: - // c->Message(0, "#bot illusion - Enchanter Bot cast an illusion buff spell on you or your target."); c->Message(0, "#bot pull [] [target] - Bot Pulling Target NPC's"); c->Message(0, "#bot setinspectmessage - Copies your inspect message to a targeted bot that you own"); c->Message(0, "#bot bardoutofcombat [on|off] - Determines wheter bard bots use out of combat songs."); @@ -11470,7 +9570,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(!strcasecmp(sep->arg[1], "giveitem")) { if(c->GetTarget() && c->GetTarget()->IsBot() && (c->GetTarget()->CastToBot()->GetBotOwner() == c)) { - // Its a bot targetted and this client is the bots owner Bot* targetedBot = c->GetTarget()->CastToBot(); if(targetedBot) targetedBot->FinishTrade(c, BotTradeClientNoDropNoTrade); @@ -11484,17 +9583,13 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(!strcasecmp(sep->arg[1], "camp")) { if(!strcasecmp(sep->arg[2], "all")) { - // Camp out all bots owned by this bot owner BotOrderCampAll(c); - } - else { - // Camp only the targetted bot + } else { if(c->GetTarget() && c->GetTarget()->IsBot() && (c->GetTarget()->CastToBot()->GetBotOwner()->CastToClient() == c)) { Bot* targetedBot = c->GetTarget()->CastToBot(); if(targetedBot) targetedBot->Camp(); - } - else + } else c->Message(15, "You must target a bot you own to do this."); } @@ -11505,16 +9600,13 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(sep->arg[2][0] == '\0' || sep->arg[3][0] == '\0' || sep->arg[4][0] == '\0' || sep->arg[5][0] == '\0' || sep->arg[6][0] != '\0') { c->Message(0, "Usage: #bot create [name] [class(id)] [race(id)] [gender (male/female)]"); return; - } - else if(strcasecmp(sep->arg[3],"1") && strcasecmp(sep->arg[3],"2") && strcasecmp(sep->arg[3],"3") && strcasecmp(sep->arg[3],"4") && strcasecmp(sep->arg[3],"5") && strcasecmp(sep->arg[3],"6") && strcasecmp(sep->arg[3],"7") && strcasecmp(sep->arg[3],"8") && strcasecmp(sep->arg[3],"9") && strcasecmp(sep->arg[3],"10") && strcasecmp(sep->arg[3],"11") && strcasecmp(sep->arg[3],"12") && strcasecmp(sep->arg[3],"13") && strcasecmp(sep->arg[3],"14") && strcasecmp(sep->arg[3],"15") && strcasecmp(sep->arg[3],"16")) { + } else if(strcasecmp(sep->arg[3],"1") && strcasecmp(sep->arg[3],"2") && strcasecmp(sep->arg[3],"3") && strcasecmp(sep->arg[3],"4") && strcasecmp(sep->arg[3],"5") && strcasecmp(sep->arg[3],"6") && strcasecmp(sep->arg[3],"7") && strcasecmp(sep->arg[3],"8") && strcasecmp(sep->arg[3],"9") && strcasecmp(sep->arg[3],"10") && strcasecmp(sep->arg[3],"11") && strcasecmp(sep->arg[3],"12") && strcasecmp(sep->arg[3],"13") && strcasecmp(sep->arg[3],"14") && strcasecmp(sep->arg[3],"15") && strcasecmp(sep->arg[3],"16")) { c->Message(0, "Usage: #bot create [name] [class(id)] [race(id)] [gender (male/female)]"); return; - } - else if(strcasecmp(sep->arg[4],"1") && strcasecmp(sep->arg[4],"2") && strcasecmp(sep->arg[4],"3") && strcasecmp(sep->arg[4],"4") && strcasecmp(sep->arg[4],"5") && strcasecmp(sep->arg[4],"6") && strcasecmp(sep->arg[4],"7") && strcasecmp(sep->arg[4],"8") && strcasecmp(sep->arg[4],"9") && strcasecmp(sep->arg[4],"10") && strcasecmp(sep->arg[4],"11") && strcasecmp(sep->arg[4],"12") && strcasecmp(sep->arg[4],"330") && strcasecmp(sep->arg[4],"128") && strcasecmp(sep->arg[4],"130") && strcasecmp(sep->arg[4],"522")) { + } else if(strcasecmp(sep->arg[4],"1") && strcasecmp(sep->arg[4],"2") && strcasecmp(sep->arg[4],"3") && strcasecmp(sep->arg[4],"4") && strcasecmp(sep->arg[4],"5") && strcasecmp(sep->arg[4],"6") && strcasecmp(sep->arg[4],"7") && strcasecmp(sep->arg[4],"8") && strcasecmp(sep->arg[4],"9") && strcasecmp(sep->arg[4],"10") && strcasecmp(sep->arg[4],"11") && strcasecmp(sep->arg[4],"12") && strcasecmp(sep->arg[4],"330") && strcasecmp(sep->arg[4],"128") && strcasecmp(sep->arg[4],"130") && strcasecmp(sep->arg[4],"522")) { c->Message(0, "Usage: #bot create [name] [class(1-16)] [race(1-12,128,130,330,522)] [gender (male/female)]"); return; - } - else if(strcasecmp(sep->arg[5],"male") && strcasecmp(sep->arg[5],"female")) { + } else if(strcasecmp(sep->arg[5],"male") && strcasecmp(sep->arg[5],"female")) { c->Message(0, "Usage: #bot create [name] [class(1-16)] [race(1-12,128,130,330,522)] [gender (male/female)]"); return; } @@ -11558,7 +9650,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { return; } - // Now that all validation is complete, we can save our newly created bot if(!NewBot->Save()) c->Message(0, "Unable to save %s as a bot.", NewBot->GetCleanName()); else @@ -11567,8 +9658,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { else { // TODO: Log error message here } - - // Bot creation is complete return; } @@ -11615,7 +9704,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(!strcasecmp(sep->arg[1], "list")) { bool listAll = true; int iClass = atoi(sep->arg[2]); - if(iClass > 0 && iClass < 17) listAll = false; @@ -11633,8 +9721,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { c->Message(0, "Name: %s -- Class: %s -- Level: %u -- Race: %s", TempAvailableBotsList->BotName, ClassIdToString(TempAvailableBotsList->BotClass).c_str(), TempAvailableBotsList->BotLevel, RaceIdToString(TempAvailableBotsList->BotRace).c_str()); } - } - else { + } else { c->Message(0, "You have no bots created. Use the #bot create command to create a bot."); } } @@ -11642,7 +9729,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(!strcasecmp(sep->arg[1], "mana")) { bool listAll = false; Bot* bot = 0; - if(sep->argnum == 2) { if(std::string(sep->arg[2]).compare("all") == 0) listAll = true; @@ -11652,21 +9738,16 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(tempBot && tempBot->GetBotOwner() == c) bot = tempBot; } - } - else { + } else { if(c->GetTarget() && c->GetTarget()->IsBot()) bot = c->GetTarget()->CastToBot(); } if(bot && !listAll) { - // Specific bot only if(bot->GetClass() != WARRIOR && bot->GetClass() != MONK && bot->GetClass() != BARD && bot->GetClass() != BERSERKER && bot->GetClass() != ROGUE) c->Message(0, "Name: %s -- Class: %s -- Mana: %3.1f%%", bot->GetCleanName(), ClassIdToString(bot->GetClass()).c_str(), bot->GetManaRatio()); - } - else { - // List all + } else { std::list spawnedBots = entity_list.GetBotsByBotOwnerCharacterID(c->CharacterID()); - if(!spawnedBots.empty()) { for(std::list::iterator botsListItr = spawnedBots.begin(); botsListItr != spawnedBots.end(); ++botsListItr) { Bot* tempBot = *botsListItr; @@ -11675,12 +9756,10 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { c->Message(0, "Name: %s -- Class: %s -- Mana: %3.1f%%", tempBot->GetCleanName(), ClassIdToString(tempBot->GetClass()).c_str(), tempBot->GetManaRatio()); } } - } - else { + } else { c->Message(0, "You have no spawned bots in this zone."); } } - return; } @@ -11702,16 +9781,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { return; } - /*if(c->GetBotRaidID() > 0) { - BotRaids *br = entity_list.GetBotRaidByMob(c->CastToMob()); - if(br) { - if(br->GetBotRaidAggro()) { - c->Message(15, "You can't summon bots while you are engaged."); - return; - } - } - }*/ - if(c->IsGrouped()) { Group *g = entity_list.GetGroupByClient(c); for (int i=0; iSpawn(c, &TempErrorMessage); if(!TempErrorMessage.empty()) { @@ -11790,9 +9858,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } TempBot->BotGroupSay(TempBot, "I am ready for battle."); - } - else { - // We did not find a bot for the specified bot id from the database + } else { c->Message(0, "BotID: %i not found", atoi(sep->arg[2])); } @@ -11841,18 +9907,12 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { else if(c->GetTarget()->IsMob() && !c->GetTarget()->IsPet()) { Mob *b = c->GetTarget(); - if(b) - { - // Is our target "botable" ? + if(b) { if(!b->IsBot()){ c->Message(15, "You must target a bot!"); - } - else if((b->CastToBot()->GetBotOwnerCharacterID() != c->CharacterID())) - { + } else if((b->CastToBot()->GetBotOwnerCharacterID() != c->CharacterID())) { b->CastToBot()->BotGroupSay(b->CastToBot(), "You can only summon your own bots."); - } - else - { + } else { b->SetTarget(c->CastToMob()); b->Warp(glm::vec3(c->GetPosition())); } @@ -11914,12 +9974,10 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { c->Message(15, "Using %s in my %s (Item %i)", item_link.c_str(), equipped[i], (i == 22 ? 9999 : i)); } - } - else { + } else { c->Message(15, "You must group your bot first."); } - } - else { + } else { c->Message(15, "You must target a bot first."); } return; @@ -11960,16 +10018,14 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(itminst) { for (int m = AUG_BEGIN; m < EmuConstants::ITEM_COMMON_SIZE; ++m) { ItemInst *itma = itminst->GetAugment(m); - if(itma) - { - if(c->CheckLoreConflict(itma->GetItem())) { + if(itma) { + if(c->CheckLoreConflict(itma->GetItem())) failedLoreCheck = true; - } } } - if(c->CheckLoreConflict(itm)) { + + if(c->CheckLoreConflict(itm)) failedLoreCheck = true; - } } if(!failedLoreCheck) { if(itm) { @@ -12061,20 +10117,16 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } if(!strcasecmp(sep->arg[1], "update")) { - // Congdar: add IsEngaged check for exploit to keep bots alive by repeatedly using #bot update. if((c->GetTarget() != nullptr) && c->GetTarget()->IsBot()) { if(c->GetLevel() <= c->GetTarget()->GetLevel()) { c->Message(15, "This bot has already been updated."); return; } - if(c->IsGrouped()) - { + if(c->IsGrouped()) { Group *g = entity_list.GetGroupByClient(c); - for (int i=0; imembers[i] && g->members[i]->IsEngaged()) - { + for (int i=0; imembers[i] && g->members[i]->IsEngaged()) { c->Message(15, "You can't update bots while you are engaged."); return; } @@ -12083,20 +10135,16 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if((c->GetTarget()->CastToBot()->GetBotOwner() == c->CastToMob()) && !c->GetFeigned()) { Bot* bot = c->GetTarget()->CastToBot(); - //bot->SetLevel(c->GetLevel()); bot->SetPetChooser(false); bot->CalcBotStats(); - } - else { + } else { if(c->GetFeigned()) { c->Message(15, "You cannot update bots while feigned."); - } - else { + } else { c->Message(15, "You must target your bot first"); } } - } - else { + } else { c->Message(15, "You must target a bot first"); } @@ -12107,23 +10155,21 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(!strcasecmp(sep->arg[1], "bindme")) { Mob *binder = nullptr; bool hasbinder = false; - if(c->IsGrouped()) - { + if(c->IsGrouped()) { Group *g = c->GetGroup(); if(g) { - for(int i=0; imembers[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == CLERIC)) - { + for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(g->members[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == CLERIC)) { hasbinder = true; binder = g->members[i]; } } - if(!hasbinder) { + + if(!hasbinder) c->Message(15, "You must have a Cleric in your group."); - } } } + if(hasbinder) { binder->CastToBot()->BotGroupSay(binder->CastToBot(), "Attempting to bind you %s.", c->GetName()); binder->CastToNPC()->CastSpell(35, c->GetID(), 1, -1, -1); @@ -12135,48 +10181,40 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(!strcasecmp(sep->arg[1], "runeme")) { Mob *runeer = nullptr; bool hasruneer = false; - if(c->IsGrouped()) - { + if(c->IsGrouped()) { Group *g = c->GetGroup(); if(g) { - for(int i=0; imembers[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == ENCHANTER)) - { + for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(g->members[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == ENCHANTER)) { hasruneer = true; runeer = g->members[i]; } } - if(!hasruneer) { + + if(!hasruneer) c->Message(15, "You must have an Enchanter in your group."); - } } } + if(hasruneer) { if (c->GetLevel() <= 12) { runeer->CastToBot()->BotGroupSay(runeer->CastToBot(), "I need to be level 13 or higher for this..."); - } - else if ((c->GetLevel() >= 13) && (c->GetLevel() <= 21)) { + } else if ((c->GetLevel() >= 13) && (c->GetLevel() <= 21)) { runeer->CastToBot()->BotGroupSay(runeer->CastToBot(), "Casting Rune I..."); runeer->CastSpell(481, c->GetID(), 1, -1, -1); - } - else if ((c->GetLevel() >= 22) && (c->GetLevel() <= 32)) { + } else if ((c->GetLevel() >= 22) && (c->GetLevel() <= 32)) { runeer->CastToBot()->BotGroupSay(runeer->CastToBot(), "Casting Rune II..."); runeer->CastSpell(482, c->GetID(), 1, -1, -1); - } - else if ((c->GetLevel() >= 33) && (c->GetLevel() <= 39)) { + } else if ((c->GetLevel() >= 33) && (c->GetLevel() <= 39)) { runeer->CastToBot()->BotGroupSay(runeer->CastToBot(), "Casting Rune III..."); runeer->CastSpell(483, c->GetID(), 1, -1, -1); - } - else if ((c->GetLevel() >= 40) && (c->GetLevel() <= 51)) { + } else if ((c->GetLevel() >= 40) && (c->GetLevel() <= 51)) { runeer->CastToBot()->BotGroupSay(runeer->CastToBot(), "Casting Rune IV..."); runeer->CastSpell(484, c->GetID(), 1, -1, -1); - } - else if ((c->GetLevel() >= 52) && (c->GetLevel() <= 60)) { + } else if ((c->GetLevel() >= 52) && (c->GetLevel() <= 60)) { runeer->CastToBot()->BotGroupSay(runeer->CastToBot(), "Casting Rune V..."); runeer->CastSpell(1689, c->GetID(), 1, -1, -1); - } - else if (c->GetLevel() >= 61){ + } else if (c->GetLevel() >= 61){ runeer->CastToBot()->BotGroupSay(runeer->CastToBot(), "Casting Rune of Zebuxoruk..."); runeer->CastSpell(3343, c->GetID(), 1, -1, -1); } @@ -12188,10 +10226,9 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(!strcasecmp(sep->arg[1], "track") && c->IsGrouped()) { Mob *Tracker; uint32 TrackerClass = 0; - Group *g = c->GetGroup(); if(g) { - for(int i=0; imembers[i] && g->members[i]->IsBot()) { switch(g->members[i]->GetClass()) { case RANGER: @@ -12199,14 +10236,12 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { TrackerClass = RANGER; break; case DRUID: - // Unless we have a ranger, druid is next best. if(TrackerClass != RANGER) { Tracker = g->members[i]; TrackerClass = DRUID; } break; case BARD: - // If we haven't found a tracker yet, use bard. if(TrackerClass == 0) { Tracker = g->members[i]; TrackerClass = BARD; @@ -12219,42 +10254,34 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } int Level = (c->GetLevel()); - int RangeR = (Level*80); //Ranger - int RangeD = (Level*30); //Druid - int RangeB = (Level*20); //Bard + int RangeR = (Level * 80); //Ranger + int RangeD = (Level * 30); //Druid + int RangeB = (Level * 20); //Bard switch(TrackerClass) { case RANGER: if(!strcasecmp(sep->arg[2], "all")) { Tracker->CastToBot()->BotGroupSay(Tracker->CastToBot(), "Tracking everything", c->GetName()); entity_list.ShowSpawnWindow(c, RangeR, false); - } - else if(!strcasecmp(sep->arg[2], "rare")) { + } else if(!strcasecmp(sep->arg[2], "rare")) { Tracker->CastToBot()->BotGroupSay(Tracker->CastToBot(), "Selective tracking", c->GetName()); entity_list.ShowSpawnWindow(c, RangeR, true); - } - else if(!strcasecmp(sep->arg[2], "near")) { + } else if(!strcasecmp(sep->arg[2], "near")) { Tracker->CastToBot()->BotGroupSay(Tracker->CastToBot(), "Tracking mobs nearby", c->GetName()); entity_list.ShowSpawnWindow(c, RangeD, false); - } - else + } else Tracker->CastToBot()->BotGroupSay(Tracker->CastToBot(), "You want to [track all], [track near], or [track rare]?", c->GetName()); break; - case BARD: - if(TrackerClass != RANGER) Tracker->CastToBot()->BotGroupSay(Tracker->CastToBot(), "Tracking up", c->GetName()); entity_list.ShowSpawnWindow(c, RangeB, false); break; - case DRUID: - if(TrackerClass = BARD) Tracker->CastToBot()->BotGroupSay(Tracker->CastToBot(), "Tracking up", c->GetName()); entity_list.ShowSpawnWindow(c, RangeD, false); break; - default: c->Message(15, "You must have a Ranger, Druid, or Bard in your group."); break; @@ -12268,7 +10295,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { uint32 CurerClass = 0; Group *g = c->GetGroup(); if(g) { - for(int i=0; imembers[i] && g->members[i]->IsBot()) { switch(g->members[i]->GetClass()) { case CLERIC: @@ -12280,13 +10307,13 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { Curer = g->members[i]; CurerClass = SHAMAN; } + break; case DRUID: if (CurerClass == 0){ Curer = g->members[i]; CurerClass = DRUID; } break; - break; default: break; } @@ -12297,81 +10324,61 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if (!strcasecmp(sep->arg[2], "poison") && (c->GetLevel() >= 1)) { Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Trying to cure us of %s.", sep->arg[2]); Curer->CastToBot()->Bot_Command_Cure(1, Curer->GetLevel()); - } - else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 4)) { + } else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 4)) { Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Trying to cure us of %s.", sep->arg[2]); Curer->CastToBot()->Bot_Command_Cure(2, Curer->GetLevel()); - } - else if(!strcasecmp(sep->arg[2], "curse") && (c->GetLevel() >= 8)) { + } else if(!strcasecmp(sep->arg[2], "curse") && (c->GetLevel() >= 8)) { Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Trying to cure us of %s.", sep->arg[2]); Curer->CastToBot()->Bot_Command_Cure(3, Curer->GetLevel()); - } - else if(!strcasecmp(sep->arg[2], "blindness") && (c->GetLevel() >= 3)) { + } else if(!strcasecmp(sep->arg[2], "blindness") && (c->GetLevel() >= 3)) { Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Trying to cure us of %s.", sep->arg[2]); Curer->CastToBot()->Bot_Command_Cure(4, Curer->GetLevel()); - } - else if (!strcasecmp(sep->arg[2], "curse") && (c->GetLevel() <= 8) + } else if (!strcasecmp(sep->arg[2], "curse") && (c->GetLevel() <= 8) || !strcasecmp(sep->arg[2], "blindness") && (c->GetLevel() <= 3) || !strcasecmp(sep->arg[2], "disease") && (c->GetLevel() <= 4) || !strcasecmp(sep->arg[2], "poison") && (c->GetLevel() <= 1)) { Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "I don't have the needed level yet", sep->arg[2]); - } - else + } else Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Do you want [cure poison], [cure disease], [cure curse], or [cure blindness]?", c->GetName()); break; - case SHAMAN: if (!strcasecmp(sep->arg[2], "poison") && (c->GetLevel() >= 2)) { Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Trying to cure us of %s.", sep->arg[2]); Curer->CastToBot()->Bot_Command_Cure(1, Curer->GetLevel()); - } - else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 1)) { + } else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 1)) { Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Trying to cure us of %s.", sep->arg[2]); Curer->CastToBot()->Bot_Command_Cure(2, Curer->GetLevel()); - } - else if(!strcasecmp(sep->arg[2], "curse")) { + } else if(!strcasecmp(sep->arg[2], "curse")) { Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "I don't have that spell", sep->arg[2]); - } - else if(!strcasecmp(sep->arg[2], "blindness") && (c->GetLevel() >= 7)) { + } else if(!strcasecmp(sep->arg[2], "blindness") && (c->GetLevel() >= 7)) { Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Trying to cure us of %s.", sep->arg[2]); Curer->CastToBot()->Bot_Command_Cure(4, Curer->GetLevel()); - } - else if (!strcasecmp(sep->arg[2], "blindness") && (c->GetLevel() <= 7) + } else if (!strcasecmp(sep->arg[2], "blindness") && (c->GetLevel() <= 7) || !strcasecmp(sep->arg[2], "disease") && (c->GetLevel() <= 1) || !strcasecmp(sep->arg[2], "poison") && (c->GetLevel() <= 2)) { Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "I don't have the needed level yet", sep->arg[2]); - } - else + } else Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Do you want [cure poison], [cure disease], or [cure blindness]?", c->GetName()); break; - case DRUID: - if (!strcasecmp(sep->arg[2], "poison") && (c->GetLevel() >= 5)) { Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Trying to cure us of %s.", sep->arg[2]); Curer->CastToBot()->Bot_Command_Cure(1, Curer->GetLevel()); - } - else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 4)) { + } else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 4)) { Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Trying to cure us of %s.", sep->arg[2]); Curer->CastToBot()->Bot_Command_Cure(2, Curer->GetLevel()); - } - else if(!strcasecmp(sep->arg[2], "curse")) { // Fire level 1 + } else if(!strcasecmp(sep->arg[2], "curse")) { // Fire level 1 Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "I don't have that spell", sep->arg[2]); - } - else if(!strcasecmp(sep->arg[2], "blindness") && (c->GetLevel() >= 13)) { + } else if(!strcasecmp(sep->arg[2], "blindness") && (c->GetLevel() >= 13)) { Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "I don't have that spell", sep->arg[2]); - } - else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() <= 4) + } else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() <= 4) || !strcasecmp(sep->arg[2], "poison") && (c->GetLevel() <= 5)) { Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "I don't have the needed level yet", sep->arg[2]) ; - } - else + } else Curer->CastToBot()->BotGroupSay(Curer->CastToBot(), "Do you want [cure poison], or [cure disease]?", c->GetName()); - break; - default: c->Message(15, "You must have a Cleric, Shaman, or Druid in your group."); break; @@ -12380,66 +10387,59 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } //Mez - if(!strcasecmp(sep->arg[1], "ai") && !strcasecmp(sep->arg[2], "mez")) - { + if(!strcasecmp(sep->arg[1], "ai") && !strcasecmp(sep->arg[2], "mez")) { Mob *target = c->GetTarget(); - if(target == nullptr || target == c || target->IsBot() || (target->IsPet() && target->GetOwner() && target->GetOwner()->IsBot())) - { + if(target == nullptr || target == c || target->IsBot() || (target->IsPet() && target->GetOwner() && target->GetOwner()->IsBot())) { c->Message(15, "You must select a monster"); return; } - if(c->IsGrouped()) - { + if(c->IsGrouped()) { bool hasmezzer = false; Group *g = c->GetGroup(); - for(int i=0; imembers[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == ENCHANTER)) - { + for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(g && g->members[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == ENCHANTER)) { hasmezzer = true; Mob *mezzer = g->members[i]; - mezzer->CastToBot()->BotGroupSay(mezzer->CastToBot(), "Trying to mez %s \n", target->GetCleanName()); + mezzer->CastToBot()->BotGroupSay(mezzer->CastToBot(), "Trying to mesmerize %s.", target->GetCleanName()); mezzer->CastToBot()->MesmerizeTarget(target); } } - if(!hasmezzer) { + + if(!hasmezzer) c->Message(15, "You must have an Enchanter in your group."); - } } return; } //Lore (Identify item) if(!strcasecmp(sep->arg[1], "lore")) { - if(c->IsGrouped()) - { + if(c->IsGrouped()) { bool hascaster = false; Group *g = c->GetGroup(); - for(int i=0; imembers[i] && g->members[i]->IsBot()) { uint8 casterlevel = g->members[i]->GetLevel(); switch(g->members[i]->GetClass()) { case ENCHANTER: - if(casterlevel >= 15) { + if(casterlevel >= 15) hascaster = true; - } + break; case WIZARD: - if(casterlevel >= 14) { + if(casterlevel >= 14) hascaster = true; - } + break; case NECROMANCER: - if(casterlevel >= 17) { + if(casterlevel >= 17) hascaster = true; - } + break; case MAGICIAN: - if(casterlevel >= 13) { + if(casterlevel >= 13) hascaster = true; - } + break; default: break; @@ -12451,169 +10451,127 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } } } - if(!hascaster) { + + if(!hascaster) c->Message(15, "You don't see anyone in your group that can cast Identify."); - } - } - else { + } else c->Message(15, "You don't see anyone in your group that can cast Identify."); - } + return; } //Resurrect - if(!strcasecmp(sep->arg[1], "resurrectme")) - { + if(!strcasecmp(sep->arg[1], "resurrectme")) { Mob *target = c->GetTarget(); - if(target == nullptr || !target->IsCorpse()) - { - c->Message(15, "You must select a corpse"); + if(target == nullptr || !target->IsCorpse()) { + c->Message(15, "You must select a corpse!"); return; } - if(c->IsGrouped()) - { + if(c->IsGrouped()) { bool hasrezzer = false; Group *g = c->GetGroup(); - for(int i=0; imembers[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == CLERIC)) - { + for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(g && g->members[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == CLERIC)) { hasrezzer = true; Mob *rezzer = g->members[i]; - rezzer->CastToBot()->BotGroupSay(rezzer->CastToBot(), "Trying to rez %s", target->GetCleanName()); + rezzer->CastToBot()->BotGroupSay(rezzer->CastToBot(), "Trying to resurrect %s.", target->GetCleanName()); rezzer->CastToBot()->Bot_Command_RezzTarget(target); break; } } - if(!hasrezzer) { - c->Message(15, "You must have a Cleric in your group."); - } - } - else { - c->Message(15, "You must have a Cleric in your group."); - } + + if(!hasrezzer) + c->Message(15, "You must have a Cleric in your group!"); + } else + c->Message(15, "You must have a Cleric in your group!"); + return; } - if(!strcasecmp(sep->arg[1], "magepet")) - { - if(c->GetTarget() && c->GetTarget()->IsBot() && (c->GetTarget()->GetClass() == MAGICIAN)) - { - if(c->GetTarget()->CastToBot()->GetBotOwnerCharacterID() == c->CharacterID()) - { + if(!strcasecmp(sep->arg[1], "magepet")) { + if(c->GetTarget() && c->GetTarget()->IsBot() && (c->GetTarget()->GetClass() == MAGICIAN)) { + if(c->GetTarget()->CastToBot()->GetBotOwnerCharacterID() == c->CharacterID()) { int botlevel = c->GetTarget()->GetLevel(); c->GetTarget()->CastToBot()->SetPetChooser(true); - if(botlevel == 1) - { + if(botlevel == 1) { c->GetTarget()->CastToBot()->BotGroupSay(c->GetTarget()->CastToBot(), "I don't have any pets yet."); return; } - if(!strcasecmp(sep->arg[2], "water")) - { + + if(!strcasecmp(sep->arg[2], "water")) { c->GetTarget()->CastToBot()->SetPetChooserID(0); - } - else if(!strcasecmp(sep->arg[2], "fire")) - { - if(botlevel < 3) - { + } else if(!strcasecmp(sep->arg[2], "fire")) { + if(botlevel < 3) { c->GetTarget()->CastToBot()->BotGroupSay(c->GetTarget()->CastToBot(), "I don't have that pet yet."); return; - } - else - { + } else c->GetTarget()->CastToBot()->SetPetChooserID(1); - } - } - else if(!strcasecmp(sep->arg[2], "air")) - { - if(botlevel < 4) - { + } else if(!strcasecmp(sep->arg[2], "air")) { + if(botlevel < 4) { c->GetTarget()->CastToBot()->BotGroupSay(c->GetTarget()->CastToBot(), "I don't have that pet yet."); return; - } - else - { + } else c->GetTarget()->CastToBot()->SetPetChooserID(2); - } - } - else if(!strcasecmp(sep->arg[2], "earth")) - { - if(botlevel < 5) - { + } else if(!strcasecmp(sep->arg[2], "earth")) { + if(botlevel < 5) { c->GetTarget()->CastToBot()->BotGroupSay(c->GetTarget()->CastToBot(), "I don't have that pet yet."); return; - } - else - { + } else c->GetTarget()->CastToBot()->SetPetChooserID(3); - } - } - else if(!strcasecmp(sep->arg[2], "monster")) - { - if(botlevel < 30) - { + } else if(!strcasecmp(sep->arg[2], "monster")) { + if(botlevel < 30) { c->GetTarget()->CastToBot()->BotGroupSay(c->GetTarget()->CastToBot(), "I don't have that pet yet."); return; - } - else - { + } else c->GetTarget()->CastToBot()->SetPetChooserID(4); - } } - if(c->GetTarget()->GetPet()) - { - // cast reclaim energy + + if(c->GetTarget()->GetPet()) { uint16 id = c->GetTarget()->GetPetID(); c->GetTarget()->SetPetID(0); c->GetTarget()->CastSpell(331, id); } } - } - else - { - c->Message(15, "You must target your Magician bot."); - } + } else + c->Message(15, "You must target your Magician bot!"); + return; } //Summon Corpse if(!strcasecmp(sep->arg[1], "corpse") && !strcasecmp(sep->arg[2], "summon")) { if(c->GetTarget() == nullptr) { - c->Message(15, "You must select player with his corpse in the zone."); + c->Message(15, "You must select player with his corpse in the zone!"); return; } + if(c->IsGrouped()) { bool hassummoner = false; Mob *t = c->GetTarget(); Group *g = c->GetGroup(); int summonerlevel = 0; - for(int i=0; imembers[i] && g->members[i]->IsBot() && ((g->members[i]->GetClass() == NECROMANCER)||(g->members[i]->GetClass() == SHADOWKNIGHT))) { hassummoner = true; summonerlevel = g->members[i]->GetLevel(); g->members[i]->InterruptSpell(); if(!t->IsClient()) { - g->members[i]->CastToBot()->BotGroupSay(g->members[i]->CastToBot(), "You have to target a player with a corpse in the zone"); + g->members[i]->CastToBot()->BotGroupSay(g->members[i]->CastToBot(), "You have to target a player with a corpse in the zone!"); return; - } - else { + } else { g->members[i]->SetTarget(t); - if(summonerlevel < 12) { g->members[i]->CastToBot()->BotGroupSay(g->members[i]->CastToBot(), "I don't have that spell yet."); - } - else if((summonerlevel > 11) && (summonerlevel < 35)) { + } else if((summonerlevel > 11) && (summonerlevel < 35)) { g->members[i]->CastToBot()->BotGroupSay(g->members[i]->CastToBot(), "Attempting to summon %s\'s corpse.", t->GetCleanName()); g->members[i]->CastSpell(2213, t->GetID(), 1, -1, -1); return; - } - else if((summonerlevel > 34) && (summonerlevel < 71)) { + } else if((summonerlevel > 34) && (summonerlevel < 71)) { g->members[i]->CastToBot()->BotGroupSay(g->members[i]->CastToBot(), "Attempting to summon %s\'s corpse.", t->GetCleanName()); g->members[i]->CastSpell(3, t->GetID(), 1, -1, -1); return; - } - else if(summonerlevel > 70) { + } else if(summonerlevel > 70) { g->members[i]->CastToBot()->BotGroupSay(g->members[i]->CastToBot(), "Attempting to summon %s\'s corpse.", t->GetCleanName()); g->members[i]->CastSpell(10042, t->GetID(), 1, -1, -1); return; @@ -12621,59 +10579,48 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } } } - if (!hassummoner) { - c->Message(15, "You must have a Necromancer or Shadowknight in your group."); - } + + if (!hassummoner) + c->Message(15, "You must have a Necromancer or Shadow Knight in your group."); + return; } } //Pacify - if(!strcasecmp(sep->arg[1], "target") && !strcasecmp(sep->arg[2], "calm")) - { + if(!strcasecmp(sep->arg[1], "target") && !strcasecmp(sep->arg[2], "calm")) { Mob *target = c->GetTarget(); - if(target == nullptr || target->IsClient() || target->IsBot() || (target->IsPet() && target->GetOwner() && target->GetOwner()->IsBot())) - c->Message(15, "You must select a monster"); + c->Message(15, "You must select a monster!"); else { if(c->IsGrouped()) { Group *g = c->GetGroup(); - - for(int i=0; imembers[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == ENCHANTER)) { Bot *pacer = g->members[i]->CastToBot(); - pacer->BotGroupSay(pacer, "Trying to pacify %s \n", target->GetCleanName()); - + pacer->BotGroupSay(pacer, "Trying to pacify %s.", target->GetCleanName()); if(pacer->Bot_Command_CalmTarget(target)) { if(target->FindType(SE_Lull) || target->FindType(SE_Harmony) || target->FindType(SE_InstantHate)) - //if(pacer->IsPacified(target)) c->Message(0, "I have successfully pacified %s.", target->GetCleanName()); - return; - /*else - c->Message(0, "I failed to pacify %s.", target->GetCleanName());*/ - } - else + + return; + } else c->Message(0, "I failed to pacify %s.", target->GetCleanName()); } // seperated cleric and chanter so chanter is primary if(g && g->members[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == CLERIC) && (GroupHasEnchanterClass(g) == false)) { Bot *pacer = g->members[i]->CastToBot(); - pacer->BotGroupSay(pacer, "Trying to pacify %s \n", target->GetCleanName()); + pacer->BotGroupSay(pacer, "Trying to pacify %s.", target->GetCleanName()); if(pacer->Bot_Command_CalmTarget(target)) { if(target->FindType(SE_Lull) || target->FindType(SE_Harmony) || target->FindType(SE_InstantHate)) - //if(pacer->IsPacified(target)) c->Message(0, "I have successfully pacified %s.", target->GetCleanName()); - return; - /*else - c->Message(0, "I failed to pacify %s.", target->GetCleanName());*/ - } - else + + return; + } else c->Message(0, "I failed to pacify %s.", target->GetCleanName()); } - /*else - c->Message(15, "You must have an Enchanter or Cleric in your group.");*/ } } } @@ -12682,20 +10629,19 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } //Charm - if(!strcasecmp(sep->arg[1], "charm")) - { + if(!strcasecmp(sep->arg[1], "charm")) { Mob *target = c->GetTarget(); - if(target == nullptr || target->IsClient() || target->IsBot() || (target->IsPet() && target->GetOwner() && target->GetOwner()->IsBot())) - { - c->Message(15, "You must select a monster"); + if(target == nullptr || target->IsClient() || target->IsBot() || (target->IsPet() && target->GetOwner() && target->GetOwner()->IsBot())) { + c->Message(15, "You must select a monster!"); return; } + uint32 DBtype = c->GetTarget()->GetBodyType(); Mob *Charmer; uint32 CharmerClass = 0; Group *g = c->GetGroup(); if(g) { - for(int i=0; imembers[i] && g->members[i]->IsBot()) { switch(g->members[i]->GetClass()) { case ENCHANTER: @@ -12707,13 +10653,13 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { Charmer = g->members[i]; CharmerClass = NECROMANCER; } + break; case DRUID: if (CharmerClass == 0){ Charmer = g->members[i]; CharmerClass = DRUID; } break; - break; default: break; } @@ -12722,42 +10668,33 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { switch(CharmerClass) { case ENCHANTER: if (c->GetLevel() >= 11) { - Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "Trying to charm %s \n", target->GetCleanName(), sep->arg[2]); + Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "Trying to charm %s.", target->GetCleanName(), sep->arg[2]); Charmer->CastToBot()->Bot_Command_CharmTarget (1,target); - } - else if (c->GetLevel() <= 10){ - Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "I don't have the needed level yet", sep->arg[2]); - } - else - Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "Mob level is too high or can't be charmed", c->GetName()); + } else if (c->GetLevel() <= 10){ + Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "I don't have the required level yet.", sep->arg[2]); + } else + Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "Mob level is too high or can't be charmed.", c->GetName()); break; - case NECROMANCER: if ((c->GetLevel() >= 18) && (DBtype == 3)) { - Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "Trying to Charm %s \n", target->GetCleanName(), sep->arg[2]); + Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "Trying to charm %s.", target->GetCleanName(), sep->arg[2]); Charmer->CastToBot()->Bot_Command_CharmTarget (2,target); - } - else if (c->GetLevel() <= 17){ - Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "I don't have the needed level yet", sep->arg[2]); - } - else - Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "Mob Is not undead...", c->GetName()); + } else if (c->GetLevel() <= 17){ + Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "I don't have the required level yet.", sep->arg[2]); + } else + Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "Mob is not undead.", c->GetName()); break; - case DRUID: if ((c->GetLevel() >= 13) && (DBtype == 21)) { - Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "Trying to charm %s \n", target->GetCleanName(), sep->arg[2]); + Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "Trying to charm %s.", target->GetCleanName(), sep->arg[2]); Charmer->CastToBot()->Bot_Command_CharmTarget (3,target); - } - else if (c->GetLevel() <= 12){ - Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "I don't have the needed level yet", sep->arg[2]); - } - else - Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "Mob is not an animal...", c->GetName()); + } else if (c->GetLevel() <= 12){ + Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "I don't have the required level yet.", sep->arg[2]); + } else + Charmer->CastToBot()->BotGroupSay(Charmer->CastToBot(), "Mob is not an animal.", c->GetName()); break; - default: - c->Message(15, "You must have an Enchanter, Necromancer or Druid in your group."); + c->Message(15, "You must have an Enchanter, Necromancer, or Druid in your group."); break; } } @@ -12771,35 +10708,27 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(c->GetTarget()->CastToBot()->IsBotCharmer()) { c->GetTarget()->CastToBot()->SetBotCharmer(false); c->GetTarget()->CastToBot()->BotGroupSay(c->GetTarget()->CastToBot(), "Using a summoned pet."); - } - else { - if(c->GetTarget()->GetPet()) - { + } else { + if(c->GetTarget()->GetPet()) { c->GetTarget()->GetPet()->Say_StringID(PET_GETLOST_STRING); - // c->GetTarget()->GetPet()->Kill(); c->GetTarget()->GetPet()->Depop(false); c->GetTarget()->SetPetID(0); } c->GetTarget()->CastToBot()->SetBotCharmer(true); c->GetTarget()->CastToBot()->BotGroupSay(c->GetTarget()->CastToBot(), "Available for Dire Charm command."); } - } - else { + } else c->Message(15, "You must target your Enchanter, Necromancer, or Druid bot."); - } - } - else { + } else c->Message(15, "You must target an Enchanter, Necromancer, or Druid bot."); - } + return; } //Dire Charm - if(!strcasecmp(sep->arg[1], "Dire") && !strcasecmp(sep->arg[2], "Charm")) - { + if(!strcasecmp(sep->arg[1], "Dire") && !strcasecmp(sep->arg[2], "Charm")) { Mob *target = c->GetTarget(); - if(target == nullptr || target->IsClient() || target->IsBot() || (target->IsPet() && target->GetOwner() && target->GetOwner()->IsBot())) - { + if(target == nullptr || target->IsClient() || target->IsBot() || (target->IsPet() && target->GetOwner() && target->GetOwner()->IsBot())) { c->Message(15, "You must select a monster"); return; } @@ -12808,7 +10737,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { uint32 DirerClass = 0; Group *g = c->GetGroup(); if(g) { - for(int i=0; imembers[i] && g->members[i]->IsBot()) { switch(g->members[i]->GetClass()) { case ENCHANTER: @@ -12820,13 +10749,13 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { Direr = g->members[i]; DirerClass = NECROMANCER; } + break; case DRUID: if (DirerClass == 0){ Direr = g->members[i]; DirerClass = DRUID; } break; - break; default: break; } @@ -12835,42 +10764,33 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { switch(DirerClass) { case ENCHANTER: if (c->GetLevel() >= 55) { - Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "Trying to dire charm %s \n", target->GetCleanName(), sep->arg[2]); + Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "Trying to dire charm %s.", target->GetCleanName(), sep->arg[2]); Direr->CastToBot()->Bot_Command_DireTarget (1,target); - } - else if (c->GetLevel() <= 55){ - Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "I don't have the needed level yet", sep->arg[2]); - } - else - Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "Mob level is too high or can't be charmed", c->GetName()); + } else if (c->GetLevel() <= 55){ + Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "I don't have the required level yet.", sep->arg[2]); + } else + Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "Mob level is too high or can't be charmed.", c->GetName()); break; - case NECROMANCER: if ((c->GetLevel() >= 55) && (DBtype == 3)) { - Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "Trying to dire charm %s \n", target->GetCleanName(), sep->arg[2]); + Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "Trying to dire charm %s.", target->GetCleanName(), sep->arg[2]); Direr->CastToBot()->Bot_Command_DireTarget (2,target); - } - else if (c->GetLevel() <= 55){ - Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "I don't have the needed level yet", sep->arg[2]); - } - else - Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "Mob Is not undead...", c->GetName()); + } else if (c->GetLevel() <= 55){ + Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "I don't have the required level yet.", sep->arg[2]); + } else + Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "Mob is not undead.", c->GetName()); break; - case DRUID: if ((c->GetLevel() >= 55) && (DBtype == 21)) { - Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "Trying to dire charm %s \n", target->GetCleanName(), sep->arg[2]); + Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "Trying to dire charm %s.", target->GetCleanName(), sep->arg[2]); Direr->CastToBot()->Bot_Command_DireTarget (3,target); - } - else if (c->GetLevel() <= 55){ - Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "I don't have the needed level yet", sep->arg[2]); - } - else - Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "Mob is not an animal...", c->GetName()); + } else if (c->GetLevel() <= 55){ + Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "I don't have the required level yet.", sep->arg[2]); + } else + Direr->CastToBot()->BotGroupSay(Direr->CastToBot(), "Mob is not an animal.", c->GetName()); break; - default: - c->Message(15, "You must have an Enchanter, Necromancer or Druid in your group."); + c->Message(15, "You must have an Enchanter, Necromancer, or Druid in your group."); break; } } @@ -12880,31 +10800,27 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(!strcasecmp(sep->arg[1], "evac")) { Mob *evac = nullptr; bool hasevac = false; - if(c->IsGrouped()) - { + if(c->IsGrouped()) { Group *g = c->GetGroup(); if(g) { - for(int i=0; imembers[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == DRUID)) - || (g->members[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == WIZARD))) - { + for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if((g->members[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == DRUID)) || (g->members[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == WIZARD))) { hasevac = true; evac = g->members[i]; } } - if(!hasevac) { + + if(!hasevac) c->Message(15, "You must have a Druid in your group."); - } } } + if((hasevac) && (c->GetLevel() >= 18)) { - evac->CastToBot()->BotGroupSay(evac->CastToBot(), "Attempting to Evac you %s.", c->GetName()); + evac->CastToBot()->BotGroupSay(evac->CastToBot(), "Attempting to evacuate you, %s.", c->GetName()); evac->CastToClient()->CastSpell(2183, c->GetID(), 1, -1, -1); - } - else if((hasevac) && (c->GetLevel() <= 17)) { + } else if((hasevac) && (c->GetLevel() <= 17)) evac->CastToBot()->BotGroupSay(evac->CastToBot(), "I'm not level 18 yet.", c->GetName()); - } + return; } @@ -12914,7 +10830,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { uint32 SowerClass = 0; Group *g = c->GetGroup(); if(g) { - for(int i=0; imembers[i] && g->members[i]->IsBot()) { switch(g->members[i]->GetClass()) { case DRUID: @@ -12946,92 +10862,69 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } switch(SowerClass) { case DRUID: - if ((!strcasecmp(sep->arg[2], "regular")) && (zone->CanCastOutdoor()) && (c->GetLevel() >= 10) ) { + if ((!strcasecmp(sep->arg[2], "regular")) && (zone->CanCastOutdoor()) && (c->GetLevel() >= 10)) { Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "Casting sow..."); Sower->CastSpell(278, c->GetID(), 1, -1, -1); } - else if ((!strcasecmp(sep->arg[2], "regular")) && (zone->CanCastOutdoor()) && (c->GetLevel() <= 10) ) { + else if ((!strcasecmp(sep->arg[2], "regular")) && (zone->CanCastOutdoor()) && (c->GetLevel() <= 10)) Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I'm not level 10 yet."); - } else if ((!strcasecmp(sep->arg[2], "wolf")) && zone->CanCastOutdoor() && (c->GetLevel() >= 20)) { Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "Casting group wolf..."); Sower->CastSpell(428, c->GetID(), 1, -1, -1); } - else if ((!strcasecmp(sep->arg[2], "wolf")) && (c->GetLevel() <= 20)) { + else if ((!strcasecmp(sep->arg[2], "wolf")) && (c->GetLevel() <= 20)) Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I'm not level 20 yet."); - } else if ((!strcasecmp(sep->arg[2], "feral")) && (c->GetLevel() >= 50)) { Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "Casting Feral Pack..."); Sower->CastSpell(4058, c->GetID(), 1, -1, -1); } - else if ((!strcasecmp(sep->arg[2], "feral")) && (c->GetLevel() <= 50)) { + else if ((!strcasecmp(sep->arg[2], "feral")) && (c->GetLevel() <= 50)) Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I'm not level 50 yet."); - } else if ((!strcasecmp(sep->arg[2], "shrew")) && (c->GetLevel() >= 35)) { Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "Casting Pack Shrew..."); Sower->CastSpell(4055, c->GetID(), 1, -1, -1); } - else if ((!strcasecmp(sep->arg[2], "wolf")) && (c->GetLevel() <= 35)) { + else if ((!strcasecmp(sep->arg[2], "wolf")) && (c->GetLevel() <= 35)) Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I'm not level 35 yet."); - } - else if ((!zone->CanCastOutdoor()) && (!strcasecmp(sep->arg[2], "regular")) || - (!zone->CanCastOutdoor()) && (!strcasecmp(sep->arg[2], "wolf"))) { - Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I can't cast this spell indoors, try [sow shrew] if you're 35 or higher, or [sow feral] if you're 50 or higher,", c->GetName()); - } - else if (!zone->CanCastOutdoor()) { - Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I can't cast this spell indoors, try [sow shrew] if you're 35 or higher, or [sow feral] if you're 50 or higher,", c->GetName()); - } - else if (zone->CanCastOutdoor()) { - Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "Do you want [sow regular] or [sow wolf]?", c->GetName()); - } - else if (!zone->CanCastOutdoor()) { - Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I can't cast this spell indoors, try [sow shrew] if you're 35 or higher, or [sow feral] if you're 50 or higher,", c->GetName()); - } + else if ((!zone->CanCastOutdoor()) && (!strcasecmp(sep->arg[2], "regular")) || (!zone->CanCastOutdoor()) && (!strcasecmp(sep->arg[2], "wolf"))) + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I can't cast this spell indoors, try [sow shrew] if you're 35 or higher, or [sow feral] if you're 50 or higher."); + else if (!zone->CanCastOutdoor()) + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I can't cast this spell indoors, try [sow shrew] if you're 35 or higher, or [sow feral] if you're 50 or higher."); + else if (zone->CanCastOutdoor()) + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "Do you want [sow regular] or [sow wolf]?"); + else if (!zone->CanCastOutdoor()) + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I can't cast this spell indoors, try [sow shrew] if you're 35 or higher, or [sow feral] if you're 50 or higher."); break; - case SHAMAN: - if ((zone->CanCastOutdoor()) && (c->GetLevel() >= 9)) { - Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "Casting SoW..."); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "Casting Spirit of Wolf."); Sower->CastToClient()->CastSpell(278, c->GetID(), 1, -1, -1); } - else if (!zone->CanCastOutdoor()) { - Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I can't cast this spell indoors", c->GetName()); - } - else if (c->GetLevel() <= 9) { + else if (!zone->CanCastOutdoor()) + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I can't cast this spell indoors."); + else if (c->GetLevel() <= 9) Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I'm not level 9 yet."); - } break; - case RANGER: - if ((zone->CanCastOutdoor()) && (c->GetLevel() >= 28)){ - Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "Casting SoW..."); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "Casting Spirit of Wolf."); Sower->CastToClient()->CastSpell(278, c->GetID(), 1, -1, -1); } - else if (!zone->CanCastOutdoor()) { - Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I can't cast this spell indoors", c->GetName()); - } - else if (c->GetLevel() <= 28) { + else if (!zone->CanCastOutdoor()) + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I can't cast this spell indoors."); + else if (c->GetLevel() <= 28) Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I'm not level 28 yet."); - } break; - case BEASTLORD: - if((zone->CanCastOutdoor()) && (c->GetLevel() >= 24)) { - Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "Casting SoW..."); + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "Casting Spirit of Wolf."); Sower->CastToClient()->CastSpell(278, c->GetID(), 1, -1, -1); } - else if (!zone->CanCastOutdoor()) { - Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I can't cast this spell indoors", c->GetName()); - } - else if (c->GetLevel() <= 24) { + else if (!zone->CanCastOutdoor()) + Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I can't cast this spell indoors."); + else if (c->GetLevel() <= 24) Sower->CastToBot()->BotGroupSay(Sower->CastToBot(), "I'm not level 24 yet."); - } break; - - default: c->Message(15, "You must have a Druid, Shaman, Ranger, or Beastlord in your group."); break; @@ -13045,12 +10938,11 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { uint32 ShrinkerClass = 0; Group *g = c->GetGroup(); Mob *target = c->GetTarget(); - if(target == nullptr || (!target->IsClient() && (c->GetTarget()->CastToBot()->GetBotOwner() != c))) c->Message(15, "You must select a player or bot you own"); else if(g) { - for(int i=0; imembers[i] && g->members[i]->IsBot()) { switch(g->members[i]->GetClass()) { case SHAMAN: @@ -13070,27 +10962,21 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } switch(ShrinkerClass) { case SHAMAN: - if (c->GetLevel() >= 15) { - Shrinker->CastToBot()->BotGroupSay(Shrinker->CastToBot(), "Casting Shrink..."); + Shrinker->CastToBot()->BotGroupSay(Shrinker->CastToBot(), "Casting Shrink."); Shrinker->CastToBot()->SpellOnTarget(345, target); } - else if (c->GetLevel() <= 14) { + else if (c->GetLevel() <= 14) Shrinker->CastToBot()->BotGroupSay(Shrinker->CastToBot(), "I'm not level 15 yet."); - } break; - case BEASTLORD: - if (c->GetLevel() >= 23) { - Shrinker->CastToBot()->BotGroupSay(Shrinker->CastToBot(), "Casting Shrink..."); + Shrinker->CastToBot()->BotGroupSay(Shrinker->CastToBot(), "Casting Shrink."); Shrinker->CastToBot()->SpellOnTarget(345, target); } - else if (c->GetLevel() <= 22) { + else if (c->GetLevel() <= 22) Shrinker->CastToBot()->BotGroupSay(Shrinker->CastToBot(), "I'm not level 23 yet."); - } break; - default: c->Message(15, "You must have a Shaman or Beastlord in your group."); break; @@ -13104,7 +10990,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { uint32 GaterClass = 0; Group *g = c->GetGroup(); if(g) { - for(int i=0; imembers[i] && g->members[i]->IsBot()) { switch(g->members[i]->GetClass()) { case DRUID: @@ -13125,106 +11011,81 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { switch(GaterClass) { case DRUID: if ((!strcasecmp(sep->arg[2], "karana")) && (c->GetLevel() >= 25) ) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Karana..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Karana."); Gater->CastSpell(550, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "commons")) && (c->GetLevel() >= 27)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Commons..."); + } else if ((!strcasecmp(sep->arg[2], "commons")) && (c->GetLevel() >= 27)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Commons."); Gater->CastSpell(551, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "tox")) && (c->GetLevel() >= 25)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Toxxulia..."); + } else if ((!strcasecmp(sep->arg[2], "tox")) && (c->GetLevel() >= 25)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Toxxulia."); Gater->CastSpell(552, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "butcher")) && (c->GetLevel() >= 25)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Butcherblock..."); + } else if ((!strcasecmp(sep->arg[2], "butcher")) && (c->GetLevel() >= 25)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Butcherblock."); Gater->CastSpell(553, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "lava")) && (c->GetLevel() >= 30)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Lavastorm..."); + } else if ((!strcasecmp(sep->arg[2], "lava")) && (c->GetLevel() >= 30)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Lavastorm."); Gater->CastSpell(554, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "ro")) && (c->GetLevel() >= 32)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Ro..."); + } else if ((!strcasecmp(sep->arg[2], "ro")) && (c->GetLevel() >= 32)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Ro."); Gater->CastSpell(555, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "feerrott")) && (c->GetLevel() >= 32)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of feerrott..."); + } else if ((!strcasecmp(sep->arg[2], "feerrott")) && (c->GetLevel() >= 32)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Feerrott."); Gater->CastSpell(556, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "steamfont")) && (c->GetLevel() >= 31)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Steamfont..."); + } else if ((!strcasecmp(sep->arg[2], "steamfont")) && (c->GetLevel() >= 31)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Steamfont."); Gater->CastSpell(557, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "misty")) && (c->GetLevel() >= 36)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Misty..."); + } else if ((!strcasecmp(sep->arg[2], "misty")) && (c->GetLevel() >= 36)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Misty."); Gater->CastSpell(558, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "wakening")) && (c->GetLevel() >= 40)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Wakening Lands..."); + } else if ((!strcasecmp(sep->arg[2], "wakening")) && (c->GetLevel() >= 40)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Wakening Lands."); Gater->CastSpell(1398, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "iceclad")) && (c->GetLevel() >= 32)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Iceclad Ocean..."); + } else if ((!strcasecmp(sep->arg[2], "iceclad")) && (c->GetLevel() >= 32)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Iceclad Ocean."); Gater->CastSpell(1434, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "divide")) && (c->GetLevel() >= 36)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of The Great Divide..."); + } else if ((!strcasecmp(sep->arg[2], "divide")) && (c->GetLevel() >= 36)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of The Great Divide."); Gater->CastSpell(1438, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "cobalt")) && (c->GetLevel() >= 42)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Cobalt Scar..."); + } else if ((!strcasecmp(sep->arg[2], "cobalt")) && (c->GetLevel() >= 42)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Cobalt Scar."); Gater->CastSpell(1440, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "combines")) && (c->GetLevel() >= 33)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of The Combines..."); + } else if ((!strcasecmp(sep->arg[2], "combines")) && (c->GetLevel() >= 33)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of The Combines."); Gater->CastSpell(1517, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "surefall")) && (c->GetLevel() >= 26)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Surefall Glade..."); + } else if ((!strcasecmp(sep->arg[2], "surefall")) && (c->GetLevel() >= 26)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Surefall Glade."); Gater->CastSpell(2020, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "grimling")) && (c->GetLevel() >= 29)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Grimling Forest..."); + } else if ((!strcasecmp(sep->arg[2], "grimling")) && (c->GetLevel() >= 29)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Grimling Forest."); Gater->CastSpell(2419, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "twilight")) && (c->GetLevel() >= 33)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Twilight..."); + } else if ((!strcasecmp(sep->arg[2], "twilight")) && (c->GetLevel() >= 33)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Twilight."); Gater->CastSpell(2424, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "dawnshroud")) && (c->GetLevel() >= 37)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Dawnshroud..."); + } else if ((!strcasecmp(sep->arg[2], "dawnshroud")) && (c->GetLevel() >= 37)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Dawnshroud."); Gater->CastSpell(2429, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "nexus")) && (c->GetLevel() >= 26)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of The Nexus..."); + } else if ((!strcasecmp(sep->arg[2], "nexus")) && (c->GetLevel() >= 26)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of The Nexus."); Gater->CastSpell(2432, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "pok")) && (c->GetLevel() >= 38)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Knowledge..."); + } else if ((!strcasecmp(sep->arg[2], "pok")) && (c->GetLevel() >= 38)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Knowledge."); Gater->CastSpell(3184, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "stonebrunt")) && (c->GetLevel() >= 28)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Stonebrunt Mountains..."); + } else if ((!strcasecmp(sep->arg[2], "stonebrunt")) && (c->GetLevel() >= 28)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Stonebrunt Mountains."); Gater->CastSpell(3792, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "bloodfields")) && (c->GetLevel() >= 55)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Bloodfields..."); + } else if ((!strcasecmp(sep->arg[2], "bloodfields")) && (c->GetLevel() >= 55)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Bloodfields."); Gater->CastSpell(6184, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "emerald")) && (c->GetLevel() >= 39)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Wind of the South..."); + } else if ((!strcasecmp(sep->arg[2], "emerald")) && (c->GetLevel() >= 39)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Wind of the South."); Gater->CastSpell(1737, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "skyfire")) && (c->GetLevel() >= 44)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Wind of the North..."); + } else if ((!strcasecmp(sep->arg[2], "skyfire")) && (c->GetLevel() >= 44)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Wind of the North."); Gater->CastSpell(1736, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "slaughter")) && (c->GetLevel() >= 64)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Slaughter..."); + } else if ((!strcasecmp(sep->arg[2], "slaughter")) && (c->GetLevel() >= 64)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Circle of Slaughter."); Gater->CastSpell(6179, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "karana") + } else if ((!strcasecmp(sep->arg[2], "karana") || !strcasecmp(sep->arg[2], "tox") || !strcasecmp(sep->arg[2], "butcher") && (c->GetLevel() <= 25)) || !strcasecmp(sep->arg[2], "commons") && (c->GetLevel() <= 27) @@ -13249,112 +11110,84 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { || !strcasecmp(sep->arg[2], "emerald") && (c->GetLevel() <= 38) || !strcasecmp(sep->arg[2], "skyfire") && (c->GetLevel() <= 43) || !strcasecmp(sep->arg[2], "wos") && (c->GetLevel() <= 64)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "I don't have the needed level yet", sep->arg[2]); - } - else { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "With the proper level I can [gate] to [karana],[commons],[tox],[butcher],[lava],[ro],[feerrott],[steamfont],[misty],[wakening],[iceclad],[divide],[cobalt],[combines],[surefall],[grimling],[twilight],[dawnshroud],[nexus],[pok],[stonebrunt],[bloodfields],[emerald],[skyfire] or [wos].", c->GetName()); - } + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "I don't have the needed level yet."); + } else + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "With the proper level I can [gate] to [karana], [commons], [tox], [butcher], [lava], [ro], [feerrott], [steamfont], [misty], [wakening], [iceclad], [divide], [cobalt], [combines], [surefall], [grimling], [twilight], [dawnshroud], [nexus], [pok], [stonebrunt], [bloodfields], [emerald], [skyfire] or [wos]."); break; - case WIZARD: - if ((!strcasecmp(sep->arg[2], "commons")) && (c->GetLevel() >= 35) ) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Common Portal..."); + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Common Portal."); Gater->CastSpell(566, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "fay")) && (c->GetLevel() >= 27)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Fay Portal..."); + } else if ((!strcasecmp(sep->arg[2], "fay")) && (c->GetLevel() >= 27)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Fay Portal."); Gater->CastSpell(563, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "ro")) && (c->GetLevel() >= 37)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Ro Portal..."); + } else if ((!strcasecmp(sep->arg[2], "ro")) && (c->GetLevel() >= 37)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Ro Portal."); Gater->CastSpell(567, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "tox")) && (c->GetLevel() >= 25)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Toxxula Portal..."); + } else if ((!strcasecmp(sep->arg[2], "tox")) && (c->GetLevel() >= 25)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Toxxulia Portal."); Gater->CastSpell(561, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "nk")) && (c->GetLevel() >= 27)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting North Karana Portal..."); + } else if ((!strcasecmp(sep->arg[2], "nk")) && (c->GetLevel() >= 27)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting North Karana Portal."); Gater->CastSpell(562, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "nek")) && (c->GetLevel() >= 32)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Nektulos Portal..."); + } else if ((!strcasecmp(sep->arg[2], "nek")) && (c->GetLevel() >= 32)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Nektulos Portal."); Gater->CastSpell(564, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "wakening")) && (c->GetLevel() >= 43)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Wakening Lands Portal..."); + } else if ((!strcasecmp(sep->arg[2], "wakening")) && (c->GetLevel() >= 43)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Wakening Lands Portal."); Gater->CastSpell(1399, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "iceclad")) && (c->GetLevel() >= 33)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Iceclad Ocean Portal..."); + } else if ((!strcasecmp(sep->arg[2], "iceclad")) && (c->GetLevel() >= 33)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Iceclad Ocean Portal."); Gater->CastSpell(1418, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "divide")) && (c->GetLevel() >= 36)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Great Divide Portal..."); + } else if ((!strcasecmp(sep->arg[2], "divide")) && (c->GetLevel() >= 36)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Great Divide Portal."); Gater->CastSpell(1423, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "cobalt")) && (c->GetLevel() >= 43)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Cobalt Scar Portal..."); + } else if ((!strcasecmp(sep->arg[2], "cobalt")) && (c->GetLevel() >= 43)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Cobalt Scar Portal."); Gater->CastSpell(1425, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "combines")) && (c->GetLevel() >= 34)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Combines Portal..."); + } else if ((!strcasecmp(sep->arg[2], "combines")) && (c->GetLevel() >= 34)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Combines Portal."); Gater->CastSpell(1516, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "wk")) && (c->GetLevel() >= 27)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting West Karana Portal..."); + } else if ((!strcasecmp(sep->arg[2], "wk")) && (c->GetLevel() >= 27)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting West Karana Portal."); Gater->CastSpell(568, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "twilight")) && (c->GetLevel() >= 27)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Twilight Portal..."); + } else if ((!strcasecmp(sep->arg[2], "twilight")) && (c->GetLevel() >= 27)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Twilight Portal."); Gater->CastSpell(2425, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "dawnshroud")) && (c->GetLevel() >= 27)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Dawnshroud Portal..."); + } else if ((!strcasecmp(sep->arg[2], "dawnshroud")) && (c->GetLevel() >= 27)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Dawnshroud Portal."); Gater->CastSpell(2430, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "nexus")) && (c->GetLevel() >= 29)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Nexus Portal..."); + } else if ((!strcasecmp(sep->arg[2], "nexus")) && (c->GetLevel() >= 29)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Nexus Portal."); Gater->CastSpell(2944, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "pok")) && (c->GetLevel() >= 27)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Plane of Knowledge Portal..."); + } else if ((!strcasecmp(sep->arg[2], "pok")) && (c->GetLevel() >= 27)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Plane of Knowledge Portal."); Gater->CastSpell(3180, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "wos")) && (c->GetLevel() >= 27)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Wall of Slaughter Portal..."); + } else if ((!strcasecmp(sep->arg[2], "wos")) && (c->GetLevel() >= 27)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Wall of Slaughter Portal."); Gater->CastSpell(6178, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "grimling")) && (c->GetLevel() >= 29)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Fay Portal..."); + } else if ((!strcasecmp(sep->arg[2], "grimling")) && (c->GetLevel() >= 29)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Fay Portal."); Gater->CastSpell(2420, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "emerald")) && (c->GetLevel() >= 37)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Porting to Emerald Jungle..."); + } else if ((!strcasecmp(sep->arg[2], "emerald")) && (c->GetLevel() >= 37)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Porting to Emerald Jungle."); Gater->CastSpell(1739, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "hateplane")) && (c->GetLevel() >= 39)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Porting to Hate Plane..."); + } else if ((!strcasecmp(sep->arg[2], "hateplane")) && (c->GetLevel() >= 39)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Porting to Hate Plane."); Gater->CastSpell(666, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "airplane")) && (c->GetLevel() >= 39)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Porting to airplane..."); + } else if ((!strcasecmp(sep->arg[2], "airplane")) && (c->GetLevel() >= 39)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Porting to Airplane."); Gater->CastSpell(674, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "skyfire")) && (c->GetLevel() >= 36)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Porting to Skyfire..."); + } else if ((!strcasecmp(sep->arg[2], "skyfire")) && (c->GetLevel() >= 36)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Porting to Skyfire."); Gater->CastSpell(1738, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "bloodfields")) && (c->GetLevel() >= 55)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Bloodfields Portal..."); + } else if ((!strcasecmp(sep->arg[2], "bloodfields")) && (c->GetLevel() >= 55)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Bloodfields Portal."); Gater->CastSpell(6183, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "stonebrunt")) && (c->GetLevel() >= 27)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Stonebrunt Portal..."); + } else if ((!strcasecmp(sep->arg[2], "stonebrunt")) && (c->GetLevel() >= 27)) { + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "Casting Stonebrunt Portal."); Gater->CastSpell(3793, c->GetID(), 1, -1, -1); - } - else if ((!strcasecmp(sep->arg[2], "commons") && (c->GetLevel() <= 35)) + } else if ((!strcasecmp(sep->arg[2], "commons") && (c->GetLevel() <= 35)) || !strcasecmp(sep->arg[2], "fay") && (c->GetLevel() <= 27) || (!strcasecmp(sep->arg[2], "ro") && (c->GetLevel() <= 37)) || !strcasecmp(sep->arg[2], "tox") && (c->GetLevel() <= 25) @@ -13378,11 +11211,9 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { || !strcasecmp(sep->arg[2], "emerald") && (c->GetLevel() <= 36) || !strcasecmp(sep->arg[2], "skyfire") && (c->GetLevel() <= 36) || !strcasecmp(sep->arg[2], "wos") && (c->GetLevel() <= 64)) { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "I don't have the needed level yet", sep->arg[2]); - } - else { - Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "With the proper level I can [gate] to [commons],[fay],[ro],[tox],[nk],[wakening],[iceclad],[divide],[cobalt],[combines],[wk],[grimling],[twilight],[dawnshroud],[nexus],[pok],[stonebrunt],[bloodfields],[emerald],[skyfire],[hateplane],[airplane] or [wos].", c->GetName()); - } + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "I don't have the needed level yet."); + } else + Gater->CastToBot()->BotGroupSay(Gater->CastToBot(), "With the proper level I can [gate] to [commons], [fay], [ro], [tox], [nk], [wakening], [iceclad], [divide], [cobalt], [combines], [wk], [grimling], [twilight], [dawnshroud], [nexus], [pok], [stonebrunt], [bloodfields], [emerald], [skyfire], [hateplane], [airplane] or [wos].", c->GetName()); break; default: c->Message(15, "You must have a Druid or Wizard in your group."); @@ -13397,7 +11228,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { uint32 EndurerClass = 0; Group *g = c->GetGroup(); if(g) { - for(int i=0; imembers[i] && g->members[i]->IsBot()) { switch(g->members[i]->GetClass()) { case DRUID: @@ -13435,48 +11266,43 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } switch(EndurerClass) { case DRUID: - if (c->GetLevel() < 6) { + if (c->GetLevel() < 6) Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "I'm not level 6 yet."); - } else { - Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "Casting Enduring Breath..."); + Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "Casting Enduring Breath."); Endurer->CastSpell(86, c->GetID(), 1, -1, -1); break; } break; case SHAMAN: - if (c->GetLevel() < 12) { + if (c->GetLevel() < 12) Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "I'm not level 12 yet."); - } else { - Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "Casting Enduring Breath..."); + Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "Casting Enduring Breath."); Endurer->CastSpell(86, c->GetID(), 1, -1, -1); } break; case RANGER: - if (c->GetLevel() < 20) { + if (c->GetLevel() < 20) Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "I'm not level 20 yet."); - } else { - Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "Casting Enduring Breath..."); + Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "Casting Enduring Breath."); Endurer->CastSpell(86, c->GetID(), 1, -1, -1); } break; case ENCHANTER: - if (c->GetLevel() < 12) { + if (c->GetLevel() < 12) Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "I'm not level 12 yet."); - } else { - Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "Casting Enduring Breath..."); + Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "Casting Enduring Breath."); Endurer->CastSpell(86, c->GetID(), 1, -1, -1); } break; case BEASTLORD: - if (c->GetLevel() < 25) { + if (c->GetLevel() < 25) Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "I'm not level 25 yet."); - } else { - Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "Casting Enduring Breath..."); + Endurer->CastToBot()->BotGroupSay(Endurer->CastToBot(), "Casting Enduring Breath."); Endurer->CastSpell(86, c->GetID(), 1, -1, -1); } break; @@ -13493,7 +11319,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { uint32 InviserClass = 0; Group *g = c->GetGroup(); if(g) { - for(int i=0; imembers[i] && g->members[i]->IsBot()) { switch(g->members[i]->GetClass()) { case ENCHANTER: @@ -13532,137 +11358,111 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } switch(InviserClass) { case ENCHANTER: - if ((c->GetLevel() <= 14) && (!strcasecmp(sep->arg[2], "undead"))) { + if ((c->GetLevel() <= 14) && (!strcasecmp(sep->arg[2], "undead"))) Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I'm not level 14 yet."); - } else if ((!c->IsInvisible(c)) && (!c->invisible_undead) && (c->GetLevel() >= 14) && (!strcasecmp(sep->arg[2], "undead"))) { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting invis undead..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting invis undead."); Inviser->CastSpell(235, c->GetID(), 1, -1, -1); } - else if ((c->GetLevel() <= 4) && (!strcasecmp(sep->arg[2], "live"))) { + else if ((c->GetLevel() <= 4) && (!strcasecmp(sep->arg[2], "live"))) Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I'm not level 4 yet."); - } else if ((!c->IsInvisible(c))&& (!c->invisible_undead) && (c->GetLevel() >= 4) && (!strcasecmp(sep->arg[2], "live"))) { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting invisibilty..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting invisibilty."); Inviser->CastSpell(42, c->GetID(), 1, -1, -1); } - else if ((c->GetLevel() <= 6) && (!strcasecmp(sep->arg[2], "see"))) { + else if ((c->GetLevel() <= 6) && (!strcasecmp(sep->arg[2], "see"))) Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I'm not level 6 yet."); - } else if ((c->GetLevel() >= 6) && (!strcasecmp(sep->arg[2], "see"))) { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting see invisible..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting see invisible."); Inviser->CastSpell(80, c->GetID(), 1, -1, -1); } - else if ((c->IsInvisible(c)) || (c->invisible_undead)) { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I can't cast this if you're already invis-buffed..."); - } - else { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Do you want [invis undead], [invis live] or [invis see] ?", c->GetName()); - } + else if ((c->IsInvisible(c)) || (c->invisible_undead)) + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I can't cast this if you're already invis-buffed."); + else + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Do you want [invis undead], [invis live] or [invis see]?"); break; case MAGICIAN: - if (!strcasecmp(sep->arg[2], "undead")) { + if (!strcasecmp(sep->arg[2], "undead")) Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I don't have that spell."); - } - else if ((c->GetLevel() <= 8) && (!strcasecmp(sep->arg[2], "live"))) { + else if ((c->GetLevel() <= 8) && (!strcasecmp(sep->arg[2], "live"))) Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I'm not level 8 yet."); - } else if ((!c->IsInvisible(c))&& (!c->invisible_undead) && (c->GetLevel() >= 8) && (!strcasecmp(sep->arg[2], "live"))) { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting invisibilty..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting invisibilty."); Inviser->CastSpell(42, c->GetID(), 1, -1, -1); } - else if ((c->GetLevel() <= 16) && (!strcasecmp(sep->arg[2], "see"))) { + else if ((c->GetLevel() <= 16) && (!strcasecmp(sep->arg[2], "see"))) Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I'm not level 16 yet."); - } else if ((c->GetLevel() >= 16) && (!strcasecmp(sep->arg[2], "see"))) { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting see invisible..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting see invisible."); Inviser->CastSpell(80, c->GetID(), 1, -1, -1); } - else if ((c->IsInvisible(c)) || (c->invisible_undead)) { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I can't cast this if you're already invis-buffed..."); - } - else { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Do you want [invis live] or [invis see] ?", c->GetName()); - } + else if ((c->IsInvisible(c)) || (c->invisible_undead)) + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I can't cast this if you're already invis-buffed."); + else + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Do you want [invis live] or [invis see]?"); break; case WIZARD: - if ((c->GetLevel() <= 39) && (!strcasecmp(sep->arg[2], "undead"))) { + if ((c->GetLevel() <= 39) && (!strcasecmp(sep->arg[2], "undead"))) Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I'm not level 39 yet."); - } else if ((!c->IsInvisible(c))&& (!c->invisible_undead) && (c->GetLevel() >= 39) && (!strcasecmp(sep->arg[2], "undead"))) { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting invis undead..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting invis undead."); Inviser->CastSpell(235, c->GetID(), 1, -1, -1); } - else if ((c->GetLevel() <= 16) && (!strcasecmp(sep->arg[2], "live"))) { + else if ((c->GetLevel() <= 16) && (!strcasecmp(sep->arg[2], "live"))) Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I'm not level 16 yet."); - } else if ((!c->IsInvisible(c))&& (!c->invisible_undead) && (c->GetLevel() >= 16) && (!strcasecmp(sep->arg[2], "live"))) { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting invisibilty..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting invisibilty."); Inviser->CastSpell(42, c->GetID(), 1, -1, -1); } - else if ((c->GetLevel() <= 4) && (!strcasecmp(sep->arg[2], "see"))) { + else if ((c->GetLevel() <= 4) && (!strcasecmp(sep->arg[2], "see"))) Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I'm not level 6 yet."); - } else if ((c->GetLevel() >= 4) && (!strcasecmp(sep->arg[2], "see"))) { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting see invisible..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting see invisible."); Inviser->CastSpell(80, c->GetID(), 1, -1, -1); } - else if ((c->IsInvisible(c)) || (c->invisible_undead)) { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I can't cast this if you're already invis-buffed..."); - } - else { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Do you want [invis undead], [invis live] or [invis see] ?", c->GetName()); - } + else if ((c->IsInvisible(c)) || (c->invisible_undead)) + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I can't cast this if you're already invis-buffed."); + else + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Do you want [invis undead], [invis live] or [invis see]?"); break; case NECROMANCER: if ((!c->IsInvisible(c))&& (!c->invisible_undead) && (!strcasecmp(sep->arg[2], "undead"))) { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting invis undead..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting invis undead."); Inviser->CastSpell(235, c->GetID(), 1, -1, -1); } - else if (!strcasecmp(sep->arg[2], "see")) { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I don't have that spell..."); - } - else if (!strcasecmp(sep->arg[2], "live")) { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I don't have that spell..."); - } - else if ((c->IsInvisible(c))|| (c->invisible_undead)) { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I can't cast this if you're already invis-buffed..."); - } - else { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I only have [invis undead]", c->GetName()); - } + else if (!strcasecmp(sep->arg[2], "see")) + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I don't have that spell."); + else if (!strcasecmp(sep->arg[2], "live")) + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I don't have that spell."); + else if ((c->IsInvisible(c))|| (c->invisible_undead)) + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I can't cast this if you're already invis-buffed."); + else + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I only have [invis undead]"); break; case DRUID: - if (!strcasecmp(sep->arg[2], "undead")) { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I don't have that spell..."); - } - else if ((c->GetLevel() <= 4) && (!strcasecmp(sep->arg[2], "live"))) { + if (!strcasecmp(sep->arg[2], "undead")) + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I don't have that spell."); + else if ((c->GetLevel() <= 4) && (!strcasecmp(sep->arg[2], "live"))) Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I'm not level 4 yet."); - } else if ((!c->IsInvisible(c))&& (!c->invisible_undead) && (c->GetLevel() >= 18) && (!strcasecmp(sep->arg[2], "live"))) { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting Superior Camouflage..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting Superior Camouflage."); Inviser->CastSpell(34, c->GetID(), 1, -1, -1); - } - else if ((!c->IsInvisible(c))&& (!c->invisible_undead) && (c->GetLevel() >= 4) && (!strcasecmp(sep->arg[2], "live")) && (zone->CanCastOutdoor())) { + } else if ((!c->IsInvisible(c))&& (!c->invisible_undead) && (c->GetLevel() >= 4) && (!strcasecmp(sep->arg[2], "live")) && (zone->CanCastOutdoor())) { Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting Camouflage..."); Inviser->CastSpell(247, c->GetID(), 1, -1, -1); } - else if ((c->GetLevel() >= 4) && (!strcasecmp(sep->arg[2], "live")) && (!zone->CanCastOutdoor())) { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I can't cast this spell indoors..."); - } - else if ((c->GetLevel() <= 13) && (!strcasecmp(sep->arg[2], "see"))) { + else if ((c->GetLevel() >= 4) && (!strcasecmp(sep->arg[2], "live")) && (!zone->CanCastOutdoor())) + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I can't cast this spell indoors."); + else if ((c->GetLevel() <= 13) && (!strcasecmp(sep->arg[2], "see"))) Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I'm not level 13 yet."); - } else if ((c->GetLevel() >= 13) && (!strcasecmp(sep->arg[2], "see"))) { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting see invisible..."); + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Casting see invisible."); Inviser->CastSpell(80, c->GetID(), 1, -1, -1); } - else if ((c->IsInvisible(c)) || (c->invisible_undead)) { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I can't cast this if you're already invis-buffed..."); - } - else { - Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Do you want [invis live] or [invis see] ?", c->GetName()); - } + else if ((c->IsInvisible(c)) || (c->invisible_undead)) + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "I can't cast this if you're already invis-buffed."); + else + Inviser->CastToBot()->BotGroupSay(Inviser->CastToBot(), "Do you want [invis live] or [invis see]?"); break; default: c->Message(15, "You must have a Enchanter, Magician, Wizard, Druid, or Necromancer in your group."); @@ -13677,7 +11477,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { uint32 LeverClass = 0; Group *g = c->GetGroup(); if(g) { - for(int i=0; imembers[i] && g->members[i]->IsBot()) { switch(g->members[i]->GetClass()) { case DRUID: @@ -13709,62 +11509,46 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } switch(LeverClass) { case DRUID: - if (c->GetLevel() <= 14) { + if (c->GetLevel() <= 14) Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "I'm not level 14 yet."); - } else if (zone->CanCastOutdoor()) { - Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "Casting Levitate..."); + Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "Casting Levitate."); Lever->CastSpell(261, c->GetID(), 1, -1, -1); break; } - else if (!zone->CanCastOutdoor()) { - Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "I can't cast this spell indoors", c->GetName()); - } + else if (!zone->CanCastOutdoor()) + Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "I can't cast this spell indoors."); break; - case SHAMAN: - if ((zone->CanCastOutdoor()) && (c->GetLevel() >= 10)) { - Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "Casting Levitate..."); + Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "Casting Levitate."); Lever->CastToClient()->CastSpell(261, c->GetID(), 1, -1, -1); } - else if (!zone->CanCastOutdoor()) { - Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "I can't cast this spell indoors", c->GetName()); - } - else if (c->GetLevel() <= 10) { + else if (!zone->CanCastOutdoor()) + Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "I can't cast this spell indoors."); + else if (c->GetLevel() <= 10) Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "I'm not level 10 yet."); - } break; - case WIZARD: - - if((zone->CanCastOutdoor()) && (c->GetLevel() >= 22)){ - Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "Casting Levitate..."); + if((zone->CanCastOutdoor()) && (c->GetLevel() >= 22)) { + Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "Casting Levitate."); Lever->CastToClient()->CastSpell(261, c->GetID(), 1, -1, -1); } - else if (!zone->CanCastOutdoor()) { - Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "I can't cast this spell indoors", c->GetName()); - } - else if (c->GetLevel() <= 22) { + else if (!zone->CanCastOutdoor()) + Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "I can't cast this spell indoors."); + else if (c->GetLevel() <= 22) Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "I'm not level 22 yet."); - } break; - case ENCHANTER: - if((zone->CanCastOutdoor()) && (c->GetLevel() >= 15)) { - Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "Casting Levitate..."); + Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "Casting Levitate."); Lever->CastToClient()->CastSpell(261, c->GetID(), 1, -1, -1); } - else if (!zone->CanCastOutdoor()) { - Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "I can't cast this spell indoors", c->GetName()); - } - else if (c->GetLevel() <= 15) { + else if (!zone->CanCastOutdoor()) + Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "I can't cast this spell indoors."); + else if (c->GetLevel() <= 15) Lever->CastToBot()->BotGroupSay(Lever->CastToBot(), "I'm not level 15 yet."); - } break; - - default: c->Message(15, "You must have a Druid, Shaman, Wizard, or Enchanter in your group."); break; @@ -13778,7 +11562,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { uint32 ResisterClass = 0; Group *g = c->GetGroup(); if(g) { - for(int i=0; imembers[i] && g->members[i]->IsBot()) { switch(g->members[i]->GetClass()) { case CLERIC: @@ -13790,13 +11574,13 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { Resister = g->members[i]; ResisterClass = SHAMAN; } + break; case DRUID: if (ResisterClass == 0){ Resister = g->members[i]; ResisterClass = DRUID; } break; - break; default: break; } @@ -13804,104 +11588,81 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } switch(ResisterClass) { case CLERIC: - if (!strcasecmp(sep->arg[2], "poison") && (c->GetLevel() >= 6)) { - Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting poison protection...", sep->arg[2]); + if(!strcasecmp(sep->arg[2], "poison") && (c->GetLevel() >= 6)) { + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting poison protection."); Resister->CastToBot()->Bot_Command_Resist(1, Resister->GetLevel()); - } - else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 11)) { - Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting disease protection...", sep->arg[2]); + } else if(!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 11)) { + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting disease protection."); Resister->CastToBot()->Bot_Command_Resist(2, Resister->GetLevel()); - } - else if(!strcasecmp(sep->arg[2], "fire") && (c->GetLevel() >= 8)) { - Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting fire protection...", sep->arg[2]); + } else if(!strcasecmp(sep->arg[2], "fire") && (c->GetLevel() >= 8)) { + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting fire protection."); Resister->CastToBot()->Bot_Command_Resist(3, Resister->GetLevel()); - } - else if(!strcasecmp(sep->arg[2], "cold") && (c->GetLevel() >= 13)) { - Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting cold protection...", sep->arg[2]); + } else if(!strcasecmp(sep->arg[2], "cold") && (c->GetLevel() >= 13)) { + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting cold protection."); Resister->CastToBot()->Bot_Command_Resist(4, Resister->GetLevel()); - } - else if(!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() >= 16)) { - Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting magic protection...", sep->arg[2]); + } else if(!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() >= 16)) { + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting magic protection."); Resister->CastToBot()->Bot_Command_Resist(5, Resister->GetLevel()); - } - else if (!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() <= 16) + } else if(!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() <= 16) || !strcasecmp(sep->arg[2], "cold") && (c->GetLevel() <= 13) || !strcasecmp(sep->arg[2], "fire") && (c->GetLevel() <= 8) || !strcasecmp(sep->arg[2], "disease") && (c->GetLevel() <= 11) || !strcasecmp(sep->arg[2], "poison") && (c->GetLevel() <= 6)) { - Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "I don't have the needed level yet", sep->arg[2]); - } - else - Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Do you want [resist poison], [resist disease], [resist fire], [resist cold], or [resist magic]?", c->GetName()); + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "I don't have the required level yet."); + } else + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Do you want [resist poison], [resist disease], [resist fire], [resist cold], or [resist magic]?"); break; - case SHAMAN: - if (!strcasecmp(sep->arg[2], "poison") && (c->GetLevel() >= 20)) { - Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting poison protection...", sep->arg[2]); + if(!strcasecmp(sep->arg[2], "poison") && (c->GetLevel() >= 20)) { + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting poison protection."); Resister->CastToBot()->Bot_Command_Resist(12, Resister->GetLevel()); - } - else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 8)) { - Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting disease protection...", sep->arg[2]); + } else if(!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 8)) { + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting disease protection."); Resister->CastToBot()->Bot_Command_Resist(13, Resister->GetLevel()); - } - else if(!strcasecmp(sep->arg[2], "fire") && (c->GetLevel() >= 5)) { - Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting fire protection...", sep->arg[2]); + } else if(!strcasecmp(sep->arg[2], "fire") && (c->GetLevel() >= 5)) { + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting fire protection."); Resister->CastToBot()->Bot_Command_Resist(14, Resister->GetLevel()); - } - else if(!strcasecmp(sep->arg[2], "cold") && (c->GetLevel() >= 1)) { - Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting cold protection...", sep->arg[2]); + } else if(!strcasecmp(sep->arg[2], "cold") && (c->GetLevel() >= 1)) { + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting cold protection."); Resister->CastToBot()->Bot_Command_Resist(15, Resister->GetLevel()); - } - else if(!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() >= 19)) { - Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting magic protection...", sep->arg[2]); + } else if(!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() >= 19)) { + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting magic protection."); Resister->CastToBot()->Bot_Command_Resist(16, Resister->GetLevel()); - } - else if (!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() <= 19) + } else if(!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() <= 19) || !strcasecmp(sep->arg[2], "cold") && (c->GetLevel() <= 1) || !strcasecmp(sep->arg[2], "fire") && (c->GetLevel() <= 5) || !strcasecmp(sep->arg[2], "disease") && (c->GetLevel() <= 8) || !strcasecmp(sep->arg[2], "poison") && (c->GetLevel() <= 20)) { - Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "I don't have the needed level yet", sep->arg[2]); - } - else - Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Do you want [resist poison], [resist disease], [resist fire], [resist cold], or [resist magic]?", c->GetName()); + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "I don't have the needed level yet."); + } else + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Do you want [resist poison], [resist disease], [resist fire], [resist cold], or [resist magic]?"); break; - case DRUID: - if (!strcasecmp(sep->arg[2], "poison") && (c->GetLevel() >= 19)) { - Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting poison protection...", sep->arg[2]); + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting poison protection."); Resister->CastToBot()->Bot_Command_Resist(7, Resister->GetLevel()); - } - else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 19)) { - Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting disease protection...", sep->arg[2]); + } else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 19)) { + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting disease protection."); Resister->CastToBot()->Bot_Command_Resist(8, Resister->GetLevel()); - } - else if(!strcasecmp(sep->arg[2], "fire")) { // Fire level 1 - Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting fire protection...", sep->arg[2]); + } else if(!strcasecmp(sep->arg[2], "fire")) { // Fire level 1 + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting fire protection."); Resister->CastToBot()->Bot_Command_Resist(9, Resister->GetLevel()); - } - else if(!strcasecmp(sep->arg[2], "cold") && (c->GetLevel() >= 13)) { - Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting cold protection...", sep->arg[2]); + } else if(!strcasecmp(sep->arg[2], "cold") && (c->GetLevel() >= 13)) { + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting cold protection."); Resister->CastToBot()->Bot_Command_Resist(10, Resister->GetLevel()); - } - else if(!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() >= 16)) { - Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting magic protection...", sep->arg[2]); + } else if(!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() >= 16)) { + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Casting magic protection."); Resister->CastToBot()->Bot_Command_Resist(11, Resister->GetLevel()); - } - else if (!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() <= 16) + } else if (!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() <= 16) || !strcasecmp(sep->arg[2], "cold") && (c->GetLevel() <= 9) || !strcasecmp(sep->arg[2], "disease") && (c->GetLevel() <= 19) || !strcasecmp(sep->arg[2], "poison") && (c->GetLevel() <= 19)) { - Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "I don't have the needed level yet", sep->arg[2]) ; - } - else - Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Do you want [resist poison], [resist disease], [resist fire], [resist cold], or [resist magic]?", c->GetName()); - + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "I don't have the required level yet.") ; + } else + Resister->CastToBot()->BotGroupSay(Resister->CastToBot(), "Do you want [resist poison], [resist disease], [resist fire], [resist cold], or [resist magic]?"); break; - default: c->Message(15, "You must have a Cleric, Shaman, or Druid in your group."); break; @@ -13909,52 +11670,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } } - // debug commands - if(!strcasecmp(sep->arg[1], "debug") && !strcasecmp(sep->arg[2], "inventory")) { - Mob *target = c->GetTarget(); - - if(target && target->IsBot()) { - for(int i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_END; i++) { - c->Message(15,"Equiped slot: %i , item: %i \n", i, target->CastToBot()->GetEquipment(i)); - } - if(target->CastToBot()->GetEquipment(MaterialSecondary) > 0) - c->Message(15,"This bot has an item in off-hand."); - } - return; - } - - if(!strcasecmp(sep->arg[1], "debug") && !strcasecmp(sep->arg[2], "botcaracs")) - { - Mob *target = c->GetTarget(); - if(target && target->IsBot()) - { - if(target->CanThisClassDualWield()) - c->Message(15, "This class can dual wield."); - if(target->CanThisClassDoubleAttack()) - c->Message(15, "This class can double attack."); - } - if(target->GetPetID()) - c->Message(15, "I've a pet and its name is %s", target->GetPet()->GetCleanName() ); - return; - } - - if(!strcasecmp(sep->arg[1], "debug") && !strcasecmp(sep->arg[2], "spells")) - { - Mob *target = c->GetTarget(); - if(target && target->IsBot()) - { - for(int i=0; iCastToBot()->AIspells.size(); i++) - { - if(target->CastToBot()->BotGetSpells(i) != 0) - { - SPDat_Spell_Struct botspell = spells[target->CastToBot()->BotGetSpells(i)]; - c->Message(15, "(DEBUG) %s , Slot(%i), Spell (%s) Priority (%i) \n", target->GetCleanName(), i, botspell.name, target->CastToBot()->BotGetSpellPriority(i)); - } - } - } - return; - } - // #bot group ... if(!strcasecmp(sep->arg[1], "group") && !strcasecmp(sep->arg[2], "help")) { c->Message(0, "#bot group help - will show this help."); @@ -13962,7 +11677,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { c->Message(0, "#bot group follow "); c->Message(0, "#bot group guard "); c->Message(0, "#bot group attack "); - return; } @@ -13970,23 +11684,18 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(!strcasecmp(sep->arg[2], "follow")) { if(c->IsGrouped()) BotGroupOrderFollow(c->GetGroup(), c); - } - else if(!strcasecmp(sep->arg[2], "guard")) { + } else if(!strcasecmp(sep->arg[2], "guard")) { if(c->IsGrouped()) BotGroupOrderGuard(c->GetGroup(), c); - } - else if(!strcasecmp(sep->arg[2], "attack")) { - if(c->IsGrouped() && (c->GetTarget() != nullptr) && c->IsAttackAllowed(c->GetTarget())) { + } else if(!strcasecmp(sep->arg[2], "attack")) { + if(c->IsGrouped() && (c->GetTarget() != nullptr) && c->IsAttackAllowed(c->GetTarget())) BotGroupOrderAttack(c->GetGroup(), c->GetTarget(), c); - } else c->Message(15, "You must target a monster."); - } - else if(!strcasecmp(sep->arg[2], "summon")) { + } else if(!strcasecmp(sep->arg[2], "summon")) { if(c->IsGrouped()) BotGroupSummon(c->GetGroup(), c); } - return; } @@ -14013,10 +11722,8 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { Mob* targetMob = c->GetTarget(); std::string targetName = std::string(sep->arg[3]); Bot* botGroupLeader = 0; - - if(!targetName.empty()) { + if(!targetName.empty()) botGroupLeader = entity_list.GetBotByBotName(targetName); - } else if(targetMob) { if(targetMob->IsBot()) botGroupLeader = targetMob->CastToBot(); @@ -14027,8 +11734,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { botGroupLeader->BotGroupSay(botGroupLeader, "I am prepared to lead."); else botGroupLeader->BotGroupSay(botGroupLeader, "I cannot lead."); - } - else + } else c->Message(13, "You must target a spawned bot first."); return; @@ -14036,17 +11742,13 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(!strcasecmp(sep->arg[1], "botgroup") && !strcasecmp(sep->arg[2], "add")) { int argCount = 0; - argCount = sep->argnum; - std::string botGroupLeaderName; std::string botGroupMemberName; - if(argCount >= 3) botGroupMemberName = std::string(sep->arg[3]); Bot* botGroupMember = entity_list.GetBotByBotName(botGroupMemberName); - if(!botGroupMember) { if(botGroupMemberName.empty()) c->Message(13, "You must target a bot in this zone. Please try again."); @@ -14057,13 +11759,10 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } Bot* botGroupLeader = 0; - if(argCount == 4) { botGroupLeaderName = std::string(sep->arg[4]); - botGroupLeader = entity_list.GetBotByBotName(botGroupLeaderName); - } - else if(c->GetTarget() && c->GetTarget()->IsBot()) + } else if(c->GetTarget() && c->GetTarget()->IsBot()) botGroupLeader = c->GetTarget()->CastToBot(); if(!botGroupLeader) { @@ -14087,41 +11786,27 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(botGroupMember) { if(!botGroupMember->HasGroup()) { - // invite if(Bot::AddBotToGroup(botGroupMember, g)) { database.SetGroupID(botGroupMember->GetName(), g->GetID(), botGroupMember->GetBotID()); botGroupMember->BotGroupSay(botGroupMember, "I have joined %s\'s group.", botGroupLeader->GetName()); - } - else { + } else botGroupMember->BotGroupSay(botGroupMember, "I can not join %s\'s group.", botGroupLeader->GetName()); - } - } - else { - // "I am already in a group." + } else { Group* tempGroup = botGroupMember->GetGroup(); if(tempGroup) botGroupMember->BotGroupSay(botGroupMember, "I can not join %s\'s group. I am already a member in %s\'s group.", botGroupLeader->GetName(), tempGroup->GetLeaderName()); } - } - else { - // must target a bot message + } else c->Message(13, "You must target a spawned bot first."); - } - } - else { - // "My group is full." + } else botGroupLeader->BotGroupSay(botGroupMember, "I have no more openings in my group, %s.", c->GetName()); - } - } - else { - // "I am not a group leader." + } else { Group* tempGroup = botGroupLeader->GetGroup(); if(tempGroup) botGroupLeader->BotGroupSay(botGroupLeader, "I can not lead anyone because I am a member in %s\'s group.", tempGroup->GetLeaderName()); } } } - return; } @@ -14130,9 +11815,8 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { std::string targetName = std::string(sep->arg[3]); Bot* botGroupMember = 0; - if(!targetName.empty()) { + if(!targetName.empty()) botGroupMember = entity_list.GetBotByBotName(targetName); - } else if(targetMob) { if(targetMob->IsBot()) botGroupMember = targetMob->CastToBot(); @@ -14141,16 +11825,13 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(botGroupMember) { if(botGroupMember->HasGroup()) { Group* g = botGroupMember->GetGroup(); - if(Bot::RemoveBotFromGroup(botGroupMember, g)) botGroupMember->BotGroupSay(botGroupMember, "I am no longer in a group."); else botGroupMember->BotGroupSay(botGroupMember, "I can not leave %s\'s group.", g->GetLeaderName()); - } - else + } else botGroupMember->BotGroupSay(botGroupMember, "I am not in a group."); - } - else + } else c->Message(13, "You must target a spawned bot first."); return; @@ -14160,10 +11841,8 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { Mob* targetMob = c->GetTarget(); std::string targetName = std::string(sep->arg[3]); Bot* botGroupLeader = 0; - - if(!targetName.empty()) { + if(!targetName.empty()) botGroupLeader = entity_list.GetBotByBotName(targetName); - } else if(targetMob) { if(targetMob->IsBot()) botGroupLeader = targetMob->CastToBot(); @@ -14172,21 +11851,16 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(botGroupLeader) { if(botGroupLeader->HasGroup()) { Group* g = botGroupLeader->GetGroup(); - if(g->IsLeader(botGroupLeader)) { if(Bot::RemoveBotFromGroup(botGroupLeader, g)) botGroupLeader->BotGroupSay(botGroupLeader, "I have disbanded my group, %s.", c->GetName()); else botGroupLeader->BotGroupSay(botGroupLeader, "I was not able to disband my group, %s.", c->GetName()); - } - else { + } else botGroupLeader->BotGroupSay(botGroupLeader, "I can not disband my group, %s, because I am not the leader. %s is the leader of my group.", c->GetName(), g->GetLeaderName()); - } - } - else + } else botGroupLeader->BotGroupSay(botGroupLeader, "I am not a group leader, %s.", c->GetName()); - } - else + } else c->Message(13, "You must target a spawned bot group leader first."); return; @@ -14197,9 +11871,8 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { std::string targetName = std::string(sep->arg[3]); Bot* botGroupLeader = 0; - if(!targetName.empty()) { + if(!targetName.empty()) botGroupLeader = entity_list.GetBotByBotName(targetName); - } else if(targetMob) { if(targetMob->IsBot()) botGroupLeader = targetMob->CastToBot(); @@ -14208,12 +11881,10 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(botGroupLeader) { if(botGroupLeader->HasGroup()) { Group* g = botGroupLeader->GetGroup(); - if(g->IsLeader(botGroupLeader)) BotGroupSummon(g, c); } - } - else if(c->HasGroup()) + } else if(c->HasGroup()) BotGroupSummon(c->GetGroup(), c); return; @@ -14223,10 +11894,8 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { Mob* targetMob = c->GetTarget(); std::string targetName = std::string(sep->arg[3]); Bot* botGroupLeader = 0; - - if(!targetName.empty()) { + if(!targetName.empty()) botGroupLeader = entity_list.GetBotByBotName(targetName); - } else if(targetMob) { if(targetMob->IsBot()) botGroupLeader = targetMob->CastToBot(); @@ -14235,12 +11904,10 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(botGroupLeader) { if(botGroupLeader->HasGroup()) { Group* g = botGroupLeader->GetGroup(); - if(g->IsLeader(botGroupLeader)) BotGroupOrderFollow(g, c); } - } - else if(c->HasGroup()) + } else if(c->HasGroup()) BotGroupOrderFollow(c->GetGroup(), c); return; @@ -14250,10 +11917,8 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { Mob* targetMob = c->GetTarget(); std::string targetName = std::string(sep->arg[3]); Bot* botGroupLeader = 0; - - if(!targetName.empty()) { + if(!targetName.empty()) botGroupLeader = entity_list.GetBotByBotName(targetName); - } else if(targetMob) { if(targetMob->IsBot()) botGroupLeader = targetMob->CastToBot(); @@ -14262,12 +11927,10 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(botGroupLeader) { if(botGroupLeader->HasGroup()) { Group* g = botGroupLeader->GetGroup(); - if(g->IsLeader(botGroupLeader)) BotGroupOrderGuard(g, c); } - } - else if(c->HasGroup()) + } else if(c->HasGroup()) BotGroupOrderGuard(c->GetGroup(), c); return; @@ -14278,95 +11941,73 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { Bot* botGroupLeader = 0; std::string botGroupLeaderName = std::string(sep->arg[3]); std::string targetName = std::string(sep->arg[4]); - if(!botGroupLeaderName.empty()) { botGroupLeader = entity_list.GetBotByBotName(botGroupLeaderName); - if(botGroupLeader) { - if(!targetName.empty()) { + if(!targetName.empty()) targetMob = entity_list.GetMob(targetName.c_str()); - } if(targetMob) { if(c->IsAttackAllowed(targetMob)) { if(botGroupLeader->HasGroup()) { Group* g = botGroupLeader->GetGroup(); - if(g) { if(g->IsLeader(botGroupLeader)) BotGroupOrderAttack(g, targetMob, c); } - } - else if(c->HasGroup()) + } else if(c->HasGroup()) BotGroupOrderAttack(c->GetGroup(), targetMob, c); - } - else + } else c->Message(13, "You must target a monster."); - } - else + } else c->Message(13, "You must target a monster."); - } - else + } else c->Message(13, "You must target a spawned bot group leader first."); } - return; } if(!strcasecmp(sep->arg[1], "botgroup") && !strcasecmp(sep->arg[2], "list")) { std::list botGroupList = GetBotGroupListByBotOwnerCharacterId(c->CharacterID(), &TempErrorMessage); - if(!TempErrorMessage.empty()) { c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); return; } if(!botGroupList.empty()) { - for(std::list::iterator botGroupListItr = botGroupList.begin(); botGroupListItr != botGroupList.end(); ++botGroupListItr) { + for(std::list::iterator botGroupListItr = botGroupList.begin(); botGroupListItr != botGroupList.end(); ++botGroupListItr) c->Message(0, "Bot Group Name: %s -- Bot Group Leader: %s", botGroupListItr->BotGroupName.c_str(), botGroupListItr->BotGroupLeaderName.c_str()); - } - } - else { + } else c->Message(0, "You have no bot groups created. Use the #bot botgroup save command to save bot groups."); - } return; } if(!strcasecmp(sep->arg[1], "botgroup") && !strcasecmp(sep->arg[2], "load")) { - - // If client is grouped, check for aggro on each group member. Group *g = c->GetGroup(); if(g) { for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { - // Skip invalid group members. - if(!g->members[i]) { continue; } - - // Fail if current group member is client and has aggro - // OR has a popuplated hate list (assume bot). - if((g->members[i]->IsClient() && g->members[i]->CastToClient()->GetAggroCount()) - || g->members[i]->IsEngaged()) { + if(!g->members[i]) + continue; + + if((g->members[i]->IsClient() && g->members[i]->CastToClient()->GetAggroCount()) || g->members[i]->IsEngaged()) { c->Message(0, "You can't spawn bots while your group is engaged."); return; } } - } - // Fail if ungrouped client has aggro. - else { + } else { if(c->GetAggroCount() > 0) { c->Message(0, "You can't spawn bots while you are engaged."); return; } } - // Parse botgroup name. std::string botGroupName = std::string(sep->arg[3]); if(botGroupName.empty()) { c->Message(13, "Invalid botgroup name supplied."); return; } - // Get botgroup id. uint32 botGroupID = CanLoadBotGroup(c->CharacterID(), botGroupName, &TempErrorMessage); if(!TempErrorMessage.empty()) { c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); @@ -14377,59 +12018,43 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { return; } - // Get list of bots in specified group. std::list botGroup = LoadBotGroup(botGroupName, &TempErrorMessage); if(!TempErrorMessage.empty()) { c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); return; } - // Count of client's currently spawned bots. int spawnedBots = SpawnedBotCount(c->CharacterID(), &TempErrorMessage); if(!TempErrorMessage.empty()) { c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); return; } - // BotQuest rule value in database is True. if(RuleB(Bots, BotQuest)) { - // Max number of allowed spawned bots for client. const int allowedBotsBQ = AllowedBotSpawns(c->CharacterID(), &TempErrorMessage); if(!TempErrorMessage.empty()) { c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); return; } - // Fail if no bots allowed for client. if(allowedBotsBQ == 0) { c->Message(0, "You can't spawn any bots."); return; } - // Fail if maximum number of spawned bots allowed for client met or exceeded - // OR will be when bot group is spawned. - if(spawnedBots >= allowedBotsBQ - || spawnedBots + (int)botGroup.size() > allowedBotsBQ) { - c->Message(0, "You can't spawn more than %i bot(s). [Rule:BQ]", allowedBotsBQ); + if(spawnedBots >= allowedBotsBQ || spawnedBots + (int)botGroup.size() > allowedBotsBQ) { + c->Message(0, "You can't spawn more than %i bot(s).", allowedBotsBQ); return; } } - // Fail if maximum number of spawned bots allowed for client met or exceeded - // OR will be when bot group is spawned. const int allowedBotsSBC = RuleI(Bots, SpawnBotCount); - if(spawnedBots >= allowedBotsSBC - || spawnedBots + (int)botGroup.size() > allowedBotsSBC) { - c->Message(0, "You can't spawn more than %i bots. [Rule:SBC]", allowedBotsSBC); + if(spawnedBots >= allowedBotsSBC || spawnedBots + (int)botGroup.size() > allowedBotsSBC) { + c->Message(0, "You can't spawn more than %i bots.", allowedBotsSBC); return; } - // Passed all checks. Spawn requested bot group. - - // Get botgroup's leader's id. uint32 botGroupLeaderBotID = GetBotGroupLeaderIdByBotGroupName(botGroupName); - - // Load botgroup's leader. Bot *botGroupLeader = LoadBot(botGroupLeaderBotID, &TempErrorMessage); if(!TempErrorMessage.empty()) { c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); @@ -14442,7 +12067,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { return; } - // Spawn botgroup's leader. botGroupLeader->Spawn(c, &TempErrorMessage); if(!TempErrorMessage.empty()) { c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); @@ -14450,11 +12074,11 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { return; } - // Create botgroup. if(!BotGroupCreate(botGroupLeader)) { c->Message(13, "Unable to create botgroup."); return; } + Group *newBotGroup = botGroupLeader->GetGroup(); if(!newBotGroup) { c->Message(13, "Unable to find valid botgroup"); @@ -14462,23 +12086,21 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } for(auto botGroupItr = botGroup.begin(); botGroupItr != botGroup.end(); ++botGroupItr) { - // Don't try to re-spawn the botgroup's leader. - if(botGroupItr->BotID == botGroupLeader->GetBotID()) { continue; } + if(botGroupItr->BotID == botGroupLeader->GetBotID()) + continue; - // Load current botgroup member Bot *botGroupMember = LoadBot(botGroupItr->BotID, &TempErrorMessage); if(!TempErrorMessage.empty()) { c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); safe_delete(botGroupMember); return; } - // Skip invalid botgroup members. + if(!botGroupMember) { safe_delete(botGroupMember); continue; } - // Spawn current botgroup member. botGroupMember->Spawn(c, &TempErrorMessage); if(!TempErrorMessage.empty()) { c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); @@ -14486,19 +12108,15 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { return; } - // Add current botgroup member to botgroup. AddBotToGroup(botGroupMember, newBotGroup); } - return; } if(!strcasecmp(sep->arg[1], "botgroup") && !strcasecmp(sep->arg[2], "delete")) { std::string botGroupName = std::string(sep->arg[3]); - if(!botGroupName.empty()) { uint32 botGroupId = CanLoadBotGroup(c->CharacterID(), botGroupName, &TempErrorMessage); - if(!TempErrorMessage.empty()) { c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); return; @@ -14506,24 +12124,20 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(botGroupId > 0) { DeleteBotGroup(botGroupName, &TempErrorMessage); - if(!TempErrorMessage.empty()) { c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); return; } } } - return; } if(!strcasecmp(sep->arg[1], "botgroup") && !strcasecmp(sep->arg[2], "save")) { std::string botGroupName = std::string(sep->arg[3]); - if(!botGroupName.empty()) { if(!DoesBotGroupNameExist(botGroupName)) { Bot* groupLeader = 0; - if(c->GetTarget() && c->GetTarget()->IsBot()) groupLeader = c->GetTarget()->CastToBot(); else @@ -14532,23 +12146,17 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(groupLeader) { if(groupLeader->HasGroup() && groupLeader->GetGroup()->IsLeader(groupLeader)) { SaveBotGroup(groupLeader->GetGroup(), botGroupName, &TempErrorMessage); - - if(!TempErrorMessage.empty()) { + if(!TempErrorMessage.empty()) c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); - } else c->Message(0, "%s's bot group has been saved as %s.", groupLeader->GetName(), botGroupName.c_str()); - } - else + } else c->Message(0, "You must target a bot group leader only."); - } - else + } else c->Message(0, "You must target a bot that is in the same zone as you."); - } - else + } else c->Message(0, "The bot group name already exists. Please choose another name to save your bot group as."); } - return; } @@ -14567,22 +12175,23 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { uint8 BeardColor = target->GetBeardColor(); uint8 EyeColor1 = target->GetEyeColor1(); uint8 EyeColor2 = target->GetEyeColor2(); - uint8 LuclinFace = target->GetLuclinFace(); uint8 Beard = target->GetBeard(); uint32 DrakkinHeritage = target->GetDrakkinHeritage(); uint32 DrakkinTattoo = target->GetDrakkinTattoo(); uint32 DrakkinDetails = target->GetDrakkinDetails(); float Size = target->GetSize(); - if (!strcasecmp(sep->arg[1], "hair")) HairStyle = atoi(sep->arg[2]); + if (!strcasecmp(sep->arg[1], "haircolor")) HairColor = atoi(sep->arg[2]); + if (!strcasecmp(sep->arg[1], "beard") || !strcasecmp(sep->arg[1], "beardcolor")) { if (!Gender || Race == 8) { if (!strcasecmp(sep->arg[1], "beard")) Beard = atoi(sep->arg[2]); + if (!strcasecmp(sep->arg[1], "beardcolor")) BeardColor = atoi(sep->arg[2]); } else { @@ -14590,6 +12199,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { return; } } + if (!strcasecmp(sep->arg[1], "face")) LuclinFace = atoi(sep->arg[2]); @@ -14597,46 +12207,42 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { EyeColor1 = EyeColor2 = atoi(sep->arg[2]); c->Message(0, "Eye Values = 0 - 11"); } + if(!strcasecmp(sep->arg[1], "heritage") || !strcasecmp(sep->arg[1], "tattoo") || !strcasecmp(sep->arg[1], "details")) { if(Race == 522) { if(!strcasecmp(sep->arg[1], "heritage")) { DrakkinHeritage = atoi(sep->arg[2]); c->Message(0, "Heritage Values = 0 - 6"); } + if(!strcasecmp(sep->arg[1], "tattoo")) { DrakkinTattoo = atoi(sep->arg[2]); c->Message(0, "Tattoo Values = 0 - 7"); } + if(!strcasecmp(sep->arg[1], "details")) { DrakkinDetails = atoi(sep->arg[2]); c->Message(0, "Details Values = 0 - 7"); } - } - else { + } else { c->Message(0, "Drakkin only."); return; } } - target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, - EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, - DrakkinHeritage, DrakkinTattoo, DrakkinDetails, Size); - + target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, DrakkinHeritage, DrakkinTattoo, DrakkinDetails, Size); if(target->CastToBot()->Save()) c->Message(0, "%s saved.", target->GetCleanName()); else c->Message(13, "%s save failed!", target->GetCleanName()); - c->Message(0,"Feature changed."); - } else { + c->Message(0, "Feature changed."); + } else c->Message(0, "You must own the bot to make changes."); - } - } else { + } else c->Message(0, "Requires a value."); - } - } else { - c->Message(0,"A bot needs to be targeted."); - } + } else + c->Message(0, "A bot needs to be targeted."); return; } @@ -14655,7 +12261,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } Bot *targetedBot = nullptr; - if(c->GetTarget() != nullptr) { if (c->GetTarget()->IsBot() && (c->GetTarget()->CastToBot()->GetBotOwner() == c)) targetedBot = c->GetTarget()->CastToBot(); @@ -14670,25 +12275,20 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(taunt) { if(!targetedBot->taunting) targetedBot->BotGroupSay(targetedBot, "I am now taunting."); - } - else { + } else { if(targetedBot->taunting) targetedBot->BotGroupSay(targetedBot, "I am no longer taunting."); } targetedBot->SetTaunting(taunt); - } - else + } else c->Message(13, "You must select a bot with the taunt skill."); - } - else { + } else { c->Message(13, "You must target a spawned bot."); } } - } - else { + } else c->Message(0, "Usage #bot taunt [on|off]"); - } return; } @@ -14697,7 +12297,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(sep->argnum == 3){ Bot* tempBot = nullptr; std::string botName = std::string(sep->arg[2]); - if(!botName.empty()) tempBot = entity_list.GetBotByBotName(botName); else @@ -14706,23 +12305,19 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(tempBot) { std::string stanceName; BotStanceType botStance; - if (tempBot->GetBotOwner() != c) { c->Message(13, "You must target a bot that you own."); return; } - if(!strcasecmp(sep->arg[3], "list")) { + if(!strcasecmp(sep->arg[3], "list")) botStance = tempBot->GetBotStance(); - } else { int stance = atoi(sep->arg[3]); - if(stance >= MaxStances || stance < 0){ c->Message(0, "Usage #bot stance [name] [stance (id)] (Passive = 0, Balanced = 1, Efficient = 2, Reactive = 3, Aggressive = 4, Burn = 5, BurnAE = 6)"); return; - } - else { + } else { botStance = (BotStanceType)stance; if(botStance != tempBot->GetBotStance()) { tempBot->SetBotStance(botStance); @@ -14767,20 +12362,16 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } } c->Message(0, "Stance for %s: %s.", tempBot->GetCleanName(), stanceName.c_str()); - } - else { + } else c->Message(13, "You must name a valid bot."); - } - } - else { + } else c->Message(0, "Usage #bot stance [name] [stance (id)] (Passive = 0, Balanced = 1, Efficient = 2, Reactive = 3, Aggressive = 4, Burn = 5, BurnAE = 6)"); - } + return; } if(!strcasecmp(sep->arg[1], "groupmessages")) { bool groupMessages = false; - if(sep->arg[2] && sep->arg[3]){ if(!strcasecmp(sep->arg[2], "on")) groupMessages = true; @@ -14792,10 +12383,8 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } Bot* tempBot; - if(!strcasecmp(sep->arg[3], "all")) { std::list spawnedBots = entity_list.GetBotsByBotOwnerCharacterID(c->CharacterID()); - if(!spawnedBots.empty()) { for(std::list::iterator botsListItr = spawnedBots.begin(); botsListItr != spawnedBots.end(); ++botsListItr) { Bot* tempBot = *botsListItr; @@ -14803,16 +12392,12 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { tempBot->SetGroupMessagesOn(groupMessages); } } - } - else { + } else c->Message(0, "You have no spawned bots in this zone."); - } c->Message(0, "Group messages now %s for all bots.", groupMessages?"on":"off"); - } - else { + } else { std::string botName = std::string(sep->arg[3]); - if(!botName.empty()) tempBot = entity_list.GetBotByBotName(botName); else { @@ -14825,25 +12410,21 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { c->Message(13, "You must target a bot that you own."); return; } - tempBot->SetGroupMessagesOn(groupMessages); c->Message(0, "Group messages now %s.", groupMessages?"on":"off"); - } - else { + } else { c->Message(13, "You must name a valid bot."); } } - } - else { + } else c->Message(0, "Usage #bot groupmessages [on|off] [bot name|all]"); - } + return; } if(!strcasecmp(sep->arg[1], "defensive")) { Bot* tempBot; std::string botName = std::string(sep->arg[2]); - if(!botName.empty()) tempBot = entity_list.GetBotByBotName(botName); else { @@ -14854,7 +12435,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(tempBot) { uint8 botlevel = tempBot->GetLevel(); uint32 defensiveSpellID = 0; - if (tempBot->GetBotOwner() != c) { c->Message(13, "You must target a bot that you own."); return; @@ -14871,7 +12451,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { else if(botlevel >= 52) defensiveSpellID = 4503; //Evasive discipline else - c->Message(0, "Error: warrior must be level 52+"); + c->Message(0, "Warrior must be level 52 or higher."); break; case PALADIN: if(botlevel >= 73) @@ -14883,7 +12463,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { else if(botlevel >= 56) defensiveSpellID = 7004; //Guard of Piety else - c->Message(0, "Error: paladin must be level 56+"); + c->Message(0, "Paladin must be level 56 or higher."); break; case SHADOWKNIGHT: if(botlevel >= 73) @@ -14895,20 +12475,18 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { else if(botlevel >= 56) defensiveSpellID = 7005; //Ichor guard else - c->Message(0, "Error: shadowknight must be level 56+"); + c->Message(0, "Shadow Knight must be level 56 or higher."); break; default: - c->Message(0, "Error: you must select a warrior or knight"); + c->Message(0, "You must select a Warrior, Paladin, or Shadow Knight."); break; } - if(defensiveSpellID > 0) { + if(defensiveSpellID > 0) tempBot->UseDiscipline(defensiveSpellID, tempBot->GetID()); - } - } - else { + } else c->Message(13, "You must name a valid bot."); - } + return; } @@ -14926,16 +12504,13 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { c->Message(0, "#bot healrotation start "); c->Message(0, "#bot healrotation stop "); c->Message(0, "#bot healrotation list "); - return; } if(!strcasecmp(sep->arg[2], "create")) { - if(sep->argnum == 5 || sep->argnum == 6) { //allows for target or not + if(sep->argnum == 5 || sep->argnum == 6) { Bot* leaderBot; - std::string botName = std::string(sep->arg[3]); - if(!botName.empty()) leaderBot = entity_list.GetBotByBotName(botName); else { @@ -14947,14 +12522,12 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { Mob* target = nullptr; uint32 timer; bool fastHeals = false; - if (!sep->IsNumber(4)) { c->Message(0, "Usage #bot healrotation create [target]."); return; } timer = (uint32)(atof(sep->arg[4]) * 1000); - if (leaderBot->GetBotOwner() != c) { c->Message(13, "You must target a bot that you own."); return; @@ -14965,7 +12538,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { return; } - //get percentage heals if(!strcasecmp(sep->arg[5], "fasthealson")) fastHeals = true; else if(strcasecmp(sep->arg[5], "fasthealsoff")) { @@ -14974,10 +12546,8 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } if(!leaderBot->GetInHealRotation()) { - //check for target if(sep->argnum == 6) { std::string targetName = std::string(sep->arg[6]); - if(!targetName.empty()) target = entity_list.GetMob(targetName.c_str()); else { @@ -14990,23 +12560,18 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { return; } } - - //create rotation leaderBot->CreateHealRotation(target, timer); leaderBot->SetHealRotationUseFastHeals(fastHeals); c->Message(0, "Bot heal rotation created successfully."); - } - else { + } else { c->Message(13, "That bot is already in a heal rotation."); return; } - } - else { + } else { c->Message(13, "You must name a valid bot."); return; } - } - else { + } else { c->Message(0, "Usage #bot healrotation create [target]."); return; } @@ -15016,7 +12581,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(sep->argnum == 4) { Bot* leaderBot; std::string botName = std::string(sep->arg[3]); - if(!botName.empty()) leaderBot = entity_list.GetBotByBotName(botName); else { @@ -15027,7 +12591,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(leaderBot) { Bot* healer; std::string healerName = std::string(sep->arg[4]); - if (leaderBot->GetBotOwner() != c) { c->Message(13, "You must target a bot that you own."); return; @@ -15051,21 +12614,16 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { return; } - //add to rotation - if(leaderBot->AddHealRotationMember(healer)) { + if(leaderBot->AddHealRotationMember(healer)) c->Message(0, "Bot heal rotation member added successfully."); - } - else { - c->Message(13, "Unable to add bot to rotation. "); - } + else + c->Message(13, "Unable to add bot to rotation."); } - } - else { + } else { c->Message(13, "You must name a valid bot."); return; } - } - else { + } else { c->Message(0, "#bot healrotation addmember "); return; } @@ -15091,7 +12649,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { Bot* healer; std::string healerName = std::string(sep->arg[4]); - if(!healerName.empty()) healer = entity_list.GetBotByBotName(healerName); else { @@ -15105,25 +12662,19 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { return; } - //remove from rotation - if(leaderBot->RemoveHealRotationMember(healer)) { + if(leaderBot->RemoveHealRotationMember(healer)) c->Message(0, "Bot heal rotation member removed successfully."); - } - else { - c->Message(13, "Unable to remove bot from rotation. "); - } - } - else { + else + c->Message(13, "Unable to remove bot from rotation."); + } else { c->Message(13, "You must name a valid bot."); return; } - } - else { + } else { c->Message(13, "You must name a valid bot."); return; } - } - else { + } else { c->Message(0, "#bot healrotation removemember "); return; } @@ -15133,7 +12684,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(sep->argnum == 3 || sep->argnum == 4) { Bot* leaderBot; std::string botName = std::string(sep->arg[3]); - if(!botName.empty()) leaderBot = entity_list.GetBotByBotName(botName); else { @@ -15149,35 +12699,27 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { Mob* target = nullptr; std::string targetName = std::string(sep->arg[4]); - if(!targetName.empty()) target = entity_list.GetMob(targetName.c_str()); else { - if(c->GetTarget() != nullptr) { + if(c->GetTarget() != nullptr) target = c->GetTarget(); - } } if(target) { - //add target - if(leaderBot->AddHealRotationTarget(target)) { + if(leaderBot->AddHealRotationTarget(target)) c->Message(0, "Bot heal rotation target added successfully."); - } - else { - c->Message(13, "Unable to add rotation target. "); - } - } - else { + else + c->Message(13, "Unable to add rotation target."); + } else { c->Message(13, "Invalid target."); return; } - } - else { + } else { c->Message(13, "You must name a valid bot."); return; } - } - else { + } else { c->Message(0, "#bot healrotation addtarget [bot healrotation target name to add] "); return; } @@ -15187,7 +12729,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(sep->argnum == 4) { Bot* leaderBot; std::string botName = std::string(sep->arg[3]); - if(!botName.empty()) leaderBot = entity_list.GetBotByBotName(botName); else { @@ -15203,7 +12744,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { Mob* target; std::string targetName = std::string(sep->arg[4]); - if(!targetName.empty()) target = entity_list.GetMob(targetName.c_str()); else { @@ -15212,21 +12752,16 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } if(target) { - //add to rotation - if(leaderBot->RemoveHealRotationTarget(target)) { + if(leaderBot->RemoveHealRotationTarget(target)) c->Message(0, "Bot heal rotation target removed successfully."); - } - else { - c->Message(13, "Unable to remove rotation target. "); - } + else + c->Message(13, "Unable to remove rotation target."); } - } - else { + } else { c->Message(13, "You must name a valid bot."); return; } - } - else { + } else { c->Message(0, "#bot healrotation removetarget "); return; } @@ -15236,36 +12771,27 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(sep->argnum == 3) { if(!strcasecmp(sep->arg[3], "all")) { std::list BotList = entity_list.GetBotsByBotOwnerCharacterID(c->CharacterID()); - for(std::list::iterator botListItr = BotList.begin(); botListItr != BotList.end(); ++botListItr) { Bot* leaderBot = *botListItr; if(leaderBot->GetInHealRotation() && leaderBot->GetHealRotationLeader() == leaderBot) { - //start all heal rotations std::list rotationMemberList; int index = 0; - rotationMemberList = GetBotsInHealRotation(leaderBot); - for(std::list::iterator rotationMemberItr = rotationMemberList.begin(); rotationMemberItr != rotationMemberList.end(); ++rotationMemberItr) { Bot* tempBot = *rotationMemberItr; - if(tempBot) { tempBot->SetHealRotationActive(true); tempBot->SetHealRotationNextHealTime(Timer::GetCurrentTime() + index * leaderBot->GetHealRotationTimer() * 1000); tempBot->SetHasHealedThisCycle(false); } - index++; } - c->Message(0, "Bot heal rotation started successfully."); } } - } - else { + } else { Bot* leaderBot; std::string botName = std::string(sep->arg[3]); - if(!botName.empty()) leaderBot = entity_list.GetBotByBotName(botName); else { @@ -15282,28 +12808,22 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } botList = GetBotsInHealRotation(leaderBot); - for(std::list::iterator botListItr = botList.begin(); botListItr != botList.end(); ++botListItr) { Bot* tempBot = *botListItr; - if(tempBot) { tempBot->SetHealRotationActive(true); tempBot->SetHealRotationNextHealTime(Timer::GetCurrentTime() + index * leaderBot->GetHealRotationTimer() * 1000); tempBot->SetHasHealedThisCycle(false); } - index++; } - c->Message(0, "Bot heal rotation started successfully."); - } - else { + } else { c->Message(13, "You must name a valid bot."); return; } } - } - else { + } else { c->Message(0, "#bot healrotation start "); return; } @@ -15313,32 +12833,24 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(sep->argnum == 3) { if(!strcasecmp(sep->arg[3], "all")) { std::list BotList = entity_list.GetBotsByBotOwnerCharacterID(c->CharacterID()); - for(std::list::iterator botListItr = BotList.begin(); botListItr != BotList.end(); ++botListItr) { Bot* leaderBot = *botListItr; if(leaderBot->GetInHealRotation() && leaderBot->GetHealRotationLeader() == leaderBot) { - //start all heal rotations std::list rotationMemberList; - rotationMemberList = GetBotsInHealRotation(leaderBot); - for(std::list::iterator rotationMemberItr = rotationMemberList.begin(); rotationMemberItr != rotationMemberList.end(); ++rotationMemberItr) { Bot* tempBot = *rotationMemberItr; - if(tempBot) { tempBot->SetHealRotationActive(false); tempBot->SetHasHealedThisCycle(false); } } - c->Message(0, "Bot heal rotation started successfully."); } } - } - else { + } else { Bot* leaderBot; std::string botName = std::string(sep->arg[3]); - if(!botName.empty()) leaderBot = entity_list.GetBotByBotName(botName); else { @@ -15354,10 +12866,8 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } botList = GetBotsInHealRotation(leaderBot); - for(std::list::iterator botListItr = botList.begin(); botListItr != botList.end(); ++botListItr) { Bot* tempBot = *botListItr; - if(tempBot && tempBot->GetBotOwnerCharacterID() == c->CharacterID()) { tempBot->SetHealRotationActive(false); tempBot->SetHasHealedThisCycle(false); @@ -15365,14 +12875,12 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } c->Message(0, "Bot heal rotation stopped successfully."); - } - else { + } else { c->Message(13, "You must name a valid bot."); return; } } - } - else { + } else { c->Message(0, "#bot healrotation stop "); return; } @@ -15383,21 +12891,15 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { bool showAll = false; Bot* leaderBot; std::string botName = std::string(sep->arg[3]); - if(!strcasecmp(sep->arg[3], "all")) { std::list BotList = entity_list.GetBotsByBotOwnerCharacterID(c->CharacterID()); - for(std::list::iterator botListItr = BotList.begin(); botListItr != BotList.end(); ++botListItr) { Bot* tempBot = *botListItr; - if(tempBot->GetInHealRotation() && tempBot->GetHealRotationLeader() == tempBot) { - //list leaders and number of bots per rotation + if(tempBot->GetInHealRotation() && tempBot->GetHealRotationLeader() == tempBot) c->Message(0, "Bot Heal Rotation- Leader: %s, Number of Members: %i, Timer: %1.1f", tempBot->GetCleanName(), tempBot->GetNumHealRotationMembers(), (float)(tempBot->GetHealRotationTimer()/1000)); - } } - } - else { + } else { std::string botName = std::string(sep->arg[3]); - if(!botName.empty()) leaderBot = entity_list.GetBotByBotName(botName); else { @@ -15413,43 +12915,31 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } botList = GetBotsInHealRotation(leaderBot); - - //list leader and number of members c->Message(0, "Bot Heal Rotation- Leader: %s", leaderBot->GetCleanName()); c->Message(0, "Bot Heal Rotation- Timer: %1.1f", ((float)leaderBot->GetHealRotationTimer()/1000.0f)); - for(std::list::iterator botListItr = botList.begin(); botListItr != botList.end(); ++botListItr) { Bot* tempBot = *botListItr; - - if(tempBot && tempBot->GetBotOwnerCharacterID() == c->CharacterID()) { - //list rotation members + if(tempBot && tempBot->GetBotOwnerCharacterID() == c->CharacterID()) c->Message(0, "Bot Heal Rotation- Member: %s", tempBot->GetCleanName()); - } } - for(int i=0; iGetHealRotationTarget(i)) { Mob* tempTarget = leaderBot->GetHealRotationTarget(i); - if(tempTarget) { std::string targetInfo = ""; - targetInfo += tempTarget->GetHPRatio() < 0 ? "(dead) " : ""; targetInfo += tempTarget->GetZoneID() != leaderBot->GetZoneID() ? "(not in zone) " : ""; - - //list targets c->Message(0, "Bot Heal Rotation- Target: %s %s", tempTarget->GetCleanName(), targetInfo.c_str()); } } } - } - else { + } else { c->Message(13, "You must name a valid bot."); return; } } - } - else { + } else { c->Message(0, "#bot healrotation list "); return; } @@ -15459,7 +12949,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(sep->argnum == 3) { Bot* leaderBot; std::string botName = std::string(sep->arg[3]); - if(!botName.empty()) leaderBot = entity_list.GetBotByBotName(botName); else { @@ -15475,20 +12964,16 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } botList = GetBotsInHealRotation(leaderBot); - for(std::list::iterator botListItr = botList.begin(); botListItr != botList.end(); ++botListItr) { Bot* tempBot = *botListItr; - if(tempBot && tempBot->GetBotOwnerCharacterID() == c->CharacterID()) tempBot->ClearHealRotationTargets(); } - } - else { + } else { c->Message(13, "You must name a valid bot."); return; } - } - else { + } else { c->Message(0, "#bot healrotation cleartargets "); return; } @@ -15498,7 +12983,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(sep->argnum == 3) { Bot* leaderBot; std::string botName = std::string(sep->arg[3]); - if(!botName.empty()) leaderBot = entity_list.GetBotByBotName(botName); else { @@ -15514,7 +12998,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { return; } - //get percentage heals & target if(!strcasecmp(sep->arg[4], "on")) fastHeals = true; else if(strcasecmp(sep->arg[4], "off")) { @@ -15523,36 +13006,22 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } botList = GetBotsInHealRotation(leaderBot); - for(std::list::iterator botListItr = botList.begin(); botListItr != botList.end(); ++botListItr) { Bot* tempBot = *botListItr; - if(tempBot && tempBot->GetBotOwnerCharacterID() == c->CharacterID()) tempBot->SetHealRotationUseFastHeals(fastHeals); } - } - else { + } else { c->Message(13, "You must name a valid bot."); return; } - } - else { + } else { c->Message(0, "#bot healrotation fastheals "); return; } } - - if(!strcasecmp(sep->arg[2], "load")) { - } - - if(!strcasecmp(sep->arg[2], "save")) { - } - - if(!strcasecmp(sep->arg[2], "delete")) { - } } - // #bot setinspectmessage if(!strcasecmp(sep->arg[1], "setinspectmessage")) { if(!strcasecmp(sep->arg[2], "help")) { c->Message(0, "[Titanium clients:]"); @@ -15566,20 +13035,15 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { c->Message(0, "- Close the self-inspect window to update the server"); c->Message(0, "- Target a bot that you own and wish to update"); c->Message(0, "- type #bot setinspectmessage to set the bot's message"); - } - else { + } else { Mob *target = c->GetTarget(); - if(target && target->IsBot() && (c == target->GetOwner()->CastToClient())) { const InspectMessage_Struct& playermessage = c->GetInspectMessage(); InspectMessage_Struct& botmessage = target->CastToBot()->GetInspectMessage(); - memcpy(&botmessage, &playermessage, sizeof(InspectMessage_Struct)); database.SetBotInspectMessage(target->CastToBot()->GetBotID(), &botmessage); - c->Message(0, "Bot %s's inspect message now reflects your inspect message.", target->GetName()); - } - else { + } else { c->Message(0, "Your target must be a bot that you own."); } } @@ -15587,7 +13051,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(!strcasecmp(sep->arg[1], "bardoutofcombat")) { bool useOutOfCombatSongs = false; - if(sep->arg[2] && sep->arg[3]){ if(!strcasecmp(sep->arg[2], "on")) useOutOfCombatSongs = true; @@ -15599,22 +13062,16 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } Mob *target = c->GetTarget(); - if(target && target->IsBot() && (c == target->GetOwner()->CastToClient())) { Bot* bardBot = target->CastToBot(); - if(bardBot) { bardBot->SetBardUseOutOfCombatSongs(useOutOfCombatSongs); c->Message(0, "Bard use of out of combat songs updated."); } - } - else { + } else c->Message(0, "Your target must be a bot that you own."); - } - } - else { + } else c->Message(0, "Usage #bot bardoutofcombat [on|off]"); - } return; } @@ -15645,25 +13102,13 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } } -// franck: EQoffline -// This function has been reworked for the caster bots, when engaged. -// Healers bots must heal thoses who loose HP. bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint16 iSpellTypes) { - if((iSpellTypes&SpellTypes_Detrimental) != 0) { - //according to live, you can buff and heal through walls... - //now with PCs, this only applies if you can TARGET the target, but - // according to Rogean, Live NPCs will just cast through walls/floors, no problem.. - // - // This check was put in to address an idle-mob CPU issue Log.Out(Logs::General, Logs::Error, "Error: detrimental spells requested from AICheckCloseBeneficialSpells!!"); - return(false); + return false; } - if(!caster) - return false; - - if(!caster->AI_HasSpells()) + if(!caster || !caster->AI_HasSpells()) return false; if (iChance < 100) { @@ -15675,30 +13120,22 @@ bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, fl uint8 botCasterClass = caster->GetClass(); if( iSpellTypes == SpellType_Heal ) { - // Changed so heal based on health percentage is different for hybrids if( botCasterClass == CLERIC || botCasterClass == DRUID || botCasterClass == SHAMAN) { - //If AI_EngagedCastCheck() said to the healer that he had to heal - - // check in group if(caster->HasGroup()) { Group *g = caster->GetGroup(); - if(g) { for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { if(g->members[i] && !g->members[i]->qglobal) { if(g->members[i]->IsClient() && g->members[i]->GetHPRatio() < 90) { if(caster->AICastSpell(g->members[i], 100, SpellType_Heal)) return true; - } - else if((g->members[i]->GetClass() == WARRIOR || g->members[i]->GetClass() == PALADIN || g->members[i]->GetClass() == SHADOWKNIGHT) && g->members[i]->GetHPRatio() < 95) { + } else if((g->members[i]->GetClass() == WARRIOR || g->members[i]->GetClass() == PALADIN || g->members[i]->GetClass() == SHADOWKNIGHT) && g->members[i]->GetHPRatio() < 95) { if(caster->AICastSpell(g->members[i], 100, SpellType_Heal)) return true; - } - else if(g->members[i]->GetClass() == ENCHANTER && g->members[i]->GetHPRatio() < 80) { + } else if(g->members[i]->GetClass() == ENCHANTER && g->members[i]->GetHPRatio() < 80) { if(caster->AICastSpell(g->members[i], 100, SpellType_Heal)) return true; - } - else if(g->members[i]->GetHPRatio() < 70) { + } else if(g->members[i]->GetHPRatio() < 70) { if(caster->AICastSpell(g->members[i], 100, SpellType_Heal)) return true; } @@ -15714,26 +13151,13 @@ bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, fl } } } - - // TODO: raid heals } - // Changed so heal based on health percentage is different for hybrids if( botCasterClass == PALADIN || botCasterClass == BEASTLORD || botCasterClass == RANGER) { - //If AI_EngagedCastCheck() said to the healer that he had to heal - - // check in group if(caster->HasGroup()) { Group *g = caster->GetGroup(); - float hpRatioToHeal = 25.0f; - - switch(caster->GetBotStance()) - { - case BotStanceAggressive: - case BotStanceEfficient: - hpRatioToHeal = 25.0f; - break; + switch(caster->GetBotStance()) { case BotStanceReactive: case BotStanceBalanced: hpRatioToHeal = 50.0f; @@ -15742,6 +13166,8 @@ bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, fl case BotStanceBurnAE: hpRatioToHeal = 20.0f; break; + case BotStanceAggressive: + case BotStanceEfficient: default: hpRatioToHeal = 25.0f; break; @@ -15753,16 +13179,13 @@ bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, fl if(g->members[i]->IsClient() && g->members[i]->GetHPRatio() < hpRatioToHeal) { if(caster->AICastSpell(g->members[i], 100, SpellType_Heal)) return true; - } - else if((g->members[i]->GetClass() == WARRIOR || g->members[i]->GetClass() == PALADIN || g->members[i]->GetClass() == SHADOWKNIGHT) && g->members[i]->GetHPRatio() < hpRatioToHeal) { + } else if((g->members[i]->GetClass() == WARRIOR || g->members[i]->GetClass() == PALADIN || g->members[i]->GetClass() == SHADOWKNIGHT) && g->members[i]->GetHPRatio() < hpRatioToHeal) { if(caster->AICastSpell(g->members[i], 100, SpellType_Heal)) return true; - } - else if(g->members[i]->GetClass() == ENCHANTER && g->members[i]->GetHPRatio() < hpRatioToHeal) { + } else if(g->members[i]->GetClass() == ENCHANTER && g->members[i]->GetHPRatio() < hpRatioToHeal) { if(caster->AICastSpell(g->members[i], 100, SpellType_Heal)) return true; - } - else if(g->members[i]->GetHPRatio() < hpRatioToHeal/2) { + } else if(g->members[i]->GetHPRatio() < hpRatioToHeal/2) { if(caster->AICastSpell(g->members[i], 100, SpellType_Heal)) return true; } @@ -15778,15 +13201,11 @@ bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, fl } } } - - // TODO: raid heals } } - //Ok for the buffs.. if( iSpellTypes == SpellType_Buff) { - uint8 chanceToCast = caster->IsEngaged()?caster->GetChanceToCastBySpellType(SpellType_Buff):100; - // Let's try to make Bard working... + uint8 chanceToCast = caster->IsEngaged() ? caster->GetChanceToCastBySpellType(SpellType_Buff) : 100; if(botCasterClass == BARD) { if(caster->AICastSpell(caster, chanceToCast, SpellType_Buff)) return true; @@ -15796,29 +13215,22 @@ bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, fl if(caster->HasGroup()) { Group *g = caster->GetGroup(); - if(g) { - for( int i = 0; i < MAX_GROUP_MEMBERS; i++) { + for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { if(g->members[i]) { - if(caster->AICastSpell(g->members[i], chanceToCast, SpellType_Buff)) - return true; - - if(caster->AICastSpell(g->members[i]->GetPet(), chanceToCast, SpellType_Buff)) + if(caster->AICastSpell(g->members[i], chanceToCast, SpellType_Buff) || caster->AICastSpell(g->members[i]->GetPet(), chanceToCast, SpellType_Buff)) return true; } } } } - - // TODO: raid buffs } if( iSpellTypes == SpellType_Cure) { if(caster->HasGroup()) { Group *g = caster->GetGroup(); - if(g) { - for( int i = 0; i < MAX_GROUP_MEMBERS; i++) { + for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { if(g->members[i] && caster->GetNeedsCured(g->members[i])) { if(caster->AICastSpell(g->members[i], caster->GetChanceToCastBySpellType(SpellType_Cure), SpellType_Cure)) return true; @@ -15833,143 +13245,117 @@ bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, fl } } } - - // TODO: raid buffs } - return false; } Mob* EntityList::GetMobByBotID(uint32 botID) { Mob* Result = 0; - if(botID > 0) { auto it = mob_list.begin(); - - for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { - if(!it->second) continue; + for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { + if(!it->second) + continue; + if(it->second->IsBot() && it->second->CastToBot()->GetBotID() == botID) { Result = it->second; break; } } } - return Result; } Bot* EntityList::GetBotByBotID(uint32 botID) { Bot* Result = 0; - if(botID > 0) { for(std::list::iterator botListItr = bot_list.begin(); botListItr != bot_list.end(); ++botListItr) { Bot* tempBot = *botListItr; - if(tempBot && tempBot->GetBotID() == botID) { Result = tempBot; break; } } } - return Result; } Bot* EntityList::GetBotByBotName(std::string botName) { Bot* Result = 0; - if(!botName.empty()) { for(std::list::iterator botListItr = bot_list.begin(); botListItr != bot_list.end(); ++botListItr) { Bot* tempBot = *botListItr; - if(tempBot && std::string(tempBot->GetName()) == botName) { Result = tempBot; break; } } } - return Result; } void EntityList::AddBot(Bot *newBot, bool SendSpawnPacket, bool dontqueue) { if(newBot) { newBot->SetID(GetFreeID()); - if(SendSpawnPacket) { if(dontqueue) { - // Send immediately EQApplicationPacket* outapp = new EQApplicationPacket(); newBot->CreateSpawnPacket(outapp); outapp->priority = 6; QueueClients(newBot, outapp, true); safe_delete(outapp); - } - else { - // Queue the packet + } else { NewSpawn_Struct* ns = new NewSpawn_Struct; memset(ns, 0, sizeof(NewSpawn_Struct)); newBot->FillSpawnStruct(ns, newBot); AddToSpawnQueue(newBot->GetID(), &ns); safe_delete(ns); } - parse->EventNPC(EVENT_SPAWN, newBot, nullptr, "", 0); } - bot_list.push_back(newBot); - mob_list.insert(std::pair(newBot->GetID(), newBot)); } } std::list EntityList::GetBotsByBotOwnerCharacterID(uint32 botOwnerCharacterID) { std::list Result; - if(botOwnerCharacterID > 0) { for(std::list::iterator botListItr = bot_list.begin(); botListItr != bot_list.end(); ++botListItr) { Bot* tempBot = *botListItr; - if(tempBot && tempBot->GetBotOwnerCharacterID() == botOwnerCharacterID) Result.push_back(tempBot); } } - return Result; } -void EntityList::BotPickLock(Bot* rogue) -{ +void EntityList::BotPickLock(Bot* rogue) { for (auto it = door_list.begin(); it != door_list.end(); ++it) { Doors *cdoor = it->second; if(!cdoor || cdoor->IsDoorOpen()) continue; - auto diff = rogue->GetPosition() - cdoor->GetPosition(); - - float curdist = diff.x * diff.x + diff.y * diff.y; - - if((diff.z * diff.z >= 10) || (curdist > 130)) + auto diff = (rogue->GetPosition() - cdoor->GetPosition()); + float curdist = ((diff.x * diff.x) + (diff.y * diff.y)); + if(((diff.z * diff.z) >= 10) || curdist > 130) continue; - // All rogue items with lock pick bonuses are hands or primary const ItemInst* item1 = rogue->GetBotItem(MainHands); const ItemInst* item2 = rogue->GetBotItem(MainPrimary); - float bonus1 = 0.0f; float bonus2 = 0.0f; float skill = rogue->GetSkill(SkillPickLock); - - if(item1) // Hand slot item + if(item1) if(item1->GetItem()->SkillModType == SkillPickLock) - bonus1 = skill * (((float)item1->GetItem()->SkillModValue) / 100.0f); + bonus1 = (skill * (((float)item1->GetItem()->SkillModValue) / 100.0f)); - if(item2) // Primary slot item + if(item2) if(item2->GetItem()->SkillModType == SkillPickLock) - bonus2 = skill * (((float)item2->GetItem()->SkillModValue) / 100.0f); + bonus2 = (skill * (((float)item2->GetItem()->SkillModValue) / 100.0f)); - if((skill+bonus1+bonus2) >= cdoor->GetLockpick()) + if((skill + bonus1 + bonus2) >= cdoor->GetLockpick()) cdoor->ForceOpen(rogue); else rogue->BotGroupSay(rogue, "I am not skilled enough for this lock."); @@ -15978,12 +13364,9 @@ void EntityList::BotPickLock(Bot* rogue) bool EntityList::RemoveBot(uint16 entityID) { bool Result = false; - if(entityID > 0) { - for(std::list::iterator botListItr = bot_list.begin(); botListItr != bot_list.end(); ++botListItr) - { + for(std::list::iterator botListItr = bot_list.begin(); botListItr != bot_list.end(); ++botListItr) { Bot* tempBot = *botListItr; - if(tempBot && tempBot->GetID() == entityID) { bot_list.erase(botListItr); Result = true; @@ -15991,23 +13374,17 @@ bool EntityList::RemoveBot(uint16 entityID) { } } } - return Result; } void EntityList::ShowSpawnWindow(Client* client, int Distance, bool NamedOnly) { - const char *WindowTitle = "Bot Tracking Window"; - std::string WindowText; int LastCon = -1; int CurrentCon = 0; Mob* curMob = nullptr; - uint32 array_counter = 0; - auto it = mob_list.begin(); - for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { curMob = it->second; if (curMob && DistanceNoZ(curMob->GetPosition(), client->GetPosition()) <= Distance) { @@ -16046,36 +13423,34 @@ void EntityList::ShowSpawnWindow(Client* client, int Distance, bool NamedOnly) { "stone_","lava_","_","" }; unsigned int MyArraySize; - for ( MyArraySize = 0; true; MyArraySize++) { //Find empty string & get size - if (!(*(MyArray[MyArraySize]))) break; //Checks for null char in 1st pos + for ( MyArraySize = 0; true; MyArraySize++) { + if (!(*(MyArray[MyArraySize]))) + break; }; if (NamedOnly) { bool ContinueFlag = false; - const char *CurEntityName = cur_entity->GetName(); //Call function once + const char *CurEntityName = cur_entity->GetName(); for (int Index = 0; Index < MyArraySize; Index++) { if (!strncasecmp(CurEntityName, MyArray[Index], strlen(MyArray[Index])) || (Extras)) { ContinueFlag = true; - break; //From Index for + break; }; }; - if (ContinueFlag) continue; //Moved here or would apply to Index for + if (ContinueFlag) + continue; }; CurrentCon = client->GetLevelCon(cur_entity->GetLevel()); if(CurrentCon != LastCon) { - if(LastCon != -1) WindowText += ""; LastCon = CurrentCon; - switch(CurrentCon) { - case CON_GREEN: { WindowText += ""; break; } - case CON_LIGHTBLUE: { WindowText += ""; break; @@ -16084,7 +13459,6 @@ void EntityList::ShowSpawnWindow(Client* client, int Distance, bool NamedOnly) { WindowText += ""; break; } - case CON_YELLOW: { WindowText += ""; break; @@ -16099,66 +13473,50 @@ void EntityList::ShowSpawnWindow(Client* client, int Distance, bool NamedOnly) { } } } - WindowText += cur_entity->GetCleanName(); WindowText += "
"; - if(strlen(WindowText.c_str()) > 4000) { - // Popup window is limited to 4096 characters. - WindowText += "


List truncated ... too many mobs to display"; + WindowText += "


List truncated... too many mobs to display"; break; } } } } WindowText += "
"; - client->SendPopupToClient(WindowTitle, WindowText.c_str()); - return; } uint8 Bot::GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets) { uint8 needHealed = 0; Group *g; - if(this->HasGroup()) { g = this->GetGroup(); - if(g) { for( int i = 0; imembers[i] && !g->members[i]->qglobal) { - if(g->members[i]->GetHPRatio() <= hpr) needHealed++; if(includePets) { - if(g->members[i]->GetPet() && g->members[i]->GetPet()->GetHPRatio() <= hpr) { + if(g->members[i]->GetPet() && g->members[i]->GetPet()->GetHPRatio() <= hpr) needHealed++; - } } } } } } - return needHealed; } -uint32 Bot::GetEquipmentColor(uint8 material_slot) const -{ - //Bot tints +uint32 Bot::GetEquipmentColor(uint8 material_slot) const { int16 slotid = 0; uint32 botid = this->GetBotID(); - - //Translate code slot # to DB slot # slotid = Inventory::CalcSlotFromMaterial(material_slot); if (slotid == INVALID_INDEX) return 0; - //read from db - std::string query = StringFormat("SELECT color FROM botinventory " - "WHERE BotID = %u AND SlotID = %u", botid, slotid); + std::string query = StringFormat("SELECT color FROM botinventory WHERE BotID = %u AND SlotID = %u", botid, slotid); auto results = database.QueryDatabase(query); if (!results.Success() || results.RowCount() != 1) return 0; @@ -16167,21 +13525,16 @@ uint32 Bot::GetEquipmentColor(uint8 material_slot) const return atoul(row[0]); } -int Bot::GetRawACNoShield(int &shield_ac) -{ +int Bot::GetRawACNoShield(int &shield_ac) { int ac = itembonuses.AC + spellbonuses.AC; shield_ac = 0; ItemInst* inst = GetBotItem(MainSecondary); - if(inst) - { - if(inst->GetItem()->ItemType == ItemTypeShield) - { + if(inst) { + if(inst->GetItem()->ItemType == ItemTypeShield) { ac -= inst->GetItem()->AC; shield_ac = inst->GetItem()->AC; - for (uint8 i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) - { - if(inst->GetAugment(i)) - { + for (uint8 i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) { + if(inst->GetAugment(i)) { ac -= inst->GetAugment(i)->GetItem()->AC; shield_ac += inst->GetAugment(i)->GetItem()->AC; } @@ -16192,38 +13545,32 @@ int Bot::GetRawACNoShield(int &shield_ac) } uint32 Bot::CalcCurrentWeight() { - const Item_Struct* TempItem = 0; ItemInst* inst; uint32 Total = 0; - for(int i = EmuConstants::EQUIPMENT_BEGIN; i <= EmuConstants::EQUIPMENT_END; ++i) { inst = GetBotItem(i); if(inst) { - TempItem = inst->GetItem(); - if (TempItem) - Total += TempItem->Weight; + TempItem = inst->GetItem(); + if (TempItem) + Total += TempItem->Weight; } } - float Packrat = (float)spellbonuses.Packrat + (float)aabonuses.Packrat; - + float Packrat = ((float)spellbonuses.Packrat + (float)aabonuses.Packrat); if (Packrat > 0) - Total = (uint32)((float)Total * (1.0f - ((Packrat * 1.0f) / 100.0f))); //AndMetal: 1% per level, up to 5% (calculated from Titanium client). verified thru client that it reduces coin weight by the same % - //without casting to float & back to uint32, this didn't work right - + Total = (uint32)((float)Total * (1.0f - ((Packrat * 1.0f) / 100.0f))); + return Total; } -int Bot::GroupLeadershipAAHealthEnhancement() -{ +int Bot::GroupLeadershipAAHealthEnhancement() { Group *g = GetGroup(); if(!g || (g->GroupCount() < 3)) return 0; - switch(g->GetLeadershipAA(groupAAHealthEnhancement)) - { + switch(g->GetLeadershipAA(groupAAHealthEnhancement)) { case 0: return 0; case 1: @@ -16233,19 +13580,15 @@ int Bot::GroupLeadershipAAHealthEnhancement() case 3: return 100; } - return 0; } -int Bot::GroupLeadershipAAManaEnhancement() -{ +int Bot::GroupLeadershipAAManaEnhancement() { Group *g = GetGroup(); - if(!g || (g->GroupCount() < 3)) return 0; - switch(g->GetLeadershipAA(groupAAManaEnhancement)) - { + switch(g->GetLeadershipAA(groupAAManaEnhancement)) { case 0: return 0; case 1: @@ -16255,19 +13598,15 @@ int Bot::GroupLeadershipAAManaEnhancement() case 3: return 100; } - return 0; } -int Bot::GroupLeadershipAAHealthRegeneration() -{ +int Bot::GroupLeadershipAAHealthRegeneration() { Group *g = GetGroup(); - if(!g || (g->GroupCount() < 3)) return 0; - switch(g->GetLeadershipAA(groupAAHealthRegeneration)) - { + switch(g->GetLeadershipAA(groupAAHealthRegeneration)) { case 0: return 0; case 1: @@ -16281,15 +13620,13 @@ int Bot::GroupLeadershipAAHealthRegeneration() return 0; } -int Bot::GroupLeadershipAAOffenseEnhancement() -{ +int Bot::GroupLeadershipAAOffenseEnhancement() { Group *g = GetGroup(); if(!g || (g->GroupCount() < 3)) return 0; - switch(g->GetLeadershipAA(groupAAOffenseEnhancement)) - { + switch(g->GetLeadershipAA(groupAAOffenseEnhancement)) { case 0: return 0; case 1: @@ -16308,23 +13645,16 @@ int Bot::GroupLeadershipAAOffenseEnhancement() bool Bot::GetNeedsCured(Mob *tar) { bool needCured = false; - if(tar) { if(tar->FindType(SE_PoisonCounter) || tar->FindType(SE_DiseaseCounter) || tar->FindType(SE_CurseCounter) || tar->FindType(SE_CorruptionCounter)) { uint32 buff_count = GetMaxTotalSlots(); int buffsWithCounters = 0; needCured = true; - for (unsigned int j = 0; j < buff_count; j++) { if(tar->GetBuffs()[j].spellid != SPELL_UNKNOWN) { if(CalculateCounters(tar->GetBuffs()[j].spellid) > 0) { buffsWithCounters++; - if(buffsWithCounters == 1 && (tar->GetBuffs()[j].ticsremaining < 2 || (int32)((tar->GetBuffs()[j].ticsremaining * 6) / tar->GetBuffs()[j].counters) < 2)) { - // Spell has ticks remaining but may have too many counters to cure in the time remaining; - // We should try to just wait it out. Could spend entire time trying to cure spell instead of healing, buffing, etc. - // Since this is the first buff with counters, don't try to cure. Cure spell will be wasted, as cure will try to - // remove counters from the first buff that has counters remaining. needCured = false; break; } @@ -16333,27 +13663,23 @@ bool Bot::GetNeedsCured(Mob *tar) { } } } - return needCured; } bool Bot::HasOrMayGetAggro() { bool mayGetAggro = false; - if(GetTarget() && GetTarget()->GetHateTop()) { Mob *topHate = GetTarget()->GetHateTop(); - if(topHate == this) - mayGetAggro = true; //I currently have aggro + mayGetAggro = true; else { uint32 myHateAmt = GetTarget()->GetHateAmount(this); uint32 topHateAmt = GetTarget()->GetHateAmount(topHate); - if(myHateAmt > 0 && topHateAmt > 0 && (uint8)((myHateAmt/topHateAmt)*100) > 90) //I have 90% as much hate as top, next action may give me aggro + if(myHateAmt > 0 && topHateAmt > 0 && (uint8)((myHateAmt / topHateAmt) * 100) > 90) mayGetAggro = true; } } - return mayGetAggro; } @@ -16361,38 +13687,13 @@ void Bot::SetHasBeenSummoned(bool wasSummoned) { _hasBeenSummoned = wasSummoned; if(!wasSummoned) m_PreSummonLocation = glm::vec3(); - } void Bot::SetDefaultBotStance() { - BotStanceType defaultStance; - - switch(GetClass()) - { - case DRUID: - case CLERIC: - case SHAMAN: - case ENCHANTER: - case NECROMANCER: - case MAGICIAN: - case WIZARD: - case BEASTLORD: - case BERSERKER: - case MONK: - case ROGUE: - case BARD: - case SHADOWKNIGHT: - case PALADIN: - case RANGER: - defaultStance = BotStanceBalanced; - break; - case WARRIOR: - defaultStance = BotStanceAggressive; - break; - default: - defaultStance = BotStanceBalanced; - break; - } + BotStanceType defaultStance = BotStanceBalanced; + if (GetClass() == WARRIOR) + defaultStance = BotStanceAggressive; + _baseBotStance = BotStancePassive; _botStance = defaultStance; } @@ -16400,76 +13701,42 @@ void Bot::SetDefaultBotStance() { void Bot::BotGroupSay(Mob *speaker, const char *msg, ...) { char buf[1000]; va_list ap; - va_start(ap, msg); vsnprintf(buf, 1000, msg, ap); va_end(ap); - if(speaker->HasGroup()) { Group *g = speaker->GetGroup(); - if(g) g->GroupMessage(speaker->CastToMob(), 0, 100, buf); - } else { + } else speaker->Say("%s", buf); - } } bool Bot::UseDiscipline(uint32 spell_id, uint32 target) { - //make sure we have the spell... - int r; - /*for(r = 0; r < MAX_PP_DISCIPLINES; r++) { - if(m_pp.disciplines.values[r] == spell_id) - break; - } - if(r == MAX_PP_DISCIPLINES) - return(false); //not found. - - //Check the disc timer - pTimerType DiscTimer = pTimerDisciplineReuseStart + spells[spell_id].EndurTimerIndex; - if(!p_timers.Expired(&database, DiscTimer)) { - uint32 remain = p_timers.GetRemainingTime(DiscTimer); - //Message_StringID(0, DISCIPLINE_CANUSEIN, ConvertArray((remain)/60,val1), ConvertArray(remain%60,val2)); - Message(0, "You can use this discipline in %d minutes %d seconds.", ((remain)/60), (remain%60)); - return(false); - }*/ - - //make sure we can use it.. if(!IsValidSpell(spell_id)) { - BotGroupSay(this, "Not a valid spell"); - return(false); + BotGroupSay(this, "Not a valid spell."); + return false; } - //can we use the spell? const SPDat_Spell_Struct &spell = spells[spell_id]; uint8 level_to_use = spell.classes[GetClass() - 1]; - if(level_to_use == 255) { - return(false); + if(level_to_use == 255 || level_to_use > GetLevel()) { + return false; } - if(level_to_use > GetLevel()) { - return(false); - } - - if(GetEndurance() > spell.EndurCost) { + if(GetEndurance() > spell.EndurCost) SetEndurance(GetEndurance() - spell.EndurCost); - } else { - return(false); - } + else + return false; - if(spell.recast_time > 0) - { + if(spell.recast_time > 0) { if(CheckDisciplineRecastTimers(this, spells[spell_id].EndurTimerIndex)) { - - //CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT); - if(spells[spell_id].EndurTimerIndex > 0 && spells[spell_id].EndurTimerIndex < MAX_DISCIPLINE_TIMERS) { + if(spells[spell_id].EndurTimerIndex > 0 && spells[spell_id].EndurTimerIndex < MAX_DISCIPLINE_TIMERS) SetDisciplineRecastTimer(spells[spell_id].EndurTimerIndex, spell.recast_time); - } - } - else { - uint32 remain = GetDisciplineRemainingTime(this, spells[spell_id].EndurTimerIndex) / 1000; - GetOwner()->Message(0, "%s can use this discipline in %d minutes %d seconds.", GetCleanName(), ((remain)/60), (remain%60)); - return(false); + } else { + uint32 remain = (GetDisciplineRemainingTime(this, spells[spell_id].EndurTimerIndex) / 1000); + GetOwner()->Message(0, "%s can use this discipline in %d minutes %d seconds.", GetCleanName(), (remain / 60), (remain % 60)); + return false; } } @@ -16477,8 +13744,7 @@ bool Bot::UseDiscipline(uint32 spell_id, uint32 target) { InterruptSpell(); CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT); - - return(true); + return true; } void Bot::CreateHealRotation( Mob* target, uint32 timer ) { @@ -16490,7 +13756,6 @@ void Bot::CreateHealRotation( Mob* target, uint32 timer ) { SetPrevHealRotationMember(this); SetHealRotationTimer(timer); SetHasHealedThisCycle(false); - if(target) AddHealRotationTarget(target); } @@ -16499,13 +13764,11 @@ bool Bot::AddHealRotationMember( Bot* healer ) { if(healer) { if(GetNumHealRotationMembers() > 0 && GetNumHealRotationMembers() < MaxHealRotationMembers) { Bot* tempBot = GetPrevHealRotationMember(); - if(tempBot) { - //add new healer to rotation at end of list - for(int i=0; i<3; i++){ + for(int i = 0; i < 3; i++){ healer->ClearHealRotationMembers(); healer->ClearHealRotationTargets(); - healer->AddHealRotationTarget(entity_list.GetMob(_healRotationTargets[i])); // add all targets.. + healer->AddHealRotationTarget(entity_list.GetMob(_healRotationTargets[i])); } healer->SetHealRotationTimer(tempBot->GetHealRotationTimer()); healer->SetHealRotationLeader(this); @@ -16514,27 +13777,18 @@ bool Bot::AddHealRotationMember( Bot* healer ) { healer->SetInHealRotation(true); healer->SetHasHealedThisCycle(false); healer->SetHealRotationUseFastHeals(tempBot->GetHealRotationUseFastHeals()); - - //set previous rotation member's next member to new member tempBot->SetNextHealRotationMember(healer); - - //update leader's previous member (end of list) to new member and update rotation data SetPrevHealRotationMember(healer); - std::list botList = GetBotsInHealRotation(this); - for(std::list::iterator botListItr = botList.begin(); botListItr != botList.end(); ++botListItr) { Bot* tempBot = *botListItr; - if(tempBot) tempBot->SetNumHealRotationMembers(GetNumHealRotationMembers()+1); } - return true; } } } - return false; } @@ -16543,15 +13797,11 @@ bool Bot::RemoveHealRotationMember( Bot* healer ) { Bot* leader = healer->GetHealRotationLeader(); Bot* prevBot = healer->GetPrevHealRotationMember(); Bot* nextBot = healer->GetNextHealRotationMember(); - if(healer == this) { - if(nextBot != this) { - //get new leader + if(nextBot != this) leader = nextBot; - } } - //remove healer from list healer->SetHealRotationTimer(0); healer->ClearHealRotationMembers(); healer->ClearHealRotationTargets(); @@ -16559,34 +13809,22 @@ bool Bot::RemoveHealRotationMember( Bot* healer ) { healer->SetHasHealedThisCycle(false); healer->SetHealRotationActive(false); healer->SetInHealRotation(false); - if(prevBot && nextBot && GetNumHealRotationMembers() > 1) { - //set previous rotation member's next member to new member prevBot->SetNextHealRotationMember(nextBot); - - //set previous rotation member's next member to new member nextBot->SetPrevHealRotationMember(prevBot); } - //update rotation data std::list botList = GetBotsInHealRotation(leader); - for(std::list::iterator botListItr = botList.begin(); botListItr != botList.end(); ++botListItr) { Bot* tempBot = *botListItr; - if(tempBot) { - tempBot->SetNumHealRotationMembers(GetNumHealRotationMembers()-1); - - if(tempBot->GetHealRotationLeader() != leader) { - // change leader if leader is being removed + tempBot->SetNumHealRotationMembers(GetNumHealRotationMembers() - 1); + if(tempBot->GetHealRotationLeader() != leader) tempBot->SetHealRotationLeader(leader); - } } } - return true; } - return false; } @@ -16605,67 +13843,56 @@ void Bot::SetPrevHealRotationMember( Bot* healer ) { Bot* Bot::GetHealRotationLeader( ) { if(_healRotationLeader) return entity_list.GetBotByBotID(_healRotationLeader); + return 0; } Bot* Bot::GetNextHealRotationMember( ) { if(_healRotationMemberNext) return entity_list.GetBotByBotID(_healRotationMemberNext); + return 0; } Bot* Bot::GetPrevHealRotationMember( ) { if(_healRotationMemberNext) return entity_list.GetBotByBotID(_healRotationMemberPrev); + return 0; } bool Bot::AddHealRotationTarget( Mob* target ) { if(target) { - for (int i = 0; i < MaxHealRotationTargets; ++i) { if(_healRotationTargets[i] > 0) { Mob* tempTarget = entity_list.GetMob(_healRotationTargets[i]); - if(!tempTarget) { _healRotationTargets[i] = 0; - } - else if(!strcasecmp(tempTarget->GetCleanName(), target->GetCleanName())) { - //check to see if target's ID is incorrect (could have zoned, died, etc) - if(tempTarget->GetID() != target->GetID()) { + } else if(!strcasecmp(tempTarget->GetCleanName(), target->GetCleanName())) { + if(tempTarget->GetID() != target->GetID()) _healRotationTargets[i] = target->GetID(); - } - //target already in list + return false; } } - if (_healRotationTargets[i] == 0) - { + if (_healRotationTargets[i] == 0) { std::list botList = GetBotsInHealRotation(this); - _healRotationTargets[i] = target->GetID(); - for(std::list::iterator botListItr = botList.begin(); botListItr != botList.end(); ++botListItr) { Bot* tempBot = *botListItr; - - if(tempBot && tempBot != this) { - //add target to all members + if(tempBot && tempBot != this) tempBot->AddHealRotationTarget(target, i); - } } - return true; } } } - return false; } bool Bot::AddHealRotationTarget( Mob *target, int index ) { if (target && index < MaxHealRotationTargets) { - //add target to list of targets at specified index _healRotationTargets[index] = target->GetID(); return true; } @@ -16676,37 +13903,30 @@ bool Bot::RemoveHealRotationTarget( Mob* target ) { int index = 0; bool removed = false; if(target) { - //notify all heal rotation members to remove target - for(int i=0; iGetID()) { std::list botList = GetBotsInHealRotation(this); _healRotationTargets[i] = 0; index = i; removed = true; - for(std::list::iterator botListItr = botList.begin(); botListItr != botList.end(); ++botListItr) { Bot* tempBot = *botListItr; - if(tempBot) tempBot->RemoveHealRotationTarget(i); } } } } - return removed; } bool Bot::RemoveHealRotationTarget( int index ) { if(index >= 0) { - //clear rotation target at index _healRotationTargets[index] = 0; - if(index < MaxHealRotationTargets) { - for(int i=index; i 0) { - - //get first target in list target = entity_list.GetMob(_healRotationTargets[i]); - if(target) { - //check if valid target - if(target->GetZoneID() == GetZoneID() - && !(target->GetAppearance() == eaDead - && !(target->IsClient() && target->CastToClient()->GetFeigned()))) { - + if(target->GetZoneID() == GetZoneID() && !(target->GetAppearance() == eaDead && !(target->IsClient() && target->CastToClient()->GetFeigned()))) { count++; - - //get first valid target - if(!first) { + if(!first) first = target; - } - //check to see if target is group main tank - //(target first, in case top target has died and was rez'd - - //we don't want to heal them then) if(!tank) { Group* g = target->GetGroup(); - if(g && !strcasecmp(g->GetMainTankName(), target->GetCleanName())) { + if(g && !strcasecmp(g->GetMainTankName(), target->GetCleanName())) tank = target; - } } } - } - else { - //if not valid target, remove from list + } else { if(removeIndex == 0) removeIndex = i; } } } - if (removeIndex > 0) { - RemoveHealRotationTarget( removeIndex ); - } + if (removeIndex > 0) + RemoveHealRotationTarget(removeIndex); if(tank) return tank; @@ -16783,35 +13984,27 @@ Mob* Bot::GetHealRotationTarget( ) { Mob* Bot::GetHealRotationTarget( uint8 index ) { Mob* target = nullptr; - - if(_healRotationTargets[index] > 0) { - //get target at specified index + if(_healRotationTargets[index] > 0) target = entity_list.GetMob(_healRotationTargets[index]); - } return target; } std::list Bot::GetBotsInHealRotation(Bot* rotationLeader) { std::list Result; - if(rotationLeader != nullptr) { Result.push_back(rotationLeader); Bot* rotationMember = rotationLeader->GetNextHealRotationMember(); - while(rotationMember && rotationMember != rotationLeader) { Result.push_back(rotationMember); rotationMember = rotationMember->GetNextHealRotationMember(); } } - return Result; } void Bot::NotifyNextHealRotationMember(bool notifyNow) { - //check if we need to notify to start now, or after timer uint32 nextHealTime = notifyNow ? Timer::GetCurrentTime() : Timer::GetCurrentTime() + GetHealRotationTimer(); - Bot* nextMember = GetNextHealRotationMember(); if(nextMember && nextMember != this) { nextMember->SetHealRotationNextHealTime(nextHealTime); @@ -16822,11 +14015,9 @@ void Bot::NotifyNextHealRotationMember(bool notifyNow) { void Bot::BotHealRotationsClear(Client* c) { if(c) { std::list BotList = entity_list.GetBotsByBotOwnerCharacterID(c->CharacterID()); - for(std::list::iterator botListItr = BotList.begin(); botListItr != BotList.end(); ++botListItr) { Bot* tempBot = *botListItr; if(tempBot->GetInHealRotation()) { - //clear all heal rotation data for bots in a heal rotation tempBot->SetInHealRotation(false); tempBot->SetHealRotationActive(false); tempBot->SetHasHealedThisCycle(false); @@ -16840,4 +14031,4 @@ void Bot::BotHealRotationsClear(Client* c) { } } -#endif +#endif \ No newline at end of file From 6503e6371af3b90972b47b68b573946c8311d3eb Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Fri, 19 Jun 2015 19:55:10 -0400 Subject: [PATCH 128/129] Added Combat:BackstabBonus rule. - 0 = 0%, 5 = 5%, 50 = 50%, 200 = 200% --- common/ruletypes.h | 1033 +++++++++++++++++++------------------- zone/bot.cpp | 4 +- zone/special_attacks.cpp | 4 +- 3 files changed, 521 insertions(+), 520 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 9a36b77f4..1919c7168 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -20,603 +20,604 @@ -RULE_CATEGORY( Character ) -RULE_INT ( Character, MaxLevel, 65 ) -RULE_BOOL ( Character, PerCharacterQglobalMaxLevel, false) // This will check for qglobal 'CharMaxLevel' character qglobal (Type 5), if player tries to level beyond that point, it will not go beyond that level -RULE_INT ( Character, MaxExpLevel, 0 ) //Sets the Max Level attainable via Experience -RULE_INT ( Character, DeathExpLossLevel, 10 ) // Any level greater than this will lose exp on death -RULE_INT ( Character, DeathExpLossMaxLevel, 255 ) // Any level greater than this will no longer lose exp on death -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_INT ( Character, CorpseDecayTimeMS, 10800000 ) -RULE_INT ( Character, CorpseResTimeMS, 10800000 ) // time before cant res corpse(3 hours) -RULE_BOOL( Character, LeaveCorpses, true ) -RULE_BOOL( Character, LeaveNakedCorpses, false ) -RULE_INT ( Character, MaxDraggedCorpses, 2 ) -RULE_REAL( Character, DragCorpseDistance, 400) // If the corpse is <= this distance from the player, it won't move -RULE_REAL( Character, ExpMultiplier, 0.5 ) -RULE_REAL( Character, AAExpMultiplier, 0.5 ) -RULE_REAL( Character, GroupExpMultiplier, 0.5 ) -RULE_REAL( Character, RaidExpMultiplier, 0.2 ) -RULE_BOOL( Character, UseXPConScaling, true ) -RULE_INT ( Character, LightBlueModifier, 40 ) -RULE_INT ( Character, BlueModifier, 90 ) -RULE_INT ( Character, WhiteModifier, 100 ) -RULE_INT ( Character, YellowModifier, 125 ) -RULE_INT ( Character, RedModifier, 150 ) -RULE_INT ( Character, AutosaveIntervalS, 300 ) //0=disabled -RULE_INT ( Character, HPRegenMultiplier, 100) -RULE_INT ( Character, ManaRegenMultiplier, 100) -RULE_INT ( Character, EnduranceRegenMultiplier, 100) -RULE_INT ( Character, ConsumptionMultiplier, 100) //item's hunger restored = this value * item's food level, 100 = normal, 50 = people eat 2x as fast, 200 = people eat 2x as slow -RULE_BOOL( Character, HealOnLevel, false) -RULE_BOOL( Character, FeignKillsPet, false) -RULE_INT ( Character, ItemManaRegenCap, 15) -RULE_INT ( Character, ItemHealthRegenCap, 35) -RULE_INT ( Character, ItemDamageShieldCap, 30) -RULE_INT ( Character, ItemAccuracyCap, 150) -RULE_INT ( Character, ItemAvoidanceCap, 100) -RULE_INT ( Character, ItemCombatEffectsCap, 100) -RULE_INT ( Character, ItemShieldingCap, 35) -RULE_INT ( Character, ItemSpellShieldingCap, 35) -RULE_INT ( Character, ItemDoTShieldingCap, 35) -RULE_INT ( Character, ItemStunResistCap, 35) -RULE_INT ( Character, ItemStrikethroughCap, 35) -RULE_INT ( Character, ItemATKCap, 250) -RULE_INT ( Character, ItemHealAmtCap, 250) -RULE_INT ( Character, ItemSpellDmgCap, 250) -RULE_INT ( Character, ItemClairvoyanceCap, 250) -RULE_INT ( Character, ItemDSMitigationCap, 50) -RULE_INT ( Character, ItemEnduranceRegenCap, 15) -RULE_INT ( Character, ItemExtraDmgCap, 150) // Cap for bonuses to melee skills like Bash, Frenzy, etc -RULE_INT ( Character, HasteCap, 100) // Haste cap for non-v3(overhaste) haste. -RULE_INT ( Character, SkillUpModifier, 100) //skill ups are at 100% -RULE_BOOL ( Character, SharedBankPlat, false) //off by default to prevent duping for now -RULE_BOOL ( Character, BindAnywhere, false) -RULE_INT ( Character, RestRegenPercent, 0) // Set to >0 to enable rest state bonus HP and mana regen. -RULE_INT ( Character, RestRegenTimeToActivate, 30) // Time in seconds for rest state regen to kick in. -RULE_INT ( Character, RestRegenRaidTimeToActivate, 300) // Time in seconds for rest state regen to kick in with a raid target. -RULE_BOOL ( Character, RestRegenEndurance, false) // Whether rest regen will work for endurance or not. -RULE_INT ( Character, KillsPerGroupLeadershipAA, 250) // Number of dark blues or above per Group Leadership AA -RULE_INT ( Character, KillsPerRaidLeadershipAA, 250) // Number of dark blues or above per Raid Leadership AA -RULE_INT ( Character, MaxFearDurationForPlayerCharacter, 4) //4 tics, each tic calculates every 6 seconds. -RULE_INT ( Character, MaxCharmDurationForPlayerCharacter, 15) -RULE_INT ( Character, BaseHPRegenBonusRaces, 4352) //a bitmask of race(s) that receive the regen bonus. Iksar (4096) & Troll (256) = 4352. see common/races.h for the bitmask values -RULE_BOOL ( Character, SoDClientUseSoDHPManaEnd, false) // Setting this to true will allow SoD clients to use the SoD HP/Mana/End formulas and previous clients will use the old formulas -RULE_BOOL ( Character, UseRaceClassExpBonuses, true) // Setting this to true will enable Class and Racial experience rate bonuses -RULE_BOOL ( Character, RespawnFromHover, false) // Use Respawn window, or not. -RULE_INT ( Character, RespawnFromHoverTimer, 300) // Respawn Window countdown timer, in SECONDS -RULE_BOOL ( Character, UseNewStatsWindow, true) // New stats window shows everything -RULE_BOOL ( Character, ItemCastsUseFocus, false) // If true, this allows item clickies to use focuses that have limited max levels on them -RULE_INT ( Character, MinStatusForNoDropExemptions, 80) // This allows status x and higher to trade no drop items. -RULE_INT ( Character, SkillCapMaxLevel, 75 ) // Sets the Max Level used for Skill Caps (from skill_caps table). -1 makes it use MaxLevel rule value. It is set to 75 because PEQ only has skillcaps up to that level, and grabbing the players' skill past 75 will return 0, breaking all skills past that level. This helps servers with obsurd level caps (75+ level cap) function without any modifications. -RULE_INT ( Character, StatCap, 0 ) -RULE_BOOL ( Character, CheckCursorEmptyWhenLooting, true ) // If true, a player cannot loot a corpse (player or NPC) with an item on their cursor -RULE_BOOL ( Character, MaintainIntoxicationAcrossZones, true ) // If true, alcohol effects are maintained across zoning and logging out/in. -RULE_BOOL ( Character, EnableDiscoveredItems, true ) // If enabled, it enables EVENT_DISCOVER_ITEM and also saves character names and timestamps for the first time an item is discovered. -RULE_BOOL ( Character, EnableXTargetting, true) // Enable Extended Targetting Window, for users with UF and later clients. -RULE_BOOL ( Character, KeepLevelOverMax, false) // Don't delevel a character that has somehow gone over the level cap -RULE_INT ( Character, FoodLossPerUpdate, 35) // How much food/water you lose per stamina update -RULE_INT ( Character, BaseInstrumentSoftCap, 36) // Softcap for instrument mods, 36 commonly referred to as "3.6" as well. -RULE_INT ( Character, BaseRunSpeedCap, 158) // Base Run Speed Cap, on live it's 158% which will give you a runspeed of 1.580 hard capped to 225. -RULE_INT ( Character, OrnamentationAugmentType, 20) //Ornamentation Augment Type +RULE_CATEGORY(Character) +RULE_INT(Character, MaxLevel, 65) +RULE_BOOL(Character, PerCharacterQglobalMaxLevel, false) // This will check for qglobal 'CharMaxLevel' character qglobal (Type 5), if player tries to level beyond that point, it will not go beyond that level +RULE_INT(Character, MaxExpLevel, 0) //Sets the Max Level attainable via Experience +RULE_INT(Character, DeathExpLossLevel, 10) // Any level greater than this will lose exp on death +RULE_INT(Character, DeathExpLossMaxLevel, 255) // Any level greater than this will no longer lose exp on death +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_INT(Character, CorpseDecayTimeMS, 10800000) +RULE_INT(Character, CorpseResTimeMS, 10800000) // time before cant res corpse(3 hours) +RULE_BOOL(Character, LeaveCorpses, true) +RULE_BOOL(Character, LeaveNakedCorpses, false) +RULE_INT(Character, MaxDraggedCorpses, 2) +RULE_REAL(Character, DragCorpseDistance, 400) // If the corpse is <= this distance from the player, it won't move +RULE_REAL(Character, ExpMultiplier, 0.5) +RULE_REAL(Character, AAExpMultiplier, 0.5) +RULE_REAL(Character, GroupExpMultiplier, 0.5) +RULE_REAL(Character, RaidExpMultiplier, 0.2) +RULE_BOOL(Character, UseXPConScaling, true) +RULE_INT(Character, LightBlueModifier, 40) +RULE_INT(Character, BlueModifier, 90) +RULE_INT(Character, WhiteModifier, 100) +RULE_INT(Character, YellowModifier, 125) +RULE_INT(Character, RedModifier, 150) +RULE_INT(Character, AutosaveIntervalS, 300) //0=disabled +RULE_INT(Character, HPRegenMultiplier, 100) +RULE_INT(Character, ManaRegenMultiplier, 100) +RULE_INT(Character, EnduranceRegenMultiplier, 100) +RULE_INT(Character, ConsumptionMultiplier, 100) //item's hunger restored = this value * item's food level, 100 = normal, 50 = people eat 2x as fast, 200 = people eat 2x as slow +RULE_BOOL(Character, HealOnLevel, false) +RULE_BOOL(Character, FeignKillsPet, false) +RULE_INT(Character, ItemManaRegenCap, 15) +RULE_INT(Character, ItemHealthRegenCap, 35) +RULE_INT(Character, ItemDamageShieldCap, 30) +RULE_INT(Character, ItemAccuracyCap, 150) +RULE_INT(Character, ItemAvoidanceCap, 100) +RULE_INT(Character, ItemCombatEffectsCap, 100) +RULE_INT(Character, ItemShieldingCap, 35) +RULE_INT(Character, ItemSpellShieldingCap, 35) +RULE_INT(Character, ItemDoTShieldingCap, 35) +RULE_INT(Character, ItemStunResistCap, 35) +RULE_INT(Character, ItemStrikethroughCap, 35) +RULE_INT(Character, ItemATKCap, 250) +RULE_INT(Character, ItemHealAmtCap, 250) +RULE_INT(Character, ItemSpellDmgCap, 250) +RULE_INT(Character, ItemClairvoyanceCap, 250) +RULE_INT(Character, ItemDSMitigationCap, 50) +RULE_INT(Character, ItemEnduranceRegenCap, 15) +RULE_INT(Character, ItemExtraDmgCap, 150) // Cap for bonuses to melee skills like Bash, Frenzy, etc +RULE_INT(Character, HasteCap, 100) // Haste cap for non-v3(overhaste) haste. +RULE_INT(Character, SkillUpModifier, 100) //skill ups are at 100% +RULE_BOOL(Character, SharedBankPlat, false) //off by default to prevent duping for now +RULE_BOOL(Character, BindAnywhere, false) +RULE_INT(Character, RestRegenPercent, 0) // Set to >0 to enable rest state bonus HP and mana regen. +RULE_INT(Character, RestRegenTimeToActivate, 30) // Time in seconds for rest state regen to kick in. +RULE_INT(Character, RestRegenRaidTimeToActivate, 300) // Time in seconds for rest state regen to kick in with a raid target. +RULE_BOOL(Character, RestRegenEndurance, false) // Whether rest regen will work for endurance or not. +RULE_INT(Character, KillsPerGroupLeadershipAA, 250) // Number of dark blues or above per Group Leadership AA +RULE_INT(Character, KillsPerRaidLeadershipAA, 250) // Number of dark blues or above per Raid Leadership AA +RULE_INT(Character, MaxFearDurationForPlayerCharacter, 4) //4 tics, each tic calculates every 6 seconds. +RULE_INT(Character, MaxCharmDurationForPlayerCharacter, 15) +RULE_INT(Character, BaseHPRegenBonusRaces, 4352) //a bitmask of race(s) that receive the regen bonus. Iksar (4096) & Troll (256) = 4352. see common/races.h for the bitmask values +RULE_BOOL(Character, SoDClientUseSoDHPManaEnd, false) // Setting this to true will allow SoD clients to use the SoD HP/Mana/End formulas and previous clients will use the old formulas +RULE_BOOL(Character, UseRaceClassExpBonuses, true) // Setting this to true will enable Class and Racial experience rate bonuses +RULE_BOOL(Character, RespawnFromHover, false) // Use Respawn window, or not. +RULE_INT(Character, RespawnFromHoverTimer, 300) // Respawn Window countdown timer, in SECONDS +RULE_BOOL(Character, UseNewStatsWindow, true) // New stats window shows everything +RULE_BOOL(Character, ItemCastsUseFocus, false) // If true, this allows item clickies to use focuses that have limited max levels on them +RULE_INT(Character, MinStatusForNoDropExemptions, 80) // This allows status x and higher to trade no drop items. +RULE_INT(Character, SkillCapMaxLevel, 75) // Sets the Max Level used for Skill Caps (from skill_caps table). -1 makes it use MaxLevel rule value. It is set to 75 because PEQ only has skillcaps up to that level, and grabbing the players' skill past 75 will return 0, breaking all skills past that level. This helps servers with obsurd level caps (75+ level cap) function without any modifications. +RULE_INT(Character, StatCap, 0) +RULE_BOOL(Character, CheckCursorEmptyWhenLooting, true) // If true, a player cannot loot a corpse (player or NPC) with an item on their cursor +RULE_BOOL(Character, MaintainIntoxicationAcrossZones, true) // If true, alcohol effects are maintained across zoning and logging out/in. +RULE_BOOL(Character, EnableDiscoveredItems, true) // If enabled, it enables EVENT_DISCOVER_ITEM and also saves character names and timestamps for the first time an item is discovered. +RULE_BOOL(Character, EnableXTargetting, true) // Enable Extended Targetting Window, for users with UF and later clients. +RULE_BOOL(Character, KeepLevelOverMax, false) // Don't delevel a character that has somehow gone over the level cap +RULE_INT(Character, FoodLossPerUpdate, 35) // How much food/water you lose per stamina update +RULE_INT(Character, BaseInstrumentSoftCap, 36) // Softcap for instrument mods, 36 commonly referred to as "3.6" as well. +RULE_INT(Character, BaseRunSpeedCap, 158) // Base Run Speed Cap, on live it's 158% which will give you a runspeed of 1.580 hard capped to 225. +RULE_INT(Character, OrnamentationAugmentType, 20) //Ornamentation Augment Type RULE_REAL(Character, EnvironmentDamageMulipliter, 1) RULE_BOOL(Character, UnmemSpellsOnDeath, true) -RULE_INT ( Character, TradeskillUpAlchemy, 2 ) // Alchemy skillup rate adjust. Lower is faster. -RULE_INT ( Character, TradeskillUpBaking, 2 ) // Baking skillup rate adjust. Lower is faster. -RULE_INT ( Character, TradeskillUpBlacksmithing, 2 ) // Blacksmithing skillup rate adjust. Lower is faster. -RULE_INT ( Character, TradeskillUpBrewing, 3 ) // Brewing skillup rate adjust. Lower is faster. -RULE_INT ( Character, TradeskillUpFletching, 2 ) // Fletching skillup rate adjust. Lower is faster. -RULE_INT ( Character, TradeskillUpJewelcrafting, 2 ) // Jewelcrafting skillup rate adjust. Lower is faster. -RULE_INT ( Character, TradeskillUpMakePoison, 2 ) // Make Poison skillup rate adjust. Lower is faster. -RULE_INT ( Character, TradeskillUpPottery, 4 ) // Pottery skillup rate adjust. Lower is faster. -RULE_INT ( Character, TradeskillUpResearch, 1 ) // Research skillup rate adjust. Lower is faster. -RULE_INT ( Character, TradeskillUpTinkering, 2 ) // Tinkering skillup rate adjust. Lower is faster. +RULE_INT(Character, TradeskillUpAlchemy, 2) // Alchemy skillup rate adjust. Lower is faster. +RULE_INT(Character, TradeskillUpBaking, 2) // Baking skillup rate adjust. Lower is faster. +RULE_INT(Character, TradeskillUpBlacksmithing, 2) // Blacksmithing skillup rate adjust. Lower is faster. +RULE_INT(Character, TradeskillUpBrewing, 3) // Brewing skillup rate adjust. Lower is faster. +RULE_INT(Character, TradeskillUpFletching, 2) // Fletching skillup rate adjust. Lower is faster. +RULE_INT(Character, TradeskillUpJewelcrafting, 2) // Jewelcrafting skillup rate adjust. Lower is faster. +RULE_INT(Character, TradeskillUpMakePoison, 2) // Make Poison skillup rate adjust. Lower is faster. +RULE_INT(Character, TradeskillUpPottery, 4) // Pottery skillup rate adjust. Lower is faster. +RULE_INT(Character, TradeskillUpResearch, 1) // Research skillup rate adjust. Lower is faster. +RULE_INT(Character, TradeskillUpTinkering, 2) // Tinkering skillup rate adjust. Lower is faster. RULE_CATEGORY_END() -RULE_CATEGORY( Mercs ) -RULE_INT (Mercs, SuspendIntervalMS, 10000) -RULE_INT (Mercs, UpkeepIntervalMS, 180000) -RULE_INT (Mercs, SuspendIntervalS, 10) -RULE_INT (Mercs, UpkeepIntervalS, 180) -RULE_BOOL (Mercs, AllowMercs, false) -RULE_BOOL (Mercs, ChargeMercPurchaseCost, false) -RULE_BOOL (Mercs, ChargeMercUpkeepCost, false) -RULE_INT (Mercs, AggroRadius, 100) // Determines the distance from which a merc will aggro group member's target(also used to determine the distance at which a healer merc will begin healing a group member) -RULE_INT (Mercs, AggroRadiusPuller, 25) // Determines the distance from which a merc will aggro group member's target, if they have the group role of puller (also used to determine the distance at which a healer merc will begin healing a group member, if they have the group role of puller) -RULE_INT (Mercs, ResurrectRadius, 50) // Determines the distance from which a healer merc will attempt to resurrect a group member's corpse -RULE_INT (Mercs, ScaleRate, 100) +RULE_CATEGORY(Mercs) +RULE_INT(Mercs, SuspendIntervalMS, 10000) +RULE_INT(Mercs, UpkeepIntervalMS, 180000) +RULE_INT(Mercs, SuspendIntervalS, 10) +RULE_INT(Mercs, UpkeepIntervalS, 180) +RULE_BOOL(Mercs, AllowMercs, false) +RULE_BOOL(Mercs, ChargeMercPurchaseCost, false) +RULE_BOOL(Mercs, ChargeMercUpkeepCost, false) +RULE_INT(Mercs, AggroRadius, 100) // Determines the distance from which a merc will aggro group member's target(also used to determine the distance at which a healer merc will begin healing a group member) +RULE_INT(Mercs, AggroRadiusPuller, 25) // Determines the distance from which a merc will aggro group member's target, if they have the group role of puller (also used to determine the distance at which a healer merc will begin healing a group member, if they have the group role of puller) +RULE_INT(Mercs, ResurrectRadius, 50) // Determines the distance from which a healer merc will attempt to resurrect a group member's corpse +RULE_INT(Mercs, ScaleRate, 100) RULE_CATEGORY_END() -RULE_CATEGORY( Guild ) -RULE_INT ( Guild, MaxMembers, 2048 ) -RULE_BOOL ( Guild, PlayerCreationAllowed, false) // Allow players to create a guild using the window in Underfoot+ -RULE_INT ( Guild, PlayerCreationLimit, 1) // Only allow use of the UF+ window if the account has < than this number of guild leaders on it -RULE_INT ( Guild, PlayerCreationRequiredStatus, 0) // Required admin status. -RULE_INT ( Guild, PlayerCreationRequiredLevel, 0) // Required Level of the player attempting to create the guild. -RULE_INT ( Guild, PlayerCreationRequiredTime, 0) // Required Time Entitled On Account (in Minutes) to create the guild. +RULE_CATEGORY(Guild) +RULE_INT(Guild, MaxMembers, 2048) +RULE_BOOL(Guild, PlayerCreationAllowed, false) // Allow players to create a guild using the window in Underfoot+ +RULE_INT(Guild, PlayerCreationLimit, 1) // Only allow use of the UF+ window if the account has < than this number of guild leaders on it +RULE_INT(Guild, PlayerCreationRequiredStatus, 0) // Required admin status. +RULE_INT(Guild, PlayerCreationRequiredLevel, 0) // Required Level of the player attempting to create the guild. +RULE_INT(Guild, PlayerCreationRequiredTime, 0) // Required Time Entitled On Account (in Minutes) to create the guild. RULE_CATEGORY_END() -RULE_CATEGORY( Skills ) -RULE_INT ( Skills, MaxTrainTradeskills, 21 ) -RULE_BOOL ( Skills, UseLimitTradeskillSearchSkillDiff, true ) -RULE_INT ( Skills, MaxTradeskillSearchSkillDiff, 50 ) -RULE_INT ( Skills, MaxTrainSpecializations, 50 ) // Max level a GM trainer will train casting specializations -RULE_INT ( Skills, SwimmingStartValue, 100 ) -RULE_BOOL ( Skills, TrainSenseHeading, false ) -RULE_INT ( Skills, SenseHeadingStartValue, 200 ) +RULE_CATEGORY(Skills) +RULE_INT(Skills, MaxTrainTradeskills, 21) +RULE_BOOL(Skills, UseLimitTradeskillSearchSkillDiff, true) +RULE_INT(Skills, MaxTradeskillSearchSkillDiff, 50) +RULE_INT(Skills, MaxTrainSpecializations, 50) // Max level a GM trainer will train casting specializations +RULE_INT(Skills, SwimmingStartValue, 100) +RULE_BOOL(Skills, TrainSenseHeading, false) +RULE_INT(Skills, SenseHeadingStartValue, 200) RULE_CATEGORY_END() -RULE_CATEGORY( Pets ) -RULE_REAL( Pets, AttackCommandRange, 150 ) -RULE_BOOL( Pets, UnTargetableSwarmPet, false ) -RULE_REAL( Pets, PetPowerLevelCap, 10 ) // Max number of levels your pet can go up with pet power +RULE_CATEGORY(Pets) +RULE_REAL(Pets, AttackCommandRange, 150) +RULE_BOOL(Pets, UnTargetableSwarmPet, false) +RULE_REAL(Pets, PetPowerLevelCap, 10) // Max number of levels your pet can go up with pet power RULE_CATEGORY_END() -RULE_CATEGORY( GM ) -RULE_INT ( GM, MinStatusToSummonItem, 250) -RULE_INT ( GM, MinStatusToZoneAnywhere, 250 ) +RULE_CATEGORY(GM) +RULE_INT(GM, MinStatusToSummonItem, 250) +RULE_INT(GM, MinStatusToZoneAnywhere, 250) RULE_CATEGORY_END() -RULE_CATEGORY( World ) -RULE_INT ( World, ZoneAutobootTimeoutMS, 60000 ) -RULE_INT ( World, ClientKeepaliveTimeoutMS, 65000 ) -RULE_BOOL ( World, UseBannedIPsTable, false ) // Toggle whether or not to check incoming client connections against the Banned_IPs table. Set this value to false to disable this feature. -RULE_BOOL ( World, EnableTutorialButton, true) -RULE_BOOL ( World, EnableReturnHomeButton, true) -RULE_INT ( World, MaxLevelForTutorial, 10) -RULE_INT ( World, TutorialZoneID, 189) -RULE_INT ( World, GuildBankZoneID, 345) -RULE_INT ( World, MinOfflineTimeToReturnHome, 21600) // 21600 seconds is 6 Hours -RULE_INT ( World, MaxClientsPerIP, -1 ) // Maximum number of clients allowed to connect per IP address if account status is < AddMaxClientsStatus. Default value: -1 (feature disabled) -RULE_INT ( World, ExemptMaxClientsStatus, -1 ) // Exempt accounts from the MaxClientsPerIP and AddMaxClientsStatus rules, if their status is >= this value. Default value: -1 (feature disabled) -RULE_INT ( World, AddMaxClientsPerIP, -1 ) // Maximum number of clients allowed to connect per IP address if account status is < ExemptMaxClientsStatus. Default value: -1 (feature disabled) -RULE_INT ( World, AddMaxClientsStatus, -1 ) // Accounts with status >= this rule will be allowed to use the amount of accounts defined in the AddMaxClientsPerIP. Default value: -1 (feature disabled) -RULE_BOOL ( World, MaxClientsSetByStatus, false) // If True, IP Limiting will be set to the status on the account as long as the status is > MaxClientsPerIP -RULE_BOOL ( World, ClearTempMerchantlist, true) // Clears temp merchant items when world boots. -RULE_BOOL ( World, DeleteStaleCorpeBackups, true) // Deletes stale corpse backups older than 2 weeks. -RULE_INT ( World, AccountSessionLimit, -1 ) //Max number of characters allowed on at once from a single account (-1 is disabled) -RULE_INT ( World, ExemptAccountLimitStatus, -1 ) //Min status required to be exempt from multi-session per account limiting (-1 is disabled) -RULE_BOOL ( World, GMAccountIPList, false) // Check ip list against GM Accounts, AntiHack GM Accounts. -RULE_INT ( World, MinGMAntiHackStatus, 1 ) //Minimum GM status to check against AntiHack list -RULE_INT ( World, SoFStartZoneID, -1 ) //Sets the Starting Zone for SoF Clients separate from Titanium Clients (-1 is disabled) -RULE_INT ( World, TitaniumStartZoneID, -1) //Sets the Starting Zone for Titanium Clients (-1 is disabled). Replaces the old method. -RULE_INT ( World, ExpansionSettings, 16383) // Sets the expansion settings for the server, This is sent on login to world and affects client expansion settings. Defaults to all expansions enabled up to TSS. -RULE_INT ( World, PVPSettings, 0) // Sets the PVP settings for the server, 1 = Rallos Zek RuleSet, 2 = Tallon/Vallon Zek Ruleset, 4 = Sullon Zek Ruleset, 6 = Discord Ruleset, anything above 6 is the Discord Ruleset without the no-drop restrictions removed. TODO: Edit IsAttackAllowed in Zone to accomodate for these rules. +RULE_CATEGORY(World) +RULE_INT(World, ZoneAutobootTimeoutMS, 60000) +RULE_INT(World, ClientKeepaliveTimeoutMS, 65000) +RULE_BOOL(World, UseBannedIPsTable, false) // Toggle whether or not to check incoming client connections against the Banned_IPs table. Set this value to false to disable this feature. +RULE_BOOL(World, EnableTutorialButton, true) +RULE_BOOL(World, EnableReturnHomeButton, true) +RULE_INT(World, MaxLevelForTutorial, 10) +RULE_INT(World, TutorialZoneID, 189) +RULE_INT(World, GuildBankZoneID, 345) +RULE_INT(World, MinOfflineTimeToReturnHome, 21600) // 21600 seconds is 6 Hours +RULE_INT(World, MaxClientsPerIP, -1) // Maximum number of clients allowed to connect per IP address if account status is < AddMaxClientsStatus. Default value: -1 (feature disabled) +RULE_INT(World, ExemptMaxClientsStatus, -1) // Exempt accounts from the MaxClientsPerIP and AddMaxClientsStatus rules, if their status is >= this value. Default value: -1 (feature disabled) +RULE_INT(World, AddMaxClientsPerIP, -1) // Maximum number of clients allowed to connect per IP address if account status is < ExemptMaxClientsStatus. Default value: -1 (feature disabled) +RULE_INT(World, AddMaxClientsStatus, -1) // Accounts with status >= this rule will be allowed to use the amount of accounts defined in the AddMaxClientsPerIP. Default value: -1 (feature disabled) +RULE_BOOL(World, MaxClientsSetByStatus, false) // If True, IP Limiting will be set to the status on the account as long as the status is > MaxClientsPerIP +RULE_BOOL(World, ClearTempMerchantlist, true) // Clears temp merchant items when world boots. +RULE_BOOL(World, DeleteStaleCorpeBackups, true) // Deletes stale corpse backups older than 2 weeks. +RULE_INT(World, AccountSessionLimit, -1) //Max number of characters allowed on at once from a single account (-1 is disabled) +RULE_INT(World, ExemptAccountLimitStatus, -1) //Min status required to be exempt from multi-session per account limiting (-1 is disabled) +RULE_BOOL(World, GMAccountIPList, false) // Check ip list against GM Accounts, AntiHack GM Accounts. +RULE_INT(World, MinGMAntiHackStatus, 1) //Minimum GM status to check against AntiHack list +RULE_INT(World, SoFStartZoneID, -1) //Sets the Starting Zone for SoF Clients separate from Titanium Clients (-1 is disabled) +RULE_INT(World, TitaniumStartZoneID, -1) //Sets the Starting Zone for Titanium Clients (-1 is disabled). Replaces the old method. +RULE_INT(World, ExpansionSettings, 16383) // Sets the expansion settings for the server, This is sent on login to world and affects client expansion settings. Defaults to all expansions enabled up to TSS. +RULE_INT(World, PVPSettings, 0) // Sets the PVP settings for the server, 1 = Rallos Zek RuleSet, 2 = Tallon/Vallon Zek Ruleset, 4 = Sullon Zek Ruleset, 6 = Discord Ruleset, anything above 6 is the Discord Ruleset without the no-drop restrictions removed. TODO: Edit IsAttackAllowed in Zone to accomodate for these rules. RULE_BOOL (World, IsGMPetitionWindowEnabled, false) RULE_INT (World, FVNoDropFlag, 0) // Sets the Firiona Vie settings on the client. If set to 2, the flag will be set for GMs only, allowing trading of no-drop items. RULE_BOOL (World, IPLimitDisconnectAll, false) RULE_INT (World, TellQueueSize, 20) RULE_CATEGORY_END() -RULE_CATEGORY( Zone ) -RULE_INT ( Zone, NPCPositonUpdateTicCount, 32 ) //ms between intervals of sending a position update to the entire zone. -RULE_INT ( Zone, ClientLinkdeadMS, 180000) //the time a client remains link dead on the server after a sudden disconnection -RULE_INT ( Zone, GraveyardTimeMS, 1200000) //ms time until a player corpse is moved to a zone's graveyard, if one is specified for the zone -RULE_BOOL ( Zone, EnableShadowrest, 1 ) // enables or disables the shadowrest zone feature for player corpses. Default is turned on. -RULE_BOOL ( Zone, UsePlayerCorpseBackups, true) // Keeps backups of player corpses. -RULE_INT ( Zone, MQWarpExemptStatus, -1 ) // Required status level to exempt the MQWarpDetector. Set to -1 to disable this feature. -RULE_INT ( Zone, MQZoneExemptStatus, -1 ) // Required status level to exempt the MQZoneDetector. Set to -1 to disable this feature. -RULE_INT ( Zone, MQGateExemptStatus, -1 ) // Required status level to exempt the MQGateDetector. Set to -1 to disable this feature. -RULE_INT ( Zone, MQGhostExemptStatus, -1 ) // Required status level to exempt the MGhostDetector. Set to -1 to disable this feature. -RULE_BOOL ( Zone, EnableMQWarpDetector, true ) // Enable the MQWarp Detector. Set to False to disable this feature. -RULE_BOOL ( Zone, EnableMQZoneDetector, true ) // Enable the MQZone Detector. Set to False to disable this feature. -RULE_BOOL ( Zone, EnableMQGateDetector, true ) // Enable the MQGate Detector. Set to False to disable this feature. -RULE_BOOL ( Zone, EnableMQGhostDetector, true ) // Enable the MQGhost Detector. Set to False to disable this feature. -RULE_REAL ( Zone, MQWarpDetectionDistanceFactor, 9.0) //clients move at 4.4 about if in a straight line but with movement and to acct for lag we raise it a bit -RULE_BOOL ( Zone, MarkMQWarpLT, false ) -RULE_INT ( Zone, AutoShutdownDelay, 5000 ) //How long a dynamic zone stays loaded while empty -RULE_INT ( Zone, PEQZoneReuseTime, 900 ) //How long, in seconds, until you can reuse the #peqzone command. -RULE_INT ( Zone, PEQZoneDebuff1, 4454 ) //First debuff casted by #peqzone Default is Cursed Keeper's Blight. -RULE_INT ( Zone, PEQZoneDebuff2, 2209 ) //Second debuff casted by #peqzone Default is Tendrils of Apathy. -RULE_BOOL ( Zone, UsePEQZoneDebuffs, true ) //Will determine if #peqzone will debuff players or not when used. -RULE_REAL ( Zone, HotZoneBonus, 0.75 ) -RULE_INT ( Zone, ReservedInstances, 30 ) //Will reserve this many instance ids for globals... probably not a good idea to change this while a server is running. -RULE_INT ( Zone, EbonCrystalItemID, 40902) -RULE_INT ( Zone, RadiantCrystalItemID, 40903) -RULE_BOOL ( Zone, LevelBasedEXPMods, false) // Allows you to use the level_exp_mods table in consideration to your players EXP hits -RULE_INT ( Zone, WeatherTimer, 600) // Weather timer when no duration is available -RULE_BOOL ( Zone, EnableLoggedOffReplenishments, true) -RULE_INT ( Zone, MinOfflineTimeToReplenishments, 21600) // 21600 seconds is 6 Hours +RULE_CATEGORY(Zone) +RULE_INT(Zone, NPCPositonUpdateTicCount, 32) //ms between intervals of sending a position update to the entire zone. +RULE_INT(Zone, ClientLinkdeadMS, 180000) //the time a client remains link dead on the server after a sudden disconnection +RULE_INT(Zone, GraveyardTimeMS, 1200000) //ms time until a player corpse is moved to a zone's graveyard, if one is specified for the zone +RULE_BOOL(Zone, EnableShadowrest, 1) // enables or disables the shadowrest zone feature for player corpses. Default is turned on. +RULE_BOOL(Zone, UsePlayerCorpseBackups, true) // Keeps backups of player corpses. +RULE_INT(Zone, MQWarpExemptStatus, -1) // Required status level to exempt the MQWarpDetector. Set to -1 to disable this feature. +RULE_INT(Zone, MQZoneExemptStatus, -1) // Required status level to exempt the MQZoneDetector. Set to -1 to disable this feature. +RULE_INT(Zone, MQGateExemptStatus, -1) // Required status level to exempt the MQGateDetector. Set to -1 to disable this feature. +RULE_INT(Zone, MQGhostExemptStatus, -1) // Required status level to exempt the MGhostDetector. Set to -1 to disable this feature. +RULE_BOOL(Zone, EnableMQWarpDetector, true) // Enable the MQWarp Detector. Set to False to disable this feature. +RULE_BOOL(Zone, EnableMQZoneDetector, true) // Enable the MQZone Detector. Set to False to disable this feature. +RULE_BOOL(Zone, EnableMQGateDetector, true) // Enable the MQGate Detector. Set to False to disable this feature. +RULE_BOOL(Zone, EnableMQGhostDetector, true) // Enable the MQGhost Detector. Set to False to disable this feature. +RULE_REAL(Zone, MQWarpDetectionDistanceFactor, 9.0) //clients move at 4.4 about if in a straight line but with movement and to acct for lag we raise it a bit +RULE_BOOL(Zone, MarkMQWarpLT, false) +RULE_INT(Zone, AutoShutdownDelay, 5000) //How long a dynamic zone stays loaded while empty +RULE_INT(Zone, PEQZoneReuseTime, 900) //How long, in seconds, until you can reuse the #peqzone command. +RULE_INT(Zone, PEQZoneDebuff1, 4454) //First debuff casted by #peqzone Default is Cursed Keeper's Blight. +RULE_INT(Zone, PEQZoneDebuff2, 2209) //Second debuff casted by #peqzone Default is Tendrils of Apathy. +RULE_BOOL(Zone, UsePEQZoneDebuffs, true) //Will determine if #peqzone will debuff players or not when used. +RULE_REAL(Zone, HotZoneBonus, 0.75) +RULE_INT(Zone, ReservedInstances, 30) //Will reserve this many instance ids for globals... probably not a good idea to change this while a server is running. +RULE_INT(Zone, EbonCrystalItemID, 40902) +RULE_INT(Zone, RadiantCrystalItemID, 40903) +RULE_BOOL(Zone, LevelBasedEXPMods, false) // Allows you to use the level_exp_mods table in consideration to your players EXP hits +RULE_INT(Zone, WeatherTimer, 600) // Weather timer when no duration is available +RULE_BOOL(Zone, EnableLoggedOffReplenishments, true) +RULE_INT(Zone, MinOfflineTimeToReplenishments, 21600) // 21600 seconds is 6 Hours RULE_CATEGORY_END() -RULE_CATEGORY( Map ) +RULE_CATEGORY(Map) //enable these to help prevent mob hopping when they are pathing -RULE_BOOL ( Map, FixPathingZWhenLoading, true ) //increases zone boot times a bit to reduce hopping. -RULE_BOOL ( Map, FixPathingZAtWaypoints, false ) //alternative to `WhenLoading`, accomplishes the same thing but does it at each waypoint instead of once at boot time. -RULE_BOOL ( Map, FixPathingZWhenMoving, false ) //very CPU intensive, but helps hopping with widely spaced waypoints. -RULE_BOOL ( Map, FixPathingZOnSendTo, false ) //try to repair Z coords in the SendTo routine as well. -RULE_REAL ( Map, FixPathingZMaxDeltaMoving, 20 ) //at runtime while pathing: max change in Z to allow the BestZ code to apply. -RULE_REAL ( Map, FixPathingZMaxDeltaWaypoint, 20 ) //at runtime at each waypoint: max change in Z to allow the BestZ code to apply. -RULE_REAL ( Map, FixPathingZMaxDeltaSendTo, 20 ) //at runtime in SendTo: max change in Z to allow the BestZ code to apply. -RULE_REAL ( Map, FixPathingZMaxDeltaLoading, 45 ) //while loading each waypoint: max change in Z to allow the BestZ code to apply. -RULE_INT ( Map, FindBestZHeightAdjust, 1) // Adds this to the current Z before seeking the best Z position +RULE_BOOL(Map, FixPathingZWhenLoading, true) //increases zone boot times a bit to reduce hopping. +RULE_BOOL(Map, FixPathingZAtWaypoints, false) //alternative to `WhenLoading`, accomplishes the same thing but does it at each waypoint instead of once at boot time. +RULE_BOOL(Map, FixPathingZWhenMoving, false) //very CPU intensive, but helps hopping with widely spaced waypoints. +RULE_BOOL(Map, FixPathingZOnSendTo, false) //try to repair Z coords in the SendTo routine as well. +RULE_REAL(Map, FixPathingZMaxDeltaMoving, 20) //at runtime while pathing: max change in Z to allow the BestZ code to apply. +RULE_REAL(Map, FixPathingZMaxDeltaWaypoint, 20) //at runtime at each waypoint: max change in Z to allow the BestZ code to apply. +RULE_REAL(Map, FixPathingZMaxDeltaSendTo, 20) //at runtime in SendTo: max change in Z to allow the BestZ code to apply. +RULE_REAL(Map, FixPathingZMaxDeltaLoading, 45) //while loading each waypoint: max change in Z to allow the BestZ code to apply. +RULE_INT(Map, FindBestZHeightAdjust, 1) // Adds this to the current Z before seeking the best Z position RULE_CATEGORY_END() -RULE_CATEGORY( Pathing ) +RULE_CATEGORY(Pathing) // Some of these rules may benefit by being made into columns in the zone table, // for instance, in dungeons, the min LOS distances could be substantially lowered. -RULE_BOOL ( Pathing, Aggro, true ) // Enable pathing for aggroed mobs. -RULE_BOOL ( Pathing, AggroReturnToGrid, true ) // Enable pathing for aggroed roaming mobs returning to their previous waypoint. -RULE_BOOL ( Pathing, Guard, true ) // Enable pathing for mobs moving to their guard point. -RULE_BOOL ( Pathing, Find, true ) // Enable pathing for FindPerson requests from the client. -RULE_BOOL ( Pathing, Fear, true ) // Enable pathing for fear -RULE_REAL ( Pathing, ZDiffThreshold, 10) // If a mob las LOS to it's target, it will run to it if the Z difference is < this. -RULE_INT ( Pathing, LOSCheckFrequency, 1000) // A mob will check for LOS to it's target this often (milliseconds). -RULE_INT ( Pathing, RouteUpdateFrequencyShort, 1000) // How often a new route will be calculated if the target has moved. -RULE_INT ( Pathing, RouteUpdateFrequencyLong, 5000) // How often a new route will be calculated if the target has moved. +RULE_BOOL(Pathing, Aggro, true) // Enable pathing for aggroed mobs. +RULE_BOOL(Pathing, AggroReturnToGrid, true) // Enable pathing for aggroed roaming mobs returning to their previous waypoint. +RULE_BOOL(Pathing, Guard, true) // Enable pathing for mobs moving to their guard point. +RULE_BOOL(Pathing, Find, true) // Enable pathing for FindPerson requests from the client. +RULE_BOOL(Pathing, Fear, true) // Enable pathing for fear +RULE_REAL(Pathing, ZDiffThreshold, 10) // If a mob las LOS to it's target, it will run to it if the Z difference is < this. +RULE_INT(Pathing, LOSCheckFrequency, 1000) // A mob will check for LOS to it's target this often (milliseconds). +RULE_INT(Pathing, RouteUpdateFrequencyShort, 1000) // How often a new route will be calculated if the target has moved. +RULE_INT(Pathing, RouteUpdateFrequencyLong, 5000) // How often a new route will be calculated if the target has moved. // When a path has a path node route and it's target changes position, if it has RouteUpdateFrequencyNodeCount or less nodes to go on it's // current path, it will recalculate it's path based on the RouteUpdateFrequencyShort timer, otherwise it will use the // RouteUpdateFrequencyLong timer. -RULE_INT ( Pathing, RouteUpdateFrequencyNodeCount, 5) -RULE_REAL ( Pathing, MinDistanceForLOSCheckShort, 40000) // (NoRoot). While following a path, only check for LOS to target within this distance. -RULE_REAL ( Pathing, MinDistanceForLOSCheckLong, 1000000) // (NoRoot). Min distance when initially attempting to acquire the target. -RULE_INT ( Pathing, MinNodesLeftForLOSCheck, 4) // Only check for LOS when we are down to this many path nodes left to run. +RULE_INT(Pathing, RouteUpdateFrequencyNodeCount, 5) +RULE_REAL(Pathing, MinDistanceForLOSCheckShort, 40000) // (NoRoot). While following a path, only check for LOS to target within this distance. +RULE_REAL(Pathing, MinDistanceForLOSCheckLong, 1000000) // (NoRoot). Min distance when initially attempting to acquire the target. +RULE_INT(Pathing, MinNodesLeftForLOSCheck, 4) // Only check for LOS when we are down to this many path nodes left to run. // This next rule was put in for situations where the mob and it's target may be on different sides of a 'hazard', e.g. a pit // If the mob has LOS to it's target, even though there is a hazard in it's way, it may break off from the node path and run at // the target, only to later detect the hazard and re-acquire a node path. Depending upon the placement of the path nodes, this // can lead to the mob looping. The rule is intended to allow the mob to at least get closer to it's target each time before // checking LOS and trying to head straight for it. -RULE_INT ( Pathing, MinNodesTraversedForLOSCheck, 3) // Only check for LOS after we have traversed this many path nodes. -RULE_INT ( Pathing, CullNodesFromStart, 1) // Checks LOS from Start point to second node for this many nodes and removes first node if there is LOS -RULE_INT ( Pathing, CullNodesFromEnd, 1) // Checks LOS from End point to second to last node for this many nodes and removes last node if there is LOS -RULE_REAL ( Pathing, CandidateNodeRangeXY, 400) // When searching for path start/end nodes, only nodes within this range will be considered. -RULE_REAL ( Pathing, CandidateNodeRangeZ, 10) // When searching for path start/end nodes, only nodes within this range will be considered. +RULE_INT(Pathing, MinNodesTraversedForLOSCheck, 3) // Only check for LOS after we have traversed this many path nodes. +RULE_INT(Pathing, CullNodesFromStart, 1) // Checks LOS from Start point to second node for this many nodes and removes first node if there is LOS +RULE_INT(Pathing, CullNodesFromEnd, 1) // Checks LOS from End point to second to last node for this many nodes and removes last node if there is LOS +RULE_REAL(Pathing, CandidateNodeRangeXY, 400) // When searching for path start/end nodes, only nodes within this range will be considered. +RULE_REAL(Pathing, CandidateNodeRangeZ, 10) // When searching for path start/end nodes, only nodes within this range will be considered. RULE_CATEGORY_END() -RULE_CATEGORY( Watermap ) +RULE_CATEGORY(Watermap) // enable these to use the water detection code. Requires Water Maps generated by awater utility -RULE_BOOL ( Watermap, CheckWaypointsInWaterWhenLoading, false ) // Does not apply BestZ as waypoints are loaded if they are in water -RULE_BOOL ( Watermap, CheckForWaterAtWaypoints, false) // Check if a mob has moved into/out of water when at waypoints and sets flymode -RULE_BOOL ( Watermap, CheckForWaterWhenMoving, false) // Checks if a mob has moved into/out of water each time it's loc is recalculated -RULE_BOOL ( Watermap, CheckForWaterOnSendTo, false) // Checks if a mob has moved into/out of water on SendTo -RULE_BOOL ( Watermap, CheckForWaterWhenFishing, false) // Only lets a player fish near water (if a water map exists for the zone) -RULE_REAL ( Watermap, FishingRodLength, 30) // How far in front of player water must be for fishing to work -RULE_REAL ( Watermap, FishingLineLength, 100) // If water is more than this far below the player, it is considered too far to fish -RULE_REAL ( Watermap, FishingLineStepSize, 1) // Basic step size for fishing calc, too small and it will eat cpu, too large and it will miss potential water +RULE_BOOL(Watermap, CheckWaypointsInWaterWhenLoading, false) // Does not apply BestZ as waypoints are loaded if they are in water +RULE_BOOL(Watermap, CheckForWaterAtWaypoints, false) // Check if a mob has moved into/out of water when at waypoints and sets flymode +RULE_BOOL(Watermap, CheckForWaterWhenMoving, false) // Checks if a mob has moved into/out of water each time it's loc is recalculated +RULE_BOOL(Watermap, CheckForWaterOnSendTo, false) // Checks if a mob has moved into/out of water on SendTo +RULE_BOOL(Watermap, CheckForWaterWhenFishing, false) // Only lets a player fish near water (if a water map exists for the zone) +RULE_REAL(Watermap, FishingRodLength, 30) // How far in front of player water must be for fishing to work +RULE_REAL(Watermap, FishingLineLength, 100) // If water is more than this far below the player, it is considered too far to fish +RULE_REAL(Watermap, FishingLineStepSize, 1) // Basic step size for fishing calc, too small and it will eat cpu, too large and it will miss potential water RULE_CATEGORY_END() -RULE_CATEGORY( Spells ) -RULE_INT ( Spells, AutoResistDiff, 15) -RULE_REAL ( Spells, ResistChance, 2.0) //chance to resist given no resists and same level -RULE_REAL ( Spells, ResistMod, 0.40) //multiplier, chance to resist = this * ResistAmount -RULE_REAL ( Spells, PartialHitChance, 0.7) //The chance when a spell is resisted that it will partial hit. -RULE_REAL ( Spells, PartialHitChanceFear, 0.25) //The chance when a fear spell is resisted that it will partial hit. -RULE_INT ( Spells, BaseCritChance, 0) //base % chance that everyone has to crit a spell -RULE_INT ( Spells, BaseCritRatio, 100) //base % bonus to damage on a successful spell crit. 100 = 2x damage -RULE_INT ( Spells, WizCritLevel, 12) //level wizards first get spell crits -RULE_INT ( Spells, WizCritChance, 7) //wiz's crit chance, on top of BaseCritChance -RULE_INT ( Spells, WizCritRatio, 0) //wiz's crit bonus, on top of BaseCritRatio (should be 0 for Live-like) -RULE_INT ( Spells, ResistPerLevelDiff, 85) //8.5 resist per level difference. -RULE_INT ( Spells, TranslocateTimeLimit, 0) // If not zero, time in seconds to accept a Translocate. -RULE_INT ( Spells, SacrificeMinLevel, 46) //first level Sacrifice will work on -RULE_INT ( Spells, SacrificeMaxLevel, 69) //last level Sacrifice will work on -RULE_INT ( Spells, SacrificeItemID, 9963) //Item ID of the item Sacrifice will return (defaults to an EE) -RULE_BOOL ( Spells, EnableSpellGlobals, false) // If Enabled, spells check the spell_globals table and compare character data from the quest globals before allowing that spell to scribe with scribespells -RULE_INT ( Spells, MaxBuffSlotsNPC, 25) -RULE_INT ( Spells, MaxSongSlotsNPC, 10) -RULE_INT ( Spells, MaxDiscSlotsNPC, 1) -RULE_INT ( Spells, MaxTotalSlotsNPC, 36) -RULE_INT ( Spells, MaxTotalSlotsPET, 25) // do not set this higher than 25 until the player profile is removed from the blob +RULE_CATEGORY(Spells) +RULE_INT(Spells, AutoResistDiff, 15) +RULE_REAL(Spells, ResistChance, 2.0) //chance to resist given no resists and same level +RULE_REAL(Spells, ResistMod, 0.40) //multiplier, chance to resist = this * ResistAmount +RULE_REAL(Spells, PartialHitChance, 0.7) //The chance when a spell is resisted that it will partial hit. +RULE_REAL(Spells, PartialHitChanceFear, 0.25) //The chance when a fear spell is resisted that it will partial hit. +RULE_INT(Spells, BaseCritChance, 0) //base % chance that everyone has to crit a spell +RULE_INT(Spells, BaseCritRatio, 100) //base % bonus to damage on a successful spell crit. 100 = 2x damage +RULE_INT(Spells, WizCritLevel, 12) //level wizards first get spell crits +RULE_INT(Spells, WizCritChance, 7) //wiz's crit chance, on top of BaseCritChance +RULE_INT(Spells, WizCritRatio, 0) //wiz's crit bonus, on top of BaseCritRatio (should be 0 for Live-like) +RULE_INT(Spells, ResistPerLevelDiff, 85) //8.5 resist per level difference. +RULE_INT(Spells, TranslocateTimeLimit, 0) // If not zero, time in seconds to accept a Translocate. +RULE_INT(Spells, SacrificeMinLevel, 46) //first level Sacrifice will work on +RULE_INT(Spells, SacrificeMaxLevel, 69) //last level Sacrifice will work on +RULE_INT(Spells, SacrificeItemID, 9963) //Item ID of the item Sacrifice will return (defaults to an EE) +RULE_BOOL(Spells, EnableSpellGlobals, false) // If Enabled, spells check the spell_globals table and compare character data from the quest globals before allowing that spell to scribe with scribespells +RULE_INT(Spells, MaxBuffSlotsNPC, 25) +RULE_INT(Spells, MaxSongSlotsNPC, 10) +RULE_INT(Spells, MaxDiscSlotsNPC, 1) +RULE_INT(Spells, MaxTotalSlotsNPC, 36) +RULE_INT(Spells, MaxTotalSlotsPET, 25) // do not set this higher than 25 until the player profile is removed from the blob RULE_BOOL (Spells, EnableBlockedBuffs, true) -RULE_INT ( Spells, ReflectType, 1) //0 = disabled, 1 = single target player spells only, 2 = all player spells, 3 = all single target spells, 4 = all spells -RULE_INT ( Spells, VirusSpreadDistance, 30) // The distance a viral spell will jump to its next victim -RULE_BOOL( Spells, LiveLikeFocusEffects, true) // Determines whether specific healing, dmg and mana reduction focuses are randomized -RULE_INT ( Spells, BaseImmunityLevel, 55) // The level that targets start to be immune to stun, fear and mez spells with a max level of 0. -RULE_BOOL ( Spells, NPCIgnoreBaseImmunity, true) // Whether or not NPCs get to ignore the BaseImmunityLevel for their spells. -RULE_REAL ( Spells, AvgSpellProcsPerMinute, 6.0) //Adjust rate for sympathetic spell procs -RULE_INT ( Spells, ResistFalloff, 67) //Max that level that will adjust our resist chance based on level modifiers -RULE_INT ( Spells, CharismaEffectiveness, 10) // Deterimes how much resist modification charisma applies to charm/pacify checks. Default 10 CHA = -1 resist mod. -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_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. -RULE_INT ( Spells, DivineInterventionHeal, 8000) //Divine intervention heal amount. -RULE_INT ( Spells, AdditiveBonusWornType, 0) //Calc worn bonuses to add together (instead of taking highest) if set to THIS worn type. (2=Will covert live items automatically) -RULE_BOOL ( Spells, UseCHAScribeHack, false) //ScribeSpells and TrainDiscs quest functions will ignore entries where field 12 is CHA. What's the best way to do this? -RULE_BOOL ( Spells, BuffLevelRestrictions, true) //Buffs will not land on low level toons like live -RULE_INT ( Spells, RootBreakCheckChance, 70) //Determines chance for a root break check to occur each buff tick. -RULE_INT ( Spells, FearBreakCheckChance, 70) //Determines chance for a fear break check to occur each buff tick. -RULE_INT ( Spells, SuccorFailChance, 2) //Determines chance for a succor spell not to teleport an invidual player -RULE_INT ( Spells, FRProjectileItem_Titanium, 1113) // Item id for Titanium clients for Fire 'spell projectile'. -RULE_INT ( Spells, FRProjectileItem_SOF, 80684) // Item id for SOF clients for Fire 'spell projectile'. -RULE_INT ( Spells, FRProjectileItem_NPC, 80684) // Item id for NPC Fire 'spell projectile'. -RULE_BOOL ( Spells, UseLiveSpellProjectileGFX, false) // Use spell projectile graphics set in the spells_new table (player_1). Server must be using UF+ spell file. -RULE_BOOL ( Spells, FocusCombatProcs, false) //Allow all combat procs to receive focus effects. -RULE_BOOL ( Spells, PreNerfBardAEDoT, false) //Allow bard AOE dots to damage targets when moving. -RULE_INT ( Spells, AI_SpellCastFinishedFailRecast, 800) // AI spell recast time(MS) when an spell is cast but fails (ie stunned). -RULE_INT ( Spells, AI_EngagedNoSpellMinRecast, 500) // AI spell recast time(MS) check when no spell is cast while engaged. (min time in random) -RULE_INT ( Spells, AI_EngagedNoSpellMaxRecast, 1000) // AI spell recast time(MS) check when no spell is cast engaged.(max time in random) -RULE_INT ( Spells, AI_EngagedBeneficialSelfChance, 100) // Chance during first AI Cast check to do a beneficial spell on self. -RULE_INT ( Spells, AI_EngagedBeneficialOtherChance, 25) // Chance during second AI Cast check to do a beneficial spell on others. -RULE_INT ( Spells, AI_EngagedDetrimentalChance, 20) // Chance during third AI Cast check to do a determental spell on others. -RULE_INT ( Spells, AI_PursueNoSpellMinRecast, 500) // AI spell recast time(MS) check when no spell is cast while chasing target. (min time in random) -RULE_INT ( Spells, AI_PursueNoSpellMaxRecast, 2000) // AI spell recast time(MS) check when no spell is cast while chasing target. (max time in random) -RULE_INT ( Spells, AI_PursueDetrimentalChance, 90) // Chance while chasing target to cast a detrimental spell. -RULE_INT ( Spells, AI_IdleNoSpellMinRecast, 500) // AI spell recast time(MS) check when no spell is cast while idle. (min time in random) -RULE_INT ( Spells, AI_IdleNoSpellMaxRecast, 2000) // AI spell recast time(MS) check when no spell is cast while chasing target. (max time in random) -RULE_INT ( Spells, AI_IdleBeneficialChance, 100) // Chance while idle to do a beneficial spell on self or others. -RULE_BOOL ( Spells, SHDProcIDOffByOne, true) // pre June 2009 SHD spell procs were off by 1, they stopped doing this in June 2009 (so UF+ spell files need this false) -RULE_BOOL ( Spells, Jun182014HundredHandsRevamp, false) // this should be true for if you import a spell file newer than June 18, 2014 -RULE_BOOL ( Spells, SwarmPetTargetLock, false) // Use old method of swarm pets target locking till target dies then despawning. -RULE_BOOL ( Spells, NPC_UseFocusFromSpells, true) // Allow npcs to use most spell derived focus effects. -RULE_BOOL ( Spells, NPC_UseFocusFromItems, false) // Allow npcs to use most item derived focus effects. -RULE_BOOL ( Spells, UseAdditiveFocusFromWornSlot, false) // Allows an additive focus effect to be calculated from worn slot. -RULE_BOOL ( Spells, AlwaysSendTargetsBuffs, false) // ignore LAA level if true +RULE_INT(Spells, ReflectType, 1) //0 = disabled, 1 = single target player spells only, 2 = all player spells, 3 = all single target spells, 4 = all spells +RULE_INT(Spells, VirusSpreadDistance, 30) // The distance a viral spell will jump to its next victim +RULE_BOOL(Spells, LiveLikeFocusEffects, true) // Determines whether specific healing, dmg and mana reduction focuses are randomized +RULE_INT(Spells, BaseImmunityLevel, 55) // The level that targets start to be immune to stun, fear and mez spells with a max level of 0. +RULE_BOOL(Spells, NPCIgnoreBaseImmunity, true) // Whether or not NPCs get to ignore the BaseImmunityLevel for their spells. +RULE_REAL(Spells, AvgSpellProcsPerMinute, 6.0) //Adjust rate for sympathetic spell procs +RULE_INT(Spells, ResistFalloff, 67) //Max that level that will adjust our resist chance based on level modifiers +RULE_INT(Spells, CharismaEffectiveness, 10) // Deterimes how much resist modification charisma applies to charm/pacify checks. Default 10 CHA = -1 resist mod. +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_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. +RULE_INT(Spells, DivineInterventionHeal, 8000) //Divine intervention heal amount. +RULE_INT(Spells, AdditiveBonusWornType, 0) //Calc worn bonuses to add together (instead of taking highest) if set to THIS worn type. (2=Will covert live items automatically) +RULE_BOOL(Spells, UseCHAScribeHack, false) //ScribeSpells and TrainDiscs quest functions will ignore entries where field 12 is CHA. What's the best way to do this? +RULE_BOOL(Spells, BuffLevelRestrictions, true) //Buffs will not land on low level toons like live +RULE_INT(Spells, RootBreakCheckChance, 70) //Determines chance for a root break check to occur each buff tick. +RULE_INT(Spells, FearBreakCheckChance, 70) //Determines chance for a fear break check to occur each buff tick. +RULE_INT(Spells, SuccorFailChance, 2) //Determines chance for a succor spell not to teleport an invidual player +RULE_INT(Spells, FRProjectileItem_Titanium, 1113) // Item id for Titanium clients for Fire 'spell projectile'. +RULE_INT(Spells, FRProjectileItem_SOF, 80684) // Item id for SOF clients for Fire 'spell projectile'. +RULE_INT(Spells, FRProjectileItem_NPC, 80684) // Item id for NPC Fire 'spell projectile'. +RULE_BOOL(Spells, UseLiveSpellProjectileGFX, false) // Use spell projectile graphics set in the spells_new table (player_1). Server must be using UF+ spell file. +RULE_BOOL(Spells, FocusCombatProcs, false) //Allow all combat procs to receive focus effects. +RULE_BOOL(Spells, PreNerfBardAEDoT, false) //Allow bard AOE dots to damage targets when moving. +RULE_INT(Spells, AI_SpellCastFinishedFailRecast, 800) // AI spell recast time(MS) when an spell is cast but fails (ie stunned). +RULE_INT(Spells, AI_EngagedNoSpellMinRecast, 500) // AI spell recast time(MS) check when no spell is cast while engaged. (min time in random) +RULE_INT(Spells, AI_EngagedNoSpellMaxRecast, 1000) // AI spell recast time(MS) check when no spell is cast engaged.(max time in random) +RULE_INT(Spells, AI_EngagedBeneficialSelfChance, 100) // Chance during first AI Cast check to do a beneficial spell on self. +RULE_INT(Spells, AI_EngagedBeneficialOtherChance, 25) // Chance during second AI Cast check to do a beneficial spell on others. +RULE_INT(Spells, AI_EngagedDetrimentalChance, 20) // Chance during third AI Cast check to do a determental spell on others. +RULE_INT(Spells, AI_PursueNoSpellMinRecast, 500) // AI spell recast time(MS) check when no spell is cast while chasing target. (min time in random) +RULE_INT(Spells, AI_PursueNoSpellMaxRecast, 2000) // AI spell recast time(MS) check when no spell is cast while chasing target. (max time in random) +RULE_INT(Spells, AI_PursueDetrimentalChance, 90) // Chance while chasing target to cast a detrimental spell. +RULE_INT(Spells, AI_IdleNoSpellMinRecast, 500) // AI spell recast time(MS) check when no spell is cast while idle. (min time in random) +RULE_INT(Spells, AI_IdleNoSpellMaxRecast, 2000) // AI spell recast time(MS) check when no spell is cast while chasing target. (max time in random) +RULE_INT(Spells, AI_IdleBeneficialChance, 100) // Chance while idle to do a beneficial spell on self or others. +RULE_BOOL(Spells, SHDProcIDOffByOne, true) // pre June 2009 SHD spell procs were off by 1, they stopped doing this in June 2009 (so UF+ spell files need this false) +RULE_BOOL(Spells, Jun182014HundredHandsRevamp, false) // this should be true for if you import a spell file newer than June 18, 2014 +RULE_BOOL(Spells, SwarmPetTargetLock, false) // Use old method of swarm pets target locking till target dies then despawning. +RULE_BOOL(Spells, NPC_UseFocusFromSpells, true) // Allow npcs to use most spell derived focus effects. +RULE_BOOL(Spells, NPC_UseFocusFromItems, false) // Allow npcs to use most item derived focus effects. +RULE_BOOL(Spells, UseAdditiveFocusFromWornSlot, false) // Allows an additive focus effect to be calculated from worn slot. +RULE_BOOL(Spells, AlwaysSendTargetsBuffs, false) // ignore LAA level if true RULE_CATEGORY_END() -RULE_CATEGORY( Combat ) -RULE_INT ( Combat, MeleeBaseCritChance, 0 ) //The base crit chance for non warriors, NOTE: This will apply to NPCs as well -RULE_INT ( Combat, WarBerBaseCritChance, 3 ) //The base crit chance for warriors and berserkers, only applies to clients -RULE_INT ( Combat, BerserkBaseCritChance, 6 ) //The bonus base crit chance you get when you're berserk -RULE_INT ( Combat, NPCBashKickLevel, 6 ) //The level that npcs can KICK/BASH -RULE_INT ( Combat, NPCBashKickStunChance, 15 ) //Percent chance that a bash/kick will stun -RULE_INT ( Combat, RogueCritThrowingChance, 25) //Rogue throwing crit bonus -RULE_INT ( Combat, RogueDeadlyStrikeChance, 80) //Rogue chance throwing from behind crit becomes a deadly strike -RULE_INT ( Combat, RogueDeadlyStrikeMod, 2) //Deadly strike modifier to crit damage -RULE_INT ( Combat, ClientBaseCritChance, 0 ) //The base crit chance for all clients, this will stack with warrior's/zerker's crit chance. -RULE_BOOL ( Combat, UseIntervalAC, true) -RULE_INT ( Combat, PetAttackMagicLevel, 30) -RULE_BOOL ( Combat, EnableFearPathing, true) -RULE_REAL ( Combat, FleeMultiplier, 2.0) // Determines how quickly a NPC will slow down while fleeing. Decrease multiplier to slow NPC down quicker. -RULE_INT ( Combat, FleeHPRatio, 25) //HP % when a NPC begins to flee. -RULE_BOOL ( Combat, FleeIfNotAlone, false) // If false, mobs won't flee if other mobs are in combat with it. -RULE_BOOL ( Combat, AdjustProcPerMinute, true) -RULE_REAL ( Combat, AvgProcsPerMinute, 2.0) -RULE_REAL ( Combat, ProcPerMinDexContrib, 0.075) -RULE_REAL ( Combat, BaseProcChance, 0.035) -RULE_REAL ( Combat, ProcDexDivideBy, 11000) -RULE_BOOL ( Combat, AdjustSpecialProcPerMinute, true) //Set PPM for special abilities like HeadShot (Live does this as of 4-14) -RULE_REAL ( Combat, AvgSpecialProcsPerMinute, 2.0) //Unclear what best value is atm. -RULE_REAL ( Combat, BaseHitChance, 69.0) -RULE_REAL ( Combat, NPCBonusHitChance, 26.0) -RULE_REAL ( Combat, HitFalloffMinor, 5.0) //hit will fall off up to 5% over the initial level range -RULE_REAL ( Combat, HitFalloffModerate, 7.0) //hit will fall off up to 7% over the three levels after the initial level range -RULE_REAL ( Combat, HitFalloffMajor, 50.0) //hit will fall off sharply if we're outside the minor and moderate range -RULE_REAL ( Combat, HitBonusPerLevel, 1.2) //You gain this % of hit for every level you are above your target -RULE_REAL ( Combat, WeaponSkillFalloff, 0.33) //For every weapon skill point that's not maxed you lose this % of hit -RULE_REAL ( Combat, ArcheryHitPenalty, 0.25) //Archery has a hit penalty to try to help balance it with the plethora of long term +hit modifiers for it -RULE_REAL ( Combat, AgiHitFactor, 0.01) -RULE_REAL ( Combat, MinChancetoHit, 5.0) //Minimum % chance to hit with regular melee/ranged -RULE_REAL ( Combat, MaxChancetoHit, 95.0) //Maximum % chance to hit with regular melee/ranged -RULE_INT ( Combat, MinRangedAttackDist, 25) //Minimum Distance to use Ranged Attacks -RULE_BOOL ( Combat, ArcheryBonusRequiresStationary, true) //does the 2x archery bonus chance require a stationary npc -RULE_REAL ( Combat, ArcheryBaseDamageBonus, 1) // % Modifier to Base Archery Damage (.5 = 50% base damage, 1 = 100%, 2 = 200%) -RULE_REAL ( Combat, ArcheryNPCMultiplier, 1.0) // this is multiplied by the regular dmg to get the archery dmg -RULE_BOOL ( Combat, AssistNoTargetSelf, true) //when assisting a target that does not have a target: true = target self, false = leave target as was before assist (false = live like) -RULE_INT ( Combat, MaxRampageTargets, 3) //max number of people hit with rampage -RULE_INT ( Combat, DefaultRampageTargets, 1) // default number of people to hit with rampage -RULE_BOOL ( Combat, RampageHitsTarget, false) // rampage will hit the target if it still has targets left -RULE_INT ( Combat, MaxFlurryHits, 2) //max number of extra hits from flurry -RULE_INT ( Combat, MonkDamageTableBonus, 5) //% bonus monks get to their damage table calcs -RULE_INT ( Combat, FlyingKickBonus, 25) //% Modifier that this skill gets to str and skill bonuses -RULE_INT ( Combat, DragonPunchBonus, 20) //% Modifier that this skill gets to str and skill bonuses -RULE_INT ( Combat, EagleStrikeBonus, 15) //% Modifier that this skill gets to str and skill bonuses -RULE_INT ( Combat, TigerClawBonus, 10) //% Modifier that this skill gets to str and skill bonuses -RULE_INT ( Combat, RoundKickBonus, 5) //% Modifier that this skill gets to str and skill bonuses -RULE_INT ( Combat, FrenzyBonus, 0) //% Modifier to damage -RULE_BOOL ( Combat, ProcTargetOnly, true) //true = procs will only affect our target, false = procs will affect all of our targets -RULE_REAL ( Combat, NPCACFactor, 2.25) -RULE_INT ( Combat, ClothACSoftcap, 75) -RULE_INT ( Combat, LeatherACSoftcap, 100) -RULE_INT ( Combat, MonkACSoftcap, 120) -RULE_INT ( Combat, ChainACSoftcap, 200) -RULE_INT ( Combat, PlateACSoftcap, 300) -RULE_REAL ( Combat, AAMitigationACFactor, 3.0) -RULE_REAL ( Combat, WarriorACSoftcapReturn, 0.45) -RULE_REAL ( Combat, KnightACSoftcapReturn, 0.33) -RULE_REAL ( Combat, LowPlateChainACSoftcapReturn, 0.23) -RULE_REAL ( Combat, LowChainLeatherACSoftcapReturn, 0.17) -RULE_REAL ( Combat, CasterACSoftcapReturn, 0.06) -RULE_REAL ( Combat, MiscACSoftcapReturn, 0.3) -RULE_BOOL ( Combat, OldACSoftcapRules, false) // use old softcaps -RULE_BOOL ( Combat, UseOldDamageIntervalRules, false) // use old damage formulas for everything -RULE_REAL ( Combat, WarACSoftcapReturn, 0.3448) // new AC returns -RULE_REAL ( Combat, ClrRngMnkBrdACSoftcapReturn, 0.3030) -RULE_REAL ( Combat, PalShdACSoftcapReturn, 0.3226) -RULE_REAL ( Combat, DruNecWizEncMagACSoftcapReturn, 0.2000) -RULE_REAL ( Combat, RogShmBstBerACSoftcapReturn, 0.2500) -RULE_REAL ( Combat, SoftcapFactor, 1.88) -RULE_REAL ( Combat, ACthac0Factor, 0.55) -RULE_REAL ( Combat, ACthac20Factor, 0.55) -RULE_INT ( Combat, HitCapPre20, 40) // live has it capped at 40 for whatever dumb reason... this is mainly for custom servers -RULE_INT ( Combat, HitCapPre10, 20) // live has it capped at 20, see above :p -RULE_INT ( Combat, MinHastedDelay, 400) // how fast we can get with haste. -RULE_REAL ( Combat, AvgDefProcsPerMinute, 2.0) -RULE_REAL ( Combat, DefProcPerMinAgiContrib, 0.075) //How much agility contributes to defensive proc rate -RULE_INT ( Combat, SpecialAttackACBonus, 15) //Percent amount of damage per AC gained for certain special attacks (damage = AC*SpecialAttackACBonus/100). -RULE_INT ( Combat, NPCFlurryChance, 20) // Chance for NPC to flurry. +RULE_CATEGORY(Combat) +RULE_INT(Combat, MeleeBaseCritChance, 0) //The base crit chance for non warriors, NOTE: This will apply to NPCs as well +RULE_INT(Combat, WarBerBaseCritChance, 3) //The base crit chance for warriors and berserkers, only applies to clients +RULE_INT(Combat, BerserkBaseCritChance, 6) //The bonus base crit chance you get when you're berserk +RULE_INT(Combat, NPCBashKickLevel, 6) //The level that npcs can KICK/BASH +RULE_INT(Combat, NPCBashKickStunChance, 15) //Percent chance that a bash/kick will stun +RULE_INT(Combat, RogueCritThrowingChance, 25) //Rogue throwing crit bonus +RULE_INT(Combat, RogueDeadlyStrikeChance, 80) //Rogue chance throwing from behind crit becomes a deadly strike +RULE_INT(Combat, RogueDeadlyStrikeMod, 2) //Deadly strike modifier to crit damage +RULE_INT(Combat, ClientBaseCritChance, 0) //The base crit chance for all clients, this will stack with warrior's/zerker's crit chance. +RULE_BOOL(Combat, UseIntervalAC, true) +RULE_INT(Combat, PetAttackMagicLevel, 30) +RULE_BOOL(Combat, EnableFearPathing, true) +RULE_REAL(Combat, FleeMultiplier, 2.0) // Determines how quickly a NPC will slow down while fleeing. Decrease multiplier to slow NPC down quicker. +RULE_INT(Combat, FleeHPRatio, 25) //HP % when a NPC begins to flee. +RULE_BOOL(Combat, FleeIfNotAlone, false) // If false, mobs won't flee if other mobs are in combat with it. +RULE_BOOL(Combat, AdjustProcPerMinute, true) +RULE_REAL(Combat, AvgProcsPerMinute, 2.0) +RULE_REAL(Combat, ProcPerMinDexContrib, 0.075) +RULE_REAL(Combat, BaseProcChance, 0.035) +RULE_REAL(Combat, ProcDexDivideBy, 11000) +RULE_BOOL(Combat, AdjustSpecialProcPerMinute, true) //Set PPM for special abilities like HeadShot (Live does this as of 4-14) +RULE_REAL(Combat, AvgSpecialProcsPerMinute, 2.0) //Unclear what best value is atm. +RULE_REAL(Combat, BaseHitChance, 69.0) +RULE_REAL(Combat, NPCBonusHitChance, 26.0) +RULE_REAL(Combat, HitFalloffMinor, 5.0) //hit will fall off up to 5% over the initial level range +RULE_REAL(Combat, HitFalloffModerate, 7.0) //hit will fall off up to 7% over the three levels after the initial level range +RULE_REAL(Combat, HitFalloffMajor, 50.0) //hit will fall off sharply if we're outside the minor and moderate range +RULE_REAL(Combat, HitBonusPerLevel, 1.2) //You gain this % of hit for every level you are above your target +RULE_REAL(Combat, WeaponSkillFalloff, 0.33) //For every weapon skill point that's not maxed you lose this % of hit +RULE_REAL(Combat, ArcheryHitPenalty, 0.25) //Archery has a hit penalty to try to help balance it with the plethora of long term +hit modifiers for it +RULE_REAL(Combat, AgiHitFactor, 0.01) +RULE_REAL(Combat, MinChancetoHit, 5.0) //Minimum % chance to hit with regular melee/ranged +RULE_REAL(Combat, MaxChancetoHit, 95.0) //Maximum % chance to hit with regular melee/ranged +RULE_INT(Combat, MinRangedAttackDist, 25) //Minimum Distance to use Ranged Attacks +RULE_BOOL(Combat, ArcheryBonusRequiresStationary, true) //does the 2x archery bonus chance require a stationary npc +RULE_REAL(Combat, ArcheryBaseDamageBonus, 1) // % Modifier to Base Archery Damage (.5 = 50% base damage, 1 = 100%, 2 = 200%) +RULE_REAL(Combat, ArcheryNPCMultiplier, 1.0) // this is multiplied by the regular dmg to get the archery dmg +RULE_BOOL(Combat, AssistNoTargetSelf, true) //when assisting a target that does not have a target: true = target self, false = leave target as was before assist (false = live like) +RULE_INT(Combat, MaxRampageTargets, 3) //max number of people hit with rampage +RULE_INT(Combat, DefaultRampageTargets, 1) // default number of people to hit with rampage +RULE_BOOL(Combat, RampageHitsTarget, false) // rampage will hit the target if it still has targets left +RULE_INT(Combat, MaxFlurryHits, 2) //max number of extra hits from flurry +RULE_INT(Combat, MonkDamageTableBonus, 5) //% bonus monks get to their damage table calcs +RULE_INT(Combat, FlyingKickBonus, 25) //% Modifier that this skill gets to str and skill bonuses +RULE_INT(Combat, DragonPunchBonus, 20) //% Modifier that this skill gets to str and skill bonuses +RULE_INT(Combat, EagleStrikeBonus, 15) //% Modifier that this skill gets to str and skill bonuses +RULE_INT(Combat, TigerClawBonus, 10) //% Modifier that this skill gets to str and skill bonuses +RULE_INT(Combat, RoundKickBonus, 5) //% Modifier that this skill gets to str and skill bonuses +RULE_INT(Combat, FrenzyBonus, 0) //% Modifier to damage +RULE_INT(Combat, BackstabBonus, 0) //% Modifier to damage +RULE_BOOL(Combat, ProcTargetOnly, true) //true = procs will only affect our target, false = procs will affect all of our targets +RULE_REAL(Combat, NPCACFactor, 2.25) +RULE_INT(Combat, ClothACSoftcap, 75) +RULE_INT(Combat, LeatherACSoftcap, 100) +RULE_INT(Combat, MonkACSoftcap, 120) +RULE_INT(Combat, ChainACSoftcap, 200) +RULE_INT(Combat, PlateACSoftcap, 300) +RULE_REAL(Combat, AAMitigationACFactor, 3.0) +RULE_REAL(Combat, WarriorACSoftcapReturn, 0.45) +RULE_REAL(Combat, KnightACSoftcapReturn, 0.33) +RULE_REAL(Combat, LowPlateChainACSoftcapReturn, 0.23) +RULE_REAL(Combat, LowChainLeatherACSoftcapReturn, 0.17) +RULE_REAL(Combat, CasterACSoftcapReturn, 0.06) +RULE_REAL(Combat, MiscACSoftcapReturn, 0.3) +RULE_BOOL(Combat, OldACSoftcapRules, false) // use old softcaps +RULE_BOOL(Combat, UseOldDamageIntervalRules, false) // use old damage formulas for everything +RULE_REAL(Combat, WarACSoftcapReturn, 0.3448) // new AC returns +RULE_REAL(Combat, ClrRngMnkBrdACSoftcapReturn, 0.3030) +RULE_REAL(Combat, PalShdACSoftcapReturn, 0.3226) +RULE_REAL(Combat, DruNecWizEncMagACSoftcapReturn, 0.2000) +RULE_REAL(Combat, RogShmBstBerACSoftcapReturn, 0.2500) +RULE_REAL(Combat, SoftcapFactor, 1.88) +RULE_REAL(Combat, ACthac0Factor, 0.55) +RULE_REAL(Combat, ACthac20Factor, 0.55) +RULE_INT(Combat, HitCapPre20, 40) // live has it capped at 40 for whatever dumb reason... this is mainly for custom servers +RULE_INT(Combat, HitCapPre10, 20) // live has it capped at 20, see above :p +RULE_INT(Combat, MinHastedDelay, 400) // how fast we can get with haste. +RULE_REAL(Combat, AvgDefProcsPerMinute, 2.0) +RULE_REAL(Combat, DefProcPerMinAgiContrib, 0.075) //How much agility contributes to defensive proc rate +RULE_INT(Combat, SpecialAttackACBonus, 15) //Percent amount of damage per AC gained for certain special attacks (damage = AC*SpecialAttackACBonus/100). +RULE_INT(Combat, NPCFlurryChance, 20) // Chance for NPC to flurry. RULE_BOOL (Combat,TauntOverLevel, 1) //Allows you to taunt NPC's over warriors level. RULE_REAL (Combat,TauntSkillFalloff, 0.33)//For every taunt skill point that's not maxed you lose this % chance to taunt. RULE_BOOL (Combat,EXPFromDmgShield, false) //Determine if damage from a damage shield counts for EXP gain. -RULE_INT ( Combat, MonkACBonusWeight, 15) -RULE_INT ( Combat, ClientStunLevel, 55) //This is the level where client kicks and bashes can stun the target -RULE_INT ( Combat, QuiverWRHasteDiv, 3) //Weight Reduction is divided by this to get haste contribution for quivers -RULE_BOOL ( Combat, UseArcheryBonusRoll, false) //Make the 51+ archery bonus require an actual roll -RULE_INT ( Combat, ArcheryBonusChance, 50) -RULE_INT ( Combat, BerserkerFrenzyStart, 35) -RULE_INT ( Combat, BerserkerFrenzyEnd, 45) -RULE_BOOL ( Combat, OneProcPerWeapon, true) //If enabled, One proc per weapon per round -RULE_BOOL ( Combat, ProjectileDmgOnImpact, true) //If enabled, projectiles (ie arrows) will hit on impact, instead of instantly. -RULE_BOOL ( Combat, MeleePush, true) // enable melee push -RULE_INT ( Combat, MeleePushChance, 50) // (NPCs) chance the target will be pushed. Made up, 100 actually isn't that bad +RULE_INT(Combat, MonkACBonusWeight, 15) +RULE_INT(Combat, ClientStunLevel, 55) //This is the level where client kicks and bashes can stun the target +RULE_INT(Combat, QuiverWRHasteDiv, 3) //Weight Reduction is divided by this to get haste contribution for quivers +RULE_BOOL(Combat, UseArcheryBonusRoll, false) //Make the 51+ archery bonus require an actual roll +RULE_INT(Combat, ArcheryBonusChance, 50) +RULE_INT(Combat, BerserkerFrenzyStart, 35) +RULE_INT(Combat, BerserkerFrenzyEnd, 45) +RULE_BOOL(Combat, OneProcPerWeapon, true) //If enabled, One proc per weapon per round +RULE_BOOL(Combat, ProjectileDmgOnImpact, true) //If enabled, projectiles (ie arrows) will hit on impact, instead of instantly. +RULE_BOOL(Combat, MeleePush, true) // enable melee push +RULE_INT(Combat, MeleePushChance, 50) // (NPCs) chance the target will be pushed. Made up, 100 actually isn't that bad RULE_CATEGORY_END() -RULE_CATEGORY( NPC ) -RULE_INT ( NPC, MinorNPCCorpseDecayTimeMS, 450000 ) //level<55 -RULE_INT ( NPC, MajorNPCCorpseDecayTimeMS, 1500000 ) //level>=55 -RULE_INT ( NPC, CorpseUnlockTimer, 150000 ) -RULE_INT ( NPC, EmptyNPCCorpseDecayTimeMS, 0 ) +RULE_CATEGORY(NPC) +RULE_INT(NPC, MinorNPCCorpseDecayTimeMS, 450000) //level<55 +RULE_INT(NPC, MajorNPCCorpseDecayTimeMS, 1500000) //level>=55 +RULE_INT(NPC, CorpseUnlockTimer, 150000) +RULE_INT(NPC, EmptyNPCCorpseDecayTimeMS, 0) RULE_BOOL (NPC, UseItemBonusesForNonPets, true) -RULE_INT ( NPC, SayPauseTimeInSec, 5) -RULE_INT ( NPC, OOCRegen, 0) -RULE_BOOL ( NPC, BuffFriends, false ) -RULE_BOOL ( NPC, EnableNPCQuestJournal, false) -RULE_INT ( NPC, LastFightingDelayMovingMin, 10000) -RULE_INT ( NPC, LastFightingDelayMovingMax, 20000) -RULE_BOOL ( NPC, SmartLastFightingDelayMoving, true) -RULE_BOOL ( NPC, ReturnNonQuestNoDropItems, false) // Returns NO DROP items on NPCs that don't have an EVENT_TRADE sub in their script -RULE_INT ( NPC, StartEnrageValue, 9) // % HP that an NPC will begin to enrage -RULE_BOOL ( NPC, LiveLikeEnrage, false) // If set to true then only player controlled pets will enrage -RULE_BOOL ( NPC, EnableMeritBasedFaction, false) // If set to true, faction will given in the same way as experience (solo/group/raid) +RULE_INT(NPC, SayPauseTimeInSec, 5) +RULE_INT(NPC, OOCRegen, 0) +RULE_BOOL(NPC, BuffFriends, false) +RULE_BOOL(NPC, EnableNPCQuestJournal, false) +RULE_INT(NPC, LastFightingDelayMovingMin, 10000) +RULE_INT(NPC, LastFightingDelayMovingMax, 20000) +RULE_BOOL(NPC, SmartLastFightingDelayMoving, true) +RULE_BOOL(NPC, ReturnNonQuestNoDropItems, false) // Returns NO DROP items on NPCs that don't have an EVENT_TRADE sub in their script +RULE_INT(NPC, StartEnrageValue, 9) // % HP that an NPC will begin to enrage +RULE_BOOL(NPC, LiveLikeEnrage, false) // If set to true then only player controlled pets will enrage +RULE_BOOL(NPC, EnableMeritBasedFaction, false) // If set to true, faction will given in the same way as experience (solo/group/raid) RULE_CATEGORY_END() -RULE_CATEGORY ( Aggro ) -RULE_BOOL ( Aggro, SmartAggroList, true ) -RULE_INT ( Aggro, SittingAggroMod, 35 ) //35% -RULE_INT ( Aggro, MeleeRangeAggroMod, 10 ) //10% -RULE_INT ( Aggro, CurrentTargetAggroMod, 0 ) //0% -- will prefer our current target to any other; makes it harder for our npcs to switch targets. -RULE_INT ( Aggro, CriticallyWoundedAggroMod, 100 ) //100% -RULE_INT ( Aggro, SpellAggroMod, 100 ) -RULE_INT ( Aggro, SongAggroMod, 33 ) -RULE_INT ( Aggro, PetSpellAggroMod, 10 ) -RULE_REAL ( Aggro, TunnelVisionAggroMod, 0.75 ) //people not currently the top hate generate this much hate on a Tunnel Vision mob -RULE_INT ( Aggro, MaxStunProcAggro, 400 ) // Set to -1 for no limit. Maxmimum amount of aggro that a stun based proc will add. -RULE_INT ( Aggro, IntAggroThreshold, 75 ) // Int <= this will aggro regardless of level difference. +RULE_CATEGORY(Aggro) +RULE_BOOL(Aggro, SmartAggroList, true) +RULE_INT(Aggro, SittingAggroMod, 35) //35% +RULE_INT(Aggro, MeleeRangeAggroMod, 10) //10% +RULE_INT(Aggro, CurrentTargetAggroMod, 0) //0% -- will prefer our current target to any other; makes it harder for our npcs to switch targets. +RULE_INT(Aggro, CriticallyWoundedAggroMod, 100) //100% +RULE_INT(Aggro, SpellAggroMod, 100) +RULE_INT(Aggro, SongAggroMod, 33) +RULE_INT(Aggro, PetSpellAggroMod, 10) +RULE_REAL(Aggro, TunnelVisionAggroMod, 0.75) //people not currently the top hate generate this much hate on a Tunnel Vision mob +RULE_INT(Aggro, MaxStunProcAggro, 400) // Set to -1 for no limit. Maxmimum amount of aggro that a stun based proc will add. +RULE_INT(Aggro, IntAggroThreshold, 75) // Int <= this will aggro regardless of level difference. RULE_CATEGORY_END() -RULE_CATEGORY ( TaskSystem) -RULE_BOOL ( TaskSystem, EnableTaskSystem, true) // Globally enable or disable the Task system -RULE_INT ( TaskSystem, PeriodicCheckTimer, 5) // Seconds between checks for failed tasks. Also used by the 'Touch' activity -RULE_BOOL ( TaskSystem, RecordCompletedTasks, true) -RULE_BOOL ( TaskSystem, RecordCompletedOptionalActivities, false) -RULE_BOOL ( TaskSystem, KeepOneRecordPerCompletedTask, true) -RULE_BOOL ( TaskSystem, EnableTaskProximity, true) +RULE_CATEGORY(TaskSystem) +RULE_BOOL(TaskSystem, EnableTaskSystem, true) // Globally enable or disable the Task system +RULE_INT(TaskSystem, PeriodicCheckTimer, 5) // Seconds between checks for failed tasks. Also used by the 'Touch' activity +RULE_BOOL(TaskSystem, RecordCompletedTasks, true) +RULE_BOOL(TaskSystem, RecordCompletedOptionalActivities, false) +RULE_BOOL(TaskSystem, KeepOneRecordPerCompletedTask, true) +RULE_BOOL(TaskSystem, EnableTaskProximity, true) RULE_CATEGORY_END() #ifdef BOTS -RULE_CATEGORY ( Bots ) -RULE_REAL ( Bots, BotManaRegen, 2.0 ) // Adjust mana regen for bots, 1 is fast and higher numbers slow it down 3 is about the same as players. -RULE_BOOL ( Bots, BotFinishBuffing, false ) // Allow for buffs to complete even if the bot caster is out of mana. Only affects buffing out of combat. -RULE_INT ( Bots, CreateBotCount, 150 ) // Number of bots that each account can create -RULE_INT ( Bots, SpawnBotCount, 71 ) // Number of bots a character can have spawned at one time, You + 71 bots is a 12 group raid -RULE_BOOL ( Bots, BotQuest, false ) // Optional quest method to manage bot spawn limits using the quest_globals name bot_spawn_limit, see: /bazaar/Aediles_Thrall.pl -RULE_BOOL ( Bots, BotGroupBuffing, false ) // Bots will cast single target buffs as group buffs, default is false for single. Does not make single target buffs work for MGB. -RULE_BOOL ( Bots, BotSpellQuest, false ) // Anita Thrall's (Anita_Thrall.pl) Bot Spell Scriber quests. -RULE_INT ( Bots, BotAAExpansion, 8 ) // Bots get AAs through this expansion -RULE_BOOL ( Bots, BotGroupXP, false ) // Determines whether client gets xp for bots outside their group. -RULE_BOOL ( Bots, BotBardUseOutOfCombatSongs, true) // Determines whether bard bots use additional out of combat songs. -RULE_BOOL ( Bots, BotLevelsWithOwner, false) // Auto-updates spawned bots as owner levels/de-levels (false is original behavior) +RULE_CATEGORY(Bots) +RULE_REAL(Bots, BotManaRegen, 2.0) // Adjust mana regen for bots, 1 is fast and higher numbers slow it down 3 is about the same as players. +RULE_BOOL(Bots, BotFinishBuffing, false) // Allow for buffs to complete even if the bot caster is out of mana. Only affects buffing out of combat. +RULE_INT(Bots, CreateBotCount, 150) // Number of bots that each account can create +RULE_INT(Bots, SpawnBotCount, 71) // Number of bots a character can have spawned at one time, You + 71 bots is a 12 group raid +RULE_BOOL(Bots, BotQuest, false) // Optional quest method to manage bot spawn limits using the quest_globals name bot_spawn_limit, see: /bazaar/Aediles_Thrall.pl +RULE_BOOL(Bots, BotGroupBuffing, false) // Bots will cast single target buffs as group buffs, default is false for single. Does not make single target buffs work for MGB. +RULE_BOOL(Bots, BotSpellQuest, false) // Anita Thrall's (Anita_Thrall.pl) Bot Spell Scriber quests. +RULE_INT(Bots, BotAAExpansion, 8) // Bots get AAs through this expansion +RULE_BOOL(Bots, BotGroupXP, false) // Determines whether client gets xp for bots outside their group. +RULE_BOOL(Bots, BotBardUseOutOfCombatSongs, true) // Determines whether bard bots use additional out of combat songs. +RULE_BOOL(Bots, BotLevelsWithOwner, false) // Auto-updates spawned bots as owner levels/de-levels (false is original behavior) RULE_CATEGORY_END() #endif -RULE_CATEGORY ( Chat ) -RULE_BOOL ( Chat, ServerWideOOC, true) -RULE_BOOL ( Chat, ServerWideAuction, true) -RULE_BOOL ( Chat, EnableVoiceMacros, true) -RULE_BOOL ( Chat, EnableMailKeyIPVerification, true) -RULE_BOOL ( Chat, EnableAntiSpam, true) -RULE_BOOL ( Chat, SuppressCommandErrors, false) // Do not suppress by default -RULE_INT ( Chat, MinStatusToBypassAntiSpam, 100) -RULE_INT ( Chat, MinimumMessagesPerInterval, 4) -RULE_INT ( Chat, MaximumMessagesPerInterval, 12) -RULE_INT ( Chat, MaxMessagesBeforeKick, 20) -RULE_INT ( Chat, IntervalDurationMS, 60000) -RULE_INT ( Chat, KarmaUpdateIntervalMS, 1200000) -RULE_INT ( Chat, KarmaGlobalChatLimit, 72) //amount of karma you need to be able to talk in ooc/auction/chat below the level limit -RULE_INT ( Chat, GlobalChatLevelLimit, 8) //level limit you need to of reached to talk in ooc/auction/chat if your karma is too low. +RULE_CATEGORY(Chat) +RULE_BOOL(Chat, ServerWideOOC, true) +RULE_BOOL(Chat, ServerWideAuction, true) +RULE_BOOL(Chat, EnableVoiceMacros, true) +RULE_BOOL(Chat, EnableMailKeyIPVerification, true) +RULE_BOOL(Chat, EnableAntiSpam, true) +RULE_BOOL(Chat, SuppressCommandErrors, false) // Do not suppress by default +RULE_INT(Chat, MinStatusToBypassAntiSpam, 100) +RULE_INT(Chat, MinimumMessagesPerInterval, 4) +RULE_INT(Chat, MaximumMessagesPerInterval, 12) +RULE_INT(Chat, MaxMessagesBeforeKick, 20) +RULE_INT(Chat, IntervalDurationMS, 60000) +RULE_INT(Chat, KarmaUpdateIntervalMS, 1200000) +RULE_INT(Chat, KarmaGlobalChatLimit, 72) //amount of karma you need to be able to talk in ooc/auction/chat below the level limit +RULE_INT(Chat, GlobalChatLevelLimit, 8) //level limit you need to of reached to talk in ooc/auction/chat if your karma is too low. RULE_CATEGORY_END() -RULE_CATEGORY ( Merchant ) -RULE_BOOL ( Merchant, UsePriceMod, true) // Use faction/charisma price modifiers. -RULE_REAL ( Merchant, SellCostMod, 1.05) // Modifier for NPC sell price. -RULE_REAL ( Merchant, BuyCostMod, 0.95) // Modifier for NPC buy price. -RULE_INT ( Merchant, PriceBonusPct, 4) // Determines maximum price bonus from having good faction/CHA. Value is a percent. -RULE_INT ( Merchant, PricePenaltyPct, 4) // Determines maximum price penalty from having bad faction/CHA. Value is a percent. -RULE_REAL( Merchant, ChaBonusMod, 3.45) // Determines CHA cap, from 104 CHA. 3.45 is 132 CHA at apprehensive. 0.34 is 400 CHA at apprehensive. -RULE_REAL ( Merchant, ChaPenaltyMod, 1.52) // Determines CHA bottom, up to 102 CHA. 1.52 is 37 CHA at apprehensive. 0.98 is 0 CHA at apprehensive. -RULE_BOOL ( Merchant, EnableAltCurrencySell, true) // Enables the ability to resell items to alternate currency merchants +RULE_CATEGORY(Merchant) +RULE_BOOL(Merchant, UsePriceMod, true) // Use faction/charisma price modifiers. +RULE_REAL(Merchant, SellCostMod, 1.05) // Modifier for NPC sell price. +RULE_REAL(Merchant, BuyCostMod, 0.95) // Modifier for NPC buy price. +RULE_INT(Merchant, PriceBonusPct, 4) // Determines maximum price bonus from having good faction/CHA. Value is a percent. +RULE_INT(Merchant, PricePenaltyPct, 4) // Determines maximum price penalty from having bad faction/CHA. Value is a percent. +RULE_REAL(Merchant, ChaBonusMod, 3.45) // Determines CHA cap, from 104 CHA. 3.45 is 132 CHA at apprehensive. 0.34 is 400 CHA at apprehensive. +RULE_REAL(Merchant, ChaPenaltyMod, 1.52) // Determines CHA bottom, up to 102 CHA. 1.52 is 37 CHA at apprehensive. 0.98 is 0 CHA at apprehensive. +RULE_BOOL(Merchant, EnableAltCurrencySell, true) // Enables the ability to resell items to alternate currency merchants RULE_CATEGORY_END() -RULE_CATEGORY ( Bazaar ) -RULE_BOOL ( Bazaar, AuditTrail, false) -RULE_INT ( Bazaar, MaxSearchResults, 50) -RULE_BOOL ( Bazaar, EnableWarpToTrader, true) -RULE_INT ( Bazaar, MaxBarterSearchResults, 200) // The max results returned in the /barter search +RULE_CATEGORY(Bazaar) +RULE_BOOL(Bazaar, AuditTrail, false) +RULE_INT(Bazaar, MaxSearchResults, 50) +RULE_BOOL(Bazaar, EnableWarpToTrader, true) +RULE_INT(Bazaar, MaxBarterSearchResults, 200) // The max results returned in the /barter search RULE_CATEGORY_END() -RULE_CATEGORY ( Mail ) -RULE_BOOL ( Mail, EnableMailSystem, true) // If false, client won't bring up the Mail window. -RULE_INT ( Mail, ExpireTrash, 0) // Time in seconds. 0 will delete all messages in the trash when the mailserver starts -RULE_INT ( Mail, ExpireRead, 31536000 ) // 1 Year. Set to -1 for never -RULE_INT ( Mail, ExpireUnread, 31536000 ) // 1 Year. Set to -1 for never +RULE_CATEGORY(Mail) +RULE_BOOL(Mail, EnableMailSystem, true) // If false, client won't bring up the Mail window. +RULE_INT(Mail, ExpireTrash, 0) // Time in seconds. 0 will delete all messages in the trash when the mailserver starts +RULE_INT(Mail, ExpireRead, 31536000) // 1 Year. Set to -1 for never +RULE_INT(Mail, ExpireUnread, 31536000) // 1 Year. Set to -1 for never RULE_CATEGORY_END() -RULE_CATEGORY ( Channels ) -RULE_INT ( Channels, RequiredStatusAdmin, 251) // Required status to administer chat channels -RULE_INT ( Channels, RequiredStatusListAll, 251) // Required status to list all chat channels -RULE_INT ( Channels, DeleteTimer, 1440) // Empty password protected channels will be deleted after this many minutes +RULE_CATEGORY(Channels) +RULE_INT(Channels, RequiredStatusAdmin, 251) // Required status to administer chat channels +RULE_INT(Channels, RequiredStatusListAll, 251) // Required status to list all chat channels +RULE_INT(Channels, DeleteTimer, 1440) // Empty password protected channels will be deleted after this many minutes RULE_CATEGORY_END() -RULE_CATEGORY ( EventLog ) -RULE_BOOL ( EventLog, RecordSellToMerchant, false ) // Record sales from a player to an NPC merchant in eventlog table -RULE_BOOL ( EventLog, RecordBuyFromMerchant, false ) // Record purchases by a player from an NPC merchant in eventlog table +RULE_CATEGORY(EventLog) +RULE_BOOL(EventLog, RecordSellToMerchant, false) // Record sales from a player to an NPC merchant in eventlog table +RULE_BOOL(EventLog, RecordBuyFromMerchant, false) // Record purchases by a player from an NPC merchant in eventlog table RULE_CATEGORY_END() -RULE_CATEGORY ( Adventure ) -RULE_INT ( Adventure, MinNumberForGroup, 2 ) -RULE_INT ( Adventure, MaxNumberForGroup, 6 ) -RULE_INT ( Adventure, MinNumberForRaid, 18 ) -RULE_INT ( Adventure, MaxNumberForRaid, 36 ) -RULE_INT ( Adventure, MaxLevelRange, 9 ) -RULE_INT ( Adventure, NumberKillsForBossSpawn, 45) -RULE_REAL ( Adventure, DistanceForRescueAccept, 10000.0) -RULE_REAL ( Adventure, DistanceForRescueComplete, 2500.0) -RULE_INT ( Adventure, ItemIDToEnablePorts, 41000 ) //0 to disable, otherwise using a LDoN portal will require the user to have this item. -RULE_INT ( Adventure, LDoNTrapDistanceUse, 625 ) -RULE_REAL ( Adventure, LDoNBaseTrapDifficulty, 15.0 ) -RULE_REAL ( Adventure, LDoNCriticalFailTrapThreshold, 10.0 ) -RULE_INT ( Adventure, LDoNAdventureExpireTime, 1800) //30 minutes to expire +RULE_CATEGORY(Adventure) +RULE_INT(Adventure, MinNumberForGroup, 2) +RULE_INT(Adventure, MaxNumberForGroup, 6) +RULE_INT(Adventure, MinNumberForRaid, 18) +RULE_INT(Adventure, MaxNumberForRaid, 36) +RULE_INT(Adventure, MaxLevelRange, 9) +RULE_INT(Adventure, NumberKillsForBossSpawn, 45) +RULE_REAL(Adventure, DistanceForRescueAccept, 10000.0) +RULE_REAL(Adventure, DistanceForRescueComplete, 2500.0) +RULE_INT(Adventure, ItemIDToEnablePorts, 41000) //0 to disable, otherwise using a LDoN portal will require the user to have this item. +RULE_INT(Adventure, LDoNTrapDistanceUse, 625) +RULE_REAL(Adventure, LDoNBaseTrapDifficulty, 15.0) +RULE_REAL(Adventure, LDoNCriticalFailTrapThreshold, 10.0) +RULE_INT(Adventure, LDoNAdventureExpireTime, 1800) //30 minutes to expire RULE_CATEGORY_END() -RULE_CATEGORY ( AA ) -RULE_INT ( AA, ExpPerPoint, 23976503) //Amount of exp per AA. Is the same as the amount of exp to go from level 51 to level 52. -RULE_BOOL ( AA, Stacking, true) //Allow AA that belong to the same group to stack on SOF+ clients. +RULE_CATEGORY(AA) +RULE_INT(AA, ExpPerPoint, 23976503) //Amount of exp per AA. Is the same as the amount of exp to go from level 51 to level 52. +RULE_BOOL(AA, Stacking, true) //Allow AA that belong to the same group to stack on SOF+ clients. RULE_CATEGORY_END() -RULE_CATEGORY( Console ) -RULE_INT ( Console, SessionTimeOut, 600000 ) // Amount of time in ms for the console session to time out +RULE_CATEGORY(Console) +RULE_INT(Console, SessionTimeOut, 600000) // Amount of time in ms for the console session to time out RULE_CATEGORY_END() -RULE_CATEGORY( QueryServ ) -RULE_BOOL( QueryServ, PlayerLogChat, false) // Logs Player Chat -RULE_BOOL( QueryServ, PlayerLogTrades, false) // Logs Player Trades -RULE_BOOL( QueryServ, PlayerLogHandins, false) // Logs Player Handins -RULE_BOOL( QueryServ, PlayerLogNPCKills, false) // Logs Player NPC Kills -RULE_BOOL( QueryServ, PlayerLogDeletes, false) // Logs Player Deletes -RULE_BOOL( QueryServ, PlayerLogMoves, false) // Logs Player Moves -RULE_BOOL( QueryServ, PlayerLogMerchantTransactions, false) // Logs Merchant Transactions -RULE_BOOL( QueryServ, PlayerLogPCCoordinates, false) // Logs Player Coordinates with certain events -RULE_BOOL( QueryServ, PlayerLogDropItem, false) // Logs Player Drop Item -RULE_BOOL( QueryServ, PlayerLogZone, false) // Logs Player Zone Events -RULE_BOOL( QueryServ, PlayerLogDeaths, false) // Logs Player Deaths -RULE_BOOL( QueryServ, PlayerLogConnectDisconnect, false) // Logs Player Connect Disconnect State -RULE_BOOL( QueryServ, PlayerLogLevels, false) // Logs Player Leveling/Deleveling -RULE_BOOL( QueryServ, PlayerLogAARate, false) // Logs Player AA Experience Rates -RULE_BOOL( QueryServ, PlayerLogQGlobalUpdate, false) // Logs Player QGlobal Updates -RULE_BOOL( QueryServ, PlayerLogTaskUpdates, false) // Logs Player Task Updates -RULE_BOOL( QueryServ, PlayerLogKeyringAddition, false) // Log PLayer Keyring additions -RULE_BOOL( QueryServ, PlayerLogAAPurchases, false) // Log Player AA Purchases -RULE_BOOL( QueryServ, PlayerLogTradeSkillEvents, false) // Log Player Tradeskill Transactions -RULE_BOOL( QueryServ, PlayerLogIssuedCommandes, false ) // Log Player Issued Commands -RULE_BOOL( QueryServ, PlayerLogMoneyTransactions, false) // Log Player Money Transaction/Splits -RULE_BOOL( QueryServ, PlayerLogAlternateCurrencyTransactions, false) // Log Ploayer Alternate Currency Transactions +RULE_CATEGORY(QueryServ) +RULE_BOOL(QueryServ, PlayerLogChat, false) // Logs Player Chat +RULE_BOOL(QueryServ, PlayerLogTrades, false) // Logs Player Trades +RULE_BOOL(QueryServ, PlayerLogHandins, false) // Logs Player Handins +RULE_BOOL(QueryServ, PlayerLogNPCKills, false) // Logs Player NPC Kills +RULE_BOOL(QueryServ, PlayerLogDeletes, false) // Logs Player Deletes +RULE_BOOL(QueryServ, PlayerLogMoves, false) // Logs Player Moves +RULE_BOOL(QueryServ, PlayerLogMerchantTransactions, false) // Logs Merchant Transactions +RULE_BOOL(QueryServ, PlayerLogPCCoordinates, false) // Logs Player Coordinates with certain events +RULE_BOOL(QueryServ, PlayerLogDropItem, false) // Logs Player Drop Item +RULE_BOOL(QueryServ, PlayerLogZone, false) // Logs Player Zone Events +RULE_BOOL(QueryServ, PlayerLogDeaths, false) // Logs Player Deaths +RULE_BOOL(QueryServ, PlayerLogConnectDisconnect, false) // Logs Player Connect Disconnect State +RULE_BOOL(QueryServ, PlayerLogLevels, false) // Logs Player Leveling/Deleveling +RULE_BOOL(QueryServ, PlayerLogAARate, false) // Logs Player AA Experience Rates +RULE_BOOL(QueryServ, PlayerLogQGlobalUpdate, false) // Logs Player QGlobal Updates +RULE_BOOL(QueryServ, PlayerLogTaskUpdates, false) // Logs Player Task Updates +RULE_BOOL(QueryServ, PlayerLogKeyringAddition, false) // Log PLayer Keyring additions +RULE_BOOL(QueryServ, PlayerLogAAPurchases, false) // Log Player AA Purchases +RULE_BOOL(QueryServ, PlayerLogTradeSkillEvents, false) // Log Player Tradeskill Transactions +RULE_BOOL(QueryServ, PlayerLogIssuedCommandes, false) // Log Player Issued Commands +RULE_BOOL(QueryServ, PlayerLogMoneyTransactions, false) // Log Player Money Transaction/Splits +RULE_BOOL(QueryServ, PlayerLogAlternateCurrencyTransactions, false) // Log Ploayer Alternate Currency Transactions RULE_CATEGORY_END() -RULE_CATEGORY( Inventory ) -RULE_BOOL ( Inventory, EnforceAugmentRestriction, true) // Forces augment slot restrictions -RULE_BOOL ( Inventory, EnforceAugmentUsability, true) // Forces augmented item usability -RULE_BOOL ( Inventory, EnforceAugmentWear, true) // Forces augment wear slot validation -RULE_BOOL ( Inventory, DeleteTransformationMold, true) //False if you want mold to last forever -RULE_BOOL ( Inventory, AllowAnyWeaponTransformation, false) //Weapons can use any weapon transformation -RULE_BOOL ( Inventory, TransformSummonedBags, false) //Transforms summoned bags into disenchanted ones instead of deleting +RULE_CATEGORY(Inventory) +RULE_BOOL(Inventory, EnforceAugmentRestriction, true) // Forces augment slot restrictions +RULE_BOOL(Inventory, EnforceAugmentUsability, true) // Forces augmented item usability +RULE_BOOL(Inventory, EnforceAugmentWear, true) // Forces augment wear slot validation +RULE_BOOL(Inventory, DeleteTransformationMold, true) //False if you want mold to last forever +RULE_BOOL(Inventory, AllowAnyWeaponTransformation, false) //Weapons can use any weapon transformation +RULE_BOOL(Inventory, TransformSummonedBags, false) //Transforms summoned bags into disenchanted ones instead of deleting RULE_CATEGORY_END() -RULE_CATEGORY( Client ) -RULE_BOOL( Client, UseLiveFactionMessage, false) // Allows players to see faction adjustments like Live +RULE_CATEGORY(Client) +RULE_BOOL(Client, UseLiveFactionMessage, false) // Allows players to see faction adjustments like Live RULE_CATEGORY_END() #undef RULE_CATEGORY diff --git a/zone/bot.cpp b/zone/bot.cpp index 9846173ba..3a9d46e97 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -6640,10 +6640,10 @@ void Bot::RogueBackstab(Mob* other, bool min_damage, int ReuseTime) { if(primaryweapondamage > 0) { if(level > 25) { - max_hit = ((((2 * backstab_dmg) * GetDamageTable(SkillBackstab) / 100) * 10 * GetSkill(SkillBackstab) / 355) + ((level - 25) / 3) + 1); + max_hit = (((((2 * backstab_dmg) * GetDamageTable(SkillBackstab) / 100) * 10 * GetSkill(SkillBackstab) / 355) + ((level - 25) / 3) + 1) * ((100 + RuleI(Combat, BackstabBonus)) / 100)); hate = (20 * backstab_dmg * GetSkill(SkillBackstab) / 355); } else { - max_hit = ((((2 * backstab_dmg) * GetDamageTable(SkillBackstab) / 100) * 10 * GetSkill(SkillBackstab) / 355) + 1); + max_hit = (((((2 * backstab_dmg) * GetDamageTable(SkillBackstab) / 100) * 10 * GetSkill(SkillBackstab) / 355) + 1) * ((100 + RuleI(Combat, BackstabBonus)) / 100)); hate = (20 * backstab_dmg * GetSkill(SkillBackstab) / 355); } diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index a3557d021..b844e9ca1 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -618,11 +618,11 @@ void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime) if(primaryweapondamage > 0){ if(level > 25){ - max_hit = (((2*backstab_dmg) * GetDamageTable(SkillBackstab) / 100) * 10 * GetSkill(SkillBackstab) / 355) + ((level-25)/3) + 1; + max_hit = (((((2*backstab_dmg) * GetDamageTable(SkillBackstab) / 100) * 10 * GetSkill(SkillBackstab) / 355) + ((level-25)/3) + 1) * ((100 + RuleI(Combat, BackstabBonus)) / 100)); hate = 20 * backstab_dmg * GetSkill(SkillBackstab) / 355; } else{ - max_hit = (((2*backstab_dmg) * GetDamageTable(SkillBackstab) / 100) * 10 * GetSkill(SkillBackstab) / 355) + 1;; + max_hit = (((((2*backstab_dmg) * GetDamageTable(SkillBackstab) / 100) * 10 * GetSkill(SkillBackstab) / 355) + 1) * ((100 + RuleI(Combat, BackstabBonus)) / 100)); hate = 20 * backstab_dmg * GetSkill(SkillBackstab) / 355; } From 32e880f571f962a74589c6be2358fec8f31db0d8 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 20 Jun 2015 14:05:32 -0400 Subject: [PATCH 129/129] Identified the extra byte at the end of OP_TargetBuffs/OP_BuffCreate --- common/eq_packet_structs.h | 1 + common/patches/rof.cpp | 4 ++-- common/patches/rof2.cpp | 4 ++-- common/patches/uf.cpp | 2 +- zone/spells.cpp | 6 ++++++ 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 8e9f97138..f9f75838f 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -4733,6 +4733,7 @@ struct BuffIcon_Struct uint32 entity_id; uint8 all_buffs; uint16 count; + uint8 type; // 0 = self buff window, 1 = self target window, 4 = group, 5 = PC, 7 = NPC BuffIconEntry_Struct entries[0]; }; diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index f4f01e300..8080bfce1 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -417,7 +417,7 @@ namespace RoF outapp->WriteUInt32(0); // Duration outapp->WriteUInt32(0); // ? outapp->WriteUInt8(0); // Caster name - outapp->WriteUInt8(0); // Terminating byte + outapp->WriteUInt8(0); // Type } FINISH_ENCODE(); @@ -454,7 +454,7 @@ namespace RoF __packet->WriteUInt32(emu->entries[i].num_hits); // Unknown __packet->WriteString(""); } - __packet->WriteUInt8(!emu->all_buffs); // Unknown + __packet->WriteUInt8(emu->type); // Unknown FINISH_ENCODE(); } diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 096467edf..68ff8bf44 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -483,7 +483,7 @@ namespace RoF2 outapp->WriteUInt32(0); // Duration outapp->WriteUInt32(0); // ? outapp->WriteUInt8(0); // Caster name - outapp->WriteUInt8(0); // Terminating byte + outapp->WriteUInt8(0); // Type } FINISH_ENCODE(); @@ -525,7 +525,7 @@ namespace RoF2 __packet->WriteUInt32(emu->entries[i].num_hits); // Unknown __packet->WriteString(""); } - __packet->WriteUInt8(!emu->all_buffs); // Unknown + __packet->WriteUInt8(emu->type); // Unknown FINISH_ENCODE(); } diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index deedcec5f..d63a1bc04 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -387,7 +387,7 @@ namespace UF __packet->WriteUInt32(emu->entries[i].num_hits); __packet->WriteString(""); } - __packet->WriteUInt8(!emu->all_buffs); + __packet->WriteUInt8(emu->type); FINISH_ENCODE(); /* diff --git a/zone/spells.cpp b/zone/spells.cpp index 868180dff..faaace669 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -5396,6 +5396,12 @@ EQApplicationPacket *Mob::MakeBuffsPacket(bool for_target) buff->entity_id = GetID(); buff->count = count; buff->all_buffs = 1; + // there are more types, the client doesn't seem to really care though. The others are also currently hard to fill in here ... + // (see comment in common/eq_packet_structs.h) + if (for_target) + buff->type = IsClient() ? 5 : 7; + else + buff->type = 0; uint32 index = 0; for(unsigned int i = 0; i < buff_count; ++i)