diff --git a/common/emu_oplist.h b/common/emu_oplist.h index e8ce3b986..62978655a 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -74,7 +74,9 @@ N(OP_CharacterCreateRequest), N(OP_CharInventory), N(OP_Charm), N(OP_ChatMessage), +N(OP_ClearAA), N(OP_ClearBlockedBuffs), +N(OP_ClearLeadershipAbilities), N(OP_ClearNPCMarks), N(OP_ClearObject), N(OP_ClearSurname), diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index 16973c202..05a3a4a76 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -94,7 +94,8 @@ OP_ClearBlockedBuffs=0x5d3c OP_WorldObjectsSent=0x7fa8 OP_SendExpZonein=0x25ab OP_SendAATable=0x7791 -OP_ShroudClearAA=0x422e +OP_ClearAA=0x422e +OP_ClearLeadershipAbilities=0xb978 OP_RespondAA=0x379d OP_UpdateAA=0x504f OP_SendAAStats=0x3d1c diff --git a/utils/patches/patch_SoD.conf b/utils/patches/patch_SoD.conf index f42231238..a5dee668c 100644 --- a/utils/patches/patch_SoD.conf +++ b/utils/patches/patch_SoD.conf @@ -531,7 +531,8 @@ OP_Shroud=0x6d1f OP_ShroudRemove=0x17f6 OP_ShroudUnknown1=0x169a OP_ShroudUnknown2=0x4292 -OP_ShroudClearAA=0x3bef +OP_ClearAA=0x3bef +OP_ClearLeadershipAbilities=0x6e58 OP_ShroudSelectionWindow=0x4d79 OP_ShroudRequestStats=0x28ce OP_ShroudRespondStats=0x33f2 diff --git a/utils/patches/patch_SoF.conf b/utils/patches/patch_SoF.conf index 7b0c41ad2..80aee8bdc 100644 --- a/utils/patches/patch_SoF.conf +++ b/utils/patches/patch_SoF.conf @@ -85,7 +85,8 @@ OP_TaskActivity=0x2E60 #SEQ 12/04/08 OP_CompletedTasks=0x75AC #Derision 2009 OP_Weather=0x70A5 #SEQ 12/04/08 OP_SendAATable=0x6F05 #Trevius 12/20/08 -OP_ShroudClearAA=0x71b9 +OP_ClearAA=0x71b9 +OP_ClearLeadershipAbilities=0x74e5 OP_UpdateAA=0x45D2 #Trevius 12/20/08 OP_RespondAA=0x4426 #Trevius 12/20/08 OP_ReqClientSpawn=0x014C #SEQ 12/04/08 diff --git a/utils/patches/patch_Underfoot.conf b/utils/patches/patch_Underfoot.conf index 2b1c41dd9..09c6a8252 100644 --- a/utils/patches/patch_Underfoot.conf +++ b/utils/patches/patch_Underfoot.conf @@ -88,7 +88,8 @@ OP_TaskActivity=0x31f3 # C OP_CompletedTasks=0x687f # C OP_Weather=0x4658 # V OP_SendAATable=0x6ef9 # V -OP_ShroudClearAA=0x2cd4 +OP_ClearAA=0x2cd4 +OP_ClearLeadershipAbilities=0x7b77 OP_UpdateAA=0x7bf6 # V OP_RespondAA=0x1fbd # C 0x2bad OP_ReqClientSpawn=0x69cd # V diff --git a/zone/aa.cpp b/zone/aa.cpp index cf3b8c1c6..f70a4bc17 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -1470,10 +1470,11 @@ bool ZoneDatabase::LoadAAEffects2() { return true; } + void Client::ResetAA(){ RefundAA(); uint32 i; - for(i=0;iAA = 0; aa[i]->value = 0; m_pp.aa_array[MAX_PP_AA_ARRAY].AA = 0; @@ -1481,10 +1482,10 @@ void Client::ResetAA(){ } std::map::iterator itr; - for(itr=aa_points.begin();itr!=aa_points.end();++itr) + for(itr = aa_points.begin(); itr != aa_points.end(); ++itr) aa_points[itr->first] = 0; - for(int i = 0; i < _maxLeaderAA; ++i) + for(int i = 0; i < _maxLeaderAA; ++i) m_pp.leader_abilities.ranks[i] = 0; m_pp.group_leadership_points = 0; @@ -1494,9 +1495,22 @@ void Client::ResetAA(){ database.DeleteCharacterAAs(this->CharacterID()); SaveAA(); + SendClearAA(); + SendAAList(); SendAATable(); + SendAAStats(); database.DeleteCharacterLeadershipAAs(this->CharacterID()); - Kick(); + // undefined for these clients + if (GetClientVersionBit() & BIT_TitaniumAndEarlier) + Kick(); +} + +void Client::SendClearAA() +{ + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ClearLeadershipAbilities, 0); + FastQueuePacket(&outapp); + outapp = new EQApplicationPacket(OP_ClearAA, 0); + FastQueuePacket(&outapp); } int Client::GroupLeadershipAAHealthEnhancement() diff --git a/zone/attack.cpp.orig b/zone/attack.cpp.orig deleted file mode 100644 index 591be16ad..000000000 --- a/zone/attack.cpp.orig +++ /dev/null @@ -1,5054 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2002 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 -*/ - -#if EQDEBUG >= 5 -//#define ATTACK_DEBUG 20 -#endif - -#include "../common/debug.h" -#include "../common/eq_constants.h" -#include "../common/eq_packet_structs.h" -#include "../common/rulesys.h" -#include "../common/skills.h" -#include "../common/spdat.h" -#include "../common/string_util.h" -#include "queryserv.h" -#include "quest_parser_collection.h" -#include "string_ids.h" -#include "water_map.h" -#include "worldserver.h" -#include "zone.h" - -#include -#include -#include - -extern QueryServ* QServ; -extern WorldServer worldserver; - -#ifdef _WINDOWS -#define snprintf _snprintf -#define strncasecmp _strnicmp -#define strcasecmp _stricmp -#endif - -extern EntityList entity_list; -extern Zone* zone; - -bool Mob::AttackAnimation(SkillUseTypes &skillinuse, int Hand, const ItemInst* weapon) -{ - // Determine animation - int type = 0; - if (weapon && weapon->IsType(ItemClassCommon)) { - const Item_Struct* item = weapon->GetItem(); -#if EQDEBUG >= 11 - LogFile->write(EQEMuLog::Debug, "Weapon skill:%i", item->ItemType); -#endif - switch (item->ItemType) - { - case ItemType1HSlash: // 1H Slashing - { - skillinuse = Skill1HSlashing; - type = anim1HWeapon; - break; - } - case ItemType2HSlash: // 2H Slashing - { - skillinuse = Skill2HSlashing; - type = anim2HSlashing; - break; - } - case ItemType1HPiercing: // Piercing - { - skillinuse = Skill1HPiercing; - type = animPiercing; - break; - } - case ItemType1HBlunt: // 1H Blunt - { - skillinuse = Skill1HBlunt; - type = anim1HWeapon; - break; - } - case ItemType2HBlunt: // 2H Blunt - { - skillinuse = Skill2HBlunt; - type = anim2HSlashing; //anim2HWeapon - break; - } - case ItemType2HPiercing: // 2H Piercing - { - skillinuse = Skill1HPiercing; // change to Skill2HPiercing once activated - type = anim2HWeapon; - break; - } - case ItemTypeMartial: - { - skillinuse = SkillHandtoHand; - type = animHand2Hand; - break; - } - default: - { - skillinuse = SkillHandtoHand; - type = animHand2Hand; - break; - } - }// switch - } - else if(IsNPC()) { - - switch (skillinuse) - { - case Skill1HSlashing: // 1H Slashing - { - type = anim1HWeapon; - break; - } - case Skill2HSlashing: // 2H Slashing - { - type = anim2HSlashing; - break; - } - case Skill1HPiercing: // Piercing - { - type = animPiercing; - break; - } - case Skill1HBlunt: // 1H Blunt - { - type = anim1HWeapon; - break; - } - case Skill2HBlunt: // 2H Blunt - { - type = anim2HSlashing; //anim2HWeapon - break; - } - case 99: // 2H Piercing // change to Skill2HPiercing once activated - { - type = anim2HWeapon; - break; - } - case SkillHandtoHand: - { - type = animHand2Hand; - break; - } - default: - { - type = animHand2Hand; - break; - } - }// switch - } - else { - skillinuse = SkillHandtoHand; - type = animHand2Hand; - } - - // If we're attacking with the secondary hand, play the dual wield anim - if (Hand == MainSecondary) // DW anim - type = animDualWield; - - DoAnim(type); - return true; -} - -// 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. -bool Mob::CheckHitChance(Mob* other, SkillUseTypes skillinuse, int Hand, int16 chance_mod) -{ -/*/ - //Reworked a lot of this code to achieve better balance at higher levels. - //The old code basically meant that any in high level (50+) combat, - //both parties always had 95% chance to hit the other one. -/*/ - - Mob *attacker=other; - Mob *defender=this; - float chancetohit = RuleR(Combat, BaseHitChance); - - if(attacker->IsNPC() && !attacker->IsPet()) - chancetohit += RuleR(Combat, NPCBonusHitChance); - -#if ATTACK_DEBUG>=11 - LogFile->write(EQEMuLog::Debug, "CheckHitChance(%s) attacked by %s", defender->GetName(), attacker->GetName()); -#endif - mlog(COMBAT__TOHIT,"CheckHitChance(%s) attacked by %s", defender->GetName(), attacker->GetName()); - - bool pvpmode = false; - if(IsClient() && other->IsClient()) - pvpmode = true; - - if (chance_mod >= 10000) - return true; - - float avoidanceBonus = 0; - float hitBonus = 0; - - //////////////////////////////////////////////////////// - // To hit calcs go here - //////////////////////////////////////////////////////// - - uint8 attacker_level = attacker->GetLevel() ? attacker->GetLevel() : 1; - uint8 defender_level = defender->GetLevel() ? defender->GetLevel() : 1; - - //Calculate the level difference - - mlog(COMBAT__TOHIT, "Chance to hit before level diff calc %.2f", chancetohit); - - double level_difference = attacker_level - defender_level; - double range = defender->GetLevel(); - range = ((range / 4) + 3); - - if(level_difference < 0) - { - if(level_difference >= -range) - { - chancetohit += (level_difference / range) * RuleR(Combat,HitFalloffMinor); //5 - } - else if (level_difference >= -(range+3.0)) - { - chancetohit -= RuleR(Combat,HitFalloffMinor); - chancetohit += ((level_difference+range) / (3.0)) * RuleR(Combat,HitFalloffModerate); //7 - } - else - { - chancetohit -= (RuleR(Combat,HitFalloffMinor) + RuleR(Combat,HitFalloffModerate)); - chancetohit += ((level_difference+range+3.0)/12.0) * RuleR(Combat,HitFalloffMajor); //50 - } - } - else - { - chancetohit += (RuleR(Combat,HitBonusPerLevel) * level_difference); - } - - mlog(COMBAT__TOHIT, "Chance to hit after level diff calc %.2f", chancetohit); - - chancetohit -= ((float)defender->GetAGI() * RuleR(Combat, AgiHitFactor)); - - mlog(COMBAT__TOHIT, "Chance to hit after agil calc %.2f", chancetohit); - - if(attacker->IsClient()) - { - chancetohit -= (RuleR(Combat,WeaponSkillFalloff) * (attacker->CastToClient()->MaxSkill(skillinuse) - attacker->GetSkill(skillinuse))); - mlog(COMBAT__TOHIT, "Chance to hit after weapon falloff calc (attack) %.2f", chancetohit); - } - - if(defender->IsClient()) - { - chancetohit += (RuleR(Combat,WeaponSkillFalloff) * (defender->CastToClient()->MaxSkill(SkillDefense) - defender->GetSkill(SkillDefense))); - mlog(COMBAT__TOHIT, "Chance to hit after weapon falloff calc (defense) %.2f", chancetohit); - } - - //I dont think this is 100% correct, but at least it does something... - if(attacker->spellbonuses.MeleeSkillCheckSkill == skillinuse || attacker->spellbonuses.MeleeSkillCheckSkill == 255) { - chancetohit += attacker->spellbonuses.MeleeSkillCheck; - mlog(COMBAT__TOHIT, "Applied spell melee skill bonus %d, yeilding %.2f", attacker->spellbonuses.MeleeSkillCheck, chancetohit); - } - if(attacker->itembonuses.MeleeSkillCheckSkill == skillinuse || attacker->itembonuses.MeleeSkillCheckSkill == 255) { - chancetohit += attacker->itembonuses.MeleeSkillCheck; - mlog(COMBAT__TOHIT, "Applied item melee skill bonus %d, yeilding %.2f", attacker->spellbonuses.MeleeSkillCheck, chancetohit); - } - - //Avoidance Bonuses on defender decreases baseline hit chance by percent. - avoidanceBonus = defender->spellbonuses.AvoidMeleeChanceEffect + - defender->itembonuses.AvoidMeleeChanceEffect + - defender->aabonuses.AvoidMeleeChanceEffect + - (defender->itembonuses.AvoidMeleeChance / 10.0f); //Item Mod 'Avoidence' - - Mob *owner = nullptr; - if (defender->IsPet()) - owner = defender->GetOwner(); - else if ((defender->IsNPC() && defender->CastToNPC()->GetSwarmOwner())) - owner = entity_list.GetMobID(defender->CastToNPC()->GetSwarmOwner()); - - if (owner) - avoidanceBonus += owner->aabonuses.PetAvoidance + owner->spellbonuses.PetAvoidance + owner->itembonuses.PetAvoidance; - - if(defender->IsNPC()) - avoidanceBonus += (defender->CastToNPC()->GetAvoidanceRating() / 10.0f); //Modifier from database - - //Hit Chance Bonuses on attacker increases baseline hit chance by percent. - hitBonus += attacker->itembonuses.HitChanceEffect[skillinuse] + - attacker->spellbonuses.HitChanceEffect[skillinuse]+ - attacker->aabonuses.HitChanceEffect[skillinuse]+ - attacker->itembonuses.HitChanceEffect[HIGHEST_SKILL+1] + - attacker->spellbonuses.HitChanceEffect[HIGHEST_SKILL+1] + - attacker->aabonuses.HitChanceEffect[HIGHEST_SKILL+1]; - - - //Accuracy = Spell Effect , HitChance = 'Accuracy' from Item Effect - //Only AA derived accuracy can be skill limited. ie (Precision of the Pathfinder, Dead Aim) - hitBonus += (attacker->itembonuses.Accuracy[HIGHEST_SKILL+1] + - attacker->spellbonuses.Accuracy[HIGHEST_SKILL+1] + - attacker->aabonuses.Accuracy[HIGHEST_SKILL+1] + - attacker->aabonuses.Accuracy[skillinuse] + - attacker->itembonuses.HitChance) / 15.0f; //Item Mod 'Accuracy' - - hitBonus += chance_mod; //Modifier applied from casted/disc skill attacks. - - if(attacker->IsNPC()) - hitBonus += (attacker->CastToNPC()->GetAccuracyRating() / 10.0f); //Modifier from database - - if(skillinuse == SkillArchery) - hitBonus -= hitBonus*RuleR(Combat, ArcheryHitPenalty); - - //Calculate final chance to hit - chancetohit += ((chancetohit * (hitBonus - avoidanceBonus)) / 100.0f); - mlog(COMBAT__TOHIT, "Chance to hit %.2f after accuracy calc %.2f and avoidance calc %.2f", chancetohit, hitBonus, avoidanceBonus); - - chancetohit = mod_hit_chance(chancetohit, skillinuse, attacker); - - // Chance to hit; Max 95%, Min 5% DEFAULTS - if(chancetohit > 1000 || chancetohit < -1000) { - //if chance to hit is crazy high, that means a discipline is in use, and let it stay there - } - else if(chancetohit > RuleR(Combat,MaxChancetoHit)) { - chancetohit = RuleR(Combat,MaxChancetoHit); - } - else if(chancetohit < RuleR(Combat,MinChancetoHit)) { - chancetohit = RuleR(Combat,MinChancetoHit); - } - - //I dont know the best way to handle a garunteed hit discipline being used - //agains a garunteed riposte (for example) discipline... for now, garunteed hit wins - - - #if EQDEBUG>=11 - LogFile->write(EQEMuLog::Debug, "3 FINAL calculated chance to hit is: %5.2f", chancetohit); - #endif - - // - // Did we hit? - // - - float tohit_roll = zone->random.Real(0, 100); - - mlog(COMBAT__TOHIT, "Final hit chance: %.2f%%. Hit roll %.2f", chancetohit, tohit_roll); - - return(tohit_roll <= chancetohit); -} - - -bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte) -{ - /* solar: 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 - * - */ - float skill; - float bonus; - float RollTable[4] = {0,0,0,0}; - float roll; - Mob *attacker=other; - Mob *defender=this; - - //garunteed hit - bool ghit = false; - if((attacker->spellbonuses.MeleeSkillCheck + attacker->itembonuses.MeleeSkillCheck) > 500) - ghit = true; - - ////////////////////////////////////////////////////////// - // make enrage same as riposte - ///////////////////////////////////////////////////////// - if (IsEnraged() && other->InFrontMob(this, other->GetX(), other->GetY())) { - damage = -3; - mlog(COMBAT__DAMAGE, "I am enraged, riposting frontal attack."); - } - - ///////////////////////////////////////////////////////// - // riposte - ///////////////////////////////////////////////////////// - float riposte_chance = 0.0f; - if (CanRiposte && damage > 0 && CanThisClassRiposte() && other->InFrontMob(this, other->GetX(), other->GetY())) - { - riposte_chance = (100.0f + (float)defender->aabonuses.RiposteChance + (float)defender->spellbonuses.RiposteChance + (float)defender->itembonuses.RiposteChance) / 100.0f; - skill = GetSkill(SkillRiposte); - if (IsClient()) { - CastToClient()->CheckIncreaseSkill(SkillRiposte, other, -10); - } - - if (!ghit) { //if they are not using a garunteed hit discipline - bonus = 2.0 + skill/60.0 + (GetDEX()/200); - bonus *= riposte_chance; - bonus = mod_riposte_chance(bonus, attacker); - RollTable[0] = bonus + (itembonuses.HeroicDEX / 25); // 25 heroic = 1%, applies to ripo, parry, block - } - } - - /////////////////////////////////////////////////////// - // block - /////////////////////////////////////////////////////// - - bool bBlockFromRear = false; - bool bShieldBlockFromRear = false; - - if (this->IsClient()) { - 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; - - if (BlockBehindChance && zone->random.Roll(BlockBehindChance)) { - bBlockFromRear = true; - - if (spellbonuses.BlockBehind || itembonuses.BlockBehind) - bShieldBlockFromRear = true; //This bonus should allow a chance to Shield Block from behind. - } - } - - float block_chance = 0.0f; - if (damage > 0 && CanThisClassBlock() && (other->InFrontMob(this, other->GetX(), other->GetY()) || bBlockFromRear)) { - block_chance = (100.0f + (float)spellbonuses.IncreaseBlockChance + (float)itembonuses.IncreaseBlockChance) / 100.0f; - skill = CastToClient()->GetSkill(SkillBlock); - if (IsClient()) { - CastToClient()->CheckIncreaseSkill(SkillBlock, other, -10); - } - - if (!ghit) { //if they are not using a garunteed hit discipline - bonus = 2.0 + skill/35.0 + (GetDEX()/200); - bonus = mod_block_chance(bonus, attacker); - RollTable[1] = RollTable[0] + (bonus * block_chance); - } - } - else{ - RollTable[1] = RollTable[0]; - } - - if(damage > 0 && HasShieldEquiped() && (aabonuses.ShieldBlock || spellbonuses.ShieldBlock || itembonuses.ShieldBlock) - && (other->InFrontMob(this, other->GetX(), other->GetY()) || bShieldBlockFromRear)) { - - float bonusShieldBlock = 0.0f; - bonusShieldBlock = static_cast(aabonuses.ShieldBlock + spellbonuses.ShieldBlock + itembonuses.ShieldBlock); - RollTable[1] += bonusShieldBlock; - } - - if(IsClient() && damage > 0 && (aabonuses.TwoHandBluntBlock || spellbonuses.TwoHandBluntBlock || itembonuses.TwoHandBluntBlock) - && (other->InFrontMob(this, other->GetX(), other->GetY()) || bShieldBlockFromRear)) { - if(CastToClient()->m_inv.GetItem(MainPrimary)) { - float bonusStaffBlock = 0.0f; - if (CastToClient()->m_inv.GetItem(MainPrimary)->GetItem()->ItemType == ItemType2HBlunt){ - bonusStaffBlock = static_cast(aabonuses.TwoHandBluntBlock + spellbonuses.TwoHandBluntBlock + itembonuses.TwoHandBluntBlock); - RollTable[1] += bonusStaffBlock; - } - } - } - - ////////////////////////////////////////////////////// - // parry - ////////////////////////////////////////////////////// - float parry_chance = 0.0f; - if (damage > 0 && CanThisClassParry() && other->InFrontMob(this, other->GetX(), other->GetY())) - { - parry_chance = (100.0f + (float)defender->spellbonuses.ParryChance + (float)defender->itembonuses.ParryChance) / 100.0f; - skill = CastToClient()->GetSkill(SkillParry); - if (IsClient()) { - CastToClient()->CheckIncreaseSkill(SkillParry, other, -10); - } - - if (!ghit) { //if they are not using a garunteed hit discipline - bonus = 2.0 + skill/60.0 + (GetDEX()/200); - bonus *= parry_chance; - bonus = mod_parry_chance(bonus, attacker); - RollTable[2] = RollTable[1] + bonus; - } - } - else{ - RollTable[2] = RollTable[1]; - } - - //////////////////////////////////////////////////////// - // dodge - //////////////////////////////////////////////////////// - float dodge_chance = 0.0f; - if (damage > 0 && CanThisClassDodge() && other->InFrontMob(this, other->GetX(), other->GetY())) - { - dodge_chance = (100.0f + (float)defender->spellbonuses.DodgeChance + (float)defender->itembonuses.DodgeChance) / 100.0f; - skill = CastToClient()->GetSkill(SkillDodge); - if (IsClient()) { - CastToClient()->CheckIncreaseSkill(SkillDodge, other, -10); - } - - if (!ghit) { //if they are not using a garunteed hit discipline - bonus = 2.0 + skill/60.0 + (GetAGI()/200); - bonus *= dodge_chance; - //DCBOOMKAR - bonus = mod_dodge_chance(bonus, attacker); - RollTable[3] = RollTable[2] + bonus - (itembonuses.HeroicDEX / 25) + (itembonuses.HeroicAGI / 25); - } - } - else{ - RollTable[3] = RollTable[2]; - } - - if(damage > 0){ - roll = zone->random.Real(0,100); - if(roll <= RollTable[0]){ - damage = -3; - } - else if(roll <= RollTable[1]){ - damage = -1; - } - else if(roll <= RollTable[2]){ - damage = -2; - } - else if(roll <= RollTable[3]){ - damage = -4; - } - } - - mlog(COMBAT__DAMAGE, "Final damage after all avoidances: %d", damage); - - if (damage < 0) - return true; - return false; -} - -void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit, ExtraAttackOptions *opts) -{ - if (damage <= 0) - return; - - Mob* defender = this; - float aa_mit = (aabonuses.CombatStability + itembonuses.CombatStability + - spellbonuses.CombatStability) / 100.0f; - - if (RuleB(Combat, UseIntervalAC)) { - float softcap = (GetSkill(SkillDefense) + GetLevel()) * - RuleR(Combat, SoftcapFactor) * (1.0 + aa_mit); - float mitigation_rating = 0.0; - float attack_rating = 0.0; - int shield_ac = 0; - int armor = 0; - float weight = 0.0; - - float monkweight = RuleI(Combat, MonkACBonusWeight); - monkweight = mod_monk_weight(monkweight, attacker); - - if (IsClient()) { - armor = CastToClient()->GetRawACNoShield(shield_ac); - weight = (CastToClient()->CalcCurrentWeight() / 10.0); - } else if (IsNPC()) { - armor = CastToNPC()->GetRawAC(); - int PetACBonus = 0; - - if (!IsPet()) - armor = (armor / RuleR(Combat, NPCACFactor)); - - Mob *owner = nullptr; - if (IsPet()) - owner = GetOwner(); - else if ((CastToNPC()->GetSwarmOwner())) - owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner()); - - if (owner) - PetACBonus = owner->aabonuses.PetMeleeMitigation + owner->itembonuses.PetMeleeMitigation + owner->spellbonuses.PetMeleeMitigation; - - armor += spellbonuses.AC + itembonuses.AC + PetACBonus + 1; - } - - if (opts) { - armor *= (1.0f - opts->armor_pen_percent); - armor -= opts->armor_pen_flat; - } - - if (RuleB(Combat, OldACSoftcapRules)) { - if (GetClass() == WIZARD || GetClass() == MAGICIAN || - GetClass() == NECROMANCER || GetClass() == ENCHANTER) - softcap = RuleI(Combat, ClothACSoftcap); - else if (GetClass() == MONK && weight <= monkweight) - softcap = RuleI(Combat, MonkACSoftcap); - else if(GetClass() == DRUID || GetClass() == BEASTLORD || GetClass() == MONK) - softcap = RuleI(Combat, LeatherACSoftcap); - else if(GetClass() == SHAMAN || GetClass() == ROGUE || - GetClass() == BERSERKER || GetClass() == RANGER) - softcap = RuleI(Combat, ChainACSoftcap); - else - softcap = RuleI(Combat, PlateACSoftcap); - } - - softcap += shield_ac; - armor += shield_ac; - if (RuleB(Combat, OldACSoftcapRules)) - softcap += (softcap * (aa_mit * RuleR(Combat, AAMitigationACFactor))); - if (armor > softcap) { - int softcap_armor = armor - softcap; - if (RuleB(Combat, OldACSoftcapRules)) { - if (GetClass() == WARRIOR) - softcap_armor = softcap_armor * RuleR(Combat, WarriorACSoftcapReturn); - else if (GetClass() == SHADOWKNIGHT || GetClass() == PALADIN || - (GetClass() == MONK && weight <= monkweight)) - softcap_armor = softcap_armor * RuleR(Combat, KnightACSoftcapReturn); - else if (GetClass() == CLERIC || GetClass() == BARD || - GetClass() == BERSERKER || GetClass() == ROGUE || - GetClass() == SHAMAN || GetClass() == MONK) - softcap_armor = softcap_armor * RuleR(Combat, LowPlateChainACSoftcapReturn); - else if (GetClass() == RANGER || GetClass() == BEASTLORD) - softcap_armor = softcap_armor * RuleR(Combat, LowChainLeatherACSoftcapReturn); - else if (GetClass() == WIZARD || GetClass() == MAGICIAN || - GetClass() == NECROMANCER || GetClass() == ENCHANTER || - GetClass() == DRUID) - softcap_armor = softcap_armor * RuleR(Combat, CasterACSoftcapReturn); - else - softcap_armor = softcap_armor * RuleR(Combat, MiscACSoftcapReturn); - } else { - if (GetClass() == WARRIOR) - softcap_armor *= RuleR(Combat, WarACSoftcapReturn); - else if (GetClass() == PALADIN || GetClass() == SHADOWKNIGHT) - softcap_armor *= RuleR(Combat, PalShdACSoftcapReturn); - else if (GetClass() == CLERIC || GetClass() == RANGER || - GetClass() == MONK || GetClass() == BARD) - softcap_armor *= RuleR(Combat, ClrRngMnkBrdACSoftcapReturn); - else if (GetClass() == DRUID || GetClass() == NECROMANCER || - GetClass() == WIZARD || GetClass() == ENCHANTER || - GetClass() == MAGICIAN) - softcap_armor *= RuleR(Combat, DruNecWizEncMagACSoftcapReturn); - else if (GetClass() == ROGUE || GetClass() == SHAMAN || - GetClass() == BEASTLORD || GetClass() == BERSERKER) - softcap_armor *= RuleR(Combat, RogShmBstBerACSoftcapReturn); - else - softcap_armor *= RuleR(Combat, MiscACSoftcapReturn); - } - armor = softcap + softcap_armor; - } - - if (GetClass() == WIZARD || GetClass() == MAGICIAN || - GetClass() == NECROMANCER || GetClass() == ENCHANTER) - mitigation_rating = ((GetSkill(SkillDefense) + itembonuses.HeroicAGI/10) / 4.0) + armor + 1; - else - mitigation_rating = ((GetSkill(SkillDefense) + itembonuses.HeroicAGI/10) / 3.0) + (armor * 1.333333) + 1; - mitigation_rating *= 0.847; - - mitigation_rating = mod_mitigation_rating(mitigation_rating, attacker); - - if (attacker->IsClient()) - attack_rating = (attacker->CastToClient()->CalcATK() + ((attacker->GetSTR()-66) * 0.9) + (attacker->GetSkill(SkillOffense)*1.345)); - else - attack_rating = (attacker->GetATK() + (attacker->GetSkill(SkillOffense)*1.345) + ((attacker->GetSTR()-66) * 0.9)); - - attack_rating = attacker->mod_attack_rating(attack_rating, this); - - damage = GetMeleeMitDmg(attacker, damage, minhit, mitigation_rating, attack_rating); - } else { - //////////////////////////////////////////////////////// - // Scorpious2k: Include AC in the calculation - // use serverop variables to set values - int32 myac = GetAC(); - if(opts) { - myac *= (1.0f - opts->armor_pen_percent); - myac -= opts->armor_pen_flat; - } - - if (damage > 0 && myac > 0) { - int acfail=1000; - char tmp[10]; - - if (database.GetVariable("ACfail", tmp, 9)) { - acfail = (int) (atof(tmp) * 100); - if (acfail>100) acfail=100; - } - - if (acfail<=0 || zone->random.Int(0, 100)>acfail) { - float acreduction=1; - int acrandom=300; - if (database.GetVariable("ACreduction", tmp, 9)) - { - acreduction=atof(tmp); - if (acreduction>100) acreduction=100; - } - - if (database.GetVariable("ACrandom", tmp, 9)) - { - acrandom = (int) ((atof(tmp)+1) * 100); - if (acrandom>10100) acrandom=10100; - } - - if (acreduction>0) { - damage -= (int32) (GetAC() * acreduction/100.0f); - } - if (acrandom>0) { - damage -= (myac * zone->random.Int(0, acrandom) / 10000); - } - if (damage<1) damage=1; - mlog(COMBAT__DAMAGE, "AC Damage Reduction: fail chance %d%%. Failed. Reduction %.3f%%, random %d. Resulting damage %d.", acfail, acreduction, acrandom, damage); - } else { - mlog(COMBAT__DAMAGE, "AC Damage Reduction: fail chance %d%%. Did not fail.", acfail); - } - } - - damage -= (aa_mit * damage); - - if(damage != 0 && damage < minhit) - damage = minhit; - //reduce the damage from shielding item and aa based on the min dmg - //spells offer pure mitigation - damage -= (minhit * defender->itembonuses.MeleeMitigation / 100); - damage -= (damage * (defender->spellbonuses.MeleeMitigationEffect + defender->itembonuses.MeleeMitigationEffect + defender->aabonuses.MeleeMitigationEffect) / 100); - } - - if (damage < 0) - damage = 0; -} - -// This is called when the Mob is the one being hit -int32 Mob::GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, - float mit_rating, float atk_rating) -{ - float d = 10.0; - float mit_roll = zone->random.Real(0, mit_rating); - float atk_roll = zone->random.Real(0, atk_rating); - - if (atk_roll > mit_roll) { - float a_diff = atk_roll - mit_roll; - float thac0 = atk_rating * RuleR(Combat, ACthac0Factor); - float thac0cap = attacker->GetLevel() * 9 + 20; - if (thac0 > thac0cap) - thac0 = thac0cap; - - d -= 10.0 * (a_diff / thac0); - } else if (mit_roll > atk_roll) { - float m_diff = mit_roll - atk_roll; - float thac20 = mit_rating * RuleR(Combat, ACthac20Factor); - float thac20cap = GetLevel() * 9 + 20; - if (thac20 > thac20cap) - thac20 = thac20cap; - - d += 10.0 * (m_diff / thac20); - } - - if (d < 0.0) - d = 0.0; - else if (d > 20.0) - d = 20.0; - - float interval = (damage - minhit) / 20.0; - damage -= ((int)d * interval); - - damage -= (minhit * itembonuses.MeleeMitigation / 100); - damage -= (damage * (spellbonuses.MeleeMitigationEffect + itembonuses.MeleeMitigationEffect + aabonuses.MeleeMitigationEffect) / 100); - return damage; -} - -// This is called when the Client is the one being hit -int32 Client::GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, - float mit_rating, float atk_rating) -{ - if (!attacker->IsNPC() || RuleB(Combat, UseOldDamageIntervalRules)) - return Mob::GetMeleeMitDmg(attacker, damage, minhit, mit_rating, atk_rating); - int d = 10; - // floats for the rounding issues - float dmg_interval = (damage - minhit) / 19.0; - float dmg_bonus = minhit - dmg_interval; - float spellMeleeMit = (spellbonuses.MeleeMitigationEffect + itembonuses.MeleeMitigationEffect + aabonuses.MeleeMitigationEffect) / 100.0; - if (GetClass() == WARRIOR) - spellMeleeMit += 0.05; - dmg_bonus -= dmg_bonus * (itembonuses.MeleeMitigation / 100.0); - dmg_interval -= dmg_interval * spellMeleeMit; - - float mit_roll = zone->random.Real(0, mit_rating); - float atk_roll = zone->random.Real(0, atk_rating); - - if (atk_roll > mit_roll) { - float a_diff = atk_roll - mit_roll; - float thac0 = atk_rating * RuleR(Combat, ACthac0Factor); - float thac0cap = attacker->GetLevel() * 9 + 20; - if (thac0 > thac0cap) - thac0 = thac0cap; - - d += 10 * (a_diff / thac0); - } else if (mit_roll > atk_roll) { - float m_diff = mit_roll - atk_roll; - float thac20 = mit_rating * RuleR(Combat, ACthac20Factor); - float thac20cap = GetLevel() * 9 + 20; - if (thac20 > thac20cap) - thac20 = thac20cap; - - d -= 10 * (m_diff / thac20); - } - - if (d < 1) - d = 1; - else if (d > 20) - d = 20; - - return static_cast((dmg_bonus + dmg_interval * d)); -} - -//Returns the weapon damage against the input mob -//if we cannot hit the mob with the current weapon we will get a value less than or equal to zero -//Else we know we can hit. -//GetWeaponDamage(mob*, const Item_Struct*) is intended to be used for mobs or any other situation where we do not have a client inventory item -//GetWeaponDamage(mob*, const ItemInst*) is intended to be used for situations where we have a client inventory item -int Mob::GetWeaponDamage(Mob *against, const Item_Struct *weapon_item) { - int dmg = 0; - int banedmg = 0; - - //can't hit invulnerable stuff with weapons. - if(against->GetInvul() || against->GetSpecialAbility(IMMUNE_MELEE)){ - return 0; - } - - //check to see if our weapons or fists are magical. - if(against->GetSpecialAbility(IMMUNE_MELEE_NONMAGICAL)){ - if(weapon_item){ - if(weapon_item->Magic){ - dmg = weapon_item->Damage; - - //this is more for non weapon items, ex: boots for kick - //they don't have a dmg but we should be able to hit magical - dmg = dmg <= 0 ? 1 : dmg; - } - else - return 0; - } - else{ - if((GetClass() == MONK || GetClass() == BEASTLORD) && GetLevel() >= 30){ - dmg = GetMonkHandToHandDamage(); - } - else if(GetOwner() && GetLevel() >= RuleI(Combat, PetAttackMagicLevel)){ - //pets wouldn't actually use this but... - //it gives us an idea if we can hit due to the dual nature of this function - dmg = 1; - } - else if(GetSpecialAbility(SPECATK_MAGICAL)) - { - dmg = 1; - } - else - return 0; - } - } - else{ - if(weapon_item){ - dmg = weapon_item->Damage; - - dmg = dmg <= 0 ? 1 : dmg; - } - else{ - if(GetClass() == MONK || GetClass() == BEASTLORD){ - dmg = GetMonkHandToHandDamage(); - } - else{ - dmg = 1; - } - } - } - - int eledmg = 0; - if(!against->GetSpecialAbility(IMMUNE_MAGIC)){ - if(weapon_item && weapon_item->ElemDmgAmt){ - //we don't check resist for npcs here - eledmg = weapon_item->ElemDmgAmt; - dmg += eledmg; - } - } - - if(against->GetSpecialAbility(IMMUNE_MELEE_EXCEPT_BANE)){ - if(weapon_item){ - if(weapon_item->BaneDmgBody == against->GetBodyType()){ - banedmg += weapon_item->BaneDmgAmt; - } - - if(weapon_item->BaneDmgRace == against->GetRace()){ - banedmg += weapon_item->BaneDmgRaceAmt; - } - } - - if(!eledmg && !banedmg){ - if(!GetSpecialAbility(SPECATK_BANE)) - return 0; - else - return 1; - } - else - dmg += banedmg; - } - else{ - if(weapon_item){ - if(weapon_item->BaneDmgBody == against->GetBodyType()){ - banedmg += weapon_item->BaneDmgAmt; - } - - if(weapon_item->BaneDmgRace == against->GetRace()){ - banedmg += weapon_item->BaneDmgRaceAmt; - } - } - - dmg += (banedmg + eledmg); - } - - if(dmg <= 0){ - return 0; - } - else - return dmg; -} - -int Mob::GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate) -{ - int dmg = 0; - int banedmg = 0; - - if(!against || against->GetInvul() || against->GetSpecialAbility(IMMUNE_MELEE)){ - return 0; - } - - //check for items being illegally attained - if(weapon_item){ - const Item_Struct *mWeaponItem = weapon_item->GetItem(); - if(mWeaponItem){ - if(mWeaponItem->ReqLevel > GetLevel()){ - return 0; - } - - if(!weapon_item->IsEquipable(GetBaseRace(), GetClass())){ - return 0; - } - } - else{ - return 0; - } - } - - if(against->GetSpecialAbility(IMMUNE_MELEE_NONMAGICAL)){ - if(weapon_item){ - // check to see if the weapon is magic - bool MagicWeapon = false; - if(weapon_item->GetItem() && weapon_item->GetItem()->Magic) - MagicWeapon = true; - else { - if(spellbonuses.MagicWeapon || itembonuses.MagicWeapon) - MagicWeapon = true; - } - - if(MagicWeapon) { - - if(IsClient() && GetLevel() < weapon_item->GetItem()->RecLevel){ - dmg = CastToClient()->CalcRecommendedLevelBonus(GetLevel(), weapon_item->GetItem()->RecLevel, weapon_item->GetItem()->Damage); - } - else{ - dmg = weapon_item->GetItem()->Damage; - } - - for(int x = 0; x < EmuConstants::ITEM_COMMON_SIZE; x++){ - if(weapon_item->GetAugment(x) && weapon_item->GetAugment(x)->GetItem()){ - dmg += weapon_item->GetAugment(x)->GetItem()->Damage; - if (hate) *hate += weapon_item->GetAugment(x)->GetItem()->Damage + weapon_item->GetAugment(x)->GetItem()->ElemDmgAmt; - } - } - dmg = dmg <= 0 ? 1 : dmg; - } - else - return 0; - } - else{ - if((GetClass() == MONK || GetClass() == BEASTLORD) && 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)){ - dmg = 1; - } - else - return 0; - } - } - else{ - if(weapon_item){ - if(weapon_item->GetItem()){ - - if(IsClient() && GetLevel() < weapon_item->GetItem()->RecLevel){ - dmg = CastToClient()->CalcRecommendedLevelBonus(GetLevel(), weapon_item->GetItem()->RecLevel, weapon_item->GetItem()->Damage); - } - else{ - dmg = weapon_item->GetItem()->Damage; - } - - for (int x = 0; x < EmuConstants::ITEM_COMMON_SIZE; x++){ - if(weapon_item->GetAugment(x) && weapon_item->GetAugment(x)->GetItem()){ - dmg += weapon_item->GetAugment(x)->GetItem()->Damage; - if (hate) *hate += weapon_item->GetAugment(x)->GetItem()->Damage + weapon_item->GetAugment(x)->GetItem()->ElemDmgAmt; - } - } - dmg = dmg <= 0 ? 1 : dmg; - } - } - else{ - if(GetClass() == MONK || GetClass() == BEASTLORD){ - dmg = GetMonkHandToHandDamage(); - if (hate) *hate += dmg; - } - else{ - dmg = 1; - } - } - } - - int eledmg = 0; - if(!against->GetSpecialAbility(IMMUNE_MAGIC)){ - if(weapon_item && weapon_item->GetItem() && weapon_item->GetItem()->ElemDmgAmt){ - if(IsClient() && GetLevel() < weapon_item->GetItem()->RecLevel){ - eledmg = CastToClient()->CalcRecommendedLevelBonus(GetLevel(), weapon_item->GetItem()->RecLevel, weapon_item->GetItem()->ElemDmgAmt); - } - else{ - eledmg = weapon_item->GetItem()->ElemDmgAmt; - } - - if(eledmg) - { - eledmg = (eledmg * against->ResistSpell(weapon_item->GetItem()->ElemDmgType, 0, this) / 100); - } - } - - if(weapon_item){ - for (int x = 0; x < EmuConstants::ITEM_COMMON_SIZE; x++){ - if(weapon_item->GetAugment(x) && weapon_item->GetAugment(x)->GetItem()){ - if(weapon_item->GetAugment(x)->GetItem()->ElemDmgAmt) - eledmg += (weapon_item->GetAugment(x)->GetItem()->ElemDmgAmt * against->ResistSpell(weapon_item->GetAugment(x)->GetItem()->ElemDmgType, 0, this) / 100); - } - } - } - } - - if(against->GetSpecialAbility(IMMUNE_MELEE_EXCEPT_BANE)){ - if(weapon_item && weapon_item->GetItem()){ - if(weapon_item->GetItem()->BaneDmgBody == against->GetBodyType()){ - if(IsClient() && GetLevel() < weapon_item->GetItem()->RecLevel){ - banedmg += CastToClient()->CalcRecommendedLevelBonus(GetLevel(), weapon_item->GetItem()->RecLevel, weapon_item->GetItem()->BaneDmgAmt); - } - else{ - banedmg += weapon_item->GetItem()->BaneDmgAmt; - } - } - - if(weapon_item->GetItem()->BaneDmgRace == against->GetRace()){ - if(IsClient() && GetLevel() < weapon_item->GetItem()->RecLevel){ - banedmg += CastToClient()->CalcRecommendedLevelBonus(GetLevel(), weapon_item->GetItem()->RecLevel, weapon_item->GetItem()->BaneDmgRaceAmt); - } - else{ - banedmg += weapon_item->GetItem()->BaneDmgRaceAmt; - } - } - - for (int x = 0; x < EmuConstants::ITEM_COMMON_SIZE; x++){ - if(weapon_item->GetAugment(x) && weapon_item->GetAugment(x)->GetItem()){ - if(weapon_item->GetAugment(x)->GetItem()->BaneDmgBody == against->GetBodyType()){ - banedmg += weapon_item->GetAugment(x)->GetItem()->BaneDmgAmt; - } - - if(weapon_item->GetAugment(x)->GetItem()->BaneDmgRace == against->GetRace()){ - banedmg += weapon_item->GetAugment(x)->GetItem()->BaneDmgRaceAmt; - } - } - } - } - - if(!eledmg && !banedmg) - { - if(!GetSpecialAbility(SPECATK_BANE)) - return 0; - else - return 1; - } - else { - dmg += (banedmg + eledmg); - if (hate) *hate += banedmg; - } - } - else{ - if(weapon_item && weapon_item->GetItem()){ - if(weapon_item->GetItem()->BaneDmgBody == against->GetBodyType()){ - if(IsClient() && GetLevel() < weapon_item->GetItem()->RecLevel){ - banedmg += CastToClient()->CalcRecommendedLevelBonus(GetLevel(), weapon_item->GetItem()->RecLevel, weapon_item->GetItem()->BaneDmgAmt); - } - else{ - banedmg += weapon_item->GetItem()->BaneDmgAmt; - } - } - - if(weapon_item->GetItem()->BaneDmgRace == against->GetRace()){ - if(IsClient() && GetLevel() < weapon_item->GetItem()->RecLevel){ - banedmg += CastToClient()->CalcRecommendedLevelBonus(GetLevel(), weapon_item->GetItem()->RecLevel, weapon_item->GetItem()->BaneDmgRaceAmt); - } - else{ - banedmg += weapon_item->GetItem()->BaneDmgRaceAmt; - } - } - - for (int x = 0; x < EmuConstants::ITEM_COMMON_SIZE; x++){ - if(weapon_item->GetAugment(x) && weapon_item->GetAugment(x)->GetItem()){ - if(weapon_item->GetAugment(x)->GetItem()->BaneDmgBody == against->GetBodyType()){ - banedmg += weapon_item->GetAugment(x)->GetItem()->BaneDmgAmt; - } - - if(weapon_item->GetAugment(x)->GetItem()->BaneDmgRace == against->GetRace()){ - banedmg += weapon_item->GetAugment(x)->GetItem()->BaneDmgRaceAmt; - } - } - } - } - dmg += (banedmg + eledmg); - if (hate) *hate += banedmg; - } - - if(dmg <= 0){ - return 0; - } - else - return dmg; -} - -//note: throughout this method, setting `damage` to a negative is a way to -//stop the attack calculations -// IsFromSpell added to allow spell effects to use Attack. (Mainly for the Rampage AA right now.) -bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts) -{ - if (!other) { - SetTarget(nullptr); - LogFile->write(EQEMuLog::Error, "A null Mob object was passed to Client::Attack() for evaluation!"); - return false; - } - - if(!GetTarget()) - SetTarget(other); - - mlog(COMBAT__ATTACKS, "Attacking %s with hand %d %s", other?other->GetName():"(nullptr)", Hand, bRiposte?"(this is a riposte)":""); - - //SetAttackTimer(); - if ( - (IsCasting() && GetClass() != BARD && !IsFromSpell) - || other == nullptr - || ((IsClient() && CastToClient()->dead) || (other->IsClient() && other->CastToClient()->dead)) - || (GetHP() < 0) - || (!IsAttackAllowed(other)) - ) { - mlog(COMBAT__ATTACKS, "Attack canceled, invalid circumstances."); - return false; // Only bards can attack while casting - } - - if(DivineAura() && !GetGM()) {//cant attack while invulnerable unless your a gm - mlog(COMBAT__ATTACKS, "Attack canceled, Divine Aura is in effect."); - Message_StringID(MT_DefaultText, DIVINE_AURA_NO_ATK); //You can't attack while invulnerable! - return false; - } - - if (GetFeigned()) - return false; // Rogean: How can you attack while feigned? Moved up from Aggro Code. - - - ItemInst* weapon; - if (Hand == MainSecondary){ // Kaiyodo - Pick weapon from the attacking hand - weapon = GetInv().GetItem(MainSecondary); - OffHandAtk(true); - } - else{ - weapon = GetInv().GetItem(MainPrimary); - OffHandAtk(false); - } - - if(weapon != nullptr) { - if (!weapon->IsWeapon()) { - mlog(COMBAT__ATTACKS, "Attack canceled, Item %s (%d) is not a weapon.", weapon->GetItem()->Name, weapon->GetID()); - return(false); - } - mlog(COMBAT__ATTACKS, "Attacking with weapon: %s (%d)", weapon->GetItem()->Name, weapon->GetID()); - } else { - mlog(COMBAT__ATTACKS, "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); - mlog(COMBAT__ATTACKS, "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; - int weapon_damage = GetWeaponDamage(other, weapon, &hate); - 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){ - - //Berserker Berserk damage bonus - if(IsBerserk() && GetClass() == BERSERKER){ - int bonus = 3 + GetLevel()/10; //unverified - weapon_damage = weapon_damage * (100+bonus) / 100; - mlog(COMBAT__DAMAGE, "Berserker damage bonus increases DMG to %d", weapon_damage); - } - - //try a finishing blow.. if successful end the attack - if(TryFinishingBlow(other, skillinuse)) - return (true); - - int min_hit = 1; - int max_hit = (2*weapon_damage*GetDamageTable(skillinuse)) / 100; - - if(GetLevel() < 10 && max_hit > RuleI(Combat, HitCapPre10)) - max_hit = (RuleI(Combat, HitCapPre10)); - else if(GetLevel() < 20 && max_hit > RuleI(Combat, HitCapPre20)) - max_hit = (RuleI(Combat, HitCapPre20)); - - CheckIncreaseSkill(skillinuse, other, -15); - CheckIncreaseSkill(SkillOffense, other, -15); - - - // *************************************************************** - // *** Calculate the damage bonus, if applicable, for this hit *** - // *************************************************************** - -#ifndef EQEMU_NO_WEAPON_DAMAGE_BONUS - - // If you include the preprocessor directive "#define EQEMU_NO_WEAPON_DAMAGE_BONUS", that indicates that you do not - // want damage bonuses added to weapon damage at all. This feature was requested by ChaosSlayer on the EQEmu Forums. - // - // 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() ) - { - // 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 ); - - min_hit += (int) ucDamageBonus; - max_hit += (int) ucDamageBonus; - hate += ucDamageBonus; - } -#endif - //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 ); - - min_hit += (int) ucDamageBonus; - max_hit += (int) ucDamageBonus; - hate += ucDamageBonus; - } - } - - min_hit += min_hit * GetMeleeMinDamageMod_SE(skillinuse) / 100; - - if(max_hit < min_hit) - max_hit = min_hit; - - if(RuleB(Combat, UseIntervalAC)) - damage = max_hit; - else - damage = zone->random.Int(min_hit, max_hit); - - damage = mod_client_damage(damage, skillinuse, Hand, weapon, other); - - mlog(COMBAT__DAMAGE, "Damage calculated to %d (min %d, max %d, str %d, skill %d, DMG %d, lv %d)", - damage, min_hit, max_hit, GetSTR(), GetSkill(skillinuse), weapon_damage, mylevel); - - int hit_chance_bonus = 0; - - if(opts) { - damage *= opts->damage_percent; - damage += opts->damage_flat; - hate *= opts->hate_percent; - hate += opts->hate_flat; - hit_chance_bonus += opts->hit_chance; - } - - //check to see if we hit.. - if(!other->CheckHitChance(this, skillinuse, Hand, hit_chance_bonus)) { - mlog(COMBAT__ATTACKS, "Attack missed. Damage set to 0."); - damage = 0; - } else { //we hit, try to avoid it - other->AvoidDamage(this, damage); - other->MeleeMitigation(this, damage, min_hit, opts); - if(damage > 0) - CommonOutgoingHitSuccess(other, damage, skillinuse); - - mlog(COMBAT__DAMAGE, "Final damage after all reductions: %d", damage); - } - - //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 (bRiposte) 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; - OffhandRiposteFail *= -1; //Live uses a negative value for this. - - if (OffhandRiposteFail && - (OffhandRiposteFail > 99 || zone->random.Roll(OffhandRiposteFail))) { - damage = 0; // Counts as a miss - slippery_attack = true; - } else - DoRiposte(other); - if (IsDead()) return false; - } - else - DoRiposte(other); - if (IsDead()) return false; - } - } - - if (((damage < 0) || slippery_attack) && !bRiposte && !IsStrikethrough) { // Hack to still allow Strikethrough chance w/ Slippery Attacks AA - int32 bonusStrikeThrough = itembonuses.StrikeThrough + spellbonuses.StrikeThrough + aabonuses.StrikeThrough; - - if(bonusStrikeThrough && zone->random.Roll(bonusStrikeThrough)) { - Message_StringID(MT_StrikeThrough, STRIKETHROUGH_STRING); // You strike through your opponents defenses! - Attack(other, Hand, false, true); // Strikethrough only gives another attempted hit - return false; - } - } - } - 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 (!bRiposte) // Ripostes never generate any aggro. - other->AddToHateList(this, hate); - - /////////////////////////////////////////////////////////// - ////// Send Attack Damage - /////////////////////////////////////////////////////////// - other->Damage(this, damage, SPELL_UNKNOWN, skillinuse); - - if (IsDead()) return false; - - MeleeLifeTap(damage); - - if (damage > 0 && HasSkillProcSuccess() && other && other->GetHP() > 0) - TrySkillProc(other, skillinuse, 0, true, Hand); - - CommonBreakInvisible(); - - if(GetTarget()) - TriggerDefensiveProcs(weapon, other, Hand, damage); - - if (damage > 0) - return true; - - else - return false; -} - -//used by complete heal and #heal -void Mob::Heal() -{ - SetMaxHP(); - SendHPUpdate(); -} - -void Client::Damage(Mob* other, int32 damage, uint16 spell_id, SkillUseTypes attack_skill, bool avoidable, int8 buffslot, bool iBuffTic) -{ - if(dead || IsCorpse()) - return; - - if(spell_id==0) - spell_id = SPELL_UNKNOWN; - - if(spell_id!=0 && spell_id != SPELL_UNKNOWN && other && damage > 0) - { - if(other->IsNPC() && !other->IsPet()) - { - float npcspellscale = other->CastToNPC()->GetSpellScale(); - damage = ((float)damage * npcspellscale) / (float)100; - } - } - - // cut all PVP spell damage to 2/3 -solar - // Blasting ourselfs is considered PvP - //Don't do PvP mitigation if the caster is damaging himself - if(other && other->IsClient() && (other != this) && damage > 0) { - int PvPMitigation = 100; - if(attack_skill == SkillArchery) - PvPMitigation = 80; - else - PvPMitigation = 67; - damage = (damage * PvPMitigation) / 100; - } - - if(!ClientFinishedLoading()) - damage = -5; - - //do a majority of the work... - CommonDamage(other, damage, spell_id, attack_skill, avoidable, buffslot, iBuffTic); - - if (damage > 0) { - - if (spell_id == SPELL_UNKNOWN) - CheckIncreaseSkill(SkillDefense, other, -15); - } -} - -bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack_skill) -{ - if(!ClientFinishedLoading()) - return false; - - if(dead) - return false; //cant die more than once... - - if(!spell) - spell = SPELL_UNKNOWN; - - char buffer[48] = { 0 }; - snprintf(buffer, 47, "%d %d %d %d", killerMob ? killerMob->GetID() : 0, damage, spell, static_cast(attack_skill)); - if(parse->EventPlayer(EVENT_DEATH, this, buffer, 0) != 0) { - if(GetHP() < 0) { - SetHP(0); - } - return false; - } - - if(killerMob && killerMob->IsClient() && (spell != SPELL_UNKNOWN) && damage > 0) { - char val1[20]={0}; - entity_list.MessageClose_StringID(this, false, 100, MT_NonMelee, HIT_NON_MELEE, - killerMob->GetCleanName(), GetCleanName(), ConvertArray(damage, val1)); - } - - int exploss = 0; - mlog(COMBAT__HITS, "Fatal blow dealt by %s with %d damage, spell %d, skill %d", killerMob ? killerMob->GetName() : "Unknown", damage, spell, attack_skill); - - /* - #1: Send death packet to everyone - */ - uint8 killed_level = GetLevel(); - - SendLogoutPackets(); - - /* Make self become corpse packet */ - EQApplicationPacket app2(OP_BecomeCorpse, sizeof(BecomeCorpse_Struct)); - BecomeCorpse_Struct* bc = (BecomeCorpse_Struct*)app2.pBuffer; - bc->spawn_id = GetID(); - bc->x = GetX(); - bc->y = GetY(); - bc->z = GetZ(); - QueuePacket(&app2); - - /* Make Death Packet */ - EQApplicationPacket app(OP_Death, sizeof(Death_Struct)); - Death_Struct* d = (Death_Struct*)app.pBuffer; - d->spawn_id = GetID(); - d->killer_id = killerMob ? killerMob->GetID() : 0; - d->corpseid=GetID(); - d->bindzoneid = m_pp.binds[0].zoneId; - d->spell_id = spell == SPELL_UNKNOWN ? 0xffffffff : spell; - d->attack_skill = spell != SPELL_UNKNOWN ? 0xe7 : attack_skill; - d->damage = damage; - app.priority = 6; - entity_list.QueueClients(this, &app); - - /* - #2: figure out things that affect the player dying and mark them dead - */ - - InterruptSpell(); - SetPet(0); - SetHorseId(0); - dead = true; - - if(GetMerc()) { - GetMerc()->Suspend(); - } - - if (killerMob != nullptr) - { - if (killerMob->IsNPC()) { - parse->EventNPC(EVENT_SLAY, killerMob->CastToNPC(), this, "", 0); - - mod_client_death_npc(killerMob); - - uint16 emoteid = killerMob->GetEmoteID(); - if(emoteid != 0) - killerMob->CastToNPC()->DoNPCEmote(KILLEDPC,emoteid); - killerMob->TrySpellOnKill(killed_level,spell); - } - - if(killerMob->IsClient() && (IsDueling() || killerMob->CastToClient()->IsDueling())) { - SetDueling(false); - SetDuelTarget(0); - if (killerMob->IsClient() && killerMob->CastToClient()->IsDueling() && killerMob->CastToClient()->GetDuelTarget() == GetID()) - { - //if duel opponent killed us... - killerMob->CastToClient()->SetDueling(false); - killerMob->CastToClient()->SetDuelTarget(0); - entity_list.DuelMessage(killerMob,this,false); - - mod_client_death_duel(killerMob); - - } else { - //otherwise, we just died, end the duel. - Mob* who = entity_list.GetMob(GetDuelTarget()); - if(who && who->IsClient()) { - who->CastToClient()->SetDueling(false); - who->CastToClient()->SetDuelTarget(0); - } - } - } - } - - entity_list.RemoveFromTargets(this); - hate_list.RemoveEnt(this); - RemoveAutoXTargets(); - - - //remove ourself from all proximities - ClearAllProximities(); - - /* - #3: exp loss and corpse generation - */ - - // figure out if they should lose exp - if(RuleB(Character, UseDeathExpLossMult)){ - float GetNum [] = {0.005f,0.015f,0.025f,0.035f,0.045f,0.055f,0.065f,0.075f,0.085f,0.095f,0.110f }; - int Num = RuleI(Character, DeathExpLossMultiplier); - if((Num < 0) || (Num > 10)) - Num = 3; - float loss = GetNum[Num]; - exploss=(int)((float)GetEXP() * (loss)); //loose % of total XP pending rule (choose 0-10) - } - - if(!RuleB(Character, UseDeathExpLossMult)){ - exploss = (int)(GetLevel() * (GetLevel() / 18.0) * 12000); - } - - if( (GetLevel() < RuleI(Character, DeathExpLossLevel)) || (GetLevel() > RuleI(Character, DeathExpLossMaxLevel)) || IsBecomeNPC() ) - { - exploss = 0; - } - else if( killerMob ) - { - if( killerMob->IsClient() ) - { - exploss = 0; - } - else if( killerMob->GetOwner() && killerMob->GetOwner()->IsClient() ) - { - exploss = 0; - } - } - - if(spell != SPELL_UNKNOWN) - { - uint32 buff_count = GetMaxTotalSlots(); - for(uint16 buffIt = 0; buffIt < buff_count; buffIt++) - { - if(buffs[buffIt].spellid == spell && buffs[buffIt].client) - { - exploss = 0; // no exp loss for pvp dot - break; - } - } - } - - bool LeftCorpse = false; - - // now we apply the exp loss, unmem their spells, and make a corpse - // unless they're a GM (or less than lvl 10 - if(!GetGM()) - { - if(exploss > 0) { - int32 newexp = GetEXP(); - if(exploss > newexp) { - //lost more than we have... wtf.. - newexp = 1; - } else { - newexp -= exploss; - } - SetEXP(newexp, GetAAXP()); - //m_epp.perAA = 0; //reset to no AA exp on death. - } - - //this generates a lot of 'updates' to the client that the client does not need - BuffFadeNonPersistDeath(); - if((GetClientVersionBit() & BIT_SoFAndLater) && RuleB(Character, RespawnFromHover)) - UnmemSpellAll(true); - else - UnmemSpellAll(false); - - if(RuleB(Character, LeaveCorpses) && GetLevel() >= RuleI(Character, DeathItemLossLevel) || RuleB(Character, LeaveNakedCorpses)) - { - // creating the corpse takes the cash/items off the player too - Corpse *new_corpse = new Corpse(this, exploss); - - char tmp[20]; - database.GetVariable("ServerType", tmp, 9); - if(atoi(tmp)==1 && killerMob != nullptr && killerMob->IsClient()){ - char tmp2[10] = {0}; - database.GetVariable("PvPreward", tmp, 9); - int reward = atoi(tmp); - if(reward==3){ - database.GetVariable("PvPitem", tmp2, 9); - int pvpitem = atoi(tmp2); - if(pvpitem>0 && pvpitem<200000) - new_corpse->SetPlayerKillItemID(pvpitem); - } - else if(reward==2) - new_corpse->SetPlayerKillItemID(-1); - else if(reward==1) - new_corpse->SetPlayerKillItemID(1); - else - new_corpse->SetPlayerKillItemID(0); - if(killerMob->CastToClient()->isgrouped) { - Group* group = entity_list.GetGroupByClient(killerMob->CastToClient()); - if(group != 0) - { - for(int i=0;i<6;i++) - { - if(group->members[i] != nullptr) - { - new_corpse->AllowPlayerLoot(group->members[i],i); - } - } - } - } - } - - entity_list.AddCorpse(new_corpse, GetID()); - SetID(0); - - //send the become corpse packet to everybody else in the zone. - entity_list.QueueClients(this, &app2, true); - - LeftCorpse = true; - } - } else { - BuffFadeDetrimental(); - } - - /* - Finally, send em home - - We change the mob variables, not pp directly, because Save() will copy - from these and overwrite what we set in pp anyway - */ - - if(LeftCorpse && (GetClientVersionBit() & BIT_SoFAndLater) && RuleB(Character, RespawnFromHover)) - { - ClearDraggedCorpses(); - RespawnFromHoverTimer.Start(RuleI(Character, RespawnFromHoverTimer) * 1000); - SendRespawnBinds(); - } - else - { - if(isgrouped) - { - Group *g = GetGroup(); - if(g) - g->MemberZoned(this); - } - - Raid* r = entity_list.GetRaidByClient(this); - - if(r) - r->MemberZoned(this); - - dead_timer.Start(5000, true); - m_pp.zone_id = m_pp.binds[0].zoneId; - m_pp.zoneInstance = m_pp.binds[0].instance_id; - database.MoveCharacterToZone(this->CharacterID(), database.GetZoneName(m_pp.zone_id)); - Save(); - GoToDeath(); - } - - /* QS: PlayerLogDeaths */ - if (RuleB(QueryServ, PlayerLogDeaths)){ - const char * killer_name = ""; - if (killerMob && killerMob->GetCleanName()){ killer_name = killerMob->GetCleanName(); } - std::string event_desc = StringFormat("Died in zoneid:%i instid:%i by '%s', spellid:%i, damage:%i", this->GetZoneID(), this->GetInstanceID(), killer_name, spell, damage); - QServ->PlayerLogEvent(Player_Log_Deaths, this->CharacterID(), event_desc); - } - - parse->EventPlayer(EVENT_DEATH_COMPLETE, this, buffer, 0); - return true; -} - -bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts) -{ - int damage = 0; - - if (!other) { - SetTarget(nullptr); - LogFile->write(EQEMuLog::Error, "A null Mob object was passed to NPC::Attack() for evaluation!"); - return false; - } - - if(DivineAura()) - return(false); - - if(!GetTarget()) - SetTarget(other); - - //Check that we can attack before we calc heading and face our target - if (!IsAttackAllowed(other)) { - if (this->GetOwnerID()) - this->Say_StringID(NOT_LEGAL_TARGET); - if(other) { - if (other->IsClient()) - other->CastToClient()->RemoveXTarget(this, false); - RemoveFromHateList(other); - mlog(COMBAT__ATTACKS, "I am not allowed to attack %s", other->GetName()); - } - return false; - } - - FaceTarget(GetTarget()); - - SkillUseTypes skillinuse = SkillHandtoHand; - if (Hand == MainPrimary) { - skillinuse = static_cast(GetPrimSkill()); - OffHandAtk(false); - } - if (Hand == MainSecondary) { - skillinuse = static_cast(GetSecSkill()); - OffHandAtk(true); - } - - //figure out what weapon they are using, if any - const Item_Struct* weapon = nullptr; - if (Hand == MainPrimary && equipment[MainPrimary] > 0) - weapon = database.GetItem(equipment[MainPrimary]); - else if (equipment[MainSecondary]) - weapon = database.GetItem(equipment[MainSecondary]); - - //We dont factor much from the weapon into the attack. - //Just the skill type so it doesn't look silly using punching animations and stuff while wielding weapons - if(weapon) { - mlog(COMBAT__ATTACKS, "Attacking with weapon: %s (%d) (too bad im not using it for much)", weapon->Name, weapon->ID); - - if(Hand == MainSecondary && weapon->ItemType == ItemTypeShield){ - mlog(COMBAT__ATTACKS, "Attack with shield canceled."); - return false; - } - - switch(weapon->ItemType){ - case ItemType1HSlash: - skillinuse = Skill1HSlashing; - break; - case ItemType2HSlash: - skillinuse = Skill2HSlashing; - break; - case ItemType1HPiercing: - //skillinuse = Skill1HPiercing; - //break; - case ItemType2HPiercing: - skillinuse = Skill1HPiercing; // change to Skill2HPiercing once activated - break; - case ItemType1HBlunt: - skillinuse = Skill1HBlunt; - break; - case ItemType2HBlunt: - skillinuse = Skill2HBlunt; - break; - case ItemTypeBow: - skillinuse = SkillArchery; - break; - case ItemTypeLargeThrowing: - case ItemTypeSmallThrowing: - skillinuse = SkillThrowing; - break; - default: - skillinuse = SkillHandtoHand; - break; - } - } - - int weapon_damage = GetWeaponDamage(other, weapon); - - //do attack animation regardless of whether or not we can hit below - int16 charges = 0; - ItemInst weapon_inst(weapon, charges); - AttackAnimation(skillinuse, Hand, &weapon_inst); - - // Remove this once Skill2HPiercing is activated - //Work-around for there being no 2HP skill - We use 99 for the 2HB animation and 36 for pierce messages - if(skillinuse == 99) - skillinuse = static_cast(36); - - //basically "if not immune" then do the attack - if((weapon_damage) > 0) { - - //ele and bane dmg too - //NPCs add this differently than PCs - //if NPCs can't inheriently hit the target we don't add bane/magic dmg which isn't exactly the same as PCs - uint32 eleBane = 0; - if(weapon){ - if(weapon->BaneDmgBody == other->GetBodyType()){ - eleBane += weapon->BaneDmgAmt; - } - - if(weapon->BaneDmgRace == other->GetRace()){ - eleBane += weapon->BaneDmgRaceAmt; - } - - if(weapon->ElemDmgAmt){ - eleBane += (weapon->ElemDmgAmt * other->ResistSpell(weapon->ElemDmgType, 0, this) / 100); - } - } - - if(!RuleB(NPC, UseItemBonusesForNonPets)){ - if(!GetOwner()){ - eleBane = 0; - } - } - - uint8 otherlevel = other->GetLevel(); - uint8 mylevel = this->GetLevel(); - - otherlevel = otherlevel ? otherlevel : 1; - mylevel = mylevel ? mylevel : 1; - - //instead of calcing damage in floats lets just go straight to ints - if(RuleB(Combat, UseIntervalAC)) - damage = (max_dmg+eleBane); - else - damage = zone->random.Int((min_dmg+eleBane),(max_dmg+eleBane)); - - - //check if we're hitting above our max or below it. - if((min_dmg+eleBane) != 0 && damage < (min_dmg+eleBane)) { - mlog(COMBAT__DAMAGE, "Damage (%d) is below min (%d). Setting to min.", damage, (min_dmg+eleBane)); - damage = (min_dmg+eleBane); - } - if((max_dmg+eleBane) != 0 && damage > (max_dmg+eleBane)) { - mlog(COMBAT__DAMAGE, "Damage (%d) is above max (%d). Setting to max.", damage, (max_dmg+eleBane)); - damage = (max_dmg+eleBane); - } - - damage = mod_npc_damage(damage, skillinuse, Hand, weapon, other); - - int32 hate = damage; - if(IsPet()) - { - hate = hate * 100 / GetDamageTable(skillinuse); - } - - if(other->IsClient() && other->CastToClient()->IsSitting()) { - mlog(COMBAT__DAMAGE, "Client %s is sitting. Hitting for max damage (%d).", other->GetName(), (max_dmg+eleBane)); - damage = (max_dmg+eleBane); - damage += (itembonuses.HeroicSTR / 10) + (damage * other->GetSkillDmgTaken(skillinuse) / 100) + GetSkillDmgAmt(skillinuse); - - if(opts) { - damage *= opts->damage_percent; - damage += opts->damage_flat; - hate *= opts->hate_percent; - hate += opts->hate_flat; - } - - mlog(COMBAT__HITS, "Generating hate %d towards %s", hate, GetName()); - // now add done damage to the hate list - other->AddToHateList(this, hate); - - } else { - - int hit_chance_bonus = 0; - - if(opts) { - damage *= opts->damage_percent; - damage += opts->damage_flat; - hate *= opts->hate_percent; - hate += opts->hate_flat; - hit_chance_bonus += opts->hit_chance; - } - - if(!other->CheckHitChance(this, skillinuse, Hand, hit_chance_bonus)) { - damage = 0; //miss - } else { //hit, check for damage avoidance - other->AvoidDamage(this, damage); - other->MeleeMitigation(this, damage, min_dmg+eleBane, opts); - if(damage > 0) { - CommonOutgoingHitSuccess(other, damage, skillinuse); - } - mlog(COMBAT__HITS, "Generating hate %d towards %s", hate, GetName()); - // now add done damage to the hate list - if(damage > 0) - other->AddToHateList(this, hate); - else - other->AddToHateList(this, 0); - } - } - - mlog(COMBAT__DAMAGE, "Final damage against %s: %d", other->GetName(), damage); - - if(other->IsClient() && IsPet() && GetOwner()->IsClient()) { - //pets do half damage to clients in pvp - damage=damage/2; - } - } - else - damage = -5; - - //cant riposte a riposte - if (bRiposte && damage == -3) { - mlog(COMBAT__DAMAGE, "Riposte of riposte canceled."); - return false; - } - - if(GetHP() > 0 && !other->HasDied()) { - other->Damage(this, damage, SPELL_UNKNOWN, skillinuse, false); // Not avoidable client already had thier chance to Avoid - } else - return false; - - if (HasDied()) //killed by damage shield ect - return false; - - MeleeLifeTap(damage); - - CommonBreakInvisible(); - - //I doubt this works... - if (!GetTarget()) - return true; //We killed them - - if(!bRiposte && !other->HasDied()) { - TryWeaponProc(nullptr, weapon, other, Hand); //no weapon - - if (!other->HasDied()) - TrySpellProc(nullptr, weapon, other, Hand); - - if (damage > 0 && HasSkillProcSuccess() && !other->HasDied()) - TrySkillProc(other, skillinuse, 0, true, Hand); - } - - if(GetHP() > 0 && !other->HasDied()) - TriggerDefensiveProcs(nullptr, other, Hand, damage); - - // now check ripostes - if (damage == -3) { // riposting - DoRiposte(other); - } - - if (damage > 0) - return true; - - else - return false; -} - -void NPC::Damage(Mob* other, int32 damage, uint16 spell_id, SkillUseTypes attack_skill, bool avoidable, int8 buffslot, bool iBuffTic) { - if(spell_id==0) - spell_id = SPELL_UNKNOWN; - - //handle EVENT_ATTACK. Resets after we have not been attacked for 12 seconds - if(attacked_timer.Check()) - { - mlog(COMBAT__HITS, "Triggering EVENT_ATTACK due to attack by %s", other->GetName()); - parse->EventNPC(EVENT_ATTACK, this, other, "", 0); - } - attacked_timer.Start(CombatEventTimer_expire); - - if (!IsEngaged()) - zone->AddAggroMob(); - - if(GetClass() == LDON_TREASURE) - { - if(IsLDoNLocked() && GetLDoNLockedSkill() != LDoNTypeMechanical) - { - damage = -5; - } - else - { - if(IsLDoNTrapped()) - { - Message_StringID(13, LDON_ACCIDENT_SETOFF2); - SpellFinished(GetLDoNTrapSpellID(), other, 10, 0, -1, spells[GetLDoNTrapSpellID()].ResistDiff, false); - SetLDoNTrapSpellID(0); - SetLDoNTrapped(false); - SetLDoNTrapDetected(false); - } - } - } - - //do a majority of the work... - CommonDamage(other, damage, spell_id, attack_skill, avoidable, buffslot, iBuffTic); - - if(damage > 0) { - //see if we are gunna start fleeing - if(!IsPet()) CheckFlee(); - } -} - -bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack_skill) { - mlog(COMBAT__HITS, "Fatal blow dealt by %s with %d damage, spell %d, skill %d", killerMob->GetName(), damage, spell, attack_skill); - - Mob *oos = nullptr; - if(killerMob) { - oos = killerMob->GetOwnerOrSelf(); - - char buffer[48] = { 0 }; - snprintf(buffer, 47, "%d %d %d %d", killerMob ? killerMob->GetID() : 0, damage, spell, static_cast(attack_skill)); - if(parse->EventNPC(EVENT_DEATH, this, oos, buffer, 0) != 0) - { - if(GetHP() < 0) { - SetHP(0); - } - return false; - } - - if(killerMob && killerMob->IsClient() && (spell != SPELL_UNKNOWN) && damage > 0) { - char val1[20]={0}; - entity_list.MessageClose_StringID(this, false, 100, MT_NonMelee, HIT_NON_MELEE, - killerMob->GetCleanName(), GetCleanName(), ConvertArray(damage, val1)); - } - } else { - - char buffer[48] = { 0 }; - snprintf(buffer, 47, "%d %d %d %d", killerMob ? killerMob->GetID() : 0, damage, spell, static_cast(attack_skill)); - if(parse->EventNPC(EVENT_DEATH, this, nullptr, buffer, 0) != 0) - { - if(GetHP() < 0) { - SetHP(0); - } - return false; - } - } - - if (IsEngaged()) - { - zone->DelAggroMob(); -#if EQDEBUG >= 11 - LogFile->write(EQEMuLog::Debug,"NPC::Death() Mobs currently Aggro %i", zone->MobsAggroCount()); -#endif - } - SetHP(0); - SetPet(0); - - if (GetSwarmOwner()){ - Mob* owner = entity_list.GetMobID(GetSwarmOwner()); - if (owner) - owner->SetTempPetCount(owner->GetTempPetCount() - 1); - } - - Mob* killer = GetHateDamageTop(this); - - entity_list.RemoveFromTargets(this, p_depop); - - if(p_depop == true) - return false; - - HasAISpellEffects = false; - BuffFadeAll(); - uint8 killed_level = GetLevel(); - - EQApplicationPacket* app= new EQApplicationPacket(OP_Death,sizeof(Death_Struct)); - Death_Struct* d = (Death_Struct*)app->pBuffer; - d->spawn_id = GetID(); - d->killer_id = killerMob ? killerMob->GetID() : 0; - d->bindzoneid = 0; - d->spell_id = spell == SPELL_UNKNOWN ? 0xffffffff : spell; - d->attack_skill = SkillDamageTypes[attack_skill]; - d->damage = damage; - app->priority = 6; - entity_list.QueueClients(killerMob, app, false); - - if(respawn2) { - respawn2->DeathReset(1); - } - - if (killerMob) { - if(GetClass() != LDON_TREASURE) - hate_list.Add(killerMob, damage); - } - - safe_delete(app); - - Mob *give_exp = hate_list.GetDamageTop(this); - - if(give_exp == nullptr) - give_exp = killer; - - if(give_exp && give_exp->HasOwner()) { - - bool ownerInGroup = false; - if((give_exp->HasGroup() && give_exp->GetGroup()->IsGroupMember(give_exp->GetUltimateOwner())) - || (give_exp->IsPet() && (give_exp->GetOwner()->IsClient() - || ( give_exp->GetOwner()->HasGroup() && give_exp->GetOwner()->GetGroup()->IsGroupMember(give_exp->GetOwner()->GetUltimateOwner()))))) - ownerInGroup = true; - - give_exp = give_exp->GetUltimateOwner(); - -#ifdef BOTS - if(!RuleB(Bots, BotGroupXP) && !ownerInGroup) { - give_exp = nullptr; - } -#endif //BOTS - } - - int PlayerCount = 0; // QueryServ Player Counting - - Client *give_exp_client = nullptr; - if(give_exp && give_exp->IsClient()) - give_exp_client = give_exp->CastToClient(); - - bool IsLdonTreasure = (this->GetClass() == LDON_TREASURE); - if (give_exp_client && !IsCorpse()) - { - Group *kg = entity_list.GetGroupByClient(give_exp_client); - Raid *kr = entity_list.GetRaidByClient(give_exp_client); - - int32 finalxp = EXP_FORMULA; - finalxp = give_exp_client->mod_client_xp(finalxp, this); - - if(kr) - { - if(!IsLdonTreasure && MerchantType == 0) { - kr->SplitExp((finalxp), this); - if(killerMob && (kr->IsRaidMember(killerMob->GetName()) || kr->IsRaidMember(killerMob->GetUltimateOwner()->GetName()))) - killerMob->TrySpellOnKill(killed_level,spell); - } - /* Send the EVENT_KILLED_MERIT event for all raid members */ - for (int i = 0; i < MAX_RAID_MEMBERS; i++) { - if (kr->members[i].member != nullptr && kr->members[i].member->IsClient()) { // If Group Member is Client - Client *c = kr->members[i].member; - parse->EventNPC(EVENT_KILLED_MERIT, this, c, "killed", 0); - - if(RuleB(NPC, EnableMeritBasedFaction)) - c->SetFactionLevel(c->CharacterID(), GetNPCFactionID(), c->GetBaseClass(), c->GetBaseRace(), c->GetDeity()); - - mod_npc_killed_merit(kr->members[i].member); - - if(RuleB(TaskSystem, EnableTaskSystem)) - kr->members[i].member->UpdateTasksOnKill(GetNPCTypeID()); - PlayerCount++; - } - } - - // QueryServ Logging - Raid Kills - if(RuleB(QueryServ, PlayerLogNPCKills)){ - ServerPacket* pack = new ServerPacket(ServerOP_QSPlayerLogNPCKills, sizeof(QSPlayerLogNPCKill_Struct) + (sizeof(QSPlayerLogNPCKillsPlayers_Struct) * PlayerCount)); - PlayerCount = 0; - QSPlayerLogNPCKill_Struct* QS = (QSPlayerLogNPCKill_Struct*) pack->pBuffer; - QS->s1.NPCID = this->GetNPCTypeID(); - QS->s1.ZoneID = this->GetZoneID(); - QS->s1.Type = 2; // Raid Fight - for (int i = 0; i < MAX_RAID_MEMBERS; i++) { - if (kr->members[i].member != nullptr && kr->members[i].member->IsClient()) { // If Group Member is Client - Client *c = kr->members[i].member; - QS->Chars[PlayerCount].char_id = c->CharacterID(); - PlayerCount++; - } - } - worldserver.SendPacket(pack); // Send Packet to World - safe_delete(pack); - } - // End QueryServ Logging - - } - else if (give_exp_client->IsGrouped() && kg != nullptr) - { - if(!IsLdonTreasure && MerchantType == 0) { - kg->SplitExp((finalxp), this); - if(killerMob && (kg->IsGroupMember(killerMob->GetName()) || kg->IsGroupMember(killerMob->GetUltimateOwner()->GetName()))) - killerMob->TrySpellOnKill(killed_level,spell); - } - /* Send the EVENT_KILLED_MERIT event and update kill tasks - * for all group members */ - for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (kg->members[i] != nullptr && kg->members[i]->IsClient()) { // If Group Member is Client - Client *c = kg->members[i]->CastToClient(); - parse->EventNPC(EVENT_KILLED_MERIT, this, c, "killed", 0); - - if(RuleB(NPC, EnableMeritBasedFaction)) - c->SetFactionLevel(c->CharacterID(), GetNPCFactionID(), c->GetBaseClass(), c->GetBaseRace(), c->GetDeity()); - - mod_npc_killed_merit(c); - - if(RuleB(TaskSystem, EnableTaskSystem)) - c->UpdateTasksOnKill(GetNPCTypeID()); - - PlayerCount++; - } - } - - // QueryServ Logging - Group Kills - if(RuleB(QueryServ, PlayerLogNPCKills)){ - ServerPacket* pack = new ServerPacket(ServerOP_QSPlayerLogNPCKills, sizeof(QSPlayerLogNPCKill_Struct) + (sizeof(QSPlayerLogNPCKillsPlayers_Struct) * PlayerCount)); - PlayerCount = 0; - QSPlayerLogNPCKill_Struct* QS = (QSPlayerLogNPCKill_Struct*) pack->pBuffer; - QS->s1.NPCID = this->GetNPCTypeID(); - QS->s1.ZoneID = this->GetZoneID(); - QS->s1.Type = 1; // Group Fight - for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (kg->members[i] != nullptr && kg->members[i]->IsClient()) { // If Group Member is Client - Client *c = kg->members[i]->CastToClient(); - QS->Chars[PlayerCount].char_id = c->CharacterID(); - PlayerCount++; - } - } - worldserver.SendPacket(pack); // Send Packet to World - safe_delete(pack); - } - // End QueryServ Logging - } - else - { - if(!IsLdonTreasure && MerchantType == 0) { - int conlevel = give_exp->GetLevelCon(GetLevel()); - if (conlevel != CON_GREEN) - { - if(!GetOwner() || (GetOwner() && !GetOwner()->IsClient())) - { - give_exp_client->AddEXP((finalxp), conlevel); - if(killerMob && (killerMob->GetID() == give_exp_client->GetID() || killerMob->GetUltimateOwner()->GetID() == give_exp_client->GetID())) - killerMob->TrySpellOnKill(killed_level,spell); - } - } - } - /* Send the EVENT_KILLED_MERIT event */ - parse->EventNPC(EVENT_KILLED_MERIT, this, give_exp_client, "killed", 0); - - if(RuleB(NPC, EnableMeritBasedFaction)) - give_exp_client->SetFactionLevel(give_exp_client->CharacterID(), GetNPCFactionID(), give_exp_client->GetBaseClass(), - give_exp_client->GetBaseRace(), give_exp_client->GetDeity()); - - mod_npc_killed_merit(give_exp_client); - - if(RuleB(TaskSystem, EnableTaskSystem)) - give_exp_client->UpdateTasksOnKill(GetNPCTypeID()); - - // QueryServ Logging - Solo - if(RuleB(QueryServ, PlayerLogNPCKills)){ - ServerPacket* pack = new ServerPacket(ServerOP_QSPlayerLogNPCKills, sizeof(QSPlayerLogNPCKill_Struct) + (sizeof(QSPlayerLogNPCKillsPlayers_Struct) * 1)); - QSPlayerLogNPCKill_Struct* QS = (QSPlayerLogNPCKill_Struct*) pack->pBuffer; - QS->s1.NPCID = this->GetNPCTypeID(); - QS->s1.ZoneID = this->GetZoneID(); - QS->s1.Type = 0; // Solo Fight - Client *c = give_exp_client; - QS->Chars[0].char_id = c->CharacterID(); - PlayerCount++; - worldserver.SendPacket(pack); // Send Packet to World - safe_delete(pack); - } - // End QueryServ Logging - } - } - - //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()))) - { - if(killer != 0) - { - if(killer->GetOwner() != 0 && killer->GetOwner()->IsClient()) - killer = killer->GetOwner(); - - if(!killer->CastToClient()->GetGM() && killer->IsClient()) - this->CheckMinMaxLevel(killer); - } - entity_list.RemoveFromAutoXTargets(this); - uint16 emoteid = this->GetEmoteID(); - Corpse* corpse = new Corpse(this, &itemlist, GetNPCTypeID(), &NPCTypedata,level>54?RuleI(NPC,MajorNPCCorpseDecayTimeMS):RuleI(NPC,MinorNPCCorpseDecayTimeMS)); - entity_list.LimitRemoveNPC(this); - entity_list.AddCorpse(corpse, GetID()); - - entity_list.UnMarkNPC(GetID()); - entity_list.RemoveNPC(GetID()); - this->SetID(0); - if(killer != 0 && emoteid != 0) - corpse->CastToNPC()->DoNPCEmote(AFTERDEATH, emoteid); - if(killer != 0 && killer->IsClient()) { - corpse->AllowPlayerLoot(killer, 0); - if(killer->IsGrouped()) { - Group* group = entity_list.GetGroupByClient(killer->CastToClient()); - if(group != 0) { - for(int i=0;i<6;i++) { // Doesnt work right, needs work - if(group->members[i] != nullptr) { - corpse->AllowPlayerLoot(group->members[i],i); - } - } - } - } - else if(killer->IsRaidGrouped()){ - Raid* r = entity_list.GetRaidByClient(killer->CastToClient()); - if(r){ - int i = 0; - for(int x = 0; x < MAX_RAID_MEMBERS; x++) - { - switch(r->GetLootType()) - { - case 0: - case 1: - if(r->members[x].member && r->members[x].IsRaidLeader){ - corpse->AllowPlayerLoot(r->members[x].member, i); - i++; - } - break; - case 2: - if(r->members[x].member && r->members[x].IsRaidLeader){ - corpse->AllowPlayerLoot(r->members[x].member, i); - i++; - } - else if(r->members[x].member && r->members[x].IsGroupLeader){ - corpse->AllowPlayerLoot(r->members[x].member, i); - i++; - } - break; - case 3: - if(r->members[x].member && r->members[x].IsLooter){ - corpse->AllowPlayerLoot(r->members[x].member, i); - i++; - } - break; - case 4: - if(r->members[x].member) - { - corpse->AllowPlayerLoot(r->members[x].member, i); - i++; - } - break; - } - } - } - } - } - - if(zone && zone->adv_data) - { - ServerZoneAdventureDataReply_Struct *sr = (ServerZoneAdventureDataReply_Struct*)zone->adv_data; - if(sr->type == Adventure_Kill) - { - zone->DoAdventureCountIncrease(); - } - else if(sr->type == Adventure_Assassinate) - { - if(sr->data_id == GetNPCTypeID()) - { - zone->DoAdventureCountIncrease(); - } - else - { - zone->DoAdventureAssassinationCountIncrease(); - } - } - } - } - else - entity_list.RemoveFromXTargets(this); - - // Parse quests even if we're killed by an NPC - if(oos) { - mod_npc_killed(oos); - - uint16 emoteid = this->GetEmoteID(); - if(emoteid != 0) - this->DoNPCEmote(ONDEATH, emoteid); - if(oos->IsNPC()) - { - parse->EventNPC(EVENT_NPC_SLAY, oos->CastToNPC(), this, "", 0); - uint16 emoteid = oos->GetEmoteID(); - if(emoteid != 0) - oos->CastToNPC()->DoNPCEmote(KILLEDNPC, emoteid); - killerMob->TrySpellOnKill(killed_level, spell); - } - } - - WipeHateList(); - p_depop = true; - if(killerMob && killerMob->GetTarget() == this) //we can kill things without having them targeted - killerMob->SetTarget(nullptr); //via AE effects and such.. - - entity_list.UpdateFindableNPCState(this, true); - - char buffer[48] = { 0 }; - snprintf(buffer, 47, "%d %d %d %d", killerMob ? killerMob->GetID() : 0, damage, spell, static_cast(attack_skill)); - parse->EventNPC(EVENT_DEATH_COMPLETE, this, oos, buffer, 0); - return true; -} - -void Mob::AddToHateList(Mob* other, int32 hate, int32 damage, bool iYellForHelp, bool bFrenzy, bool iBuffTic) { - - assert(other != nullptr); - - if (other == this) - return; - - if(damage < 0){ - hate = 1; - } - - bool wasengaged = IsEngaged(); - Mob* owner = other->GetOwner(); - Mob* mypet = this->GetPet(); - Mob* myowner = this->GetOwner(); - Mob* targetmob = this->GetTarget(); - - if(other){ - AddRampage(other); - int hatemod = 100 + other->spellbonuses.hatemod + other->itembonuses.hatemod + other->aabonuses.hatemod; - - int32 shieldhatemod = other->spellbonuses.ShieldEquipHateMod + other->itembonuses.ShieldEquipHateMod + other->aabonuses.ShieldEquipHateMod; - - if (shieldhatemod && other->HasShieldEquiped()) - hatemod += shieldhatemod; - - if(hatemod < 1) - hatemod = 1; - hate = ((hate * (hatemod))/100); - } - - if(IsPet() && GetOwner() && GetOwner()->GetAA(aaPetDiscipline) && IsHeld() && !IsFocused()) { //ignore aggro if hold and !focus - return; - } - - if(IsPet() && GetOwner() && GetOwner()->GetAA(aaPetDiscipline) && IsHeld() && GetOwner()->GetAA(aaAdvancedPetDiscipline) >= 1 && IsFocused()) { - if (!targetmob) - return; - } - - if (other->IsNPC() && (other->IsPet() || other->CastToNPC()->GetSwarmOwner() > 0)) - TryTriggerOnValueAmount(false, false, false, true); - - if(IsClient() && !IsAIControlled()) - return; - - if(IsFamiliar() || GetSpecialAbility(IMMUNE_AGGRO)) - return; - - if (other == myowner) - return; - - if(other->GetSpecialAbility(IMMUNE_AGGRO_ON)) - return; - - if(GetSpecialAbility(NPC_TUNNELVISION)) { - int tv_mod = GetSpecialAbilityParam(NPC_TUNNELVISION, 0); - - Mob *top = GetTarget(); - if(top && top != other) { - if(tv_mod) { - float tv = tv_mod / 100.0f; - hate *= tv; - } else { - hate *= RuleR(Aggro, TunnelVisionAggroMod); - } - } - } - - auto otherPosition = xyz_location(other->GetX(), other->GetY(), other->GetZ()); - if(IsNPC() && CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) { - if(!zone->watermap->InLiquid(otherPosition)) { - return; - } - } - // first add self - - // The damage on the hate list is used to award XP to the killer. This check is to prevent Killstealing. - // e.g. Mob has 5000 hit points, Player A melees it down to 500 hp, Player B executes a headshot (10000 damage). - // If we add 10000 damage, Player B would get the kill credit, so we only award damage credit to player B of the - // amount of HP the mob had left. - // - if(damage > GetHP()) - damage = GetHP(); - - if (spellbonuses.ImprovedTaunt[1] && (GetLevel() < spellbonuses.ImprovedTaunt[0]) - && other && (buffs[spellbonuses.ImprovedTaunt[2]].casterid != other->GetID())) - hate = (hate*spellbonuses.ImprovedTaunt[1])/100; - - hate_list.Add(other, hate, damage, bFrenzy, !iBuffTic); - - if(other->IsClient()) - other->CastToClient()->AddAutoXTarget(this); - -#ifdef BOTS - // if other is a bot, add the bots client to the hate list - if(other->IsBot()) { - if(other->CastToBot()->GetBotOwner() && other->CastToBot()->GetBotOwner()->CastToClient()->GetFeigned()) { - AddFeignMemory(other->CastToBot()->GetBotOwner()->CastToClient()); - } - else { - if(!hate_list.IsOnHateList(other->CastToBot()->GetBotOwner())) - hate_list.Add(other->CastToBot()->GetBotOwner(), 0, 0, false, true); - } - } -#endif //BOTS - - - // if other is a merc, add the merc client to the hate list - if(other->IsMerc()) { - if(other->CastToMerc()->GetMercOwner() && other->CastToMerc()->GetMercOwner()->CastToClient()->GetFeigned()) { - AddFeignMemory(other->CastToMerc()->GetMercOwner()->CastToClient()); - } - else { - if(!hate_list.IsOnHateList(other->CastToMerc()->GetMercOwner())) - hate_list.Add(other->CastToMerc()->GetMercOwner(), 0, 0, false, true); - } - } //MERC - - // then add pet owner if there's one - if (owner) { // Other is a pet, add him and it - // EverHood 6/12/06 - // Can't add a feigned owner to hate list - if(owner->IsClient() && owner->CastToClient()->GetFeigned()) { - //they avoid hate due to feign death... - } else { - // cb:2007-08-17 - // owner must get on list, but he's not actually gained any hate yet - if(!owner->GetSpecialAbility(IMMUNE_AGGRO)) - { - hate_list.Add(owner, 0, 0, false, !iBuffTic); - if(owner->IsClient()) - owner->CastToClient()->AddAutoXTarget(this); - } - } - } - - if (mypet && (!(GetAA(aaPetDiscipline) && mypet->IsHeld()))) { // I have a pet, add other to it - if(!mypet->IsFamiliar() && !mypet->GetSpecialAbility(IMMUNE_AGGRO)) - mypet->hate_list.Add(other, 0, 0, bFrenzy); - } else if (myowner) { // I am a pet, add other to owner if it's NPC/LD - if (myowner->IsAIControlled() && !myowner->GetSpecialAbility(IMMUNE_AGGRO)) - myowner->hate_list.Add(other, 0, 0, bFrenzy); - } - - if (other->GetTempPetCount()) - entity_list.AddTempPetsToHateList(other, this, bFrenzy); - - if (!wasengaged) { - if(IsNPC() && other->IsClient() && other->CastToClient()) - parse->EventNPC(EVENT_AGGRO, this->CastToNPC(), other, "", 0); - AI_Event_Engaged(other, iYellForHelp); - } -} - -// solar: this is called from Damage() when 'this' is attacked by 'other. -// 'this' is the one being attacked -// 'other' is the attacker -// a damage shield causes damage (or healing) to whoever attacks the wearer -// a reverse ds causes damage to the wearer whenever it attack someone -// given this, a reverse ds must be checked each time the wearer is attacking -// and not when they're attacked -//a damage shield on a spell is a negative value but on an item it's a positive value so add the spell value and subtract the item value to get the end ds value -void Mob::DamageShield(Mob* attacker, bool spell_ds) { - - if(!attacker || this == attacker) - return; - - int DS = 0; - int rev_ds = 0; - uint16 spellid = 0; - - if(!spell_ds) - { - DS = spellbonuses.DamageShield; - rev_ds = attacker->spellbonuses.ReverseDamageShield; - - if(spellbonuses.DamageShieldSpellID != 0 && spellbonuses.DamageShieldSpellID != SPELL_UNKNOWN) - spellid = spellbonuses.DamageShieldSpellID; - } - else { - DS = spellbonuses.SpellDamageShield; - rev_ds = 0; - // This ID returns "you are burned", seemed most appropriate for spell DS - spellid = 2166; - } - - if(DS == 0 && rev_ds == 0) - return; - - mlog(COMBAT__HITS, "Applying Damage Shield of value %d to %s", DS, attacker->GetName()); - - //invert DS... spells yield negative values for a true damage shield - if(DS < 0) { - if(!spell_ds) { - - DS += aabonuses.DamageShield; //Live AA - coat of thistles. (negative value) - DS -= itembonuses.DamageShield; //+Damage Shield should only work when you already have a DS spell - - //Spell data for damage shield mitigation shows a negative value for spells for clients and positive - //value for spells that effect pets. Unclear as to why. For now will convert all positive to be consistent. - if (attacker->IsOffHandAtk()){ - int32 mitigation = attacker->itembonuses.DSMitigationOffHand + - attacker->spellbonuses.DSMitigationOffHand + - attacker->aabonuses.DSMitigationOffHand; - DS -= DS*mitigation/100; - } - DS -= DS * attacker->itembonuses.DSMitigation / 100; - } - attacker->Damage(this, -DS, spellid, SkillAbjuration/*hackish*/, false); - //we can assume there is a spell now - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct)); - CombatDamage_Struct* cds = (CombatDamage_Struct*)outapp->pBuffer; - cds->target = attacker->GetID(); - cds->source = GetID(); - cds->type = spellbonuses.DamageShieldType; - cds->spellid = 0x0; - cds->damage = DS; - entity_list.QueueCloseClients(this, outapp); - safe_delete(outapp); - } else if (DS > 0 && !spell_ds) { - //we are healing the attacker... - attacker->HealDamage(DS); - //TODO: send a packet??? - } - - //Reverse DS - //this is basically a DS, but the spell is on the attacker, not the attackee - //if we've gotten to this point, we know we know "attacker" hit "this" (us) for damage & we aren't invulnerable - uint16 rev_ds_spell_id = SPELL_UNKNOWN; - - if(spellbonuses.ReverseDamageShieldSpellID != 0 && spellbonuses.ReverseDamageShieldSpellID != SPELL_UNKNOWN) - rev_ds_spell_id = spellbonuses.ReverseDamageShieldSpellID; - - if(rev_ds < 0) { - mlog(COMBAT__HITS, "Applying Reverse Damage Shield of value %d to %s", rev_ds, attacker->GetName()); - attacker->Damage(this, -rev_ds, rev_ds_spell_id, SkillAbjuration/*hackish*/, false); //"this" (us) will get the hate, etc. not sure how this works on Live, but it'll works for now, and tanks will love us for this - //do we need to send a damage packet here also? - } -} - -uint8 Mob::GetWeaponDamageBonus( const Item_Struct *Weapon ) -{ - // This function calculates and returns the damage bonus for the weapon identified by the parameter "Weapon". - // Modified 9/21/2008 by Cantus - - - // Assert: This function should only be called for hits by the mainhand, as damage bonuses apply only to the - // weapon in the primary slot. Be sure to check that Hand == MainPrimary before calling. - - // Assert: The caller should ensure that Weapon is actually a weapon before calling this function. - // The ItemInst::IsWeapon() method can be used to quickly determine this. - - // Assert: This function should not be called if the player's level is below 28, as damage bonuses do not begin - // to apply until level 28. - - // Assert: This function should not be called unless the player is a melee class, as casters do not receive a damage bonus. - - - if( Weapon == nullptr || Weapon->ItemType == ItemType1HSlash || Weapon->ItemType == ItemType1HBlunt || Weapon->ItemType == ItemTypeMartial || Weapon->ItemType == ItemType1HPiercing ) - { - // The weapon in the player's main (primary) hand is a one-handed weapon, or there is no item equipped at all. - // - // According to player posts on Allakhazam, 1H damage bonuses apply to bare fists (nothing equipped in the mainhand, - // as indicated by Weapon == nullptr). - // - // The following formula returns the correct damage bonus for all 1H weapons: - - return (uint8) ((GetLevel() - 25) / 3); - } - - // If we've gotten to this point, the weapon in the mainhand is a two-handed weapon. - // Calculating damage bonuses for 2H weapons is more complicated, as it's based on PC level AND the delay of the weapon. - // The formula to calculate 2H bonuses is HIDEOUS. It's a huge conglomeration of ternary operators and multiple operations. - // - // The following is a hybrid approach. In cases where the Level and Delay merit a formula that does not use many operators, - // the formula is used. In other cases, lookup tables are used for speed. - // Though the following code may look bloated and ridiculous, it's actually a very efficient way of calculating these bonuses. - - // Player Level is used several times in the code below, so save it into a variable. - // If GetLevel() were an ordinary function, this would DEFINITELY make sense, as it'd cut back on all of the function calling - // overhead involved with multiple calls to GetLevel(). But in this case, GetLevel() is a simple, inline accessor method. - // So it probably doesn't matter. If anyone knows for certain that there is no overhead involved with calling GetLevel(), - // as I suspect, then please feel free to delete the following line, and replace all occurences of "ucPlayerLevel" with "GetLevel()". - uint8 ucPlayerLevel = (uint8) GetLevel(); - - - // The following may look cleaner, and would certainly be easier to understand, if it was - // a simple 53x150 cell matrix. - // - // However, that would occupy 7,950 Bytes of memory (7.76 KB), and would likely result - // in "thrashing the cache" when performing lookups. - // - // Initially, I thought the best approach would be to reverse-engineer the formula used by - // Sony/Verant to calculate these 2H weapon damage bonuses. But the more than Reno and I - // worked on figuring out this formula, the more we're concluded that the formula itself ugly - // (that is, it contains so many operations and conditionals that it's fairly CPU intensive). - // Because of that, we're decided that, in most cases, a lookup table is the most efficient way - // to calculate these damage bonuses. - // - // The code below is a hybrid between a pure formulaic approach and a pure, brute-force - // lookup table. In cases where a formula is the best bet, I use a formula. In other places - // where a formula would be ugly, I use a lookup table in the interests of speed. - - - if( Weapon->Delay <= 27 ) - { - // Damage Bonuses for all 2H weapons with delays of 27 or less are identical. - // They are the same as the damage bonus would be for a corresponding 1H weapon, plus one. - // This formula applies to all levels 28-80, and will probably continue to apply if - - // the level cap on Live ever is increased beyond 80. - - return (ucPlayerLevel - 22) / 3; - } - - - if( ucPlayerLevel == 65 && Weapon->Delay <= 59 ) - { - // Consider these two facts: - // * Level 65 is the maximum level on many EQ Emu servers. - // * If you listed the levels of all characters logged on to a server, odds are that the number you'll - // see most frequently is level 65. That is, there are more level 65 toons than any other single level. - // - // Therefore, if we can optimize this function for level 65 toons, we're speeding up the server! - // - // With that goal in mind, I create an array of Damage Bonuses for level 65 characters wielding 2H weapons with - // delays between 28 and 59 (inclusive). I suspect that this one small lookup array will therefore handle - // many of the calls to this function. - - static const uint8 ucLevel65DamageBonusesForDelays28to59[] = {35, 35, 36, 36, 37, 37, 38, 38, 39, 39, 40, 40, 42, 42, 42, 45, 45, 47, 48, 49, 49, 51, 51, 52, 53, 54, 54, 56, 56, 57, 58, 59}; - - return ucLevel65DamageBonusesForDelays28to59[Weapon->Delay-28]; - } - - - if( ucPlayerLevel > 65 ) - { - if( ucPlayerLevel > 80 ) - { - // As level 80 is currently the highest achievable level on Live, we only include - // damage bonus information up to this level. - // - // If there is a custom EQEmu server that allows players to level beyond 80, the - // damage bonus for their 2H weapons will simply not increase beyond their damage - // bonus at level 80. - - ucPlayerLevel = 80; - } - - // Lucy does not list a chart of damage bonuses for players levels 66+, - // so my original version of this function just applied the level 65 damage - // bonus for level 66+ toons. That sucked for higher level toons, as their - // 2H weapons stopped ramping up in DPS as they leveled past 65. - // - // Thanks to the efforts of two guys, this is no longer the case: - // - // Janusd (Zetrakyl) ran a nifty query against the PEQ item database to list - // the name of an example 2H weapon that represents each possible unique 2H delay. - // - // Romai then wrote an excellent script to automatically look up each of those - // weapons, open the Lucy item page associated with it, and iterate through all - // levels in the range 66 - 80. He saved the damage bonus for that weapon for - // each level, and that forms the basis of the lookup tables below. - - if( Weapon->Delay <= 59 ) - { - static const uint8 ucDelay28to59Levels66to80[32][15]= - { - /* Level: */ - /* 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 */ - - {36, 37, 38, 39, 41, 42, 43, 44, 45, 47, 49, 49, 49, 50, 53}, /* Delay = 28 */ - {36, 38, 38, 39, 42, 43, 43, 45, 46, 48, 49, 50, 51, 52, 54}, /* Delay = 29 */ - {37, 38, 39, 40, 43, 43, 44, 46, 47, 48, 50, 51, 52, 53, 55}, /* Delay = 30 */ - {37, 39, 40, 40, 43, 44, 45, 46, 47, 49, 51, 52, 52, 52, 54}, /* Delay = 31 */ - {38, 39, 40, 41, 44, 45, 45, 47, 48, 48, 50, 52, 53, 55, 57}, /* Delay = 32 */ - {38, 40, 41, 41, 44, 45, 46, 48, 49, 50, 52, 53, 54, 56, 58}, /* Delay = 33 */ - {39, 40, 41, 42, 45, 46, 47, 48, 49, 51, 53, 54, 55, 57, 58}, /* Delay = 34 */ - {39, 41, 42, 43, 46, 46, 47, 49, 50, 52, 54, 55, 56, 57, 59}, /* Delay = 35 */ - {40, 41, 42, 43, 46, 47, 48, 50, 51, 53, 55, 55, 56, 58, 60}, /* Delay = 36 */ - {40, 42, 43, 44, 47, 48, 49, 50, 51, 53, 55, 56, 57, 59, 61}, /* Delay = 37 */ - {41, 42, 43, 44, 47, 48, 49, 51, 52, 54, 56, 57, 58, 60, 62}, /* Delay = 38 */ - {41, 43, 44, 45, 48, 49, 50, 52, 53, 55, 57, 58, 59, 61, 63}, /* Delay = 39 */ - {43, 45, 46, 47, 50, 51, 52, 54, 55, 57, 59, 60, 61, 63, 65}, /* Delay = 40 */ - {43, 45, 46, 47, 50, 51, 52, 54, 55, 57, 59, 60, 61, 63, 65}, /* Delay = 41 */ - {44, 46, 47, 48, 51, 52, 53, 55, 56, 58, 60, 61, 62, 64, 66}, /* Delay = 42 */ - {46, 48, 49, 50, 53, 54, 55, 58, 59, 61, 63, 64, 65, 67, 69}, /* Delay = 43 */ - {47, 49, 50, 51, 54, 55, 56, 58, 59, 61, 64, 65, 66, 68, 70}, /* Delay = 44 */ - {48, 50, 51, 52, 56, 57, 58, 60, 61, 63, 65, 66, 68, 70, 72}, /* Delay = 45 */ - {50, 52, 53, 54, 57, 58, 59, 62, 63, 65, 67, 68, 69, 71, 74}, /* Delay = 46 */ - {50, 52, 53, 55, 58, 59, 60, 62, 63, 66, 68, 69, 70, 72, 74}, /* Delay = 47 */ - {51, 53, 54, 55, 58, 60, 61, 63, 64, 66, 69, 69, 71, 73, 75}, /* Delay = 48 */ - {52, 54, 55, 57, 60, 61, 62, 65, 66, 68, 70, 71, 73, 75, 77}, /* Delay = 49 */ - {53, 55, 56, 57, 61, 62, 63, 65, 67, 69, 71, 72, 74, 76, 78}, /* Delay = 50 */ - {53, 55, 57, 58, 61, 62, 64, 66, 67, 69, 72, 73, 74, 77, 79}, /* Delay = 51 */ - {55, 57, 58, 59, 63, 64, 65, 68, 69, 71, 74, 75, 76, 78, 81}, /* Delay = 52 */ - {57, 55, 59, 60, 63, 65, 66, 68, 70, 72, 74, 76, 77, 79, 82}, /* Delay = 53 */ - {56, 58, 59, 61, 64, 65, 67, 69, 70, 73, 75, 76, 78, 80, 82}, /* Delay = 54 */ - {57, 59, 61, 62, 66, 67, 68, 71, 72, 74, 77, 78, 80, 82, 84}, /* Delay = 55 */ - {58, 60, 61, 63, 66, 68, 69, 71, 73, 75, 78, 79, 80, 83, 85}, /* Delay = 56 */ - - /* Important Note: Janusd's search for 2H weapons did not find */ - /* any 2H weapon with a delay of 57. Therefore the values below */ - /* are interpolated, not exact! */ - {59, 61, 62, 64, 67, 69, 70, 72, 74, 76, 77, 78, 81, 84, 86}, /* Delay = 57 INTERPOLATED */ - - {60, 62, 63, 65, 68, 70, 71, 74, 75, 78, 80, 81, 83, 85, 88}, /* Delay = 58 */ - - /* Important Note: Janusd's search for 2H weapons did not find */ - /* any 2H weapon with a delay of 59. Therefore the values below */ - /* are interpolated, not exact! */ - {60, 62, 64, 65, 69, 70, 72, 74, 76, 78, 81, 82, 84, 86, 89}, /* Delay = 59 INTERPOLATED */ - }; - - return ucDelay28to59Levels66to80[Weapon->Delay-28][ucPlayerLevel-66]; - } - else - { - // Delay is 60+ - - const static uint8 ucDelayOver59Levels66to80[6][15] = - { - /* Level: */ - /* 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 */ - - {61, 63, 65, 66, 70, 71, 73, 75, 77, 79, 82, 83, 85, 87, 90}, /* Delay = 60 */ - {65, 68, 69, 71, 75, 76, 78, 80, 82, 85, 87, 89, 91, 93, 96}, /* Delay = 65 */ - - /* Important Note: Currently, the only 2H weapon with a delay */ - /* of 66 is not player equippable (it's None/None). So I'm */ - /* leaving it commented out to keep this table smaller. */ - //{66, 68, 70, 71, 75, 77, 78, 81, 83, 85, 88, 90, 91, 94, 97}, /* Delay = 66 */ - - {70, 72, 74, 76, 80, 81, 83, 86, 88, 88, 90, 95, 97, 99, 102}, /* Delay = 70 */ - {82, 85, 87, 89, 89, 94, 98, 101, 103, 106, 109, 111, 114, 117, 120}, /* Delay = 85 */ - {90, 93, 96, 98, 103, 105, 107, 111, 113, 116, 120, 122, 125, 128, 131}, /* Delay = 95 */ - - /* Important Note: Currently, the only 2H weapons with delay */ - /* 100 are GM-only items purchased from vendors in Sunset Home */ - /* (cshome). Because they are highly unlikely to be used in */ - /* combat, I'm commenting it out to keep the table smaller. */ - //{95, 98, 101, 103, 108, 110, 113, 116, 119, 122, 126, 128, 131, 134, 138},/* Delay = 100 */ - - {136, 140, 144, 148, 154, 157, 161, 166, 170, 174, 179, 183, 187, 191, 196} /* Delay = 150 */ - }; - - if( Weapon->Delay < 65 ) - { - return ucDelayOver59Levels66to80[0][ucPlayerLevel-66]; - } - else if( Weapon->Delay < 70 ) - { - return ucDelayOver59Levels66to80[1][ucPlayerLevel-66]; - } - else if( Weapon->Delay < 85 ) - { - return ucDelayOver59Levels66to80[2][ucPlayerLevel-66]; - } - else if( Weapon->Delay < 95 ) - { - return ucDelayOver59Levels66to80[3][ucPlayerLevel-66]; - } - else if( Weapon->Delay < 150 ) - { - return ucDelayOver59Levels66to80[4][ucPlayerLevel-66]; - } - else - { - return ucDelayOver59Levels66to80[5][ucPlayerLevel-66]; - } - } - } - - - // If we've gotten to this point in the function without hitting a return statement, - // we know that the character's level is between 28 and 65, and that the 2H weapon's - // delay is 28 or higher. - - // The Damage Bonus values returned by this function (in the level 28-65 range) are - // based on a table of 2H Weapon Damage Bonuses provided by Lucy at the following address: - // http://lucy.allakhazam.com/dmgbonus.html - - if( Weapon->Delay <= 39 ) - { - if( ucPlayerLevel <= 53) - { - // The Damage Bonus for all 2H weapons with delays between 28 and 39 (inclusive) is the same for players level 53 and below... - static const uint8 ucDelay28to39LevelUnder54[] = {1, 1, 2, 3, 3, 3, 4, 5, 5, 6, 6, 6, 8, 8, 8, 9, 9, 10, 11, 11, 11, 12, 13, 14, 16, 17}; - - // As a note: The following formula accurately calculates damage bonuses for 2H weapons with delays in the range 28-39 (inclusive) - // for characters levels 28-50 (inclusive): - // return ( (ucPlayerLevel - 22) / 3 ) + ( (ucPlayerLevel - 25) / 5 ); - // - // However, the small lookup array used above is actually much faster. So we'll just use it instead of the formula - // - // (Thanks to Reno for helping figure out the above formula!) - - return ucDelay28to39LevelUnder54[ucPlayerLevel-28]; - } - else - { - // Use a matrix to look up the damage bonus for 2H weapons with delays between 28 and 39 wielded by characters level 54 and above. - static const uint8 ucDelay28to39Level54to64[12][11] = - { - /* Level: */ - /* 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 */ - - {17, 21, 21, 23, 25, 26, 28, 30, 31, 31, 33}, /* Delay = 28 */ - {17, 21, 22, 23, 25, 26, 29, 30, 31, 32, 34}, /* Delay = 29 */ - {18, 21, 22, 23, 25, 27, 29, 31, 32, 32, 34}, /* Delay = 30 */ - {18, 21, 22, 23, 25, 27, 29, 31, 32, 33, 34}, /* Delay = 31 */ - {18, 21, 22, 24, 26, 27, 30, 32, 32, 33, 35}, /* Delay = 32 */ - {18, 21, 22, 24, 26, 27, 30, 32, 33, 34, 35}, /* Delay = 33 */ - {18, 22, 22, 24, 26, 28, 30, 32, 33, 34, 36}, /* Delay = 34 */ - {18, 22, 23, 24, 26, 28, 31, 33, 34, 34, 36}, /* Delay = 35 */ - {18, 22, 23, 25, 27, 28, 31, 33, 34, 35, 37}, /* Delay = 36 */ - {18, 22, 23, 25, 27, 29, 31, 33, 34, 35, 37}, /* Delay = 37 */ - {18, 22, 23, 25, 27, 29, 32, 34, 35, 36, 38}, /* Delay = 38 */ - {18, 22, 23, 25, 27, 29, 32, 34, 35, 36, 38} /* Delay = 39 */ - }; - - return ucDelay28to39Level54to64[Weapon->Delay-28][ucPlayerLevel-54]; - } - } - else if( Weapon->Delay <= 59 ) - { - if( ucPlayerLevel <= 52 ) - { - if( Weapon->Delay <= 45 ) - { - static const uint8 ucDelay40to45Levels28to52[6][25] = - { - /* Level: */ - /* 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52 */ - - {2, 2, 3, 4, 4, 4, 5, 6, 6, 7, 7, 7, 9, 9, 9, 10, 10, 11, 12, 12, 12, 13, 14, 16, 18}, /* Delay = 40 */ - {2, 2, 3, 4, 4, 4, 5, 6, 6, 7, 7, 7, 9, 9, 9, 10, 10, 11, 12, 12, 12, 13, 14, 16, 18}, /* Delay = 41 */ - {2, 2, 3, 4, 4, 4, 5, 6, 6, 7, 7, 7, 9, 9, 9, 10, 10, 11, 12, 12, 12, 13, 14, 16, 18}, /* Delay = 42 */ - {4, 4, 5, 6, 6, 6, 7, 8, 8, 9, 9, 9, 11, 11, 11, 12, 12, 13, 14, 14, 14, 15, 16, 18, 20}, /* Delay = 43 */ - {4, 4, 5, 6, 6, 6, 7, 8, 8, 9, 9, 9, 11, 11, 11, 12, 12, 13, 14, 14, 14, 15, 16, 18, 20}, /* Delay = 44 */ - {5, 5, 6, 7, 7, 7, 8, 9, 9, 10, 10, 10, 12, 12, 12, 13, 13, 14, 15, 15, 15, 16, 17, 19, 21} /* Delay = 45 */ - }; - - return ucDelay40to45Levels28to52[Weapon->Delay-40][ucPlayerLevel-28]; - } - else - { - static const uint8 ucDelay46Levels28to52[] = {6, 6, 7, 8, 8, 8, 9, 10, 10, 11, 11, 11, 13, 13, 13, 14, 14, 15, 16, 16, 16, 17, 18, 20, 22}; - - return ucDelay46Levels28to52[ucPlayerLevel-28] + ((Weapon->Delay-46) / 3); - } - } - else - { - // Player is in the level range 53 - 64 - - // Calculating damage bonus for 2H weapons with a delay between 40 and 59 (inclusive) involves, unforunately, a brute-force matrix lookup. - static const uint8 ucDelay40to59Levels53to64[20][37] = - { - /* Level: */ - /* 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 */ - - {19, 20, 24, 25, 27, 29, 31, 34, 36, 37, 38, 40}, /* Delay = 40 */ - {19, 20, 24, 25, 27, 29, 31, 34, 36, 37, 38, 40}, /* Delay = 41 */ - {19, 20, 24, 25, 27, 29, 31, 34, 36, 37, 38, 40}, /* Delay = 42 */ - {21, 22, 26, 27, 29, 31, 33, 37, 39, 40, 41, 43}, /* Delay = 43 */ - {21, 22, 26, 27, 29, 32, 34, 37, 39, 40, 41, 43}, /* Delay = 44 */ - {22, 23, 27, 28, 31, 33, 35, 38, 40, 42, 43, 45}, /* Delay = 45 */ - {23, 24, 28, 30, 32, 34, 36, 40, 42, 43, 44, 46}, /* Delay = 46 */ - {23, 24, 29, 30, 32, 34, 37, 40, 42, 43, 44, 47}, /* Delay = 47 */ - {23, 24, 29, 30, 32, 35, 37, 40, 43, 44, 45, 47}, /* Delay = 48 */ - {24, 25, 30, 31, 34, 36, 38, 42, 44, 45, 46, 49}, /* Delay = 49 */ - {24, 26, 30, 31, 34, 36, 39, 42, 44, 46, 47, 49}, /* Delay = 50 */ - {24, 26, 30, 31, 34, 36, 39, 42, 45, 46, 47, 49}, /* Delay = 51 */ - {25, 27, 31, 33, 35, 38, 40, 44, 46, 47, 49, 51}, /* Delay = 52 */ - {25, 27, 31, 33, 35, 38, 40, 44, 46, 48, 49, 51}, /* Delay = 53 */ - {26, 27, 32, 33, 36, 38, 41, 44, 47, 48, 49, 52}, /* Delay = 54 */ - {27, 28, 33, 34, 37, 39, 42, 46, 48, 50, 51, 53}, /* Delay = 55 */ - {27, 28, 33, 34, 37, 40, 42, 46, 49, 50, 51, 54}, /* Delay = 56 */ - {27, 28, 33, 34, 37, 40, 43, 46, 49, 50, 52, 54}, /* Delay = 57 */ - {28, 29, 34, 36, 39, 41, 44, 48, 50, 52, 53, 56}, /* Delay = 58 */ - {28, 29, 34, 36, 39, 41, 44, 48, 51, 52, 54, 56} /* Delay = 59 */ - }; - - return ucDelay40to59Levels53to64[Weapon->Delay-40][ucPlayerLevel-53]; - } - } - else - { - // The following table allows us to look up Damage Bonuses for weapons with delays greater than or equal to 60. - // - // There aren't a lot of 2H weapons with a delay greater than 60. In fact, both a database and Lucy search run by janusd confirm - // that the only unique 2H delays greater than 60 are: 65, 70, 85, 95, and 150. - // - // To be fair, there are also weapons with delays of 66 and 100. But they are either not equippable (None/None), or are - // only available to GMs from merchants in Sunset Home (cshome). In order to keep this table "lean and mean", I will not - // include the values for delays 66 and 100. If they ever are wielded, the 66 delay weapon will use the 65 delay bonuses, - // and the 100 delay weapon will use the 95 delay bonuses. So it's not a big deal. - // - // Still, if someone in the future decides that they do want to include them, here are the tables for these two delays: - // - // {12, 12, 13, 14, 14, 14, 15, 16, 16, 17, 17, 17, 19, 19, 19, 20, 20, 21, 22, 22, 22, 23, 24, 26, 29, 30, 32, 37, 39, 42, 45, 48, 53, 55, 57, 59, 61, 64} /* Delay = 66 */ - // {24, 24, 25, 26, 26, 26, 27, 28, 28, 29, 29, 29, 31, 31, 31, 32, 32, 33, 34, 34, 34, 35, 36, 39, 43, 45, 48, 55, 57, 62, 66, 71, 77, 80, 83, 85, 89, 92} /* Delay = 100 */ - // - // In case there are 2H weapons added in the future with delays other than those listed above (and until the damage bonuses - // associated with that new delay are added to this function), this function is designed to do the following: - // - // For weapons with delays in the range 60-64, use the Damage Bonus that would apply to a 2H weapon with delay 60. - // For weapons with delays in the range 65-69, use the Damage Bonus that would apply to a 2H weapon with delay 65 - // For weapons with delays in the range 70-84, use the Damage Bonus that would apply to a 2H weapon with delay 70. - // For weapons with delays in the range 85-94, use the Damage Bonus that would apply to a 2H weapon with delay 85. - // For weapons with delays in the range 95-149, use the Damage Bonus that would apply to a 2H weapon with delay 95. - // For weapons with delays 150 or higher, use the Damage Bonus that would apply to a 2H weapon with delay 150. - - static const uint8 ucDelayOver59Levels28to65[6][38] = - { - /* Level: */ - /* 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64. 65 */ - - {10, 10, 11, 12, 12, 12, 13, 14, 14, 15, 15, 15, 17, 17, 17, 18, 18, 19, 20, 20, 20, 21, 22, 24, 27, 28, 30, 35, 36, 39, 42, 45, 49, 51, 53, 54, 57, 59}, /* Delay = 60 */ - {12, 12, 13, 14, 14, 14, 15, 16, 16, 17, 17, 17, 19, 19, 19, 20, 20, 21, 22, 22, 22, 23, 24, 26, 29, 30, 32, 37, 39, 42, 45, 48, 52, 55, 57, 58, 61, 63}, /* Delay = 65 */ - {14, 14, 15, 16, 16, 16, 17, 18, 18, 19, 19, 19, 21, 21, 21, 22, 22, 23, 24, 24, 24, 25, 26, 28, 31, 33, 35, 40, 42, 45, 48, 52, 56, 59, 61, 62, 65, 68}, /* Delay = 70 */ - {19, 19, 20, 21, 21, 21, 22, 23, 23, 24, 24, 24, 26, 26, 26, 27, 27, 28, 29, 29, 29, 30, 31, 34, 37, 39, 41, 47, 49, 54, 57, 61, 66, 69, 72, 74, 77, 80}, /* Delay = 85 */ - {22, 22, 23, 24, 24, 24, 25, 26, 26, 27, 27, 27, 29, 29, 29, 30, 30, 31, 32, 32, 32, 33, 34, 37, 40, 43, 45, 52, 54, 59, 62, 67, 73, 76, 79, 81, 84, 88}, /* Delay = 95 */ - {40, 40, 41, 42, 42, 42, 43, 44, 44, 45, 45, 45, 47, 47, 47, 48, 48, 49, 50, 50, 50, 51, 52, 56, 61, 65, 69, 78, 82, 89, 94, 102, 110, 115, 119, 122, 127, 132} /* Delay = 150 */ - }; - - if( Weapon->Delay < 65 ) - { - return ucDelayOver59Levels28to65[0][ucPlayerLevel-28]; - } - else if( Weapon->Delay < 70 ) - { - return ucDelayOver59Levels28to65[1][ucPlayerLevel-28]; - } - else if( Weapon->Delay < 85 ) - { - return ucDelayOver59Levels28to65[2][ucPlayerLevel-28]; - } - else if( Weapon->Delay < 95 ) - { - return ucDelayOver59Levels28to65[3][ucPlayerLevel-28]; - } - else if( Weapon->Delay < 150 ) - { - return ucDelayOver59Levels28to65[4][ucPlayerLevel-28]; - } - else - { - return ucDelayOver59Levels28to65[5][ucPlayerLevel-28]; - } - } -} - -int Mob::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 - 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, - 8, 8, 8, 8, 8, 9, 9, 9, 9, 9,10,10,10,10,10,11,11,11,11,11, - 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 - - if (IsClient() && CastToClient()->GetItemIDAt(12) == 10652) - return(9); - else - { - int Level = GetLevel(); - if (Level > 65) - return(19); - else - return damage[Level]; - } -} - -int Mob::GetMonkHandToHandDelay(void) -{ - // Kaiyodo - Determine a monk's fist delay. Table data from www.monkly-business.com - // saved as static array - this should speed this function up considerably - static int delayshuman[66] = { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 - 99,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, - 36,36,36,36,36,35,35,35,35,35,34,34,34,34,34,33,33,33,33,33, - 32,32,32,32,32,31,31,31,31,31,30,30,30,29,29,29,28,28,28,27, - 26,24,22,20,20,20 }; - static int delaysiksar[66] = { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 - 99,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, - 36,36,36,36,36,36,36,36,36,36,35,35,35,35,35,34,34,34,34,34, - 33,33,33,33,33,32,32,32,32,32,31,31,31,30,30,30,29,29,29,28, - 27,24,22,20,20,20 }; - - // Have a look to see if we have epic fists on - if (IsClient() && CastToClient()->GetItemIDAt(12) == 10652) - return(16); - else - { - int Level = GetLevel(); - if (GetRace() == HUMAN) - { - if (Level > 65) - return(24); - else - return delayshuman[Level]; - } - else //heko: iksar table - { - if (Level > 65) - return(25); - else - return delaysiksar[Level]; - } - } -} - - -int32 Mob::ReduceDamage(int32 damage) -{ - if(damage <= 0) - return damage; - - int32 slot = -1; - bool DisableMeleeRune = false; - - if (spellbonuses.NegateAttacks[0]){ - slot = spellbonuses.NegateAttacks[1]; - if(slot >= 0) { - if(--buffs[slot].numhits == 0) { - - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot , true); - } - - if (spellbonuses.NegateAttacks[2] && (damage > spellbonuses.NegateAttacks[2])) - damage -= spellbonuses.NegateAttacks[2]; - else - return -6; - } - } - - //Only mitigate if damage is above the minimium specified. - if (spellbonuses.MeleeThresholdGuard[0]){ - slot = spellbonuses.MeleeThresholdGuard[1]; - - if (slot >= 0 && (damage > spellbonuses.MeleeThresholdGuard[2])) - { - DisableMeleeRune = true; - int damage_to_reduce = damage * spellbonuses.MeleeThresholdGuard[0] / 100; - if(damage_to_reduce >= buffs[slot].melee_rune) - { - mlog(SPELLS__EFFECT_VALUES, "Mob::ReduceDamage SE_MeleeThresholdGuard %d damage negated, %d" - " damage remaining, fading buff.", damage_to_reduce, buffs[slot].melee_rune); - damage -= buffs[slot].melee_rune; - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot); - } - else - { - mlog(SPELLS__EFFECT_VALUES, "Mob::ReduceDamage SE_MeleeThresholdGuard %d damage negated, %d" - " damage remaining.", damage_to_reduce, buffs[slot].melee_rune); - buffs[slot].melee_rune = (buffs[slot].melee_rune - damage_to_reduce); - damage -= damage_to_reduce; - } - } - } - - - if (spellbonuses.MitigateMeleeRune[0] && !DisableMeleeRune){ - slot = spellbonuses.MitigateMeleeRune[1]; - if(slot >= 0) - { - int damage_to_reduce = damage * spellbonuses.MitigateMeleeRune[0] / 100; - - if (spellbonuses.MitigateMeleeRune[2] && (damage_to_reduce > spellbonuses.MitigateMeleeRune[2])) - damage_to_reduce = spellbonuses.MitigateMeleeRune[2]; - - if(spellbonuses.MitigateMeleeRune[3] && (damage_to_reduce >= buffs[slot].melee_rune)) - { - mlog(SPELLS__EFFECT_VALUES, "Mob::ReduceDamage SE_MitigateMeleeDamage %d damage negated, %d" - " damage remaining, fading buff.", damage_to_reduce, buffs[slot].melee_rune); - damage -= buffs[slot].melee_rune; - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot); - } - else - { - mlog(SPELLS__EFFECT_VALUES, "Mob::ReduceDamage SE_MitigateMeleeDamage %d damage negated, %d" - " damage remaining.", damage_to_reduce, buffs[slot].melee_rune); - - if (spellbonuses.MitigateMeleeRune[3]) - buffs[slot].melee_rune = (buffs[slot].melee_rune - damage_to_reduce); - - damage -= damage_to_reduce; - } - } - } - - if(damage < 1) - return -6; - - if (spellbonuses.MeleeRune[0] && spellbonuses.MeleeRune[1] >= 0) - damage = RuneAbsorb(damage, SE_Rune); - - if(damage < 1) - return -6; - - return(damage); -} - -int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTic, Mob* attacker) -{ - if(damage <= 0) - return damage; - - bool DisableSpellRune = false; - int32 slot = -1; - - // See if we block the spell outright first - if (!iBuffTic && spellbonuses.NegateAttacks[0]){ - slot = spellbonuses.NegateAttacks[1]; - if(slot >= 0) { - if(--buffs[slot].numhits == 0) { - - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot , true); - } - - if (spellbonuses.NegateAttacks[2] && (damage > spellbonuses.NegateAttacks[2])) - damage -= spellbonuses.NegateAttacks[2]; - else - return 0; - } - } - - // If this is a DoT, use DoT Shielding... - if(iBuffTic) { - damage -= (damage * itembonuses.DoTShielding / 100); - - if (spellbonuses.MitigateDotRune[0]){ - slot = spellbonuses.MitigateDotRune[1]; - if(slot >= 0) - { - int damage_to_reduce = damage * spellbonuses.MitigateDotRune[0] / 100; - - if (spellbonuses.MitigateDotRune[2] && (damage_to_reduce > spellbonuses.MitigateDotRune[2])) - damage_to_reduce = spellbonuses.MitigateDotRune[2]; - - if(spellbonuses.MitigateDotRune[3] && (damage_to_reduce >= buffs[slot].dot_rune)) - { - damage -= buffs[slot].dot_rune; - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot); - } - else - { - if (spellbonuses.MitigateDotRune[3]) - buffs[slot].dot_rune = (buffs[slot].dot_rune - damage_to_reduce); - - damage -= damage_to_reduce; - } - } - } - } - - // This must be a DD then so lets apply Spell Shielding and runes. - else - { - // Reduce damage by the Spell Shielding first so that the runes don't take the raw damage. - damage -= (damage * itembonuses.SpellShield / 100); - - - //Only mitigate if damage is above the minimium specified. - if (spellbonuses.SpellThresholdGuard[0]){ - slot = spellbonuses.SpellThresholdGuard[1]; - - if (slot >= 0 && (damage > spellbonuses.MeleeThresholdGuard[2])) - { - DisableSpellRune = true; - int damage_to_reduce = damage * spellbonuses.SpellThresholdGuard[0] / 100; - if(damage_to_reduce >= buffs[slot].magic_rune) - { - damage -= buffs[slot].magic_rune; - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot); - } - else - { - buffs[slot].melee_rune = (buffs[slot].magic_rune - damage_to_reduce); - damage -= damage_to_reduce; - } - } - } - - - // Do runes now. - if (spellbonuses.MitigateSpellRune[0] && !DisableSpellRune){ - slot = spellbonuses.MitigateSpellRune[1]; - if(slot >= 0) - { - int damage_to_reduce = damage * spellbonuses.MitigateSpellRune[0] / 100; - - if (spellbonuses.MitigateSpellRune[2] && (damage_to_reduce > spellbonuses.MitigateSpellRune[2])) - damage_to_reduce = spellbonuses.MitigateSpellRune[2]; - - if(spellbonuses.MitigateSpellRune[3] && (damage_to_reduce >= buffs[slot].magic_rune)) - { - mlog(SPELLS__EFFECT_VALUES, "Mob::ReduceDamage SE_MitigateSpellDamage %d damage negated, %d" - " damage remaining, fading buff.", damage_to_reduce, buffs[slot].magic_rune); - damage -= buffs[slot].magic_rune; - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot); - } - else - { - mlog(SPELLS__EFFECT_VALUES, "Mob::ReduceDamage SE_MitigateMeleeDamage %d damage negated, %d" - " damage remaining.", damage_to_reduce, buffs[slot].magic_rune); - - if (spellbonuses.MitigateSpellRune[3]) - buffs[slot].magic_rune = (buffs[slot].magic_rune - damage_to_reduce); - - damage -= damage_to_reduce; - } - } - } - - if(damage < 1) - return 0; - - //Regular runes absorb spell damage (except dots) - Confirmed on live. - if (spellbonuses.MeleeRune[0] && spellbonuses.MeleeRune[1] >= 0) - damage = RuneAbsorb(damage, SE_Rune); - - if (spellbonuses.AbsorbMagicAtt[0] && spellbonuses.AbsorbMagicAtt[1] >= 0) - damage = RuneAbsorb(damage, SE_AbsorbMagicAtt); - - if(damage < 1) - return 0; - } - return damage; -} - -int32 Mob::ReduceAllDamage(int32 damage) -{ - if(damage <= 0) - return damage; - - if(spellbonuses.ManaAbsorbPercentDamage[0]) { - int32 mana_reduced = damage * spellbonuses.ManaAbsorbPercentDamage[0] / 100; - if (GetMana() >= mana_reduced){ - damage -= mana_reduced; - SetMana(GetMana() - mana_reduced); - TryTriggerOnValueAmount(false, true); - } - } - - CheckNumHitsRemaining(NUMHIT_IncomingDamage); - - return(damage); -} - -bool Mob::HasProcs() const -{ - for (int i = 0; i < MAX_PROCS; i++) - if (PermaProcs[i].spellID != SPELL_UNKNOWN || SpellProcs[i].spellID != SPELL_UNKNOWN) - return true; - return false; -} - -bool Mob::HasDefensiveProcs() const -{ - for (int i = 0; i < MAX_PROCS; i++) - if (DefensiveProcs[i].spellID != SPELL_UNKNOWN) - return true; - return false; -} - -bool Mob::HasSkillProcs() const -{ - - for(int i = 0; i < MAX_SKILL_PROCS; i++){ - if (spellbonuses.SkillProc[i] || itembonuses.SkillProc[i] || aabonuses.SkillProc[i]) - return true; - } - return false; -} - -bool Mob::HasSkillProcSuccess() const -{ - for(int i = 0; i < MAX_SKILL_PROCS; i++){ - if (spellbonuses.SkillProcSuccess[i] || itembonuses.SkillProcSuccess[i] || aabonuses.SkillProcSuccess[i]) - return true; - } - return false; -} - -bool Mob::HasRangedProcs() const -{ - for (int i = 0; i < MAX_PROCS; i++) - if (RangedProcs[i].spellID != SPELL_UNKNOWN) - return true; - return false; -} - -bool Client::CheckDoubleAttack(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; - - if(!HasSkill(SkillDoubleAttack) && !bonusGiveDA) - return false; - - float chance = 0.0f; - - uint16 skill = GetSkill(SkillDoubleAttack); - - 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; - else - 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 - //AA's can also give triple attack skill over cap. (ie Burst of Power) NOTE: Skill ID in spell data is 76 (Triple Attack) - //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; - chance *= 0.2f; //Baseline chance is 20% of your double attack chance. - chance *= float(100.0f+triple_bonus)/100.0f; //Apply modifiers. - } - - if(zone->random.Roll(chance)) - return true; - - return false; -} - -bool Client::CheckDoubleRangedAttack() { -<<<<<<< HEAD - -======= ->>>>>>> master - int32 chance = spellbonuses.DoubleRangedAttack + itembonuses.DoubleRangedAttack + aabonuses.DoubleRangedAttack; - - if(chance && zone->random.Roll(chance)) - return true; - - return false; -} - -void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, const SkillUseTypes skill_used, bool &avoidable, const int8 buffslot, const bool iBuffTic) { - // This method is called with skill_used=ABJURE for Damage Shield damage. - bool FromDamageShield = (skill_used == SkillAbjuration); - - mlog(COMBAT__HITS, "Applying damage %d done by %s with skill %d and spell %d, avoidable? %s, is %sa buff tic in slot %d", - damage, attacker?attacker->GetName():"NOBODY", skill_used, spell_id, avoidable?"yes":"no", iBuffTic?"":"not ", buffslot); - - if (GetInvul() || DivineAura()) { - mlog(COMBAT__DAMAGE, "Avoiding %d damage due to invulnerability.", damage); - damage = -5; - } - - if( spell_id != SPELL_UNKNOWN || attacker == nullptr ) - avoidable = false; - - // only apply DS if physical damage (no spell damage) - // damage shield calls this function with spell_id set, so its unavoidable - if (attacker && damage > 0 && spell_id == SPELL_UNKNOWN && skill_used != SkillArchery && skill_used != SkillThrowing) { - DamageShield(attacker); - } - - if (spell_id == SPELL_UNKNOWN && skill_used) { - CheckNumHitsRemaining(NUMHIT_IncomingHitAttempts); - - if (attacker) - attacker->CheckNumHitsRemaining(NUMHIT_OutgoingHitAttempts); - } - - if(attacker){ - if(attacker->IsClient()){ - if(!RuleB(Combat, EXPFromDmgShield)) { - // Damage shield damage shouldn't count towards who gets EXP - if(!attacker->CastToClient()->GetFeigned() && !FromDamageShield) - AddToHateList(attacker, 0, damage, true, false, iBuffTic); - } - else { - if(!attacker->CastToClient()->GetFeigned()) - AddToHateList(attacker, 0, damage, true, false, iBuffTic); - } - } - else - AddToHateList(attacker, 0, damage, true, false, iBuffTic); - } - - if(damage > 0) { - //if there is some damage being done and theres an attacker involved - if(attacker) { - if(spell_id == SPELL_HARM_TOUCH2 && attacker->IsClient() && attacker->CastToClient()->CheckAAEffect(aaEffectLeechTouch)){ - int healed = damage; - healed = attacker->GetActSpellHealing(spell_id, healed); - attacker->HealDamage(healed); - entity_list.MessageClose(this, true, 300, MT_Emote, "%s beams a smile at %s", attacker->GetCleanName(), this->GetCleanName() ); - attacker->CastToClient()->DisableAAEffect(aaEffectLeechTouch); - } - - // if spell is lifetap add hp to the caster - if (spell_id != SPELL_UNKNOWN && IsLifetapSpell( spell_id )) { - int healed = damage; - - healed = attacker->GetActSpellHealing(spell_id, healed); - mlog(COMBAT__DAMAGE, "Applying lifetap heal of %d to %s", healed, attacker->GetName()); - attacker->HealDamage(healed); - - //we used to do a message to the client, but its gone now. - // emote goes with every one ... even npcs - entity_list.MessageClose(this, true, 300, MT_Emote, "%s beams a smile at %s", attacker->GetCleanName(), this->GetCleanName() ); - } - } //end `if there is some damage being done and theres anattacker person involved` - - Mob *pet = GetPet(); - if (pet && !pet->IsFamiliar() && !pet->GetSpecialAbility(IMMUNE_AGGRO) && !pet->IsEngaged() && attacker && attacker != this && !attacker->IsCorpse()) - { - if (!pet->IsHeld()) { - mlog(PETS__AGGRO, "Sending pet %s into battle due to attack.", pet->GetName()); - pet->AddToHateList(attacker, 1); - pet->SetTarget(attacker); - Message_StringID(10, PET_ATTACKING, pet->GetCleanName(), attacker->GetCleanName()); - } - } - - //see if any runes want to reduce this damage - if(spell_id == SPELL_UNKNOWN) { - damage = ReduceDamage(damage); - mlog(COMBAT__HITS, "Melee Damage reduced to %d", damage); - damage = ReduceAllDamage(damage); - TryTriggerThreshHold(damage, SE_TriggerMeleeThreshold, attacker); - } else { - int32 origdmg = damage; - damage = AffectMagicalDamage(damage, spell_id, iBuffTic, attacker); - if (origdmg != damage && attacker && attacker->IsClient()) { - if(attacker->CastToClient()->GetFilter(FilterDamageShields) != FilterHide) - attacker->Message(15, "The Spellshield absorbed %d of %d points of damage", origdmg - damage, origdmg); - } - if (damage == 0 && attacker && origdmg != damage && IsClient()) { - //Kayen: Probably need to add a filter for this - Not sure if this msg is correct but there should be a message for spell negate/runes. - Message(263, "%s tries to cast on YOU, but YOUR magical skin absorbs the spell.",attacker->GetCleanName()); - } - damage = ReduceAllDamage(damage); - TryTriggerThreshHold(damage, SE_TriggerSpellThreshold, attacker); - } - - if (skill_used) - CheckNumHitsRemaining(NUMHIT_IncomingHitSuccess); - - if(IsClient() && CastToClient()->sneaking){ - CastToClient()->sneaking = false; - SendAppearancePacket(AT_Sneak, 0); - } - if(attacker && attacker->IsClient() && attacker->CastToClient()->sneaking){ - attacker->CastToClient()->sneaking = false; - attacker->SendAppearancePacket(AT_Sneak, 0); - } - - //final damage has been determined. - - SetHP(GetHP() - damage); - - if(HasDied()) { - bool IsSaved = false; - - if(TryDivineSave()) - IsSaved = true; - - if(!IsSaved && !TrySpellOnDeath()) { - SetHP(-500); - - if(Death(attacker, damage, spell_id, skill_used)) { - return; - } - } - } - else{ - if(GetHPRatio() < 16) - TryDeathSave(); - } - - TryTriggerOnValueAmount(true); - - //fade mez if we are mezzed - if (IsMezzed() && attacker) { - mlog(COMBAT__HITS, "Breaking mez due to attack."); - entity_list.MessageClose_StringID(this, true, 100, MT_WornOff, - HAS_BEEN_AWAKENED, GetCleanName(), attacker->GetCleanName()); - BuffFadeByEffect(SE_Mez); - } - - //check stun chances if bashing - if (damage > 0 && ((skill_used == SkillBash || skill_used == SkillKick) && attacker)) { - // NPCs can stun with their bash/kick as soon as they receive it. - // Clients can stun mobs under level 56 with their kick when they get level 55 or greater. - // Clients have a chance to stun if the mob is 56+ - - // Calculate the chance to stun - int stun_chance = 0; - if (!GetSpecialAbility(UNSTUNABLE)) { - if (attacker->IsNPC()) { - stun_chance = RuleI(Combat, NPCBashKickStunChance); - } else if (attacker->IsClient()) { - // Less than base immunity - // Client vs. Client always uses the chance - if (!IsClient() && GetLevel() <= RuleI(Spells, BaseImmunityLevel)) { - if (skill_used == SkillBash) // Bash always will - stun_chance = 100; - else if (attacker->GetLevel() >= RuleI(Combat, ClientStunLevel)) - stun_chance = 100; // only if you're over level 55 and using kick - } else { // higher than base immunity or Client vs. Client - // not sure on this number, use same as NPC for now - if (skill_used == SkillKick && attacker->GetLevel() < RuleI(Combat, ClientStunLevel)) - stun_chance = RuleI(Combat, NPCBashKickStunChance); - else if (skill_used == SkillBash) - stun_chance = RuleI(Combat, NPCBashKickStunChance) + - attacker->spellbonuses.StunBashChance + - attacker->itembonuses.StunBashChance + - attacker->aabonuses.StunBashChance; - } - } - } - - if (stun_chance && zone->random.Roll(stun_chance)) { - // Passed stun, try to resist now - int stun_resist = itembonuses.StunResist + spellbonuses.StunResist; - int frontal_stun_resist = itembonuses.FrontalStunResist + spellbonuses.FrontalStunResist; - - mlog(COMBAT__HITS, "Stun passed, checking resists. Was %d chance.", stun_chance); - if (IsClient()) { - stun_resist += aabonuses.StunResist; - frontal_stun_resist += aabonuses.FrontalStunResist; - } - - // frontal stun check for ogres/bonuses - if (((GetBaseRace() == OGRE && IsClient()) || - (frontal_stun_resist && zone->random.Roll(frontal_stun_resist))) && - !attacker->BehindMob(this, attacker->GetX(), attacker->GetY())) { - mlog(COMBAT__HITS, "Frontal stun resisted. %d chance.", frontal_stun_resist); - } else { - // Normal stun resist check. - if (stun_resist && zone->random.Roll(stun_resist)) { - if (IsClient()) - Message_StringID(MT_Stun, SHAKE_OFF_STUN); - mlog(COMBAT__HITS, "Stun Resisted. %d chance.", stun_resist); - } else { - mlog(COMBAT__HITS, "Stunned. %d resist chance.", stun_resist); - Stun(zone->random.Int(0, 2) * 1000); // 0-2 seconds - } - } - } else { - mlog(COMBAT__HITS, "Stun failed. %d chance.", stun_chance); - } - } - - if(spell_id != SPELL_UNKNOWN && !iBuffTic) { - //see if root will break - if (IsRooted() && !FromDamageShield) // neotoyko: only spells cancel root - TryRootFadeByDamage(buffslot, attacker); - } - else if(spell_id == SPELL_UNKNOWN) - { - //increment chances of interrupting - if(IsCasting()) { //shouldnt interrupt on regular spell damage - attacked_count++; - mlog(COMBAT__HITS, "Melee attack while casting. Attack count %d", attacked_count); - } - } - - //send an HP update if we are hurt - if(GetHP() < GetMaxHP()) - SendHPUpdate(); - } //end `if damage was done` - - //send damage packet... - if(!iBuffTic) { //buff ticks do not send damage, instead they just call SendHPUpdate(), which is done below - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct)); - CombatDamage_Struct* a = (CombatDamage_Struct*)outapp->pBuffer; - a->target = GetID(); - if (attacker == nullptr) - a->source = 0; - else if (attacker->IsClient() && attacker->CastToClient()->GMHideMe()) - a->source = 0; - else - a->source = attacker->GetID(); - a->type = SkillDamageTypes[skill_used]; // was 0x1c - a->damage = damage; - a->spellid = spell_id; - - //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) - eqFilterType filter; - Mob *skip = attacker; - if(attacker && attacker->GetOwnerID()) { - //attacker is a pet, let pet owners see their pet's damage - Mob* owner = attacker->GetOwner(); - if (owner && owner->IsClient()) { - if (((spell_id != SPELL_UNKNOWN) || (FromDamageShield)) && damage>0) { - //special crap for spell damage, looks hackish to me - char val1[20]={0}; - owner->Message_StringID(MT_NonMelee,OTHER_HIT_NONMELEE,GetCleanName(),ConvertArray(damage,val1)); - } else { - if(damage > 0) { - if(spell_id != SPELL_UNKNOWN) - filter = iBuffTic ? FilterDOT : FilterSpellDamage; - else - filter = FilterPetHits; - } else if(damage == -5) - filter = FilterNone; //cant filter invulnerable - else - filter = FilterPetMisses; - - if(!FromDamageShield) - owner->CastToClient()->QueuePacket(outapp,true,CLIENT_CONNECTED,filter); - } - } - skip = owner; - } else { - //attacker is not a pet, send to the attacker - - //if the attacker is a client, try them with the correct filter - if(attacker && attacker->IsClient()) { - if (((spell_id != SPELL_UNKNOWN)||(FromDamageShield)) && damage>0) { - //special crap for spell damage, looks hackish to me - char val1[20]={0}; - if (FromDamageShield) - { - if(!attacker->CastToClient()->GetFilter(FilterDamageShields) == FilterHide) - { - attacker->Message_StringID(MT_DS,OTHER_HIT_NONMELEE,GetCleanName(),ConvertArray(damage,val1)); - } - } - else - entity_list.MessageClose_StringID(this, true, 100, MT_NonMelee,HIT_NON_MELEE,attacker->GetCleanName(),GetCleanName(),ConvertArray(damage,val1)); - } else { - if(damage > 0) { - if(spell_id != SPELL_UNKNOWN) - filter = iBuffTic ? FilterDOT : FilterSpellDamage; - else - filter = FilterNone; //cant filter our own hits - } else if(damage == -5) - filter = FilterNone; //cant filter invulnerable - else - filter = FilterMyMisses; - - attacker->CastToClient()->QueuePacket(outapp, true, CLIENT_CONNECTED, filter); - } - } - skip = attacker; - } - - //send damage to all clients around except the specified skip mob (attacker or the attacker's owner) and ourself - if(damage > 0) { - if(spell_id != SPELL_UNKNOWN) - filter = iBuffTic ? FilterDOT : FilterSpellDamage; - else - filter = FilterOthersHit; - } else if(damage == -5) - filter = FilterNone; //cant filter invulnerable - else - filter = FilterOthersMiss; - //make attacker (the attacker) send the packet so we can skip them and the owner - //this call will send the packet to `this` as well (using the wrong filter) (will not happen until PC charm works) - // If this is Damage Shield damage, the correct OP_Damage packets will be sent from Mob::DamageShield, so - // we don't send them here. - if(!FromDamageShield) { - entity_list.QueueCloseClients(this, outapp, true, 200, skip, true, filter); - //send the damage to ourself if we are a client - if(IsClient()) { - //I dont think any filters apply to damage affecting us - CastToClient()->QueuePacket(outapp); - } - } - - safe_delete(outapp); - } else { - //else, it is a buff tic... - // So we can see our dot dmg like live shows it. - if(spell_id != SPELL_UNKNOWN && damage > 0 && attacker && attacker != this && attacker->IsClient()) { - //might filter on (attack_skill>200 && attack_skill<250), but I dont think we need it - attacker->FilteredMessage_StringID(attacker, MT_DoTDamage, FilterDOT, - YOUR_HIT_DOT, GetCleanName(), itoa(damage), spells[spell_id].name); - // older clients don't have the below String ID, but it will be filtered - entity_list.FilteredMessageClose_StringID(attacker, true, 200, - MT_DoTDamage, FilterDOT, OTHER_HIT_DOT, GetCleanName(), - itoa(damage), attacker->GetCleanName(), spells[spell_id].name); - } - } //end packet sending - -} - - -void Mob::HealDamage(uint32 amount, Mob *caster, uint16 spell_id) -{ - int32 maxhp = GetMaxHP(); - int32 curhp = GetHP(); - uint32 acthealed = 0; - - if (caster && amount > 0) { - if (caster->IsNPC() && !caster->IsPet()) { - float npchealscale = caster->CastToNPC()->GetHealScale(); - amount = (static_cast(amount) * npchealscale) / 100.0f; - } - } - - if (amount > (maxhp - curhp)) - acthealed = (maxhp - curhp); - else - acthealed = amount; - - if (acthealed > 100) { - if (caster) { - if (IsBuffSpell(spell_id)) { // hots - // message to caster - if (caster->IsClient() && caster == this) { - if (caster->CastToClient()->GetClientVersionBit() & BIT_SoFAndLater) - FilteredMessage_StringID(caster, MT_NonMelee, FilterHealOverTime, - HOT_HEAL_SELF, itoa(acthealed), spells[spell_id].name); - else - FilteredMessage_StringID(caster, MT_NonMelee, FilterHealOverTime, - YOU_HEALED, GetCleanName(), itoa(acthealed)); - } else if (caster->IsClient() && caster != this) { - if (caster->CastToClient()->GetClientVersionBit() & BIT_SoFAndLater) - caster->FilteredMessage_StringID(caster, MT_NonMelee, FilterHealOverTime, - HOT_HEAL_OTHER, GetCleanName(), itoa(acthealed), - spells[spell_id].name); - else - caster->FilteredMessage_StringID(caster, MT_NonMelee, FilterHealOverTime, - YOU_HEAL, GetCleanName(), itoa(acthealed)); - } - // message to target - if (IsClient() && caster != this) { - if (CastToClient()->GetClientVersionBit() & BIT_SoFAndLater) - FilteredMessage_StringID(this, MT_NonMelee, FilterHealOverTime, - HOT_HEALED_OTHER, caster->GetCleanName(), - itoa(acthealed), spells[spell_id].name); - else - FilteredMessage_StringID(this, MT_NonMelee, FilterHealOverTime, - YOU_HEALED, caster->GetCleanName(), itoa(acthealed)); - } - } else { // normal heals - FilteredMessage_StringID(caster, MT_NonMelee, FilterSpellDamage, - YOU_HEALED, caster->GetCleanName(), itoa(acthealed)); - if (caster != this) - caster->FilteredMessage_StringID(caster, MT_NonMelee, FilterSpellDamage, - YOU_HEAL, GetCleanName(), itoa(acthealed)); - } - } else { - Message(MT_NonMelee, "You have been healed for %d points of damage.", acthealed); - } - } - - if (curhp < maxhp) { - if ((curhp + amount) > maxhp) - curhp = maxhp; - else - curhp += amount; - SetHP(curhp); - - SendHPUpdate(); - } -} - -//proc chance includes proc bonus -float Mob::GetProcChances(float ProcBonus, uint16 hand) -{ - int mydex = GetDEX(); - float ProcChance = 0.0f; - - uint32 weapon_speed = GetWeaponSpeedbyHand(hand); - - if (RuleB(Combat, AdjustProcPerMinute)) { - ProcChance = (static_cast(weapon_speed) * - RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms - ProcBonus += static_cast(mydex) * RuleR(Combat, ProcPerMinDexContrib); - ProcChance += ProcChance * ProcBonus / 100.0f; - } else { - ProcChance = RuleR(Combat, BaseProcChance) + - static_cast(mydex) / RuleR(Combat, ProcDexDivideBy); - ProcChance += ProcChance * ProcBonus / 100.0f; - } - - mlog(COMBAT__PROCS, "Proc chance %.2f (%.2f from bonuses)", ProcChance, ProcBonus); - return ProcChance; -} - -float Mob::GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 hand, Mob* on) { - - if (!on) - return ProcChance; - - int myagi = on->GetAGI(); - ProcBonus = 0; - ProcChance = 0; - - uint32 weapon_speed = GetWeaponSpeedbyHand(hand); - - ProcChance = (static_cast(weapon_speed) * RuleR(Combat, AvgDefProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms - ProcBonus += static_cast(myagi) * RuleR(Combat, DefProcPerMinAgiContrib) / 100.0f; - ProcChance = ProcChance + (ProcChance * ProcBonus); - - mlog(COMBAT__PROCS, "Defensive Proc chance %.2f (%.2f from bonuses)", ProcChance, ProcBonus); - return ProcChance; -} - -void Mob::TryDefensiveProc(const ItemInst* weapon, Mob *on, uint16 hand) { - - if (!on) { - SetTarget(nullptr); - LogFile->write(EQEMuLog::Error, "A null Mob object was passed to Mob::TryDefensiveProc for evaluation!"); - return; - } - - bool bDefensiveProc = HasDefensiveProcs(); - - if (!bDefensiveProc) - return; - - float ProcChance, ProcBonus; - on->GetDefensiveProcChances(ProcBonus, ProcChance, hand , this); - - if(hand != MainPrimary) - ProcChance /= 2; - - if (bDefensiveProc){ - for (int i = 0; i < MAX_PROCS; i++) { - if (IsValidSpell(DefensiveProcs[i].spellID)) { - float chance = ProcChance * (static_cast(DefensiveProcs[i].chance)/100.0f); - if (zone->random.Roll(chance)) { - ExecWeaponProc(nullptr, DefensiveProcs[i].spellID, on); - CheckNumHitsRemaining(NUMHIT_DefensiveSpellProcs,0,DefensiveProcs[i].base_spellID); - } - } - } - } -} - -void Mob::TryWeaponProc(const ItemInst* weapon_g, Mob *on, uint16 hand) { - if(!on) { - SetTarget(nullptr); - LogFile->write(EQEMuLog::Error, "A null Mob object was passed to Mob::TryWeaponProc for evaluation!"); - return; - } - - if (!IsAttackAllowed(on)) { - mlog(COMBAT__PROCS, "Preventing procing off of unattackable things."); - return; - } - - if(!weapon_g) { - TrySpellProc(nullptr, (const Item_Struct*)nullptr, on); - return; - } - - if(!weapon_g->IsType(ItemClassCommon)) { - TrySpellProc(nullptr, (const Item_Struct*)nullptr, on); - return; - } - - // Innate + aug procs from weapons - // TODO: powersource procs - 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); - - return; -} - -void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on, uint16 hand) -{ - - if (!weapon) - return; - uint16 skillinuse = 28; - int ourlevel = GetLevel(); - float ProcBonus = static_cast(aabonuses.ProcChanceSPA + - spellbonuses.ProcChanceSPA + itembonuses.ProcChanceSPA); - ProcBonus += static_cast(itembonuses.ProcChance) / 10.0f; // Combat Effects - float ProcChance = GetProcChances(ProcBonus, hand); - - if (hand != MainPrimary) //Is Archery intened to proc at 50% rate? - ProcChance /= 2; - - // Try innate proc on weapon - // We can proc once here, either weapon or one aug - bool proced = false; // silly bool to prevent augs from going if weapon does - skillinuse = GetSkillByItemType(weapon->ItemType); - if (weapon->Proc.Type == ET_CombatProc) { - float WPC = ProcChance * (100.0f + // Proc chance for this weapon - static_cast(weapon->ProcRate)) / 100.0f; - if (zone->random.Roll(WPC)) { // 255 dex = 0.084 chance of proc. No idea what this number should be really. - if (weapon->Proc.Level > ourlevel) { - mlog(COMBAT__PROCS, - "Tried to proc (%s), but our level (%d) is lower than required (%d)", - weapon->Name, ourlevel, weapon->Proc.Level); - if (IsPet()) { - Mob *own = GetOwner(); - if (own) - own->Message_StringID(13, PROC_PETTOOLOW); - } else { - Message_StringID(13, PROC_TOOLOW); - } - } else { - mlog(COMBAT__PROCS, - "Attacking weapon (%s) successfully procing spell %d (%.2f percent chance)", - weapon->Name, weapon->Proc.Effect, WPC * 100); - ExecWeaponProc(inst, weapon->Proc.Effect, on); - proced = true; - } - } - } - //If 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. - if(!RuleB(Combat, OneProcPerWeapon)) - proced = false; - - if (!proced && inst) { - for (int r = 0; r < EmuConstants::ITEM_COMMON_SIZE; r++) { - const ItemInst *aug_i = inst->GetAugment(r); - if (!aug_i) // no aug, try next slot! - continue; - const Item_Struct *aug = aug_i->GetItem(); - if (!aug) - continue; - - if (aug->Proc.Type == ET_CombatProc) { - float APC = ProcChance * (100.0f + // Proc chance for this aug - static_cast(aug->ProcRate)) / 100.0f; - if (zone->random.Roll(APC)) { - if (aug->Proc.Level > ourlevel) { - if (IsPet()) { - Mob *own = GetOwner(); - if (own) - own->Message_StringID(13, PROC_PETTOOLOW); - } else { - Message_StringID(13, PROC_TOOLOW); - } - } else { - ExecWeaponProc(aug_i, aug->Proc.Effect, on); - if (RuleB(Combat, OneProcPerWeapon)) - break; - } - } - } - } - } - // TODO: Powersource procs - - return; -} - -void Mob::TrySpellProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on, uint16 hand) -{ - float ProcBonus = static_cast(spellbonuses.SpellProcChance + - itembonuses.SpellProcChance + aabonuses.SpellProcChance); - float ProcChance = 0.0f; - ProcChance = GetProcChances(ProcBonus, hand); - - if (hand != MainPrimary) //Is Archery intened to proc at 50% rate? - ProcChance /= 2; - - bool rangedattk = false; - if (weapon && hand == MainRange) { - if (weapon->ItemType == ItemTypeArrow || - weapon->ItemType == ItemTypeLargeThrowing || - weapon->ItemType == ItemTypeSmallThrowing || - weapon->ItemType == ItemTypeBow) - rangedattk = true; - } - - if (!weapon && hand == MainRange && GetSpecialAbility(SPECATK_RANGED_ATK)) - rangedattk = true; - - for (uint32 i = 0; i < MAX_PROCS; i++) { - if (IsPet() && hand != MainPrimary) //Pets can only proc spell procs from their primay hand (ie; beastlord pets) - continue; // If pets ever can proc from off hand, this will need to change - - // Not ranged - if (!rangedattk) { - // Perma procs (AAs) - if (PermaProcs[i].spellID != SPELL_UNKNOWN) { - if (zone->random.Roll(PermaProcs[i].chance)) { // TODO: Do these get spell bonus? - mlog(COMBAT__PROCS, - "Permanent proc %d procing spell %d (%d percent chance)", - i, PermaProcs[i].spellID, PermaProcs[i].chance); - ExecWeaponProc(nullptr, PermaProcs[i].spellID, on); - } else { - mlog(COMBAT__PROCS, - "Permanent proc %d failed to proc %d (%d percent chance)", - i, PermaProcs[i].spellID, PermaProcs[i].chance); - } - } - - // Spell procs (buffs) - if (SpellProcs[i].spellID != SPELL_UNKNOWN) { - float chance = ProcChance * (static_cast(SpellProcs[i].chance) / 100.0f); - if (zone->random.Roll(chance)) { - mlog(COMBAT__PROCS, - "Spell proc %d procing spell %d (%.2f percent chance)", - i, SpellProcs[i].spellID, chance); - ExecWeaponProc(nullptr, SpellProcs[i].spellID, on); - CheckNumHitsRemaining(NUMHIT_OffensiveSpellProcs, 0, SpellProcs[i].base_spellID); - } else { - mlog(COMBAT__PROCS, - "Spell proc %d failed to proc %d (%.2f percent chance)", - i, SpellProcs[i].spellID, chance); - } - } - } else if (rangedattk) { // ranged only - // ranged spell procs (buffs) - if (RangedProcs[i].spellID != SPELL_UNKNOWN) { - float chance = ProcChance * (static_cast(RangedProcs[i].chance) / 100.0f); - if (zone->random.Roll(chance)) { - mlog(COMBAT__PROCS, - "Ranged proc %d procing spell %d (%.2f percent chance)", - i, RangedProcs[i].spellID, chance); - ExecWeaponProc(nullptr, RangedProcs[i].spellID, on); - CheckNumHitsRemaining(NUMHIT_OffensiveSpellProcs, 0, RangedProcs[i].base_spellID); - } else { - mlog(COMBAT__PROCS, - "Ranged proc %d failed to proc %d (%.2f percent chance)", - i, RangedProcs[i].spellID, chance); - } - } - } - } - - if (HasSkillProcs() && hand != MainRange){ //We check ranged skill procs within the attack functions. - uint16 skillinuse = 28; - if (weapon) - skillinuse = GetSkillByItemType(weapon->ItemType); - - TrySkillProc(on, skillinuse, 0, false, hand); - } - - return; -} - -void Mob::TryPetCriticalHit(Mob *defender, uint16 skill, int32 &damage) -{ - if(damage < 1) - return; - - //Allows pets to perform critical hits. - //Each rank adds an additional 1% chance for any melee hit (primary, secondary, kick, bash, etc) to critical, - //dealing up to 63% more damage. http://www.magecompendium.com/aa-short-library.html - - Mob *owner = nullptr; - float critChance = 0.0f; - critChance += RuleI(Combat, MeleeBaseCritChance); - uint32 critMod = 163; - - if (damage < 1) //We can't critical hit if we don't hit. - return; - - if (IsPet()) - owner = GetOwner(); - else if ((IsNPC() && CastToNPC()->GetSwarmOwner())) - owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner()); - else - return; - - if (!owner) - return; - - int32 CritPetChance = owner->aabonuses.PetCriticalHit + owner->itembonuses.PetCriticalHit + owner->spellbonuses.PetCriticalHit; - int32 CritChanceBonus = GetCriticalChanceBonus(skill); - - if (CritPetChance || critChance) { - - //For pets use PetCriticalHit for base chance, pets do not innately critical with without it - //even if buffed with a CritChanceBonus effects. - critChance += CritPetChance; - critChance += critChance*CritChanceBonus/100.0f; - } - - if(critChance > 0){ - - critChance /= 100; - - if(zone->random.Roll(critChance)) - { - critMod += GetCritDmgMob(skill) * 2; // To account for base crit mod being 200 not 100 - damage = (damage * critMod) / 100; - entity_list.FilteredMessageClose_StringID(this, false, 200, - MT_CritMelee, FilterMeleeCrits, CRITICAL_HIT, - GetCleanName(), itoa(damage)); - } - } -} - -void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttackOptions *opts) -{ - if(damage < 1) - return; - - // decided to branch this into it's own function since it's going to be duplicating a lot of the - // code in here, but could lead to some confusion otherwise - if (IsPet() && GetOwner()->IsClient() || (IsNPC() && CastToNPC()->GetSwarmOwner())) { - TryPetCriticalHit(defender,skill,damage); - return; - } - -#ifdef BOTS - if (this->IsPet() && this->GetOwner()->IsBot()) { - this->TryPetCriticalHit(defender,skill,damage); - return; - } -#endif //BOTS - - float critChance = 0.0f; - bool IsBerskerSPA = false; - - //1: Try Slay Undead - if (defender && (defender->GetBodyType() == BT_Undead || - defender->GetBodyType() == BT_SummonedUndead || defender->GetBodyType() == BT_Vampire)) { - int32 SlayRateBonus = aabonuses.SlayUndead[0] + itembonuses.SlayUndead[0] + spellbonuses.SlayUndead[0]; - if (SlayRateBonus) { - float slayChance = static_cast(SlayRateBonus) / 10000.0f; - if (zone->random.Roll(slayChance)) { - int32 SlayDmgBonus = aabonuses.SlayUndead[1] + itembonuses.SlayUndead[1] + spellbonuses.SlayUndead[1]; - damage = (damage * SlayDmgBonus * 2.25) / 100; - if (GetGender() == 1) // female - entity_list.FilteredMessageClose_StringID(this, false, 200, - MT_CritMelee, FilterMeleeCrits, FEMALE_SLAYUNDEAD, - GetCleanName(), itoa(damage)); - else // males and neuter I guess - entity_list.FilteredMessageClose_StringID(this, false, 200, - MT_CritMelee, FilterMeleeCrits, MALE_SLAYUNDEAD, - GetCleanName(), itoa(damage)); - return; - } - } - } - - //2: Try Melee Critical - - //Base critical rate for all classes is dervived from DEX stat, this rate is then augmented - //by item,spell and AA bonuses allowing you a chance to critical hit. If the following rules - //are defined you will have an innate chance to hit at Level 1 regardless of bonuses. - //Warning: Do not define these rules if you want live like critical hits. - critChance += RuleI(Combat, MeleeBaseCritChance); - - if (IsClient()) { - critChance += RuleI(Combat, ClientBaseCritChance); - - if (spellbonuses.BerserkSPA || itembonuses.BerserkSPA || aabonuses.BerserkSPA) - IsBerskerSPA = true; - - if (((GetClass() == WARRIOR || GetClass() == BERSERKER) && GetLevel() >= 12) || IsBerskerSPA) { - if (IsBerserk() || IsBerskerSPA) - critChance += RuleI(Combat, BerserkBaseCritChance); - else - critChance += RuleI(Combat, WarBerBaseCritChance); - } - } - - int deadlyChance = 0; - int deadlyMod = 0; - if(skill == SkillArchery && GetClass() == RANGER && GetSkill(SkillArchery) >= 65) - critChance += 6; - - if (skill == SkillThrowing && GetClass() == ROGUE && GetSkill(SkillThrowing) >= 65) { - critChance += RuleI(Combat, RogueCritThrowingChance); - deadlyChance = RuleI(Combat, RogueDeadlyStrikeChance); - deadlyMod = RuleI(Combat, RogueDeadlyStrikeMod); - } - - int CritChanceBonus = GetCriticalChanceBonus(skill); - - if (CritChanceBonus || critChance) { - - //Get Base CritChance from Dex. (200 = ~1.6%, 255 = ~2.0%, 355 = ~2.20%) Fall off rate > 255 - //http://giline.versus.jp/shiden/su.htm , http://giline.versus.jp/shiden/damage_e.htm - if (GetDEX() <= 255) - critChance += (float(GetDEX()) / 125.0f); - else if (GetDEX() > 255) - critChance += (float(GetDEX()-255)/ 500.0f) + 2.0f; - critChance += critChance*(float)CritChanceBonus /100.0f; - } - - if(opts) { - critChance *= opts->crit_percent; - critChance += opts->crit_flat; - } - - if(critChance > 0) { - - critChance /= 100; - - if(zone->random.Roll(critChance)) - { - uint32 critMod = 200; - bool crip_success = false; - int32 CripplingBlowChance = GetCrippBlowChance(); - - //Crippling Blow Chance: The percent value of the effect is applied - //to the your Chance to Critical. (ie You have 10% chance to critical and you - //have a 200% Chance to Critical Blow effect, therefore you have a 20% Chance to Critical Blow. - if (CripplingBlowChance || (IsBerserk() || IsBerskerSPA)) { - if (!IsBerserk() && !IsBerskerSPA) - critChance *= float(CripplingBlowChance)/100.0f; - - if ((IsBerserk() || IsBerskerSPA) || zone->random.Roll(critChance)) { - critMod = 400; - crip_success = true; - } - } - - critMod += GetCritDmgMob(skill) * 2; // To account for base crit mod being 200 not 100 - damage = damage * critMod / 100; - - bool deadlySuccess = false; - if (deadlyChance && zone->random.Roll(static_cast(deadlyChance) / 100.0f)) { - if (BehindMob(defender, GetX(), GetY())) { - damage *= deadlyMod; - deadlySuccess = true; - } - } - - if (crip_success) { - entity_list.FilteredMessageClose_StringID(this, false, 200, - MT_CritMelee, FilterMeleeCrits, CRIPPLING_BLOW, - GetCleanName(), itoa(damage)); - // Crippling blows also have a chance to stun - //Kayen: Crippling Blow would cause a chance to interrupt for npcs < 55, with a staggers message. - if (defender->GetLevel() <= 55 && !defender->GetSpecialAbility(IMMUNE_STUN)){ - defender->Emote("staggers."); - defender->Stun(0); - } - } else if (deadlySuccess) { - entity_list.FilteredMessageClose_StringID(this, false, 200, - MT_CritMelee, FilterMeleeCrits, DEADLY_STRIKE, - GetCleanName(), itoa(damage)); - } else { - entity_list.FilteredMessageClose_StringID(this, false, 200, - MT_CritMelee, FilterMeleeCrits, CRITICAL_HIT, - GetCleanName(), itoa(damage)); - } - } - } -} - - -bool Mob::TryFinishingBlow(Mob *defender, SkillUseTypes skillinuse) -{ - if (defender && !defender->IsClient() && defender->GetHPRatio() < 10){ - - uint32 FB_Dmg = aabonuses.FinishingBlow[1] + spellbonuses.FinishingBlow[1] + itembonuses.FinishingBlow[1]; - - uint32 FB_Level = 0; - FB_Level = aabonuses.FinishingBlowLvl[0]; - if (FB_Level < spellbonuses.FinishingBlowLvl[0]) - FB_Level = spellbonuses.FinishingBlowLvl[0]; - else if (FB_Level < itembonuses.FinishingBlowLvl[0]) - FB_Level = itembonuses.FinishingBlowLvl[0]; - - //Proc Chance value of 500 = 5% - uint32 ProcChance = (aabonuses.FinishingBlow[0] + spellbonuses.FinishingBlow[0] + spellbonuses.FinishingBlow[0])/10; - - if(FB_Level && FB_Dmg && (defender->GetLevel() <= FB_Level) && (ProcChance >= zone->random.Int(0, 1000))){ - entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, FINISHING_BLOW, GetName()); - DoSpecialAttackDamage(defender, skillinuse, FB_Dmg, 1, -1, 10, false, false); - return true; - } - } - return false; -} - -void Mob::DoRiposte(Mob* defender) { - mlog(COMBAT__ATTACKS, "Preforming a riposte"); - - if (!defender) - return; - - defender->Attack(this, MainPrimary, true); - if (HasDied()) return; - - int32 DoubleRipChance = defender->aabonuses.GiveDoubleRiposte[0] + - defender->spellbonuses.GiveDoubleRiposte[0] + - defender->itembonuses.GiveDoubleRiposte[0]; - - DoubleRipChance = defender->aabonuses.DoubleRiposte + - defender->spellbonuses.DoubleRiposte + - defender->itembonuses.DoubleRiposte; - - //Live AA - Double Riposte - if(DoubleRipChance && zone->random.Roll(DoubleRipChance)) { - mlog(COMBAT__ATTACKS, "Preforming a double riposed (%d percent chance)", DoubleRipChance); - defender->Attack(this, MainPrimary, true); - if (HasDied()) return; - } - - //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->aabonuses.GiveDoubleRiposte[1]; - - if(DoubleRipChance && zone->random.Roll(DoubleRipChance)) { - mlog(COMBAT__ATTACKS, "Preforming a return SPECIAL ATTACK (%d percent chance)", DoubleRipChance); - - if (defender->GetClass() == MONK) - defender->MonkSpecialAttack(this, defender->aabonuses.GiveDoubleRiposte[2]); - else if (defender->IsClient()) - defender->CastToClient()->DoClassAttacks(this,defender->aabonuses.GiveDoubleRiposte[2], true); - } -} - -void Mob::ApplyMeleeDamageBonus(uint16 skill, int32 &damage){ - - if(!RuleB(Combat, UseIntervalAC)){ - if(IsNPC()){ //across the board NPC damage bonuses. - //only account for STR here, assume their base STR was factored into their DB damages - int dmgbonusmod = 0; - dmgbonusmod += (100*(itembonuses.STR + spellbonuses.STR))/3; - dmgbonusmod += (100*(spellbonuses.ATK + itembonuses.ATK))/5; - mlog(COMBAT__DAMAGE, "Damage bonus: %d percent from ATK and STR bonuses.", (dmgbonusmod/100)); - damage += (damage*dmgbonusmod/10000); - } - } - - damage += damage * GetMeleeDamageMod_SE(skill) / 100; -} - -bool Mob::HasDied() { - bool Result = false; - int32 hp_below = 0; - - hp_below = (GetDelayDeath() * -1); - - if((GetHP()) <= (hp_below)) - Result = true; - - return Result; -} - -uint16 Mob::GetDamageTable(SkillUseTypes skillinuse) -{ - if(GetLevel() <= 51) - { - uint32 ret_table = 0; - int str_over_75 = 0; - if(GetSTR() > 75) - str_over_75 = GetSTR() - 75; - if(str_over_75 > 255) - ret_table = (GetSkill(skillinuse)+255)/2; - else - ret_table = (GetSkill(skillinuse)+str_over_75)/2; - - if(ret_table < 100) - return 100; - - return ret_table; - } - else if(GetLevel() >= 90) - { - if(GetClass() == MONK) - return 379; - else - return 345; - } - else - { - uint32 dmg_table[] = { - 275, 275, 275, 275, 275, - 280, 280, 280, 280, 285, - 285, 285, 290, 290, 295, - 295, 300, 300, 300, 305, - 305, 305, 310, 310, 315, - 315, 320, 320, 320, 325, - 325, 325, 330, 330, 335, - 335, 340, 340, 340, - }; - if(GetClass() == MONK) - return (dmg_table[GetLevel()-51]*(100+RuleI(Combat,MonkDamageTableBonus))/100); - else - return dmg_table[GetLevel()-51]; - } -} - -void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, uint16 hand, bool IsDefensive) -{ - - if (!on) { - SetTarget(nullptr); - LogFile->write(EQEMuLog::Error, "A null Mob object was passed to Mob::TrySkillProc for evaluation!"); - return; - } - - if (!spellbonuses.LimitToSkill[skill] && !itembonuses.LimitToSkill[skill] && !aabonuses.LimitToSkill[skill]) - return; - - /*Allow one proc from each (Spell/Item/AA) - Kayen: Due to limited avialability of effects on live it is too difficult - to confirm how they stack at this time, will adjust formula when more data is avialablle to test.*/ - bool CanProc = true; - - uint16 base_spell_id = 0; - uint16 proc_spell_id = 0; - float ProcMod = 0; - float chance = 0; - - if (IsDefensive) - chance = on->GetSkillProcChances(ReuseTime, hand); - else - chance = GetSkillProcChances(ReuseTime, hand); - - if (spellbonuses.LimitToSkill[skill]){ - - for(int e = 0; e < MAX_SKILL_PROCS; e++){ -<<<<<<< HEAD - - if (CanProc && - (!Success && spellbonuses.SkillProc[e] && IsValidSpell(spellbonuses.SkillProc[e])) -======= - if (CanProc && - (!Success && spellbonuses.SkillProc[e] && IsValidSpell(spellbonuses.SkillProc[e])) ->>>>>>> master - || (Success && spellbonuses.SkillProcSuccess[e] && IsValidSpell(spellbonuses.SkillProcSuccess[e]))) { - base_spell_id = spellbonuses.SkillProc[e]; - base_spell_id = 0; - ProcMod = 0; - - for (int i = 0; i < EFFECT_COUNT; i++) { -<<<<<<< HEAD - -======= ->>>>>>> master - if (spells[base_spell_id].effectid[i] == SE_SkillProc) { - proc_spell_id = spells[base_spell_id].base[i]; - ProcMod = static_cast(spells[base_spell_id].base2[i]); - } - - else if (spells[base_spell_id].effectid[i] == SE_LimitToSkill && spells[base_spell_id].effectid[i] <= HIGHEST_SKILL) { - - if (CanProc && spells[base_spell_id].base[i] == skill && IsValidSpell(proc_spell_id)) { - float final_chance = chance * (ProcMod / 100.0f); - if (zone->random.Roll(final_chance)) { - ExecWeaponProc(nullptr, proc_spell_id, on); - CheckNumHitsRemaining(NUMHIT_OffensiveSpellProcs,0, base_spell_id); - CanProc = false; - break; - } - } - } - else { - proc_spell_id = 0; - ProcMod = 0; - } - } - } - } - } - - if (itembonuses.LimitToSkill[skill]){ - CanProc = true; - for(int e = 0; e < MAX_SKILL_PROCS; e++){ -<<<<<<< HEAD - - if (CanProc && - (!Success && itembonuses.SkillProc[e] && IsValidSpell(itembonuses.SkillProc[e])) -======= - if (CanProc && - (!Success && itembonuses.SkillProc[e] && IsValidSpell(itembonuses.SkillProc[e])) ->>>>>>> master - || (Success && itembonuses.SkillProcSuccess[e] && IsValidSpell(itembonuses.SkillProcSuccess[e]))) { - base_spell_id = itembonuses.SkillProc[e]; - base_spell_id = 0; - ProcMod = 0; - - for (int i = 0; i < EFFECT_COUNT; i++) { -<<<<<<< HEAD - -======= ->>>>>>> master - if (spells[base_spell_id].effectid[i] == SE_SkillProc) { - proc_spell_id = spells[base_spell_id].base[i]; - ProcMod = static_cast(spells[base_spell_id].base2[i]); - } - - else if (spells[base_spell_id].effectid[i] == SE_LimitToSkill && spells[base_spell_id].effectid[i] <= HIGHEST_SKILL) { - - if (CanProc && spells[base_spell_id].base[i] == skill && IsValidSpell(proc_spell_id)) { - float final_chance = chance * (ProcMod / 100.0f); - if (zone->random.Roll(final_chance)) { - ExecWeaponProc(nullptr, proc_spell_id, on); - CanProc = false; - break; - } - } - } - else { - proc_spell_id = 0; - ProcMod = 0; - } - } - } - } - } - - if (IsClient() && aabonuses.LimitToSkill[skill]){ - - CanProc = true; - uint32 effect = 0; - int32 base1 = 0; - int32 base2 = 0; - uint32 slot = 0; - - for(int e = 0; e < MAX_SKILL_PROCS; e++){ -<<<<<<< HEAD - -======= ->>>>>>> master - if (CanProc && - (!Success && aabonuses.SkillProc[e]) - || (Success && aabonuses.SkillProcSuccess[e])){ - int aaid = aabonuses.SkillProc[e]; - base_spell_id = 0; - ProcMod = 0; - - std::map >::const_iterator find_iter = aa_effects.find(aaid); - if(find_iter == aa_effects.end()) - break; - - 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; - - if (effect == SE_SkillProc) { - proc_spell_id = base1; - ProcMod = static_cast(base2); - } - - else if (effect == SE_LimitToSkill && effect <= HIGHEST_SKILL) { - - if (CanProc && base1 == skill && IsValidSpell(proc_spell_id)) { - float final_chance = chance * (ProcMod / 100.0f); - - if (zone->random.Roll(final_chance)) { - ExecWeaponProc(nullptr, proc_spell_id, on); - CanProc = false; - break; - } - } - } - else { - proc_spell_id = 0; - ProcMod = 0; - } - } - } - } - } -} - -float Mob::GetSkillProcChances(uint16 ReuseTime, uint16 hand) { - - uint32 weapon_speed; - float ProcChance = 0; -<<<<<<< HEAD - - if (!ReuseTime && hand) { -======= ->>>>>>> master - - if (!ReuseTime && hand) { - weapon_speed = GetWeaponSpeedbyHand(hand); - ProcChance = static_cast(weapon_speed) * (RuleR(Combat, AvgProcsPerMinute) / 60000.0f); -<<<<<<< HEAD - -======= ->>>>>>> master - if (hand != MainPrimary) - ProcChance /= 2; - } - - else - ProcChance = static_cast(ReuseTime) * (RuleR(Combat, AvgProcsPerMinute) / 60000.0f); - - return ProcChance; -} - -bool Mob::TryRootFadeByDamage(int buffslot, Mob* attacker) { - -<<<<<<< HEAD - /*Dev Quote 2010: http://forums.station.sony.com/eq/posts/list.m?topic_id=161443 - The Viscid Roots AA does the following: Reduces the chance for root to break by X percent. - There is no distinction of any kind between the caster inflicted damage, or anyone - else's damage. There is also no distinction between Direct and DOT damage in the root code. - - /* General Mechanics - - Check buffslot to make sure damage from a root does not cancel the root - - If multiple roots on target, always and only checks first root slot and if broken only removes that slots root. - - Only roots on determental spells can be broken by damage. - - Root break chance values obtained from live parses. - */ - - if (!attacker || !spellbonuses.Root[0] || spellbonuses.Root[1] < 0) - return false; - - if (IsDetrimentalSpell(spellbonuses.Root[1]) && spellbonuses.Root[1] != buffslot){ - - int BreakChance = RuleI(Spells, RootBreakFromSpells); - - BreakChance -= BreakChance*buffs[spellbonuses.Root[1]].RootBreakChance/100; -======= - /*Dev Quote 2010: http://forums.station.sony.com/eq/posts/list.m?topic_id=161443 - The Viscid Roots AA does the following: Reduces the chance for root to break by X percent. - There is no distinction of any kind between the caster inflicted damage, or anyone - else's damage. There is also no distinction between Direct and DOT damage in the root code. - - General Mechanics - - Check buffslot to make sure damage from a root does not cancel the root - - If multiple roots on target, always and only checks first root slot and if broken only removes that slots root. - - Only roots on determental spells can be broken by damage. - - Root break chance values obtained from live parses. - */ - - if (!attacker || !spellbonuses.Root[0] || spellbonuses.Root[1] < 0) - return false; - - if (IsDetrimentalSpell(spellbonuses.Root[1]) && spellbonuses.Root[1] != buffslot){ - int BreakChance = RuleI(Spells, RootBreakFromSpells); - - BreakChance -= BreakChance*buffs[spellbonuses.Root[1]].RootBreakChance/100; ->>>>>>> master - int level_diff = attacker->GetLevel() - GetLevel(); - - //Use baseline if level difference <= 1 (ie. If target is (1) level less than you, or equal or greater level) - - if (level_diff == 2) - BreakChance = (BreakChance * 80) /100; //Decrease by 20%; - - else if (level_diff >= 3 && level_diff <= 20) - BreakChance = (BreakChance * 60) /100; //Decrease by 40%; - - else if (level_diff > 21) - BreakChance = (BreakChance * 20) /100; //Decrease by 80%; -<<<<<<< HEAD - - if (BreakChance < 1) - BreakChance = 1; -======= ->>>>>>> master - - if (BreakChance < 1) - BreakChance = 1; - - if (zone->random.Roll(BreakChance)) { - - if (!TryFadeEffect(spellbonuses.Root[1])) { - BuffFadeBySlot(spellbonuses.Root[1]); - mlog(COMBAT__HITS, "Spell broke root! BreakChance percent chance"); - return true; - } - } - } - - mlog(COMBAT__HITS, "Spell did not break root. BreakChance percent chance"); - return false; -} - -int32 Mob::RuneAbsorb(int32 damage, uint16 type) -{ - uint32 buff_max = GetMaxTotalSlots(); - if (type == SE_Rune){ - for(uint32 slot = 0; slot < buff_max; slot++) { - if(slot == spellbonuses.MeleeRune[1] && spellbonuses.MeleeRune[0] && buffs[slot].melee_rune && IsValidSpell(buffs[slot].spellid)){ - int melee_rune_left = buffs[slot].melee_rune; - - if(melee_rune_left > damage) - { - melee_rune_left -= damage; - buffs[slot].melee_rune = melee_rune_left; - return -6; - } - - else - { - if(melee_rune_left > 0) - damage -= melee_rune_left; - - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot); - } - } - } - } - - - else{ - for(uint32 slot = 0; slot < buff_max; slot++) { - if(slot == spellbonuses.AbsorbMagicAtt[1] && spellbonuses.AbsorbMagicAtt[0] && buffs[slot].magic_rune && IsValidSpell(buffs[slot].spellid)){ - int magic_rune_left = buffs[slot].magic_rune; - if(magic_rune_left > damage) - { - magic_rune_left -= damage; - buffs[slot].magic_rune = magic_rune_left; - return 0; - } - - else - { - if(magic_rune_left > 0) - damage -= magic_rune_left; - - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot); - } - } - } - } - - return damage; -} - -void Mob::CommonOutgoingHitSuccess(Mob* defender, int32 &damage, SkillUseTypes skillInUse) -{ - if (!defender) - return; - - ApplyMeleeDamageBonus(skillInUse, damage); - damage += (damage * defender->GetSkillDmgTaken(skillInUse) / 100) + (GetSkillDmgAmt(skillInUse) + defender->GetFcDamageAmtIncoming(this, 0, true, skillInUse)); - TryCriticalHit(defender, skillInUse, damage); - CheckNumHitsRemaining(NUMHIT_OutgoingHitSuccess); -} - -void Mob::CommonBreakInvisible() -{ - //break invis when you attack - if(invisible) { - mlog(COMBAT__ATTACKS, "Removing invisibility due to melee attack."); - BuffFadeByEffect(SE_Invisibility); - BuffFadeByEffect(SE_Invisibility2); - invisible = false; - } - if(invisible_undead) { - mlog(COMBAT__ATTACKS, "Removing invisibility vs. undead due to melee attack."); - BuffFadeByEffect(SE_InvisVsUndead); - BuffFadeByEffect(SE_InvisVsUndead2); - invisible_undead = false; - } - if(invisible_animals){ - mlog(COMBAT__ATTACKS, "Removing invisibility vs. animals due to melee attack."); - BuffFadeByEffect(SE_InvisVsAnimals); - invisible_animals = false; - } - - if (spellbonuses.NegateIfCombat) - BuffFadeByEffect(SE_NegateIfCombat); - - if(hidden || improved_hidden){ - hidden = false; - improved_hidden = false; - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x03; - sa_out->parameter = 0; - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - } - - if (spellbonuses.NegateIfCombat) - BuffFadeByEffect(SE_NegateIfCombat); - - hidden = false; - improved_hidden = false; -} - -/* Dev quotes: - * Old formula - * Final delay = (Original Delay / (haste mod *.01f)) + ((Hundred Hands / 100) * Original Delay) - * New formula - * Final delay = (Original Delay / (haste mod *.01f)) + ((Hundred Hands / 1000) * (Original Delay / (haste mod *.01f)) - * Base Delay 20 25 30 37 - * Haste 2.25 2.25 2.25 2.25 - * HHE (old) -17 -17 -17 -17 - * Final Delay 5.488888889 6.861111111 8.233333333 10.15444444 - * - * Base Delay 20 25 30 37 - * Haste 2.25 2.25 2.25 2.25 - * HHE (new) -383 -383 -383 -383 - * Final Delay 5.484444444 6.855555556 8.226666667 10.14622222 - * - * Difference -0.004444444 -0.005555556 -0.006666667 -0.008222222 - * - * These times are in 10th of a second - */ - -void Mob::SetAttackTimer() -{ - attack_timer.SetAtTrigger(4000, true); -} - -void Client::SetAttackTimer() -{ - float haste_mod = GetHaste() * 0.01f; - - //default value for attack timer in case they have - //an invalid weapon equipped: - 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) - continue; - - const Item_Struct *ItemToUse = nullptr; - - //find our item - ItemInst *ci = GetInv().GetItem(i); - 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)) { - attack_dw_timer.Disable(); - continue; - } - } - - //if we cant dual wield, skip it - if (!CanThisClassDualWield()) { - 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 - 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 speed = 0; - int delay = 36; - float quiver_haste = 0.0f; - - //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 (ItemToUse->ItemType == ItemTypeBow || ItemToUse->ItemType == ItemTypeLargeThrowing) - quiver_haste = GetQuiverHaste(); - } - 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); - // this is probably wrong - if (quiver_haste > 0) - speed *= quiver_haste; - TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true); - - if (i == MainPrimary) - PrimaryWeapon = ItemToUse; - } -} - -void NPC::SetAttackTimer() -{ - float haste_mod = GetHaste() * 0.01f; - - //default value for attack timer in case they have - //an invalid weapon equipped: - attack_timer.SetAtTrigger(4000, true); - - Timer *TimerToUse = nullptr; - int hhe = itembonuses.HundredHands + spellbonuses.HundredHands; - - // Technically NPCs should do some logic for weapons, but the effect is minimal - // What they do is take the lower of their set delay and the weapon's - // ex. Mob's delay set to 20, weapon set to 19, delay 19 - // Mob's delay set to 20, weapon set to 21, delay 20 - int speed = 0; - if (RuleB(Spells, Jun182014HundredHandsRevamp)) - speed = static_cast(((attack_delay / haste_mod) + ((hhe / 1000.0f) * (attack_delay / haste_mod))) * 100); - else - speed = static_cast(((attack_delay / haste_mod) + ((hhe / 100.0f) * attack_delay)) * 100); - - 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) - continue; - - //special offhand stuff - if (i == MainSecondary) { - //NPCs get it for free at 13 - if(GetLevel() < 13) { - attack_dw_timer.Disable(); - continue; - } - } - - TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true); - } -} diff --git a/zone/client.cpp.orig b/zone/client.cpp.orig deleted file mode 100644 index f23c80b8f..000000000 --- a/zone/client.cpp.orig +++ /dev/null @@ -1,8389 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2003 EQEMu Development Team (http://eqemulator.org) - - 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 -*/ -#include "../common/debug.h" -#include -#include -#include -#include - -// for windows compile -#ifdef _WINDOWS - #define abs64 _abs64 -#else - #include - #include - #include - #include "../common/unix.h" - #define abs64 abs -#endif - -extern volatile bool RunLoops; - -#include "../common/features.h" -#include "../common/spdat.h" -#include "../common/guilds.h" -#include "../common/rulesys.h" -#include "../common/string_util.h" -#include "../common/data_verification.h" -#include "net.h" -#include "worldserver.h" -#include "zonedb.h" -#include "petitions.h" -#include "command.h" -#include "string_ids.h" -#include "client_logs.h" -#include "guild_mgr.h" -#include "quest_parser_collection.h" -#include "queryserv.h" - -extern QueryServ* QServ; -extern EntityList entity_list; -extern Zone* zone; -extern volatile bool ZoneLoaded; -extern WorldServer worldserver; -extern uint32 numclients; -extern PetitionList petition_list; -bool commandlogged; -char entirecommand[255]; - -Client::Client(EQStreamInterface* ieqs) -: Mob("No name", // name - "", // lastname - 0, // cur_hp - 0, // max_hp - 0, // gender - 0, // race - 0, // class - BT_Humanoid, // bodytype - 0, // deity - 0, // level - 0, // npctypeid - 0, // size - 0.7, // runspeed - xyz_heading::Origin(), - 0, // light - 0xFF, // texture - 0xFF, // helmtexture - 0, // ac - 0, // atk - 0, // str - 0, // sta - 0, // dex - 0, // agi - 0, // int - 0, // wis - 0, // cha - 0, // Luclin Hair Colour - 0, // Luclin Beard Color - 0, // Luclin Eye1 - 0, // Luclin Eye2 - 0, // Luclin Hair Style - 0, // Luclin Face - 0, // Luclin Beard - 0, // Drakkin Heritage - 0, // Drakkin Tattoo - 0, // Drakkin Details - 0, // Armor Tint - 0xff, // AA Title - 0, // see_invis - 0, // see_invis_undead - 0, - 0, - 0, - 0, - 0, // qglobal - 0, // maxlevel - 0 // scalerate - - ), - //these must be listed in the order they appear in client.h - position_timer(250), - hpupdate_timer(1800), - camp_timer(29000), - process_timer(100), - stamina_timer(40000), - zoneinpacket_timer(3000), - linkdead_timer(RuleI(Zone,ClientLinkdeadMS)), - dead_timer(2000), - global_channel_timer(1000), - shield_timer(500), - fishing_timer(8000), - endupkeep_timer(1000), - forget_timer(0), - autosave_timer(RuleI(Character, AutosaveIntervalS)*1000), -#ifdef REVERSE_AGGRO - scanarea_timer(AIClientScanarea_delay), -#endif - tribute_timer(Tribute_duration), - proximity_timer(ClientProximity_interval), - TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000), - charm_update_timer(6000), - rest_timer(1), - charm_class_attacks_timer(3000), - charm_cast_timer(3500), - qglobal_purge_timer(30000), - TrackingTimer(2000), - RespawnFromHoverTimer(0), - merc_timer(RuleI(Mercs, UpkeepIntervalMS)), - ItemTickTimer(10000), - ItemQuestTimer(500), - m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number - m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f), - m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f), - m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f) -{ - for(int cf=0; cf < _FilterCount; cf++) - ClientFilters[cf] = FilterShow; - character_id = 0; - conn_state = NoPacketsReceived; - client_data_loaded = false; - feigned = false; - berserk = false; - dead = false; - eqs = ieqs; - ip = eqs->GetRemoteIP(); - port = ntohs(eqs->GetRemotePort()); - client_state = CLIENT_CONNECTING; - Trader=false; - Buyer = false; - CustomerID = 0; - TrackingID = 0; - WID = 0; - account_id = 0; - admin = 0; - lsaccountid = 0; - shield_target = nullptr; - SQL_log = nullptr; - guild_id = GUILD_NONE; - guildrank = 0; - GuildBanker = false; - memset(lskey, 0, sizeof(lskey)); - strcpy(account_name, ""); - tellsoff = false; - last_reported_mana = 0; - last_reported_endur = 0; - gmhideme = false; - AFK = false; - LFG = false; - LFGFromLevel = 0; - LFGToLevel = 0; - LFGMatchFilter = false; - LFGComments[0] = '\0'; - LFP = false; - gmspeed = 0; - playeraction = 0; - SetTarget(0); - auto_attack = false; - auto_fire = false; - linkdead_timer.Disable(); - zonesummon_id = 0; - zonesummon_ignorerestrictions = 0; - zoning = false; - zone_mode = ZoneUnsolicited; - casting_spell_id = 0; - npcflag = false; - npclevel = 0; - pQueuedSaveWorkID = 0; - position_timer_counter = 0; - fishing_timer.Disable(); - shield_timer.Disable(); - dead_timer.Disable(); - camp_timer.Disable(); - autosave_timer.Disable(); - GetMercTimer()->Disable(); - instalog = false; - pLastUpdate = 0; - pLastUpdateWZ = 0; - m_pp.autosplit = false; - // initialise haste variable - m_tradeskill_object = nullptr; - delaytimer = false; - PendingRezzXP = -1; - PendingRezzDBID = 0; - PendingRezzSpellID = 0; - numclients++; - // emuerror; - UpdateWindowTitle(); - horseId = 0; - tgb = false; - tribute_master_id = 0xFFFFFFFF; - tribute_timer.Disable(); - taskstate = nullptr; - TotalSecondsPlayed = 0; - keyring.clear(); - bind_sight_target = nullptr; - mercid = 0; - mercSlot = 0; - InitializeMercInfo(); - SetMerc(0); - - logging_enabled = CLIENT_DEFAULT_LOGGING_ENABLED; - - //for good measure: - memset(&m_pp, 0, sizeof(m_pp)); - memset(&m_epp, 0, sizeof(m_epp)); - PendingTranslocate = false; - PendingSacrifice = false; - BoatID = 0; - - KarmaUpdateTimer = new Timer(RuleI(Chat, KarmaUpdateIntervalMS)); - GlobalChatLimiterTimer = new Timer(RuleI(Chat, IntervalDurationMS)); - AttemptedMessages = 0; - TotalKarma = 0; - ClientVersion = EQClientUnknown; - ClientVersionBit = 0; - AggroCount = 0; - RestRegenHP = 0; - RestRegenMana = 0; - RestRegenEndurance = 0; - XPRate = 100; - cur_end = 0; - - m_TimeSinceLastPositionCheck = 0; - m_DistanceSinceLastPositionCheck = 0.0f; - m_ShadowStepExemption = 0; - m_KnockBackExemption = 0; - m_PortExemption = 0; - m_SenseExemption = 0; - m_AssistExemption = 0; - m_CheatDetectMoved = false; - CanUseReport = true; - aa_los_them_mob = nullptr; - los_status = false; - los_status_facing = false; - qGlobals = nullptr; - HideCorpseMode = HideCorpseNone; - PendingGuildInvitation = false; - - cur_end = 0; - - InitializeBuffSlots(); - - adventure_request_timer = nullptr; - adventure_create_timer = nullptr; - adventure_leave_timer = nullptr; - adventure_door_timer = nullptr; - adv_requested_data = nullptr; - adventure_stats_timer = nullptr; - adventure_leaderboard_timer = nullptr; - adv_data = nullptr; - adv_requested_theme = 0; - adv_requested_id = 0; - adv_requested_member_count = 0; - - for(int i = 0; i < XTARGET_HARDCAP; ++i) - { - XTargets[i].Type = Auto; - XTargets[i].ID = 0; - XTargets[i].Name[0] = 0; - } - MaxXTargets = 5; - XTargetAutoAddHaters = true; - LoadAccountFlags(); - - initial_respawn_selection = 0; - alternate_currency_loaded = false; - - EngagedRaidTarget = false; - SavedRaidRestTimer = 0; - - interrogateinv_flag = false; -} - -Client::~Client() { -#ifdef BOTS - Bot::ProcessBotOwnerRefDelete(this); -#endif - if(IsInAGuild()) - guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), 0, time(nullptr)); - - Mob* horse = entity_list.GetMob(this->CastToClient()->GetHorseId()); - if (horse) - horse->Depop(); - - Mob* merc = entity_list.GetMob(this->GetMercID()); - if (merc) - merc->Depop(); - - if(Trader) - database.DeleteTraderItem(this->CharacterID()); - - if(Buyer) - ToggleBuyerMode(false); - - if(conn_state != ClientConnectFinished) { - LogFile->write(EQEMuLog::Debug, "Client '%s' was destroyed before reaching the connected state:", GetName()); - ReportConnectingState(); - } - - if(m_tradeskill_object != nullptr) { - m_tradeskill_object->Close(); - m_tradeskill_object = nullptr; - } - -#ifdef CLIENT_LOGS - client_logs.unsubscribeAll(this); -#endif - - ChangeSQLLog(nullptr); - if(IsDueling() && GetDuelTarget() != 0) { - Entity* entity = entity_list.GetID(GetDuelTarget()); - if(entity != nullptr && entity->IsClient()) { - entity->CastToClient()->SetDueling(false); - entity->CastToClient()->SetDuelTarget(0); - entity_list.DuelMessage(entity->CastToClient(),this,true); - } - } - - if (shield_target) { - for (int y = 0; y < 2; y++) { - if (shield_target->shielder[y].shielder_id == GetID()) { - shield_target->shielder[y].shielder_id = 0; - shield_target->shielder[y].shielder_bonus = 0; - } - } - shield_target = nullptr; - } - - if(GetTarget()) - GetTarget()->IsTargeted(-1); - - //if we are in a group and we are not zoning, force leave the group - if(isgrouped && !zoning && ZoneLoaded) - LeaveGroup(); - - UpdateWho(2); - - if(IsHoveringForRespawn()) - { - m_pp.zone_id = m_pp.binds[0].zoneId; - m_pp.zoneInstance = m_pp.binds[0].instance_id; - m_Position.m_X = m_pp.binds[0].x; - m_Position.m_Y = m_pp.binds[0].y; - m_Position.m_Z = m_pp.binds[0].z; - } - - // we save right now, because the client might be zoning and the world - // will need this data right away - Save(2); // This fails when database destructor is called first on shutdown - - safe_delete(taskstate); - safe_delete(KarmaUpdateTimer); - safe_delete(GlobalChatLimiterTimer); - safe_delete(qGlobals); - safe_delete(adventure_request_timer); - safe_delete(adventure_create_timer); - safe_delete(adventure_leave_timer); - safe_delete(adventure_door_timer); - safe_delete(adventure_stats_timer); - safe_delete(adventure_leaderboard_timer); - safe_delete_array(adv_requested_data); - safe_delete_array(adv_data); - - ClearRespawnOptions(); - - numclients--; - UpdateWindowTitle(); - if(zone) - zone->RemoveAuth(GetName()); - - //let the stream factory know were done with this stream - eqs->Close(); - eqs->ReleaseFromUse(); - - UninitializeBuffSlots(); -} - -void Client::SendLogoutPackets() { - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_CancelTrade, sizeof(CancelTrade_Struct)); - CancelTrade_Struct* ct = (CancelTrade_Struct*) outapp->pBuffer; - ct->fromid = GetID(); - ct->action = groupActUpdate; - FastQueuePacket(&outapp); - - outapp = new EQApplicationPacket(OP_PreLogoutReply); - FastQueuePacket(&outapp); - -} - -void Client::ReportConnectingState() { - switch(conn_state) { - case NoPacketsReceived: //havent gotten anything - LogFile->write(EQEMuLog::Debug, "Client has not sent us an initial zone entry packet."); - break; - case ReceivedZoneEntry: //got the first packet, loading up PP - LogFile->write(EQEMuLog::Debug, "Client sent initial zone packet, but we never got their player info from the database."); - break; - case PlayerProfileLoaded: //our DB work is done, sending it - LogFile->write(EQEMuLog::Debug, "We were sending the player profile, tributes, tasks, spawns, time and weather, but never finished."); - break; - case ZoneInfoSent: //includes PP, tributes, tasks, spawns, time and weather - LogFile->write(EQEMuLog::Debug, "We successfully sent player info and spawns, waiting for client to request new zone."); - break; - case NewZoneRequested: //received and sent new zone request - LogFile->write(EQEMuLog::Debug, "We received client's new zone request, waiting for client spawn request."); - break; - case ClientSpawnRequested: //client sent ReqClientSpawn - LogFile->write(EQEMuLog::Debug, "We received the client spawn request, and were sending objects, doors, zone points and some other stuff, but never finished."); - break; - case ZoneContentsSent: //objects, doors, zone points - LogFile->write(EQEMuLog::Debug, "The rest of the zone contents were successfully sent, waiting for client ready notification."); - break; - case ClientReadyReceived: //client told us its ready, send them a bunch of crap like guild MOTD, etc - LogFile->write(EQEMuLog::Debug, "We received client ready notification, but never finished Client::CompleteConnect"); - break; - case ClientConnectFinished: //client finally moved to finished state, were done here - LogFile->write(EQEMuLog::Debug, "Client is successfully connected."); - break; - }; -} - -bool Client::SaveAA(){ - int first_entry = 0; - std::string rquery; - /* Save Player AA */ - int spentpoints = 0; - for (int a = 0; a < MAX_PP_AA_ARRAY; a++) { - uint32 points = aa[a]->value; - if (points > HIGHEST_AA_VALUE) { - aa[a]->value = HIGHEST_AA_VALUE; - points = HIGHEST_AA_VALUE; - } - if (points > 0) { - SendAA_Struct* curAA = zone->FindAA(aa[a]->AA - aa[a]->value + 1); - if (curAA) { - for (int rank = 0; rank::iterator RequiredLevel = AARequiredLevelAndCost.find(aa[a]->AA - aa[a]->value + 1 + rank); - if (RequiredLevel != AARequiredLevelAndCost.end()) { - spentpoints += RequiredLevel->second.Cost; - } - else - spentpoints += (curAA->cost + (curAA->cost_inc * rank)); - } - } - } - } - 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 (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); - first_entry = 1; - } - rquery = rquery + StringFormat(", (%u, %u, %u, %u)", character_id, a, aa[a]->AA, aa[a]->value); - } - } - auto results = database.QueryDatabase(rquery); - return true; -} - -bool Client::Save(uint8 iCommitNow) { - if(!ClientDataLoaded()) - return false; - - /* Wrote current basics to PP for saves */ - m_pp.x = m_Position.m_X; - m_pp.y = m_Position.m_Y; - m_pp.z = m_Position.m_Z; - m_pp.guildrank = guildrank; - m_pp.heading = m_Position.m_Heading; - - /* Mana and HP */ - if (GetHP() <= 0) { - m_pp.cur_hp = GetMaxHP(); - } - else { - m_pp.cur_hp = GetHP(); - } - - m_pp.mana = cur_mana; - m_pp.endurance = cur_end; - - /* Save Character Currency */ - database.SaveCharacterCurrency(CharacterID(), &m_pp); - - /* Save Current Bind Points */ - auto regularBindPosition = xyz_heading(m_pp.binds[0].x, m_pp.binds[0].y, m_pp.binds[0].z, 0.0f); - auto homeBindPosition = xyz_heading(m_pp.binds[4].x, m_pp.binds[4].y, m_pp.binds[4].z, 0.0f); - database.SaveCharacterBindPoint(CharacterID(), m_pp.binds[0].zoneId, m_pp.binds[0].instance_id, regularBindPosition, 0); /* Regular bind */ - database.SaveCharacterBindPoint(CharacterID(), m_pp.binds[4].zoneId, m_pp.binds[4].instance_id, homeBindPosition, 1); /* Home Bind */ - - /* Save Character Buffs */ - database.SaveBuffs(this); - - /* Total Time Played */ - TotalSecondsPlayed += (time(nullptr) - m_pp.lastlogin); - m_pp.timePlayedMin = (TotalSecondsPlayed / 60); - m_pp.RestTimer = rest_timer.GetRemainingTime() / 1000; - - /* Save Mercs */ - if (GetMercInfo().MercTimerRemaining > RuleI(Mercs, UpkeepIntervalMS)) { - GetMercInfo().MercTimerRemaining = RuleI(Mercs, UpkeepIntervalMS); - } - - if (GetMercTimer()->Enabled()) { - GetMercInfo().MercTimerRemaining = GetMercTimer()->GetRemainingTime(); - } - - if (!(GetMerc() && !dead)) { - memset(&m_mercinfo, 0, sizeof(struct MercInfo)); - } - - m_pp.lastlogin = time(nullptr); - - if (GetPet() && !GetPet()->IsFamiliar() && GetPet()->CastToNPC()->GetPetSpellID() && !dead) { - NPC *pet = GetPet()->CastToNPC(); - m_petinfo.SpellID = pet->CastToNPC()->GetPetSpellID(); - m_petinfo.HP = pet->GetHP(); - m_petinfo.Mana = pet->GetMana(); - pet->GetPetState(m_petinfo.Buffs, m_petinfo.Items, m_petinfo.Name); - m_petinfo.petpower = pet->GetPetPower(); - m_petinfo.size = pet->GetSize(); - } else { - memset(&m_petinfo, 0, sizeof(struct PetInfo)); - } - database.SavePetInfo(this); - - if(tribute_timer.Enabled()) { - m_pp.tribute_time_remaining = tribute_timer.GetRemainingTime(); - } - else { - m_pp.tribute_time_remaining = 0xFFFFFFFF; m_pp.tribute_active = 0; - } - - p_timers.Store(&database); - - database.SaveCharacterTribute(this->CharacterID(), &m_pp); - SaveTaskState(); /* Save Character Task */ - - m_pp.hunger_level = EQEmu::Clamp(m_pp.hunger_level, 0, 50000); - m_pp.thirst_level = EQEmu::Clamp(m_pp.thirst_level, 0, 50000); - database.SaveCharacterData(this->CharacterID(), this->AccountID(), &m_pp, &m_epp); /* Save Character Data */ - - return true; -} - -void Client::SaveBackup() { -} - -CLIENTPACKET::CLIENTPACKET() -{ - app = nullptr; - ack_req = false; -} - -CLIENTPACKET::~CLIENTPACKET() -{ - safe_delete(app); -} - -//this assumes we do not own pApp, and clones it. -bool Client::AddPacket(const EQApplicationPacket *pApp, bool bAckreq) { - if (!pApp) - return false; - if(!zoneinpacket_timer.Enabled()) { - //drop the packet because it will never get sent. - return(false); - } - CLIENTPACKET *c = new CLIENTPACKET; - - c->ack_req = bAckreq; - c->app = pApp->Copy(); - - clientpackets.Append(c); - return true; -} - -//this assumes that it owns the object pointed to by *pApp -bool Client::AddPacket(EQApplicationPacket** pApp, bool bAckreq) { - if (!pApp || !(*pApp)) - return false; - if(!zoneinpacket_timer.Enabled()) { - //drop the packet because it will never get sent. - return(false); - } - CLIENTPACKET *c = new CLIENTPACKET; - - c->ack_req = bAckreq; - c->app = *pApp; - *pApp = 0; - - clientpackets.Append(c); - return true; -} - -bool Client::SendAllPackets() { - LinkedListIterator iterator(clientpackets); - - CLIENTPACKET* cp = 0; - iterator.Reset(); - while(iterator.MoreElements()) { - cp = iterator.GetData(); - if(eqs) - eqs->FastQueuePacket((EQApplicationPacket **)&cp->app, cp->ack_req); - iterator.RemoveCurrent(); -#if EQDEBUG >= 6 - LogFile->write(EQEMuLog::Normal, "Transmitting a packet"); -#endif - } - return true; -} - -void Client::QueuePacket(const EQApplicationPacket* app, bool ack_req, CLIENT_CONN_STATUS required_state, eqFilterType filter) { - if(filter!=FilterNone){ - //this is incomplete... no support for FilterShowGroupOnly or FilterShowSelfOnly - if(GetFilter(filter) == FilterHide) - return; //Client has this filter on, no need to send packet - } - if(client_state != CLIENT_CONNECTED && required_state == CLIENT_CONNECTED){ - AddPacket(app, ack_req); - return; - } - - // if the program doesnt care about the status or if the status isnt what we requested - if (required_state != CLIENT_CONNECTINGALL && client_state != required_state) - { - // todo: save packets for later use - AddPacket(app, ack_req); - } - else - if(eqs) - eqs->QueuePacket(app, ack_req); -} - -void Client::FastQueuePacket(EQApplicationPacket** app, bool ack_req, CLIENT_CONN_STATUS required_state) { - // if the program doesnt care about the status or if the status isnt what we requested - if (required_state != CLIENT_CONNECTINGALL && client_state != required_state) { - // todo: save packets for later use - AddPacket(app, ack_req); - return; - } - else { - if(eqs) - eqs->FastQueuePacket((EQApplicationPacket **)app, ack_req); - else if (app && (*app)) - delete *app; - *app = 0; - } - return; -} - -void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_skill, const char* orig_message, const char* targetname) { - char message[4096]; - strn0cpy(message, orig_message, sizeof(message)); - - - #if EQDEBUG >= 11 - LogFile->write(EQEMuLog::Debug,"Client::ChannelMessageReceived() Channel:%i message:'%s'", chan_num, message); - #endif - - if (targetname == nullptr) { - targetname = (!GetTarget()) ? "" : GetTarget()->GetName(); - } - - if(RuleB(Chat, EnableAntiSpam)) - { - if(strcmp(targetname, "discard") != 0) - { - if(chan_num == 3 || chan_num == 4 || chan_num == 5 || chan_num == 7) - { - if(GlobalChatLimiterTimer) - { - if(GlobalChatLimiterTimer->Check(false)) - { - GlobalChatLimiterTimer->Start(RuleI(Chat, IntervalDurationMS)); - AttemptedMessages = 0; - } - } - - uint32 AllowedMessages = RuleI(Chat, MinimumMessagesPerInterval) + TotalKarma; - AllowedMessages = AllowedMessages > RuleI(Chat, MaximumMessagesPerInterval) ? RuleI(Chat, MaximumMessagesPerInterval) : AllowedMessages; - - if(RuleI(Chat, MinStatusToBypassAntiSpam) <= Admin()) - AllowedMessages = 10000; - - AttemptedMessages++; - if(AttemptedMessages > AllowedMessages) - { - if(AttemptedMessages > RuleI(Chat, MaxMessagesBeforeKick)) - { - Kick(); - return; - } - if(GlobalChatLimiterTimer) - { - Message(0, "You have been rate limited, you can send more messages in %i seconds.", - GlobalChatLimiterTimer->GetRemainingTime() / 1000); - return; - } - else - { - Message(0, "You have been rate limited, you can send more messages in 60 seconds."); - return; - } - } - } - } - } - - /* Logs Player Chat */ - if (RuleB(QueryServ, PlayerLogChat)) { - ServerPacket* pack = new ServerPacket(ServerOP_Speech, sizeof(Server_Speech_Struct) + strlen(message) + 1); - Server_Speech_Struct* sem = (Server_Speech_Struct*) pack->pBuffer; - - if(chan_num == 0) - sem->guilddbid = GuildID(); - else - sem->guilddbid = 0; - - strcpy(sem->message, message); - sem->minstatus = this->Admin(); - sem->type = chan_num; - if(targetname != 0) - strcpy(sem->to, targetname); - - if(GetName() != 0) - strcpy(sem->from, GetName()); - - pack->Deflate(); - if(worldserver.Connected()) - worldserver.SendPacket(pack); - safe_delete(pack); - } - - //Return true to proceed, false to return - if(!mod_client_message(message, chan_num)) { return; } - - // Garble the message based on drunkness - if (m_pp.intoxication > 0) { - GarbleMessage(message, (int)(m_pp.intoxication / 3)); - language = 0; // No need for language when drunk - } - - switch(chan_num) - { - case 0: { /* Guild Chat */ - if (!IsInAGuild()) - Message_StringID(MT_DefaultText, GUILD_NOT_MEMBER2); //You are not a member of any guild. - else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_SPEAK)) - Message(0, "Error: You dont have permission to speak to the guild."); - else if (!worldserver.SendChannelMessage(this, targetname, chan_num, GuildID(), language, message)) - Message(0, "Error: World server disconnected"); - break; - } - case 2: { /* Group Chat */ - Raid* raid = entity_list.GetRaidByClient(this); - if(raid) { - raid->RaidGroupSay((const char*) message, this); - break; - } - - Group* group = GetGroup(); - if(group != nullptr) { - group->GroupMessage(this,language,lang_skill,(const char*) message); - } - break; - } - case 15: { /* Raid Say */ - Raid* raid = entity_list.GetRaidByClient(this); - if(raid){ - raid->RaidSay((const char*) message, this); - } - break; - } - case 3: { /* Shout */ - Mob *sender = this; - if (GetPet() && GetPet()->FindType(SE_VoiceGraft)) - sender = GetPet(); - - entity_list.ChannelMessage(sender, chan_num, language, lang_skill, message); - break; - } - case 4: { /* Auction */ - if(RuleB(Chat, ServerWideAuction)) - { - if(!global_channel_timer.Check()) - { - if(strlen(targetname) == 0) - ChannelMessageReceived(5, language, lang_skill, message, "discard"); //Fast typer or spammer?? - else - return; - } - - if(GetRevoked()) - { - Message(0, "You have been revoked. You may not talk on Auction."); - return; - } - - if(TotalKarma < RuleI(Chat, KarmaGlobalChatLimit)) - { - if(GetLevel() < RuleI(Chat, GlobalChatLevelLimit)) - { - Message(0, "You do not have permission to talk in Auction at this time."); - return; - } - } - - if (!worldserver.SendChannelMessage(this, 0, 4, 0, language, message)) - Message(0, "Error: World server disconnected"); - } - else if(!RuleB(Chat, ServerWideAuction)) { - Mob *sender = this; - - if (GetPet() && GetPet()->FindType(SE_VoiceGraft)) - sender = GetPet(); - - entity_list.ChannelMessage(sender, chan_num, language, message); - } - break; - } - case 5: { /* OOC */ - if(RuleB(Chat, ServerWideOOC)) - { - if(!global_channel_timer.Check()) - { - if(strlen(targetname) == 0) - ChannelMessageReceived(5, language, lang_skill, message, "discard"); //Fast typer or spammer?? - else - return; - } - if(worldserver.IsOOCMuted() && admin < 100) - { - Message(0,"OOC has been muted. Try again later."); - return; - } - - if(GetRevoked()) - { - Message(0, "You have been revoked. You may not talk on OOC."); - return; - } - - if(TotalKarma < RuleI(Chat, KarmaGlobalChatLimit)) - { - if(GetLevel() < RuleI(Chat, GlobalChatLevelLimit)) - { - Message(0, "You do not have permission to talk in OOC at this time."); - return; - } - } - - if (!worldserver.SendChannelMessage(this, 0, 5, 0, language, message)) - { - Message(0, "Error: World server disconnected"); - } - } - else if(!RuleB(Chat, ServerWideOOC)) - { - Mob *sender = this; - - if (GetPet() && GetPet()->FindType(SE_VoiceGraft)) - sender = GetPet(); - - entity_list.ChannelMessage(sender, chan_num, language, message); - } - break; - } - case 6: /* Broadcast */ - case 11: { /* GM Say */ - if (!(admin >= 80)) - Message(0, "Error: Only GMs can use this channel"); - else if (!worldserver.SendChannelMessage(this, targetname, chan_num, 0, language, message)) - Message(0, "Error: World server disconnected"); - break; - } - case 7: { /* Tell */ - if(!global_channel_timer.Check()) - { - if(strlen(targetname) == 0) - ChannelMessageReceived(7, language, lang_skill, message, "discard"); //Fast typer or spammer?? - else - return; - } - - if(GetRevoked()) - { - Message(0, "You have been revoked. You may not send tells."); - return; - } - - if(TotalKarma < RuleI(Chat, KarmaGlobalChatLimit)) - { - if(GetLevel() < RuleI(Chat, GlobalChatLevelLimit)) - { - Message(0, "You do not have permission to send tells at this time."); - return; - } - } - - char target_name[64]; - - if(targetname) - { - size_t i = strlen(targetname); - int x; - for(x = 0; x < i; ++x) - { - if(targetname[x] == '%') - { - target_name[x] = '/'; - } - else - { - target_name[x] = targetname[x]; - } - } - target_name[x] = '\0'; - } - - if(!worldserver.SendChannelMessage(this, target_name, chan_num, 0, language, message)) - Message(0, "Error: World server disconnected"); - break; - } - case 8: { /* Say */ - if(message[0] == COMMAND_CHAR) { - if(command_dispatch(this, message) == -2) { - if(parse->PlayerHasQuestSub(EVENT_COMMAND)) { - int i = parse->EventPlayer(EVENT_COMMAND, this, message, 0); - if(i == 0 && !RuleB(Chat, SuppressCommandErrors)) { - Message(13, "Command '%s' not recognized.", message); - } - } else { - if(!RuleB(Chat, SuppressCommandErrors)) - Message(13, "Command '%s' not recognized.", message); - } - } - break; - } - - Mob* sender = this; - if (GetPet() && GetPet()->FindType(SE_VoiceGraft)) - sender = GetPet(); - - entity_list.ChannelMessage(sender, chan_num, language, lang_skill, message); - parse->EventPlayer(EVENT_SAY, this, message, language); - - if (sender != this) - break; - - if(quest_manager.ProximitySayInUse()) - entity_list.ProcessProximitySay(message, this, language); - - if (GetTarget() != 0 && GetTarget()->IsNPC()) { - if(!GetTarget()->CastToNPC()->IsEngaged()) { - CheckLDoNHail(GetTarget()); - CheckEmoteHail(GetTarget(), message); - - - if(DistNoRootNoZ(*GetTarget()) <= 200) { - NPC *tar = GetTarget()->CastToNPC(); - parse->EventNPC(EVENT_SAY, tar->CastToNPC(), this, message, language); - - if(RuleB(TaskSystem, EnableTaskSystem)) { - if(UpdateTasksOnSpeakWith(tar->GetNPCTypeID())) { - tar->DoQuestPause(this); - } - } - } - } - else { - if (DistNoRootNoZ(*GetTarget()) <= 200) { - parse->EventNPC(EVENT_AGGRO_SAY, GetTarget()->CastToNPC(), this, message, language); - } - } - - } - break; - } - case 20: - { - // UCS Relay for Underfoot and later. - if(!worldserver.SendChannelMessage(this, 0, chan_num, 0, language, message)) - Message(0, "Error: World server disconnected"); - break; - } - case 22: - { - // Emotes for Underfoot and later. - // crash protection -- cheater - message[1023] = '\0'; - size_t msg_len = strlen(message); - if (msg_len > 512) - message[512] = '\0'; - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Emote, 4 + msg_len + strlen(GetName()) + 2); - Emote_Struct* es = (Emote_Struct*)outapp->pBuffer; - char *Buffer = (char *)es; - Buffer += 4; - snprintf(Buffer, sizeof(Emote_Struct) - 4, "%s %s", GetName(), message); - entity_list.QueueCloseClients(this, outapp, true, 100, 0, true, FilterSocials); - safe_delete(outapp); - break; - } - default: { - Message(0, "Channel (%i) not implemented", (uint16)chan_num); - } - } -} - -// if no language skill is specified, call the function with a skill of 100. -void Client::ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, const char* message, ...) { - ChannelMessageSend(from, to, chan_num, language, 100, message); -} - -void Client::ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, uint8 lang_skill, const char* message, ...) { - if ((chan_num==11 && !(this->GetGM())) || (chan_num==10 && this->Admin()<80)) // dont need to send /pr & /petition to everybody - return; - va_list argptr; - char buffer[4096]; - char message_sender[64]; - - va_start(argptr, message); - vsnprintf(buffer, 4096, message, argptr); - va_end(argptr); - - EQApplicationPacket app(OP_ChannelMessage, sizeof(ChannelMessage_Struct)+strlen(buffer)+1); - ChannelMessage_Struct* cm = (ChannelMessage_Struct*)app.pBuffer; - - if (from == 0) - strcpy(cm->sender, "ZServer"); - else if (from[0] == 0) - strcpy(cm->sender, "ZServer"); - else { - CleanMobName(from, message_sender); - strcpy(cm->sender, message_sender); - } - if (to != 0) - strcpy((char *) cm->targetname, to); - else if (chan_num == 7) - strcpy(cm->targetname, m_pp.name); - else - cm->targetname[0] = 0; - - uint8 ListenerSkill; - - if (language < MAX_PP_LANGUAGE) { - ListenerSkill = m_pp.languages[language]; - if (ListenerSkill == 0) { - cm->language = (MAX_PP_LANGUAGE - 1); // in an unknown tongue - } - else { - cm->language = language; - } - } - else { - ListenerSkill = m_pp.languages[0]; - cm->language = 0; - } - - // set effective language skill = lower of sender and receiver skills - int32 EffSkill = (lang_skill < ListenerSkill ? lang_skill : ListenerSkill); - if (EffSkill > 100) // maximum language skill is 100 - EffSkill = 100; - cm->skill_in_language = EffSkill; - - // Garble the message based on listener skill - if (ListenerSkill < 100) { - GarbleMessage(buffer, (100 - ListenerSkill)); - } - - cm->chan_num = chan_num; - strcpy(&cm->message[0], buffer); - QueuePacket(&app); - - if ((chan_num == 2) && (ListenerSkill < 100)) { // group message in unmastered language, check for skill up - if ((m_pp.languages[language] <= lang_skill) && (from != this->GetName())) - CheckLanguageSkillIncrease(language, lang_skill); - } -} - -void Client::Message(uint32 type, const char* message, ...) { - if (GetFilter(FilterSpellDamage) == FilterHide && type == MT_NonMelee) - return; - if (GetFilter(FilterMeleeCrits) == FilterHide && type == MT_CritMelee) //98 is self... - return; - if (GetFilter(FilterSpellCrits) == FilterHide && type == MT_SpellCrits) - return; - - va_list argptr; - char *buffer = new char[4096]; - va_start(argptr, message); - vsnprintf(buffer, 4096, message, argptr); - va_end(argptr); - - size_t len = strlen(buffer); - - //client dosent like our packet all the time unless - //we make it really big, then it seems to not care that - //our header is malformed. - //len = 4096 - sizeof(SpecialMesg_Struct); - - uint32 len_packet = sizeof(SpecialMesg_Struct)+len; - EQApplicationPacket* app = new EQApplicationPacket(OP_SpecialMesg, len_packet); - SpecialMesg_Struct* sm=(SpecialMesg_Struct*)app->pBuffer; - sm->header[0] = 0x00; // Header used for #emote style messages.. - sm->header[1] = 0x00; // Play around with these to see other types - sm->header[2] = 0x00; - sm->msg_type = type; - memcpy(sm->message, buffer, len+1); - - FastQueuePacket(&app); - - safe_delete_array(buffer); -} - -void Client::QuestJournalledMessage(const char *npcname, const char* message) { - - // npcnames longer than 60 characters crash the client when they log back in - const int MaxNPCNameLength = 60; - // I assume there is an upper safe limit on the message length. Don't know what it is, but 4000 doesn't crash - // the client. - const int MaxMessageLength = 4000; - - char OutNPCName[MaxNPCNameLength+1]; - char OutMessage[MaxMessageLength+1]; - - // Apparently Visual C++ snprintf is not C99 compliant and doesn't put the null terminator - // in if the formatted string >= the maximum length, so we put it in. - // - snprintf(OutNPCName, MaxNPCNameLength, "%s", npcname); OutNPCName[MaxNPCNameLength]='\0'; - snprintf(OutMessage, MaxMessageLength, "%s", message); OutMessage[MaxMessageLength]='\0'; - - uint32 len_packet = sizeof(SpecialMesg_Struct) + strlen(OutNPCName) + strlen(OutMessage); - EQApplicationPacket* app = new EQApplicationPacket(OP_SpecialMesg, len_packet); - SpecialMesg_Struct* sm=(SpecialMesg_Struct*)app->pBuffer; - - sm->header[0] = 0; - sm->header[1] = 2; - sm->header[2] = 0; - sm->msg_type = 0x0a; - sm->target_spawn_id = GetID(); - - char *dest = &sm->sayer[0]; - - memcpy(dest, OutNPCName, strlen(OutNPCName) + 1); - - dest = dest + strlen(OutNPCName) + 13; - - memcpy(dest, OutMessage, strlen(OutMessage) + 1); - - QueuePacket(app); - - safe_delete(app); -} - -void Client::SetMaxHP() { - if(dead) - return; - SetHP(CalcMaxHP()); - SendHPUpdate(); - Save(); -} - -bool Client::UpdateLDoNPoints(int32 points, uint32 theme) -{ - -/* make sure total stays in sync with individual buckets - m_pp.ldon_points_available = m_pp.ldon_points_guk - +m_pp.ldon_points_mir - +m_pp.ldon_points_mmc - +m_pp.ldon_points_ruj - +m_pp.ldon_points_tak; */ - - if(points < 0) - { - if(m_pp.ldon_points_available < (0-points)) - return false; - } - switch(theme) - { - // handle generic points (theme=0) - case 0: - { // no theme, so distribute evenly across all - int splitpts=points/5; - int gukpts=splitpts+(points%5); - int mirpts=splitpts; - int mmcpts=splitpts; - int rujpts=splitpts; - int takpts=splitpts; - - splitpts=0; - - if(points < 0) - { - if(m_pp.ldon_points_available < (0-points)) - { - return false; - } - if(m_pp.ldon_points_guk < (0-gukpts)) - { - mirpts+=gukpts+m_pp.ldon_points_guk; - gukpts=0-m_pp.ldon_points_guk; - } - if(m_pp.ldon_points_mir < (0-mirpts)) - { - mmcpts+=mirpts+m_pp.ldon_points_mir; - mirpts=0-m_pp.ldon_points_mir; - } - if(m_pp.ldon_points_mmc < (0-mmcpts)) - { - rujpts+=mmcpts+m_pp.ldon_points_mmc; - mmcpts=0-m_pp.ldon_points_mmc; - } - if(m_pp.ldon_points_ruj < (0-rujpts)) - { - takpts+=rujpts+m_pp.ldon_points_ruj; - rujpts=0-m_pp.ldon_points_ruj; - } - if(m_pp.ldon_points_tak < (0-takpts)) - { - splitpts=takpts+m_pp.ldon_points_tak; - takpts=0-m_pp.ldon_points_tak; - } - } - m_pp.ldon_points_guk += gukpts; - m_pp.ldon_points_mir+=mirpts; - m_pp.ldon_points_mmc += mmcpts; - m_pp.ldon_points_ruj += rujpts; - m_pp.ldon_points_tak += takpts; - points-=splitpts; - // if anything left, recursively loop thru again - if (splitpts !=0) - UpdateLDoNPoints(splitpts,0); - break; - } - case 1: - { - if(points < 0) - { - if(m_pp.ldon_points_guk < (0-points)) - return false; - } - m_pp.ldon_points_guk += points; - break; - } - case 2: - { - if(points < 0) - { - if(m_pp.ldon_points_mir < (0-points)) - return false; - } - m_pp.ldon_points_mir += points; - break; - } - case 3: - { - if(points < 0) - { - if(m_pp.ldon_points_mmc < (0-points)) - return false; - } - m_pp.ldon_points_mmc += points; - break; - } - case 4: - { - if(points < 0) - { - if(m_pp.ldon_points_ruj < (0-points)) - return false; - } - m_pp.ldon_points_ruj += points; - break; - } - case 5: - { - if(points < 0) - { - if(m_pp.ldon_points_tak < (0-points)) - return false; - } - m_pp.ldon_points_tak += points; - break; - } - } - m_pp.ldon_points_available += points; - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventurePointsUpdate, sizeof(AdventurePoints_Update_Struct)); - AdventurePoints_Update_Struct* apus = (AdventurePoints_Update_Struct*)outapp->pBuffer; - apus->ldon_available_points = m_pp.ldon_points_available; - apus->ldon_guk_points = m_pp.ldon_points_guk; - apus->ldon_mirugal_points = m_pp.ldon_points_mir; - apus->ldon_mistmoore_points = m_pp.ldon_points_mmc; - apus->ldon_rujarkian_points = m_pp.ldon_points_ruj; - apus->ldon_takish_points = m_pp.ldon_points_tak; - outapp->priority = 6; - QueuePacket(outapp); - safe_delete(outapp); - return true; - - return(false); -} - -void Client::SetSkill(SkillUseTypes skillid, uint16 value) { - if (skillid > HIGHEST_SKILL) - return; - m_pp.skills[skillid] = value; // We need to be able to #setskill 254 and 255 to reset skills - - database.SaveCharacterSkill(this->CharacterID(), skillid, value); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SkillUpdate, sizeof(SkillUpdate_Struct)); - SkillUpdate_Struct* skill = (SkillUpdate_Struct*)outapp->pBuffer; - skill->skillId=skillid; - skill->value=value; - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::IncreaseLanguageSkill(int skill_id, int value) { - - if (skill_id >= MAX_PP_LANGUAGE) - return; //Invalid lang id - - m_pp.languages[skill_id] += value; - - if (m_pp.languages[skill_id] > 100) //Lang skill above max - m_pp.languages[skill_id] = 100; - - database.SaveCharacterLanguage(this->CharacterID(), skill_id, m_pp.languages[skill_id]); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SkillUpdate, sizeof(SkillUpdate_Struct)); - SkillUpdate_Struct* skill = (SkillUpdate_Struct*)outapp->pBuffer; - skill->skillId = 100 + skill_id; - skill->value = m_pp.languages[skill_id]; - QueuePacket(outapp); - safe_delete(outapp); - - Message_StringID( MT_Skills, LANG_SKILL_IMPROVED ); //Notify client -} - -void Client::AddSkill(SkillUseTypes skillid, uint16 value) { - if (skillid > HIGHEST_SKILL) - return; - value = GetRawSkill(skillid) + value; - uint16 max = GetMaxSkillAfterSpecializationRules(skillid, MaxSkill(skillid)); - if (value > max) - value = max; - SetSkill(skillid, value); -} - -void Client::SendSound(){//Makes a sound. - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Sound, 68); - unsigned char x[68]; - memset(x, 0, 68); - x[0]=0x22; - memset(&x[4],0x8002,sizeof(uint16)); - memset(&x[8],0x8624,sizeof(uint16)); - memset(&x[12],0x4A01,sizeof(uint16)); - x[16]=0x05; - x[28]=0x00;//change this value to give gold to the client - memset(&x[40],0xFFFFFFFF,sizeof(uint32)); - memset(&x[44],0xFFFFFFFF,sizeof(uint32)); - memset(&x[48],0xFFFFFFFF,sizeof(uint32)); - memset(&x[52],0xFFFFFFFF,sizeof(uint32)); - memset(&x[56],0xFFFFFFFF,sizeof(uint32)); - memset(&x[60],0xFFFFFFFF,sizeof(uint32)); - memset(&x[64],0xffffffff,sizeof(uint32)); - memcpy(outapp->pBuffer,x,outapp->size); - QueuePacket(outapp); - safe_delete(outapp); - -} -void Client::UpdateWho(uint8 remove) { - if (account_id == 0) - return; - if (!worldserver.Connected()) - return; - ServerPacket* pack = new ServerPacket(ServerOP_ClientList, sizeof(ServerClientList_Struct)); - ServerClientList_Struct* scl = (ServerClientList_Struct*) pack->pBuffer; - scl->remove = remove; - scl->wid = this->GetWID(); - scl->IP = this->GetIP(); - scl->charid = this->CharacterID(); - strcpy(scl->name, this->GetName()); - - scl->gm = GetGM(); - scl->Admin = this->Admin(); - scl->AccountID = this->AccountID(); - strcpy(scl->AccountName, this->AccountName()); - scl->LSAccountID = this->LSAccountID(); - strn0cpy(scl->lskey, lskey, sizeof(scl->lskey)); - scl->zone = zone->GetZoneID(); - scl->instance_id = zone->GetInstanceID(); - scl->race = this->GetRace(); - scl->class_ = GetClass(); - scl->level = GetLevel(); - if (m_pp.anon == 0) - scl->anon = 0; - else if (m_pp.anon == 1) - scl->anon = 1; - else if (m_pp.anon >= 2) - scl->anon = 2; - - scl->ClientVersion = GetClientVersion(); - scl->tellsoff = tellsoff; - scl->guild_id = guild_id; - scl->LFG = LFG; - if(LFG) { - scl->LFGFromLevel = LFGFromLevel; - scl->LFGToLevel = LFGToLevel; - scl->LFGMatchFilter = LFGMatchFilter; - memcpy(scl->LFGComments, LFGComments, sizeof(scl->LFGComments)); - } - - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void Client::WhoAll(Who_All_Struct* whom) { - - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - ServerPacket* pack = new ServerPacket(ServerOP_Who, sizeof(ServerWhoAll_Struct)); - ServerWhoAll_Struct* whoall = (ServerWhoAll_Struct*) pack->pBuffer; - whoall->admin = this->Admin(); - whoall->fromid=this->GetID(); - strcpy(whoall->from, this->GetName()); - strn0cpy(whoall->whom, whom->whom, 64); - whoall->lvllow = whom->lvllow; - whoall->lvlhigh = whom->lvlhigh; - whoall->gmlookup = whom->gmlookup; - whoall->wclass = whom->wclass; - whoall->wrace = whom->wrace; - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - -void Client::FriendsWho(char *FriendsString) { - - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - ServerPacket* pack = new ServerPacket(ServerOP_FriendsWho, sizeof(ServerFriendsWho_Struct) + strlen(FriendsString)); - ServerFriendsWho_Struct* FriendsWho = (ServerFriendsWho_Struct*) pack->pBuffer; - FriendsWho->FromID = this->GetID(); - strcpy(FriendsWho->FromName, GetName()); - strcpy(FriendsWho->FriendsString, FriendsString); - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - - -void Client::UpdateAdmin(bool iFromDB) { - int16 tmp = admin; - if (iFromDB) - admin = database.CheckStatus(account_id); - if (tmp == admin && iFromDB) - return; - - if(m_pp.gm) - { -#if EQDEBUG >= 5 - printf("%s is a GM\n", GetName()); -#endif -// no need for this, having it set in pp you already start as gm -// and it's also set in your spawn packet so other people see it too -// SendAppearancePacket(AT_GM, 1, false); - petition_list.UpdateGMQueue(); - } - - UpdateWho(); -} - -void Client::SetStats(uint8 type,int16 set_val){ - if(type>STAT_DISEASE){ - printf("Error in Client::IncStats, received invalid type of: %i\n",type); - return; - } - EQApplicationPacket* outapp = new EQApplicationPacket(OP_IncreaseStats,sizeof(IncreaseStat_Struct)); - IncreaseStat_Struct* iss=(IncreaseStat_Struct*)outapp->pBuffer; - switch(type){ - case STAT_STR: - if(set_val>0) - iss->str=set_val; - if(set_val<0) - m_pp.STR=0; - else if(set_val>255) - m_pp.STR=255; - else - m_pp.STR=set_val; - break; - case STAT_STA: - if(set_val>0) - iss->sta=set_val; - if(set_val<0) - m_pp.STA=0; - else if(set_val>255) - m_pp.STA=255; - else - m_pp.STA=set_val; - break; - case STAT_AGI: - if(set_val>0) - iss->agi=set_val; - if(set_val<0) - m_pp.AGI=0; - else if(set_val>255) - m_pp.AGI=255; - else - m_pp.AGI=set_val; - break; - case STAT_DEX: - if(set_val>0) - iss->dex=set_val; - if(set_val<0) - m_pp.DEX=0; - else if(set_val>255) - m_pp.DEX=255; - else - m_pp.DEX=set_val; - break; - case STAT_INT: - if(set_val>0) - iss->int_=set_val; - if(set_val<0) - m_pp.INT=0; - else if(set_val>255) - m_pp.INT=255; - else - m_pp.INT=set_val; - break; - case STAT_WIS: - if(set_val>0) - iss->wis=set_val; - if(set_val<0) - m_pp.WIS=0; - else if(set_val>255) - m_pp.WIS=255; - else - m_pp.WIS=set_val; - break; - case STAT_CHA: - if(set_val>0) - iss->cha=set_val; - if(set_val<0) - m_pp.CHA=0; - else if(set_val>255) - m_pp.CHA=255; - else - m_pp.CHA=set_val; - break; - } - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::IncStats(uint8 type,int16 increase_val){ - if(type>STAT_DISEASE){ - printf("Error in Client::IncStats, received invalid type of: %i\n",type); - return; - } - EQApplicationPacket* outapp = new EQApplicationPacket(OP_IncreaseStats,sizeof(IncreaseStat_Struct)); - IncreaseStat_Struct* iss=(IncreaseStat_Struct*)outapp->pBuffer; - switch(type){ - case STAT_STR: - if(increase_val>0) - iss->str=increase_val; - if((m_pp.STR+increase_val*2)<0) - m_pp.STR=0; - else if((m_pp.STR+increase_val*2)>255) - m_pp.STR=255; - else - m_pp.STR+=increase_val*2; - break; - case STAT_STA: - if(increase_val>0) - iss->sta=increase_val; - if((m_pp.STA+increase_val*2)<0) - m_pp.STA=0; - else if((m_pp.STA+increase_val*2)>255) - m_pp.STA=255; - else - m_pp.STA+=increase_val*2; - break; - case STAT_AGI: - if(increase_val>0) - iss->agi=increase_val; - if((m_pp.AGI+increase_val*2)<0) - m_pp.AGI=0; - else if((m_pp.AGI+increase_val*2)>255) - m_pp.AGI=255; - else - m_pp.AGI+=increase_val*2; - break; - case STAT_DEX: - if(increase_val>0) - iss->dex=increase_val; - if((m_pp.DEX+increase_val*2)<0) - m_pp.DEX=0; - else if((m_pp.DEX+increase_val*2)>255) - m_pp.DEX=255; - else - m_pp.DEX+=increase_val*2; - break; - case STAT_INT: - if(increase_val>0) - iss->int_=increase_val; - if((m_pp.INT+increase_val*2)<0) - m_pp.INT=0; - else if((m_pp.INT+increase_val*2)>255) - m_pp.INT=255; - else - m_pp.INT+=increase_val*2; - break; - case STAT_WIS: - if(increase_val>0) - iss->wis=increase_val; - if((m_pp.WIS+increase_val*2)<0) - m_pp.WIS=0; - else if((m_pp.WIS+increase_val*2)>255) - m_pp.WIS=255; - else - m_pp.WIS+=increase_val*2; - break; - case STAT_CHA: - if(increase_val>0) - iss->cha=increase_val; - if((m_pp.CHA+increase_val*2)<0) - m_pp.CHA=0; - else if((m_pp.CHA+increase_val*2)>255) - m_pp.CHA=255; - else - m_pp.CHA+=increase_val*2; - break; - } - QueuePacket(outapp); - safe_delete(outapp); -} - -const int32& Client::SetMana(int32 amount) { - bool update = false; - if (amount < 0) - amount = 0; - if (amount > GetMaxMana()) - amount = GetMaxMana(); - if (amount != cur_mana) - update = true; - cur_mana = amount; - if (update) - Mob::SetMana(amount); - SendManaUpdatePacket(); - return cur_mana; -} - -void Client::SendManaUpdatePacket() { - if (!Connected() || IsCasting()) - return; - - if (GetClientVersion() >= EQClientSoD) { - SendManaUpdate(); - SendEnduranceUpdate(); - } - - //std::cout << "Sending mana update: " << (cur_mana - last_reported_mana) << std::endl; - if (last_reported_mana != cur_mana || last_reported_endur != cur_end) { - - - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ManaChange, sizeof(ManaChange_Struct)); - ManaChange_Struct* manachange = (ManaChange_Struct*)outapp->pBuffer; - manachange->new_mana = cur_mana; - manachange->stamina = cur_end; - manachange->spell_id = casting_spell_id; //always going to be 0... since we check IsCasting() - outapp->priority = 6; - QueuePacket(outapp); - safe_delete(outapp); - - Group *g = GetGroup(); - - if(g) - { - outapp = new EQApplicationPacket(OP_MobManaUpdate, sizeof(MobManaUpdate_Struct)); - EQApplicationPacket *outapp2 = new EQApplicationPacket(OP_MobEnduranceUpdate, sizeof(MobEnduranceUpdate_Struct)); - - MobManaUpdate_Struct *mmus = (MobManaUpdate_Struct *)outapp->pBuffer; - MobEnduranceUpdate_Struct *meus = (MobEnduranceUpdate_Struct *)outapp2->pBuffer; - - mmus->spawn_id = meus->spawn_id = GetID(); - - mmus->mana = GetManaPercent(); - meus->endurance = GetEndurancePercent(); - - - for(int i = 0; i < MAX_GROUP_MEMBERS; ++i) - if(g->members[i] && g->members[i]->IsClient() && (g->members[i] != this) && (g->members[i]->CastToClient()->GetClientVersion() >= EQClientSoD)) - { - g->members[i]->CastToClient()->QueuePacket(outapp); - g->members[i]->CastToClient()->QueuePacket(outapp2); - } - - safe_delete(outapp); - safe_delete(outapp2); - } - - - last_reported_mana = cur_mana; - last_reported_endur = cur_end; - } -} - -// sends mana update to self -void Client::SendManaUpdate() -{ - EQApplicationPacket* mana_app = new EQApplicationPacket(OP_ManaUpdate,sizeof(ManaUpdate_Struct)); - ManaUpdate_Struct* mus = (ManaUpdate_Struct*)mana_app->pBuffer; - mus->cur_mana = GetMana(); - mus->max_mana = GetMaxMana(); - mus->spawn_id = GetID(); - QueuePacket(mana_app); - entity_list.QueueClientsByXTarget(this, mana_app, false); - safe_delete(mana_app); -} - -// sends endurance update to self -void Client::SendEnduranceUpdate() -{ - EQApplicationPacket* end_app = new EQApplicationPacket(OP_EnduranceUpdate,sizeof(EnduranceUpdate_Struct)); - EnduranceUpdate_Struct* eus = (EnduranceUpdate_Struct*)end_app->pBuffer; - eus->cur_end = GetEndurance(); - eus->max_end = GetMaxEndurance(); - eus->spawn_id = GetID(); - QueuePacket(end_app); - entity_list.QueueClientsByXTarget(this, end_app, false); - safe_delete(end_app); -} - -void Client::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) -{ - Mob::FillSpawnStruct(ns, ForWho); - - // Populate client-specific spawn information - ns->spawn.afk = AFK; - ns->spawn.lfg = LFG; // afk and lfg are cleared on zoning on live - ns->spawn.anon = m_pp.anon; - ns->spawn.gm = GetGM() ? 1 : 0; - ns->spawn.guildID = GuildID(); -// ns->spawn.linkdead = IsLD() ? 1 : 0; -// ns->spawn.pvp = GetPVP() ? 1 : 0; - - - strcpy(ns->spawn.title, m_pp.title); - strcpy(ns->spawn.suffix, m_pp.suffix); - - if (IsBecomeNPC() == true) - ns->spawn.NPC = 1; - else if (ForWho == this) - ns->spawn.NPC = 10; - else - ns->spawn.NPC = 0; - ns->spawn.is_pet = 0; - - if (!IsInAGuild()) { - ns->spawn.guildrank = 0xFF; - } else { - ns->spawn.guildrank = guild_mgr.GetDisplayedRank(GuildID(), GuildRank(), AccountID()); - } - ns->spawn.size = 0; // Changing size works, but then movement stops! (wth?) - ns->spawn.runspeed = (gmspeed == 0) ? runspeed : 3.125f; - if (!m_pp.showhelm) ns->spawn.showhelm = 0; - - // pp also hold this info; should we pull from there or inventory? - // (update: i think pp should do it, as this holds LoY dye - plus, this is ugly code with Inventory!) - const Item_Struct* item = nullptr; - const ItemInst* inst = nullptr; - if ((inst = m_inv[MainHands]) && inst->IsType(ItemClassCommon)) { - item = inst->GetItem(); - ns->spawn.equipment[MaterialHands] = item->Material; - ns->spawn.colors[MaterialHands].color = GetEquipmentColor(MaterialHands); - } - if ((inst = m_inv[MainHead]) && inst->IsType(ItemClassCommon)) { - item = inst->GetItem(); - ns->spawn.equipment[MaterialHead] = item->Material; - ns->spawn.colors[MaterialHead].color = GetEquipmentColor(MaterialHead); - } - if ((inst = m_inv[MainArms]) && inst->IsType(ItemClassCommon)) { - item = inst->GetItem(); - ns->spawn.equipment[MaterialArms] = item->Material; - ns->spawn.colors[MaterialArms].color = GetEquipmentColor(MaterialArms); - } - if ((inst = m_inv[MainWrist1]) && inst->IsType(ItemClassCommon)) { - item = inst->GetItem(); - ns->spawn.equipment[MaterialWrist]= item->Material; - ns->spawn.colors[MaterialWrist].color = GetEquipmentColor(MaterialWrist); - } - - /* - // non-live behavior - if ((inst = m_inv[SLOT_BRACER02]) && inst->IsType(ItemClassCommon)) { - item = inst->GetItem(); - ns->spawn.equipment[MaterialWrist]= item->Material; - ns->spawn.colors[MaterialWrist].color = GetEquipmentColor(MaterialWrist); - } - */ - - if ((inst = m_inv[MainChest]) && inst->IsType(ItemClassCommon)) { - item = inst->GetItem(); - ns->spawn.equipment[MaterialChest] = item->Material; - ns->spawn.colors[MaterialChest].color = GetEquipmentColor(MaterialChest); - } - if ((inst = m_inv[MainLegs]) && inst->IsType(ItemClassCommon)) { - item = inst->GetItem(); - ns->spawn.equipment[MaterialLegs] = item->Material; - ns->spawn.colors[MaterialLegs].color = GetEquipmentColor(MaterialLegs); - } - if ((inst = m_inv[MainFeet]) && inst->IsType(ItemClassCommon)) { - item = inst->GetItem(); - ns->spawn.equipment[MaterialFeet] = item->Material; - ns->spawn.colors[MaterialFeet].color = GetEquipmentColor(MaterialFeet); - } - int ornamentationAugtype = RuleI(Character, OrnamentationAugmentType); - if ((inst = m_inv[MainPrimary]) && inst->IsType(ItemClassCommon)) { - if (inst->GetOrnamentationAug(ornamentationAugtype)) { - item = inst->GetOrnamentationAug(ornamentationAugtype)->GetItem(); - if (strlen(item->IDFile) > 2) - ns->spawn.equipment[MaterialPrimary] = atoi(&item->IDFile[2]); - } - else if (inst->GetOrnamentationIcon() && inst->GetOrnamentationIDFile()) { - ns->spawn.equipment[MaterialPrimary] = inst->GetOrnamentationIDFile(); - } - else { - item = inst->GetItem(); - if (strlen(item->IDFile) > 2) - ns->spawn.equipment[MaterialPrimary] = atoi(&item->IDFile[2]); - } - } - if ((inst = m_inv[MainSecondary]) && inst->IsType(ItemClassCommon)) { - if (inst->GetOrnamentationAug(ornamentationAugtype)) { - item = inst->GetOrnamentationAug(ornamentationAugtype)->GetItem(); - if (strlen(item->IDFile) > 2) - ns->spawn.equipment[MaterialSecondary] = atoi(&item->IDFile[2]); - } - else if (inst->GetOrnamentationIcon() && inst->GetOrnamentationIDFile()) { - ns->spawn.equipment[MaterialSecondary] = inst->GetOrnamentationIDFile(); - } - else { - item = inst->GetItem(); - if (strlen(item->IDFile) > 2) - ns->spawn.equipment[MaterialSecondary] = atoi(&item->IDFile[2]); - } - } - - //these two may be related to ns->spawn.texture - /* - ns->spawn.npc_armor_graphic = texture; - ns->spawn.npc_helm_graphic = helmtexture; - */ - - //filling in some unknowns to make the client happy -// ns->spawn.unknown0002[2] = 3; - -} - -bool Client::GMHideMe(Client* client) { - if (gmhideme) { - if (client == 0) - return true; - else if (admin > client->Admin()) - return true; - else - return false; - } - else - return false; -} - -void Client::Duck() { - SetAppearance(eaCrouching, false); -} - -void Client::Stand() { - SetAppearance(eaStanding, false); -} - -void Client::ChangeLastName(const char* in_lastname) { - memset(m_pp.last_name, 0, sizeof(m_pp.last_name)); - strn0cpy(m_pp.last_name, in_lastname, sizeof(m_pp.last_name)); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_GMLastName, sizeof(GMLastName_Struct)); - GMLastName_Struct* gmn = (GMLastName_Struct*)outapp->pBuffer; - strcpy(gmn->name, name); - strcpy(gmn->gmname, name); - strcpy(gmn->lastname, in_lastname); - gmn->unknown[0]=1; - gmn->unknown[1]=1; - gmn->unknown[2]=1; - gmn->unknown[3]=1; - entity_list.QueueClients(this, outapp, false); - // Send name update packet here... once know what it is - safe_delete(outapp); -} - -bool Client::ChangeFirstName(const char* in_firstname, const char* gmname) -{ - // check duplicate name - bool usedname = database.CheckUsedName((const char*) in_firstname); - if (!usedname) { - return false; - } - - // update character_ - if(!database.UpdateName(GetName(), in_firstname)) - return false; - - // update pp - memset(m_pp.name, 0, sizeof(m_pp.name)); - snprintf(m_pp.name, sizeof(m_pp.name), "%s", in_firstname); - strcpy(name, m_pp.name); - Save(); - - // send name update packet - EQApplicationPacket* outapp = new EQApplicationPacket(OP_GMNameChange, sizeof(GMName_Struct)); - GMName_Struct* gmn=(GMName_Struct*)outapp->pBuffer; - strn0cpy(gmn->gmname,gmname,64); - strn0cpy(gmn->oldname,GetName(),64); - strn0cpy(gmn->newname,in_firstname,64); - gmn->unknown[0] = 1; - gmn->unknown[1] = 1; - gmn->unknown[2] = 1; - entity_list.QueueClients(this, outapp, false); - safe_delete(outapp); - - // finally, update the /who list - UpdateWho(); - - // success - return true; -} - -void Client::SetGM(bool toggle) { - m_pp.gm = toggle ? 1 : 0; - Message(13, "You are %s a GM.", m_pp.gm ? "now" : "no longer"); - SendAppearancePacket(AT_GM, m_pp.gm); - Save(); - UpdateWho(); -} - -void Client::ReadBook(BookRequest_Struct *book) { - char *txtfile = book->txtfile; - - if(txtfile[0] == '0' && txtfile[1] == '\0') { - //invalid book... coming up on non-book items. - return; - } - - std::string booktxt2 = database.GetBook(txtfile); - int length = booktxt2.length(); - - if (booktxt2[0] != '\0') { -#if EQDEBUG >= 6 - LogFile->write(EQEMuLog::Normal,"Client::ReadBook() textfile:%s Text:%s", txtfile, booktxt2.c_str()); -#endif - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ReadBook, length + sizeof(BookText_Struct)); - - BookText_Struct *out = (BookText_Struct *) outapp->pBuffer; - out->window = book->window; - if(GetClientVersion() >= EQClientSoF) - { - const ItemInst *inst = m_inv[book->invslot]; - if(inst) - out->type = inst->GetItem()->Book; - else - out->type = book->type; - } - else - { - out->type = book->type; - } - out->invslot = book->invslot; - memcpy(out->booktext, booktxt2.c_str(), length); - - QueuePacket(outapp); - safe_delete(outapp); - } -} - -void Client::QuestReadBook(const char* text, uint8 type) { - std::string booktxt2 = text; - int length = booktxt2.length(); - if (booktxt2[0] != '\0') { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ReadBook, length + sizeof(BookText_Struct)); - BookText_Struct *out = (BookText_Struct *) outapp->pBuffer; - out->window = 0xFF; - out->type = type; - out->invslot = 0; - memcpy(out->booktext, booktxt2.c_str(), length); - QueuePacket(outapp); - safe_delete(outapp); - } -} - -void Client::SendClientMoneyUpdate(uint8 type,uint32 amount){ - EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeMoneyUpdate,sizeof(TradeMoneyUpdate_Struct)); - TradeMoneyUpdate_Struct* mus= (TradeMoneyUpdate_Struct*)outapp->pBuffer; - mus->amount=amount; - mus->trader=0; - mus->type=type; - QueuePacket(outapp); - safe_delete(outapp); -} - -bool Client::TakeMoneyFromPP(uint64 copper, bool updateclient) { - int64 copperpp,silver,gold,platinum; - copperpp = m_pp.copper; - silver = static_cast(m_pp.silver) * 10; - gold = static_cast(m_pp.gold) * 100; - platinum = static_cast(m_pp.platinum) * 1000; - - int64 clienttotal = copperpp + silver + gold + platinum; - - clienttotal -= copper; - if(clienttotal < 0) - { - return false; // Not enough money! - } - else - { - copperpp -= copper; - if(copperpp <= 0) - { - copper = abs64(copperpp); - m_pp.copper = 0; - } - else - { - m_pp.copper = copperpp; - if(updateclient) - SendMoneyUpdate(); - SaveCurrency(); - return true; - } - silver -= copper; - if(silver <= 0) - { - copper = abs64(silver); - m_pp.silver = 0; - } - else - { - m_pp.silver = silver/10; - m_pp.copper += (silver-(m_pp.silver*10)); - if(updateclient) - SendMoneyUpdate(); - SaveCurrency(); - return true; - } - - gold -=copper; - - if(gold <= 0) - { - copper = abs64(gold); - m_pp.gold = 0; - } - else - { - m_pp.gold = gold/100; - uint64 silvertest = (gold-(static_cast(m_pp.gold)*100))/10; - m_pp.silver += silvertest; - uint64 coppertest = (gold-(static_cast(m_pp.gold)*100+silvertest*10)); - m_pp.copper += coppertest; - if(updateclient) - SendMoneyUpdate(); - SaveCurrency(); - return true; - } - - platinum -= copper; - - //Impossible for plat to be negative, already checked above - - m_pp.platinum = platinum/1000; - uint64 goldtest = (platinum-(static_cast(m_pp.platinum)*1000))/100; - m_pp.gold += goldtest; - uint64 silvertest = (platinum-(static_cast(m_pp.platinum)*1000+goldtest*100))/10; - m_pp.silver += silvertest; - uint64 coppertest = (platinum-(static_cast(m_pp.platinum)*1000+goldtest*100+silvertest*10)); - m_pp.copper = coppertest; - if(updateclient) - SendMoneyUpdate(); - RecalcWeight(); - SaveCurrency(); - return true; - } -} - -void Client::AddMoneyToPP(uint64 copper, bool updateclient){ - uint64 tmp; - uint64 tmp2; - tmp = copper; - - /* Add Amount of Platinum */ - tmp2 = tmp/1000; - int32 new_val = m_pp.platinum + tmp2; - if(new_val < 0) { m_pp.platinum = 0; } - else { m_pp.platinum = m_pp.platinum + tmp2; } - tmp-=tmp2*1000; - - //if (updateclient) - // SendClientMoneyUpdate(3,tmp2); - - /* Add Amount of Gold */ - tmp2 = tmp/100; - new_val = m_pp.gold + tmp2; - if(new_val < 0) { m_pp.gold = 0; } - else { m_pp.gold = m_pp.gold + tmp2; } - - tmp-=tmp2*100; - //if (updateclient) - // SendClientMoneyUpdate(2,tmp2); - - /* Add Amount of Silver */ - tmp2 = tmp/10; - new_val = m_pp.silver + tmp2; - if(new_val < 0) { - m_pp.silver = 0; - } else { - m_pp.silver = m_pp.silver + tmp2; - } - tmp-=tmp2*10; - //if (updateclient) - // SendClientMoneyUpdate(1,tmp2); - - // Add Copper - //tmp = tmp - (tmp2* 10); - //if (updateclient) - // SendClientMoneyUpdate(0,tmp); - tmp2 = tmp; - new_val = m_pp.copper + tmp2; - if(new_val < 0) { - m_pp.copper = 0; - } else { - m_pp.copper = m_pp.copper + tmp2; - } - - - //send them all at once, since the above code stopped working. - if(updateclient) - SendMoneyUpdate(); - - RecalcWeight(); - - SaveCurrency(); - - LogFile->write(EQEMuLog::Debug, "Client::AddMoneyToPP() %s should have: plat:%i gold:%i silver:%i copper:%i", GetName(), m_pp.platinum, m_pp.gold, m_pp.silver, m_pp.copper); -} - -void Client::EVENT_ITEM_ScriptStopReturn(){ - /* Set a timestamp in an entity variable for plugin check_handin.pl in return_items - This will stopgap players from items being returned if global_npc.pl has a catch all return_items - */ - struct timeval read_time; - char buffer[50]; - gettimeofday(&read_time, 0); - sprintf(buffer, "%li.%li \n", read_time.tv_sec, read_time.tv_usec); - this->SetEntityVariable("Stop_Return", buffer); -} - -void Client::AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, bool updateclient){ - this->EVENT_ITEM_ScriptStopReturn(); - - int32 new_value = m_pp.platinum + platinum; - if(new_value >= 0 && new_value > m_pp.platinum) - m_pp.platinum += platinum; - - new_value = m_pp.gold + gold; - if(new_value >= 0 && new_value > m_pp.gold) - m_pp.gold += gold; - - new_value = m_pp.silver + silver; - if(new_value >= 0 && new_value > m_pp.silver) - m_pp.silver += silver; - - new_value = m_pp.copper + copper; - if(new_value >= 0 && new_value > m_pp.copper) - m_pp.copper += copper; - - if(updateclient) - SendMoneyUpdate(); - - RecalcWeight(); - SaveCurrency(); - -#if (EQDEBUG>=5) - LogFile->write(EQEMuLog::Debug, "Client::AddMoneyToPP() %s should have: plat:%i gold:%i silver:%i copper:%i", - GetName(), m_pp.platinum, m_pp.gold, m_pp.silver, m_pp.copper); -#endif -} - -void Client::SendMoneyUpdate() { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_MoneyUpdate,sizeof(MoneyUpdate_Struct)); - MoneyUpdate_Struct* mus= (MoneyUpdate_Struct*)outapp->pBuffer; - - mus->platinum = m_pp.platinum; - mus->gold = m_pp.gold; - mus->silver = m_pp.silver; - mus->copper = m_pp.copper; - - FastQueuePacket(&outapp); -} - -bool Client::HasMoney(uint64 Copper) { - - if ((static_cast(m_pp.copper) + - (static_cast(m_pp.silver) * 10) + - (static_cast(m_pp.gold) * 100) + - (static_cast(m_pp.platinum) * 1000)) >= Copper) - return true; - - return false; -} - -uint64 Client::GetCarriedMoney() { - - return ((static_cast(m_pp.copper) + - (static_cast(m_pp.silver) * 10) + - (static_cast(m_pp.gold) * 100) + - (static_cast(m_pp.platinum) * 1000))); -} - -uint64 Client::GetAllMoney() { - - return ( - (static_cast(m_pp.copper) + - (static_cast(m_pp.silver) * 10) + - (static_cast(m_pp.gold) * 100) + - (static_cast(m_pp.platinum) * 1000) + - (static_cast(m_pp.copper_bank) + - (static_cast(m_pp.silver_bank) * 10) + - (static_cast(m_pp.gold_bank) * 100) + - (static_cast(m_pp.platinum_bank) * 1000) + - (static_cast(m_pp.copper_cursor) + - (static_cast(m_pp.silver_cursor) * 10) + - (static_cast(m_pp.gold_cursor) * 100) + - (static_cast(m_pp.platinum_cursor) * 1000) + - (static_cast(m_pp.platinum_shared) * 1000))))); -} - -bool Client::CheckIncreaseSkill(SkillUseTypes skillid, Mob *against_who, int chancemodi) { - if (IsDead() || IsUnconscious()) - return false; - if (IsAIControlled()) // no skillups while chamred =p - return false; - if (skillid > HIGHEST_SKILL) - return false; - int skillval = GetRawSkill(skillid); - int maxskill = GetMaxSkillAfterSpecializationRules(skillid, MaxSkill(skillid)); - - if(against_who) - { - if(against_who->GetSpecialAbility(IMMUNE_AGGRO) || against_who->IsClient() || - GetLevelCon(against_who->GetLevel()) == CON_GREEN) - { - //false by default - if( !mod_can_increase_skill(skillid, against_who) ) { return(false); } - } - } - - // Make sure we're not already at skill cap - if (skillval < maxskill) - { - // the higher your current skill level, the harder it is - int32 Chance = 10 + chancemodi + ((252 - skillval) / 20); - - Chance = (Chance * RuleI(Character, SkillUpModifier) / 100); - - Chance = mod_increase_skill_chance(Chance, against_who); - - if(Chance < 1) - Chance = 1; // Make it always possible - - if(zone->random.Real(0, 99) < Chance) - { - SetSkill(skillid, GetRawSkill(skillid) + 1); - _log(SKILLS__GAIN, "Skill %d at value %d successfully gain with %.4f%%chance (mod %d)", skillid, skillval, Chance, chancemodi); - return true; - } else { - _log(SKILLS__GAIN, "Skill %d at value %d failed to gain with %.4f%%chance (mod %d)", skillid, skillval, Chance, chancemodi); - } - } else { - _log(SKILLS__GAIN, "Skill %d at value %d cannot increase due to maxmum %d", skillid, skillval, maxskill); - } - return false; -} - -void Client::CheckLanguageSkillIncrease(uint8 langid, uint8 TeacherSkill) { - if (IsDead() || IsUnconscious()) - return; - if (IsAIControlled()) - return; - if (langid >= MAX_PP_LANGUAGE) - return; // do nothing if langid is an invalid language - - int LangSkill = m_pp.languages[langid]; // get current language skill - - if (LangSkill < 100) { // if the language isn't already maxed - int32 Chance = 5 + ((TeacherSkill - LangSkill)/10); // greater chance to learn if teacher's skill is much higher than yours - Chance = (Chance * RuleI(Character, SkillUpModifier)/100); - - if(zone->random.Real(0,100) < Chance) { // if they make the roll - IncreaseLanguageSkill(langid); // increase the language skill by 1 - _log(SKILLS__GAIN, "Language %d at value %d successfully gain with %.4f%%chance", langid, LangSkill, Chance); - } - else - _log(SKILLS__GAIN, "Language %d at value %d failed to gain with %.4f%%chance", langid, LangSkill, Chance); - } -} - -bool Client::HasSkill(SkillUseTypes skill_id) const { - return((GetSkill(skill_id) > 0) && CanHaveSkill(skill_id)); -} - -bool Client::CanHaveSkill(SkillUseTypes skill_id) const { - return(database.GetSkillCap(GetClass(), skill_id, RuleI(Character, MaxLevel)) > 0); - //if you don't have it by max level, then odds are you never will? -} - -uint16 Client::MaxSkill(SkillUseTypes skillid, uint16 class_, uint16 level) const { - return(database.GetSkillCap(class_, skillid, level)); -} - -uint8 Client::SkillTrainLevel(SkillUseTypes skillid, uint16 class_){ - return(database.GetTrainLevel(class_, skillid, RuleI(Character, MaxLevel))); -} - -uint16 Client::GetMaxSkillAfterSpecializationRules(SkillUseTypes skillid, uint16 maxSkill) -{ - uint16 Result = maxSkill; - - uint16 PrimarySpecialization = 0, SecondaryForte = 0; - - uint16 PrimarySkillValue = 0, SecondarySkillValue = 0; - - uint16 MaxSpecializations = GetAA(aaSecondaryForte) ? 2 : 1; - - if(skillid >= SkillSpecializeAbjure && skillid <= SkillSpecializeEvocation) - { - bool HasPrimarySpecSkill = false; - - int NumberOfPrimarySpecSkills = 0; - - for(int i = SkillSpecializeAbjure; i <= SkillSpecializeEvocation; ++i) - { - if(m_pp.skills[i] > 50) - { - HasPrimarySpecSkill = true; - NumberOfPrimarySpecSkills++; - } - if(m_pp.skills[i] > PrimarySkillValue) - { - if(PrimarySkillValue > SecondarySkillValue) - { - SecondarySkillValue = PrimarySkillValue; - SecondaryForte = PrimarySpecialization; - } - - PrimarySpecialization = i; - PrimarySkillValue = m_pp.skills[i]; - } - else if(m_pp.skills[i] > SecondarySkillValue) - { - SecondaryForte = i; - SecondarySkillValue = m_pp.skills[i]; - } - } - - if(SecondarySkillValue <=50) - SecondaryForte = 0; - - if(HasPrimarySpecSkill) - { - if(NumberOfPrimarySpecSkills <= MaxSpecializations) - { - if(MaxSpecializations == 1) - { - if(skillid != PrimarySpecialization) - { - Result = 50; - } - } - else - { - if((skillid != PrimarySpecialization) && ((skillid == SecondaryForte) || (SecondaryForte == 0))) - { - if((PrimarySkillValue > 100) || (!SecondaryForte)) - Result = 100; - } - else if(skillid != PrimarySpecialization) - { - Result = 50; - } - } - } - else - { - Message(13, "Your spell casting specializations skills have been reset. " - "Only %i primary specialization skill is allowed.", MaxSpecializations); - - for(int i = SkillSpecializeAbjure; i <= SkillSpecializeEvocation; ++i) - SetSkill((SkillUseTypes)i, 1); - - Save(); - - LogFile->write(EQEMuLog::Normal, "Reset %s's caster specialization skills to 1. " - "Too many specializations skills were above 50.", GetCleanName()); - } - - } - } - // This should possibly be handled by bonuses rather than here. - switch(skillid) - { - case SkillTracking: - { - Result += ((GetAA(aaAdvancedTracking) * 10) + (GetAA(aaTuneofPursuance) * 10)); - break; - } - - default: - break; - } - - return Result; -} - -void Client::SetPVP(bool toggle) { - m_pp.pvp = toggle ? 1 : 0; - - if(GetPVP()) - this->Message_StringID(MT_Shout,PVP_ON); - else - Message(13, "You no longer follow the ways of discord."); - - SendAppearancePacket(AT_PVP, GetPVP()); - Save(); -} - -void Client::WorldKick() { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_GMKick, sizeof(GMKick_Struct)); - GMKick_Struct* gmk = (GMKick_Struct *)outapp->pBuffer; - strcpy(gmk->name,GetName()); - QueuePacket(outapp); - safe_delete(outapp); - Kick(); -} - -void Client::GMKill() { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_GMKill, sizeof(GMKill_Struct)); - GMKill_Struct* gmk = (GMKill_Struct *)outapp->pBuffer; - strcpy(gmk->name,GetName()); - QueuePacket(outapp); - safe_delete(outapp); -} - -bool Client::CheckAccess(int16 iDBLevel, int16 iDefaultLevel) { - if ((admin >= iDBLevel) || (iDBLevel == 255 && admin >= iDefaultLevel)) - return true; - else - return false; -} - -void Client::MemorizeSpell(uint32 slot,uint32 spellid,uint32 scribing){ - EQApplicationPacket* outapp = new EQApplicationPacket(OP_MemorizeSpell,sizeof(MemorizeSpell_Struct)); - MemorizeSpell_Struct* mss=(MemorizeSpell_Struct*)outapp->pBuffer; - mss->scribing=scribing; - mss->slot=slot; - mss->spell_id=spellid; - outapp->priority = 5; - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::SetFeigned(bool in_feigned) { - if (in_feigned) - { - if(RuleB(Character, FeignKillsPet)) - { - SetPet(0); - } - SetHorseId(0); - entity_list.ClearFeignAggro(this); - forget_timer.Start(FeignMemoryDuration); - } else { - forget_timer.Disable(); - } - feigned=in_feigned; - } - -void Client::LogMerchant(Client* player, Mob* merchant, uint32 quantity, uint32 price, const Item_Struct* item, bool buying) -{ - if(!player || !merchant || !item) - return; - - std::string LogText = "Qty: "; - - char Buffer[255]; - memset(Buffer, 0, sizeof(Buffer)); - - snprintf(Buffer, sizeof(Buffer)-1, "%3i", quantity); - LogText += Buffer; - snprintf(Buffer, sizeof(Buffer)-1, "%10i", price); - LogText += " TotalValue: "; - LogText += Buffer; - snprintf(Buffer, sizeof(Buffer)-1, " ItemID: %7i", item->ID); - LogText += Buffer; - LogText += " "; - snprintf(Buffer, sizeof(Buffer)-1, " %s", item->Name); - LogText += Buffer; - - if (buying==true) { - database.logevents(player->AccountName(),player->AccountID(),player->admin,player->GetName(),merchant->GetName(),"Buying from Merchant",LogText.c_str(),2); - } - else { - database.logevents(player->AccountName(),player->AccountID(),player->admin,player->GetName(),merchant->GetName(),"Selling to Merchant",LogText.c_str(),3); - } -} - -bool Client::BindWound(Mob* bindmob, bool start, bool fail){ - EQApplicationPacket* outapp = 0; - 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()) { - //make sure we actually have a bandage... and consume it. - int16 bslot = m_inv.HasItemByUse(ItemTypeBandage, 1, invWhereWorn|invWherePersonal); - if (bslot == INVALID_INDEX) { - bind_out->type = 3; - QueuePacket(outapp); - bind_out->type = 7; //this is the wrong message, dont know the right one. - QueuePacket(outapp); - return(true); - } - DeleteItemInInventory(bslot, 1, true); //do we need client update? - - // start complete timer - bindwound_timer.Start(10000); - bindwound_target = bindmob; - - // Send client unlock - bind_out->type = 3; - QueuePacket(outapp); - bind_out->type = 0; - // Client Unlocked - if(!bindmob) { - // send "bindmob dead" to client - bind_out->type = 4; - QueuePacket(outapp); - bind_out->type = 0; - bindwound_timer.Disable(); - bindwound_target = 0; - } - else { - // send bindmob "stand still" - if(!bindmob->IsAIControlled() && bindmob != this ) { - bind_out->type = 2; // ? - //bind_out->type = 3; // ? - bind_out->to = GetID(); // ? - bindmob->CastToClient()->QueuePacket(outapp); - bind_out->type = 0; - bind_out->to = 0; - } - else if (bindmob->IsAIControlled() && bindmob != this ){ - ; // Tell IPC to stand still? - } - else { - ; // Binding self - } - } - } else { - // finish bind - // disable complete timer - bindwound_timer.Disable(); - bindwound_target = 0; - if(!bindmob){ - // send "bindmob gone" to client - bind_out->type = 5; // not in zone - QueuePacket(outapp); - bind_out->type = 0; - } - - else { - if (!GetFeigned() && (bindmob->DistNoRoot(*this) <= 400)) { - // send bindmob bind done - if(!bindmob->IsAIControlled() && bindmob != this ) { - - } - else if(bindmob->IsAIControlled() && bindmob != this ) { - // Tell IPC to resume?? - } - else { - // Binding self - } - // Send client bind done - - bind_out->type = 1; // Done - QueuePacket(outapp); - bind_out->type = 0; - CheckIncreaseSkill(SkillBindWound, nullptr, 5); - - int maxHPBonus = spellbonuses.MaxBindWound + itembonuses.MaxBindWound + aabonuses.MaxBindWound; - - int max_percent = 50 + 10 * maxHPBonus; - - if(GetClass() == MONK && GetSkill(SkillBindWound) > 200) { - max_percent = 70 + 10 * maxHPBonus; - } - - max_percent = mod_bindwound_percent(max_percent, bindmob); - - int max_hp = bindmob->GetMaxHP()*max_percent/100; - - // send bindmob new hp's - if (bindmob->GetHP() < bindmob->GetMaxHP() && bindmob->GetHP() <= (max_hp)-1){ - // 0.120 per skill point, 0.60 per skill level, minimum 3 max 30 - int bindhps = 3; - - - if (GetSkill(SkillBindWound) > 200) { - bindhps += GetSkill(SkillBindWound)*4/10; - } else if (GetSkill(SkillBindWound) >= 10) { - bindhps += GetSkill(SkillBindWound)/4; - } - - //Implementation of aaMithanielsBinding is a guess (the multiplier) - int bindBonus = spellbonuses.BindWound + itembonuses.BindWound + aabonuses.BindWound; - - bindhps += bindhps*bindBonus / 100; - - bindhps = mod_bindwound_hp(bindhps, bindmob); - - //if the bind takes them above the max bindable - //cap it at that value. Dont know if live does it this way - //but it makes sense to me. - int chp = bindmob->GetHP() + bindhps; - if(chp > max_hp) - chp = max_hp; - - bindmob->SetHP(chp); - bindmob->SendHPUpdate(); - } - else { - //I dont have the real, live - Message(15, "You cannot bind wounds above %d%% hitpoints.", max_percent); - if(bindmob->IsClient()) - bindmob->CastToClient()->Message(15, "You cannot have your wounds bound above %d%% hitpoints.", max_percent); - // Too many hp message goes here. - } - } - else { - // Send client bind failed - if(bindmob != this) - bind_out->type = 6; // They moved - else - bind_out->type = 7; // Bandager moved - - QueuePacket(outapp); - bind_out->type = 0; - } - } - } - } - else if (bindwound_timer.Enabled()) { - // You moved - outapp = new EQApplicationPacket(OP_Bind_Wound, sizeof(BindWound_Struct)); - BindWound_Struct* bind_out = (BindWound_Struct*) outapp->pBuffer; - bindwound_timer.Disable(); - bindwound_target = 0; - bind_out->type = 7; - QueuePacket(outapp); - bind_out->type = 3; - QueuePacket(outapp); - } - safe_delete(outapp); - return true; -} - -void Client::SetMaterial(int16 in_slot, uint32 item_id) { - const Item_Struct* item = database.GetItem(item_id); - int ornamentationAugtype = RuleI(Character, OrnamentationAugmentType); - if (item && (item->ItemClass==ItemClassCommon)) { - if (in_slot==MainHead) - m_pp.item_material[MaterialHead] = item->Material; - else if (in_slot==MainChest) - m_pp.item_material[MaterialChest] = item->Material; - else if (in_slot==MainArms) - m_pp.item_material[MaterialArms] = item->Material; - else if (in_slot==MainWrist1) - m_pp.item_material[MaterialWrist] = item->Material; - else if (in_slot==MainHands) - m_pp.item_material[MaterialHands] = item->Material; - else if (in_slot==MainLegs) - m_pp.item_material[MaterialLegs] = item->Material; - else if (in_slot==MainFeet) - m_pp.item_material[MaterialFeet] = item->Material; - else if (in_slot == MainPrimary) { - const ItemInst* inst = m_inv[MainPrimary]; - if (inst && inst->GetOrnamentationAug(ornamentationAugtype)) { - item = inst->GetOrnamentationAug(ornamentationAugtype)->GetItem(); - m_pp.item_material[MaterialPrimary] = atoi(item->IDFile + 2); - } - else if (inst && inst->GetOrnamentationIcon() && inst->GetOrnamentationIDFile()) { - m_pp.item_material[MaterialPrimary] = inst->GetOrnamentationIDFile(); - } - else { - m_pp.item_material[MaterialPrimary] = atoi(item->IDFile + 2); - } - } - else if (in_slot == MainSecondary) { - const ItemInst* inst = m_inv[MainSecondary]; - if (inst && inst->GetOrnamentationAug(ornamentationAugtype)) { - item = inst->GetOrnamentationAug(ornamentationAugtype)->GetItem(); - m_pp.item_material[MaterialSecondary] = atoi(item->IDFile + 2); - } - else if (inst && inst->GetOrnamentationIcon() && inst->GetOrnamentationIDFile()) { - m_pp.item_material[MaterialSecondary] = inst->GetOrnamentationIDFile(); - } - else { - m_pp.item_material[MaterialSecondary] = atoi(item->IDFile + 2); - } - } - } -} - -void Client::ServerFilter(SetServerFilter_Struct* filter){ - -/* this code helps figure out the filter IDs in the packet if needed - static SetServerFilter_Struct ssss; - int r; - uint32 *o = (uint32 *) &ssss; - uint32 *n = (uint32 *) filter; - for(r = 0; r < (sizeof(SetServerFilter_Struct)/4); r++) { - if(*o != *n) - LogFile->write(EQEMuLog::Debug, "Filter %d changed from %d to %d", r, *o, *n); - o++; n++; - } - memcpy(&ssss, filter, sizeof(SetServerFilter_Struct)); -*/ -#define Filter0(type) \ - if(filter->filters[type] == 1) \ - ClientFilters[type] = FilterShow; \ - else \ - ClientFilters[type] = FilterHide; -#define Filter1(type) \ - if(filter->filters[type] == 0) \ - ClientFilters[type] = FilterShow; \ - else \ - ClientFilters[type] = FilterHide; - - Filter0(FilterGuildChat); - Filter0(FilterSocials); - Filter0(FilterGroupChat); - Filter0(FilterShouts); - Filter0(FilterAuctions); - Filter0(FilterOOC); - Filter0(FilterBadWords); - - if(filter->filters[FilterPCSpells] == 0) - ClientFilters[FilterPCSpells] = FilterShow; - else if(filter->filters[FilterPCSpells] == 1) - ClientFilters[FilterPCSpells] = FilterHide; - else - ClientFilters[FilterPCSpells] = FilterShowGroupOnly; - - Filter1(FilterNPCSpells); - - if(filter->filters[FilterBardSongs] == 0) - ClientFilters[FilterBardSongs] = FilterShow; - else if(filter->filters[FilterBardSongs] == 1) - ClientFilters[FilterBardSongs] = FilterShowSelfOnly; - else if(filter->filters[FilterBardSongs] == 2) - ClientFilters[FilterBardSongs] = FilterShowGroupOnly; - else - ClientFilters[FilterBardSongs] = FilterHide; - - if(filter->filters[FilterSpellCrits] == 0) - ClientFilters[FilterSpellCrits] = FilterShow; - else if(filter->filters[FilterSpellCrits] == 1) - ClientFilters[FilterSpellCrits] = FilterShowSelfOnly; - else - ClientFilters[FilterSpellCrits] = FilterHide; - - if (filter->filters[FilterMeleeCrits] == 0) - ClientFilters[FilterMeleeCrits] = FilterShow; - else if (filter->filters[FilterMeleeCrits] == 1) - ClientFilters[FilterMeleeCrits] = FilterShowSelfOnly; - else - ClientFilters[FilterMeleeCrits] = FilterHide; - - if(filter->filters[FilterSpellDamage] == 0) - ClientFilters[FilterSpellDamage] = FilterShow; - else if(filter->filters[FilterSpellDamage] == 1) - ClientFilters[FilterSpellDamage] = FilterShowSelfOnly; - else - ClientFilters[FilterSpellDamage] = FilterHide; - - Filter0(FilterMyMisses); - Filter0(FilterOthersMiss); - Filter0(FilterOthersHit); - Filter0(FilterMissedMe); - Filter1(FilterDamageShields); - - if (GetClientVersionBit() & BIT_SoDAndLater) { - if (filter->filters[FilterDOT] == 0) - ClientFilters[FilterDOT] = FilterShow; - else if (filter->filters[FilterDOT] == 1) - ClientFilters[FilterDOT] = FilterShowSelfOnly; - else if (filter->filters[FilterDOT] == 2) - ClientFilters[FilterDOT] = FilterShowGroupOnly; - else - ClientFilters[FilterDOT] = FilterHide; - } else { - if (filter->filters[FilterDOT] == 0) // show functions as self only - ClientFilters[FilterDOT] = FilterShowSelfOnly; - else - ClientFilters[FilterDOT] = FilterHide; - } - - Filter1(FilterPetHits); - Filter1(FilterPetMisses); - Filter1(FilterFocusEffects); - Filter1(FilterPetSpells); - - if (GetClientVersionBit() & BIT_SoDAndLater) { - if (filter->filters[FilterHealOverTime] == 0) - ClientFilters[FilterHealOverTime] = FilterShow; - // This is called 'Show Mine Only' in the clients, but functions the same as show - // so instead of apply special logic, just set to show - else if (filter->filters[FilterHealOverTime] == 1) - ClientFilters[FilterHealOverTime] = FilterShow; - else - ClientFilters[FilterHealOverTime] = FilterHide; - } else { - // these clients don't have a 'self only' filter - Filter1(FilterHealOverTime); - } -} - -// this version is for messages with no parameters -void Client::Message_StringID(uint32 type, uint32 string_id, uint32 distance) -{ - if (GetFilter(FilterSpellDamage) == FilterHide && type == MT_NonMelee) - return; - if (GetFilter(FilterMeleeCrits) == FilterHide && type == MT_CritMelee) //98 is self... - return; - if (GetFilter(FilterSpellCrits) == FilterHide && type == MT_SpellCrits) - return; - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SimpleMessage,12); - SimpleMessage_Struct* sms = (SimpleMessage_Struct*)outapp->pBuffer; - sms->color=type; - sms->string_id=string_id; - - sms->unknown8=0; - - if(distance>0) - entity_list.QueueCloseClients(this,outapp,false,distance); - else - QueuePacket(outapp); - safe_delete(outapp); -} - -// -// this list of 9 args isn't how I want to do it, but to use va_arg -// you have to know how many args you're expecting, and to do that we have -// to load the eqstr file and count them in the string. -// This hack sucks but it's gonna work for now. -// -void Client::Message_StringID(uint32 type, uint32 string_id, const char* message1, - const char* message2,const char* message3,const char* message4, - const char* message5,const char* message6,const char* message7, - const char* message8,const char* message9, uint32 distance) -{ - if (GetFilter(FilterSpellDamage) == FilterHide && type == MT_NonMelee) - return; - if (GetFilter(FilterMeleeCrits) == FilterHide && type == MT_CritMelee) //98 is self... - return; - if (GetFilter(FilterSpellCrits) == FilterHide && type == MT_SpellCrits) - return; - if (GetFilter(FilterDamageShields) == FilterHide && type == MT_DS) - return; - - int i, argcount, length; - char *bufptr; - const char *message_arg[9] = {0}; - - if(type==MT_Emote) - type=4; - - if(!message1) - { - Message_StringID(type, string_id); // use the simple message instead - return; - } - - i = 0; - message_arg[i++] = message1; - message_arg[i++] = message2; - message_arg[i++] = message3; - message_arg[i++] = message4; - message_arg[i++] = message5; - message_arg[i++] = message6; - message_arg[i++] = message7; - message_arg[i++] = message8; - message_arg[i++] = message9; - - for(argcount = length = 0; message_arg[argcount]; argcount++) - length += strlen(message_arg[argcount]) + 1; - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_FormattedMessage, length+13); - FormattedMessage_Struct *fm = (FormattedMessage_Struct *)outapp->pBuffer; - fm->string_id = string_id; - fm->type = type; - bufptr = fm->message; - for(i = 0; i < argcount; i++) - { - strcpy(bufptr, message_arg[i]); - bufptr += strlen(message_arg[i]) + 1; - } - - - if(distance>0) - entity_list.QueueCloseClients(this,outapp,false,distance); - else - QueuePacket(outapp); - safe_delete(outapp); -} - -// helper function, returns true if we should see the message -bool Client::FilteredMessageCheck(Mob *sender, eqFilterType filter) -{ - eqFilterMode mode = GetFilter(filter); - // easy ones first - if (mode == FilterShow) - return true; - else if (mode == FilterHide) - return false; - - if (!sender && mode == FilterHide) { - return false; - } else if (sender) { - if (this == sender) { - if (mode == FilterHide) // don't need to check others - return false; - } else if (mode == FilterShowSelfOnly) { // we know sender isn't us - return false; - } else if (mode == FilterShowGroupOnly) { - Group *g = GetGroup(); - Raid *r = GetRaid(); - if (g) { - if (g->IsGroupMember(sender)) - return true; - } else if (r && sender->IsClient()) { - uint32 rgid1 = r->GetGroup(this); - uint32 rgid2 = r->GetGroup(sender->CastToClient()); - if (rgid1 != 0xFFFFFFFF && rgid1 == rgid2) - return true; - } else { - return false; - } - } - } - - // we passed our checks - return true; -} - -void Client::FilteredMessage_StringID(Mob *sender, uint32 type, - eqFilterType filter, uint32 string_id) -{ - if (!FilteredMessageCheck(sender, filter)) - return; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_SimpleMessage, 12); - SimpleMessage_Struct *sms = (SimpleMessage_Struct *)outapp->pBuffer; - sms->color = type; - sms->string_id = string_id; - - sms->unknown8 = 0; - - QueuePacket(outapp); - safe_delete(outapp); - - return; -} - -void Client::FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter, uint32 string_id, - const char *message1, const char *message2, const char *message3, - const char *message4, const char *message5, const char *message6, - const char *message7, const char *message8, const char *message9) -{ - if (!FilteredMessageCheck(sender, filter)) - return; - - int i, argcount, length; - char *bufptr; - const char *message_arg[9] = {0}; - - if (type == MT_Emote) - type = 4; - - if (!message1) { - FilteredMessage_StringID(sender, type, filter, string_id); // use the simple message instead - return; - } - - i = 0; - message_arg[i++] = message1; - message_arg[i++] = message2; - message_arg[i++] = message3; - message_arg[i++] = message4; - message_arg[i++] = message5; - message_arg[i++] = message6; - message_arg[i++] = message7; - message_arg[i++] = message8; - message_arg[i++] = message9; - - for (argcount = length = 0; message_arg[argcount]; argcount++) - length += strlen(message_arg[argcount]) + 1; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_FormattedMessage, length+13); - FormattedMessage_Struct *fm = (FormattedMessage_Struct *)outapp->pBuffer; - fm->string_id = string_id; - fm->type = type; - bufptr = fm->message; - for (i = 0; i < argcount; i++) { - strcpy(bufptr, message_arg[i]); - bufptr += strlen(message_arg[i]) + 1; - } - - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::Tell_StringID(uint32 string_id, const char *who, const char *message) -{ - char string_id_str[10]; - snprintf(string_id_str, 10, "%d", string_id); - - Message_StringID(MT_TellEcho, TELL_QUEUED_MESSAGE, who, string_id_str, message); -} - -void Client::SetTint(int16 in_slot, uint32 color) { - Color_Struct new_color; - new_color.color = color; - SetTint(in_slot, new_color); - database.SaveCharacterMaterialColor(this->CharacterID(), in_slot, color); -} - -// Still need to reconcile bracer01 versus bracer02 -void Client::SetTint(int16 in_slot, Color_Struct& color) { - if (in_slot==MainHead) - m_pp.item_tint[MaterialHead].color=color.color; - else if (in_slot==MainArms) - m_pp.item_tint[MaterialArms].color=color.color; - else if (in_slot==MainWrist1) - m_pp.item_tint[MaterialWrist].color=color.color; - /* - // non-live behavior - else if (in_slot==SLOT_BRACER02) - m_pp.item_tint[MaterialWrist].color=color.color; - */ - else if (in_slot==MainHands) - m_pp.item_tint[MaterialHands].color=color.color; - else if (in_slot==MainPrimary) - m_pp.item_tint[MaterialPrimary].color=color.color; - else if (in_slot==MainSecondary) - m_pp.item_tint[MaterialSecondary].color=color.color; - else if (in_slot==MainChest) - m_pp.item_tint[MaterialChest].color=color.color; - else if (in_slot==MainLegs) - m_pp.item_tint[MaterialLegs].color=color.color; - else if (in_slot==MainFeet) - m_pp.item_tint[MaterialFeet].color=color.color; - - database.SaveCharacterMaterialColor(this->CharacterID(), in_slot, color.color); -} - -void Client::SetHideMe(bool flag) -{ - EQApplicationPacket app; - - gmhideme = flag; - - if(gmhideme) - { - database.SetHideMe(AccountID(),true); - CreateDespawnPacket(&app, false); - entity_list.RemoveFromTargets(this); - trackable = false; - } - else - { - database.SetHideMe(AccountID(),false); - CreateSpawnPacket(&app); - trackable = true; - } - - entity_list.QueueClientsStatus(this, &app, true, 0, Admin()-1); -} - -void Client::SetLanguageSkill(int langid, int value) -{ - if (langid >= MAX_PP_LANGUAGE) - return; //Invalid Language - - if (value > 100) - value = 100; //Max lang value - - m_pp.languages[langid] = value; - database.SaveCharacterLanguage(this->CharacterID(), langid, value); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SkillUpdate, sizeof(SkillUpdate_Struct)); - SkillUpdate_Struct* skill = (SkillUpdate_Struct*)outapp->pBuffer; - skill->skillId = 100 + langid; - skill->value = m_pp.languages[langid]; - QueuePacket(outapp); - safe_delete(outapp); - - Message_StringID( MT_Skills, LANG_SKILL_IMPROVED ); //Notify the client -} - -void Client::LinkDead() -{ - if (GetGroup()) - { - entity_list.MessageGroup(this,true,15,"%s has gone linkdead.",GetName()); - GetGroup()->DelMember(this); - if (GetMerc()) - { - GetMerc()->RemoveMercFromGroup(GetMerc(), GetMerc()->GetGroup()); - } - } - Raid *raid = entity_list.GetRaidByClient(this); - if(raid){ - raid->MemberZoned(this); - } -// save_timer.Start(2500); - linkdead_timer.Start(RuleI(Zone,ClientLinkdeadMS)); - SendAppearancePacket(AT_Linkdead, 1); - client_state = CLIENT_LINKDEAD; - AI_Start(CLIENT_LD_TIMEOUT); -} - -uint8 Client::SlotConvert(uint8 slot,bool bracer){ - uint8 slot2=0; // why are we returning MainCharm instead of INVALID_INDEX? (must be a pre-charm segment...) - if(bracer) - return MainWrist2; - switch(slot){ - case MaterialHead: - slot2=MainHead; - break; - case MaterialChest: - slot2=MainChest; - break; - case MaterialArms: - slot2=MainArms; - break; - case MaterialWrist: - slot2=MainWrist1; - break; - case MaterialHands: - slot2=MainHands; - break; - case MaterialLegs: - slot2=MainLegs; - break; - case MaterialFeet: - slot2=MainFeet; - break; - } - return slot2; -} - -uint8 Client::SlotConvert2(uint8 slot){ - uint8 slot2=0; // same as above... - switch(slot){ - case MainHead: - slot2=MaterialHead; - break; - case MainChest: - slot2=MaterialChest; - break; - case MainArms: - slot2=MaterialArms; - break; - case MainWrist1: - slot2=MaterialWrist; - break; - case MainHands: - slot2=MaterialHands; - break; - case MainLegs: - slot2=MaterialLegs; - break; - case MainFeet: - slot2=MaterialFeet; - break; - } - return slot2; -} - -void Client::Escape() -{ - entity_list.RemoveFromTargets(this, true); - SetInvisible(1); - - Message_StringID(MT_Skills, ESCAPE); -} - -float Client::CalcPriceMod(Mob* other, bool reverse) -{ - float chaformula = 0; - if (other) - { - int factionlvl = GetFactionLevel(CharacterID(), other->CastToNPC()->GetNPCTypeID(), GetRace(), GetClass(), GetDeity(), other->CastToNPC()->GetPrimaryFaction(), other); - if (factionlvl >= FACTION_APPREHENSIVE) // Apprehensive or worse. - { - if (GetCHA() > 103) - { - chaformula = (GetCHA() - 103)*((-(RuleR(Merchant, ChaBonusMod))/100)*(RuleI(Merchant, PriceBonusPct))); // This will max out price bonus. - if (chaformula < -1*(RuleI(Merchant, PriceBonusPct))) - chaformula = -1*(RuleI(Merchant, PriceBonusPct)); - } - else if (GetCHA() < 103) - { - chaformula = (103 - GetCHA())*(((RuleR(Merchant, ChaPenaltyMod))/100)*(RuleI(Merchant, PricePenaltyPct))); // This will bottom out price penalty. - if (chaformula > 1*(RuleI(Merchant, PricePenaltyPct))) - chaformula = 1*(RuleI(Merchant, PricePenaltyPct)); - } - } - if (factionlvl <= FACTION_INDIFFERENT) // Indifferent or better. - { - if (GetCHA() > 75) - { - chaformula = (GetCHA() - 75)*((-(RuleR(Merchant, ChaBonusMod))/100)*(RuleI(Merchant, PriceBonusPct))); // This will max out price bonus. - if (chaformula < -1*(RuleI(Merchant, PriceBonusPct))) - chaformula = -1*(RuleI(Merchant, PriceBonusPct)); - } - else if (GetCHA() < 75) - { - chaformula = (75 - GetCHA())*(((RuleR(Merchant, ChaPenaltyMod))/100)*(RuleI(Merchant, PricePenaltyPct))); // Faction modifier keeps up from reaching bottom price penalty. - if (chaformula > 1*(RuleI(Merchant, PricePenaltyPct))) - chaformula = 1*(RuleI(Merchant, PricePenaltyPct)); - } - } - } - - if (reverse) - chaformula *= -1; //For selling - //Now we have, for example, 10 - chaformula /= 100; //Convert to 0.10 - chaformula += 1; //Convert to 1.10; - return chaformula; //Returns 1.10, expensive stuff! -} - -//neat idea from winter's roar, not implemented -void Client::Insight(uint32 t_id) -{ - Mob* who = entity_list.GetMob(t_id); - if (!who) - return; - if (!who->IsNPC()) - { - Message(0,"This ability can only be used on NPCs."); - return; - } - if (Dist(*who) > 200) - { - Message(0,"You must get closer to your target!"); - return; - } - if (!CheckLosFN(who)) - { - Message(0,"You must be able to see your target!"); - return; - } - char hitpoints[64]; - char resists[320]; - char dmg[64]; - memset(hitpoints,0,sizeof(hitpoints)); - memset(resists,0,sizeof(resists)); - memset(dmg,0,sizeof(dmg)); - //Start with HP blah - int avg_hp = GetLevelHP(who->GetLevel()); - int cur_hp = who->GetHP(); - if (cur_hp == avg_hp) - { - strn0cpy(hitpoints,"averagely tough",32); - } - else if (cur_hp >= avg_hp*5) - { - strn0cpy(hitpoints,"extremely tough",32); - } - else if (cur_hp >= avg_hp*4) - { - strn0cpy(hitpoints,"exceptionally tough",32); - } - else if (cur_hp >= avg_hp*3) - { - strn0cpy(hitpoints,"very tough",32); - } - else if (cur_hp >= avg_hp*2) - { - strn0cpy(hitpoints,"quite tough",32); - } - else if (cur_hp >= avg_hp*1.25) - { - strn0cpy(hitpoints,"rather tough",32); - } - else if (cur_hp > avg_hp) - { - strn0cpy(hitpoints,"slightly tough",32); - } - else if (cur_hp <= avg_hp*0.20) - { - strn0cpy(hitpoints,"extremely frail",32); - } - else if (cur_hp <= avg_hp*0.25) - { - strn0cpy(hitpoints,"exceptionally frail",32); - } - else if (cur_hp <= avg_hp*0.33) - { - strn0cpy(hitpoints,"very frail",32); - } - else if (cur_hp <= avg_hp*0.50) - { - strn0cpy(hitpoints,"quite frail",32); - } - else if (cur_hp <= avg_hp*0.75) - { - strn0cpy(hitpoints,"rather frail",32); - } - else if (cur_hp < avg_hp) - { - strn0cpy(hitpoints,"slightly frail",32); - } - - int avg_dmg = who->CastToNPC()->GetMaxDamage(who->GetLevel()); - int cur_dmg = who->CastToNPC()->GetMaxDMG(); - if (cur_dmg == avg_dmg) - { - strn0cpy(dmg,"averagely strong",32); - } - else if (cur_dmg >= avg_dmg*4) - { - strn0cpy(dmg,"extremely strong",32); - } - else if (cur_dmg >= avg_dmg*3) - { - strn0cpy(dmg,"exceptionally strong",32); - } - else if (cur_dmg >= avg_dmg*2) - { - strn0cpy(dmg,"very strong",32); - } - else if (cur_dmg >= avg_dmg*1.25) - { - strn0cpy(dmg,"quite strong",32); - } - else if (cur_dmg >= avg_dmg*1.10) - { - strn0cpy(dmg,"rather strong",32); - } - else if (cur_dmg > avg_dmg) - { - strn0cpy(dmg,"slightly strong",32); - } - else if (cur_dmg <= avg_dmg*0.20) - { - strn0cpy(dmg,"extremely weak",32); - } - else if (cur_dmg <= avg_dmg*0.25) - { - strn0cpy(dmg,"exceptionally weak",32); - } - else if (cur_dmg <= avg_dmg*0.33) - { - strn0cpy(dmg,"very weak",32); - } - else if (cur_dmg <= avg_dmg*0.50) - { - strn0cpy(dmg,"quite weak",32); - } - else if (cur_dmg <= avg_dmg*0.75) - { - strn0cpy(dmg,"rather weak",32); - } - else if (cur_dmg < avg_dmg) - { - strn0cpy(dmg,"slightly weak",32); - } - - //Resists - int res; - int i = 1; - - //MR - res = who->GetResist(i); - i++; - if (res >= 1000) - { - strcat(resists,"immune"); - } - else if (res >= 500) - { - strcat(resists,"practically immune"); - } - else if (res >= 250) - { - strcat(resists,"exceptionally resistant"); - } - else if (res >= 150) - { - strcat(resists,"very resistant"); - } - else if (res >= 100) - { - strcat(resists,"fairly resistant"); - } - else if (res >= 50) - { - strcat(resists,"averagely resistant"); - } - else if (res >= 25) - { - strcat(resists,"weakly resistant"); - } - else - { - strcat(resists,"barely resistant"); - } - strcat(resists," to magic, "); - - //FR - res = who->GetResist(i); - i++; - if (res >= 1000) - { - strcat(resists,"immune"); - } - else if (res >= 500) - { - strcat(resists,"practically immune"); - } - else if (res >= 250) - { - strcat(resists,"exceptionally resistant"); - } - else if (res >= 150) - { - strcat(resists,"very resistant"); - } - else if (res >= 100) - { - strcat(resists,"fairly resistant"); - } - else if (res >= 50) - { - strcat(resists,"averagely resistant"); - } - else if (res >= 25) - { - strcat(resists,"weakly resistant"); - } - else - { - strcat(resists,"barely resistant"); - } - strcat(resists," to fire, "); - - //CR - res = who->GetResist(i); - i++; - if (res >= 1000) - { - strcat(resists,"immune"); - } - else if (res >= 500) - { - strcat(resists,"practically immune"); - } - else if (res >= 250) - { - strcat(resists,"exceptionally resistant"); - } - else if (res >= 150) - { - strcat(resists,"very resistant"); - } - else if (res >= 100) - { - strcat(resists,"fairly resistant"); - } - else if (res >= 50) - { - strcat(resists,"averagely resistant"); - } - else if (res >= 25) - { - strcat(resists,"weakly resistant"); - } - else - { - strcat(resists,"barely resistant"); - } - strcat(resists," to cold, "); - - //PR - res = who->GetResist(i); - i++; - if (res >= 1000) - { - strcat(resists,"immune"); - } - else if (res >= 500) - { - strcat(resists,"practically immune"); - } - else if (res >= 250) - { - strcat(resists,"exceptionally resistant"); - } - else if (res >= 150) - { - strcat(resists,"very resistant"); - } - else if (res >= 100) - { - strcat(resists,"fairly resistant"); - } - else if (res >= 50) - { - strcat(resists,"averagely resistant"); - } - else if (res >= 25) - { - strcat(resists,"weakly resistant"); - } - else - { - strcat(resists,"barely resistant"); - } - strcat(resists," to poison, and "); - - //MR - res = who->GetResist(i); - i++; - if (res >= 1000) - { - strcat(resists,"immune"); - } - else if (res >= 500) - { - strcat(resists,"practically immune"); - } - else if (res >= 250) - { - strcat(resists,"exceptionally resistant"); - } - else if (res >= 150) - { - strcat(resists,"very resistant"); - } - else if (res >= 100) - { - strcat(resists,"fairly resistant"); - } - else if (res >= 50) - { - strcat(resists,"averagely resistant"); - } - else if (res >= 25) - { - strcat(resists,"weakly resistant"); - } - else - { - strcat(resists,"barely resistant"); - } - strcat(resists," to disease."); - - Message(0,"Your target is a level %i %s. It appears %s and %s for its level. It seems %s",who->GetLevel(),GetEQClassName(who->GetClass(),1),dmg,hitpoints,resists); -} - -void Client::ChangeSQLLog(const char *file) { - if(SQL_log != nullptr) { - fclose(SQL_log); - SQL_log = nullptr; - } - if(file != nullptr) { - if(strstr(file, "..") != nullptr) { - Message(13, ".. is forbibben in SQL log file names."); - return; - } - char buf[512]; - snprintf(buf, 511, "%s%s", SQL_LOG_PATH, file); - buf[511] = '\0'; - SQL_log = fopen(buf, "a"); - if(SQL_log == nullptr) { - Message(13, "Unable to open SQL log file: %s\n", strerror(errno)); - } - } -} - -void Client::LogSQL(const char *fmt, ...) { - if(SQL_log == nullptr) - return; - - va_list argptr; - va_start(argptr, fmt); - vfprintf(SQL_log, fmt, argptr ); - fputc('\n', SQL_log); - va_end(argptr); -} - -void Client::GetGroupAAs(GroupLeadershipAA_Struct *into) const { - memcpy(into, &m_pp.leader_abilities.group, sizeof(GroupLeadershipAA_Struct)); -} - -void Client::GetRaidAAs(RaidLeadershipAA_Struct *into) const { - memcpy(into, &m_pp.leader_abilities.raid, sizeof(RaidLeadershipAA_Struct)); -} - -void Client::EnteringMessages(Client* client) -{ - //server rules - char *rules; - rules = new char [4096]; - - if(database.GetVariable("Rules", rules, 4096)) - { - uint8 flag = database.GetAgreementFlag(client->AccountID()); - if(!flag) - { - client->Message(13,"You must agree to the Rules, before you can move. (type #serverrules to view the rules)"); - client->Message(13,"You must agree to the Rules, before you can move. (type #serverrules to view the rules)"); - client->Message(13,"You must agree to the Rules, before you can move. (type #serverrules to view the rules)"); - client->SendAppearancePacket(AT_Anim, ANIM_FREEZE); - } - } - safe_delete_array(rules); -} - -void Client::SendRules(Client* client) -{ - char *rules; - rules = new char [4096]; - char *ptr; - - database.GetVariable("Rules", rules, 4096); - - ptr = strtok(rules, "\n"); - while(ptr != nullptr) - { - - client->Message(0,"%s",ptr); - ptr = strtok(nullptr, "\n"); - } - safe_delete_array(rules); -} - -void Client::SetEndurance(int32 newEnd) -{ - /*Endurance can't be less than 0 or greater than max*/ - if(newEnd < 0) - newEnd = 0; - else if(newEnd > GetMaxEndurance()){ - newEnd = GetMaxEndurance(); - } - - cur_end = newEnd; - SendManaUpdatePacket(); -} - -void Client::SacrificeConfirm(Client *caster) { - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Sacrifice, sizeof(Sacrifice_Struct)); - Sacrifice_Struct *ss = (Sacrifice_Struct*)outapp->pBuffer; - - if(!caster || PendingSacrifice) return; - - if(GetLevel() < RuleI(Spells, SacrificeMinLevel)){ - caster->Message_StringID(13, SAC_TOO_LOW); //This being is not a worthy sacrifice. - return; - } - if (GetLevel() > RuleI(Spells, SacrificeMaxLevel)) { - caster->Message_StringID(13, SAC_TOO_HIGH); - return; - } - - ss->CasterID = caster->GetID(); - ss->TargetID = GetID(); - ss->Confirm = 0; - QueuePacket(outapp); - safe_delete(outapp); - // We store the Caster's name, because when the packet comes back, it only has the victim's entityID in it, - // not the caster. - SacrificeCaster += caster->GetName(); - PendingSacrifice = true; -} - -//Essentially a special case death function -void Client::Sacrifice(Client *caster) -{ - if(GetLevel() >= RuleI(Spells, SacrificeMinLevel) && GetLevel() <= RuleI(Spells, SacrificeMaxLevel)){ - int exploss = (int)(GetLevel() * (GetLevel() / 18.0) * 12000); - if(exploss < GetEXP()){ - SetEXP(GetEXP()-exploss, GetAAXP()); - SendLogoutPackets(); - - //make our become corpse packet, and queue to ourself before OP_Death. - EQApplicationPacket app2(OP_BecomeCorpse, sizeof(BecomeCorpse_Struct)); - BecomeCorpse_Struct* bc = (BecomeCorpse_Struct*)app2.pBuffer; - bc->spawn_id = GetID(); - bc->x = GetX(); - bc->y = GetY(); - bc->z = GetZ(); - QueuePacket(&app2); - - // make death packet - EQApplicationPacket app(OP_Death, sizeof(Death_Struct)); - Death_Struct* d = (Death_Struct*)app.pBuffer; - d->spawn_id = GetID(); - d->killer_id = caster ? caster->GetID() : 0; - d->bindzoneid = GetPP().binds[0].zoneId; - d->spell_id = SPELL_UNKNOWN; - d->attack_skill = 0xe7; - d->damage = 0; - app.priority = 6; - entity_list.QueueClients(this, &app); - - BuffFadeAll(); - UnmemSpellAll(); - Group *g = GetGroup(); - if(g){ - g->MemberZoned(this); - } - Raid *r = entity_list.GetRaidByClient(this); - if(r){ - r->MemberZoned(this); - } - ClearAllProximities(); - if(RuleB(Character, LeaveCorpses)){ - Corpse *new_corpse = new Corpse(this, 0); - entity_list.AddCorpse(new_corpse, GetID()); - SetID(0); - entity_list.QueueClients(this, &app2, true); - } - Save(); - GoToDeath(); - caster->SummonItem(RuleI(Spells, SacrificeItemID)); - } - } - else{ - caster->Message_StringID(13, SAC_TOO_LOW); //This being is not a worthy sacrifice. - } -} - -void Client::SendOPTranslocateConfirm(Mob *Caster, uint16 SpellID) { - - if(!Caster || PendingTranslocate) - return; - - const SPDat_Spell_Struct &Spell = spells[SpellID]; - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Translocate, sizeof(Translocate_Struct)); - Translocate_Struct *ts = (Translocate_Struct*)outapp->pBuffer; - - strcpy(ts->Caster, Caster->GetName()); - PendingTranslocateData.spell_id = ts->SpellID = SpellID; - - if((SpellID == 1422) || (SpellID == 1334) || (SpellID == 3243)) { - PendingTranslocateData.zone_id = ts->ZoneID = m_pp.binds[0].zoneId; - PendingTranslocateData.instance_id = m_pp.binds[0].instance_id; - PendingTranslocateData.x = ts->x = m_pp.binds[0].x; - PendingTranslocateData.y = ts->y = m_pp.binds[0].y; - PendingTranslocateData.z = ts->z = m_pp.binds[0].z; - PendingTranslocateData.heading = m_pp.binds[0].heading; - } - else { - PendingTranslocateData.zone_id = ts->ZoneID = database.GetZoneID(Spell.teleport_zone); - PendingTranslocateData.instance_id = 0; - PendingTranslocateData.y = ts->y = Spell.base[0]; - PendingTranslocateData.x = ts->x = Spell.base[1]; - PendingTranslocateData.z = ts->z = Spell.base[2]; - PendingTranslocateData.heading = 0.0; - } - - ts->unknown008 = 0; - ts->Complete = 0; - - PendingTranslocate = true; - TranslocateTime = time(nullptr); - - QueuePacket(outapp); - safe_delete(outapp); - - return; -} -void Client::SendPickPocketResponse(Mob *from, uint32 amt, int type, const Item_Struct* item){ - EQApplicationPacket* outapp = new EQApplicationPacket(OP_PickPocket, sizeof(sPickPocket_Struct)); - sPickPocket_Struct* pick_out = (sPickPocket_Struct*) outapp->pBuffer; - pick_out->coin = amt; - pick_out->from = GetID(); - pick_out->to = from->GetID(); - pick_out->myskill = GetSkill(SkillPickPockets); - - if((type >= PickPocketPlatinum) && (type <= PickPocketCopper) && (amt == 0)) - type = PickPocketFailed; - - pick_out->type = type; - if(item) - strcpy(pick_out->itemname, item->Name); - else - pick_out->itemname[0] = '\0'; - //if we do not send this packet the client will lock up and require the player to relog. - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::SetHoTT(uint32 mobid) { - EQApplicationPacket *outapp = new EQApplicationPacket(OP_TargetHoTT, sizeof(ClientTarget_Struct)); - ClientTarget_Struct *ct = (ClientTarget_Struct *) outapp->pBuffer; - ct->new_target = mobid; - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::SendPopupToClient(const char *Title, const char *Text, uint32 PopupID, uint32 Buttons, uint32 Duration) { - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_OnLevelMessage, sizeof(OnLevelMessage_Struct)); - OnLevelMessage_Struct *olms = (OnLevelMessage_Struct *) outapp->pBuffer; - - if((strlen(Title) > (sizeof(olms->Title)-1)) || - (strlen(Text) > (sizeof(olms->Text)-1))) return; - - strcpy(olms->Title, Title); - strcpy(olms->Text, Text); - - olms->Buttons = Buttons; - - if(Duration > 0) - olms->Duration = Duration * 1000; - else - olms->Duration = 0xffffffff; - - olms->PopupID = PopupID; - olms->NegativeID = 0; - - sprintf(olms->ButtonName0, "%s", "Yes"); - sprintf(olms->ButtonName1, "%s", "No"); - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::SendWindow(uint32 PopupID, uint32 NegativeID, uint32 Buttons, const char *ButtonName0, const char *ButtonName1, uint32 Duration, int title_type, Client* target, const char *Title, const char *Text, ...) { - va_list argptr; - char buffer[4096]; - - va_start(argptr, Text); - vsnprintf(buffer, sizeof(buffer), Text, argptr); - va_end(argptr); - - size_t len = strlen(buffer); - - EQApplicationPacket* app = new EQApplicationPacket(OP_OnLevelMessage, sizeof(OnLevelMessage_Struct)); - OnLevelMessage_Struct* olms=(OnLevelMessage_Struct*)app->pBuffer; - - if(strlen(Text) > (sizeof(olms->Text)-1)) - return; - - if(!target) - title_type = 0; - - switch (title_type) - { - case 1: { - char name[64] = ""; - strcpy(name, target->GetName()); - if(target->GetLastName()) { - char last_name[64] = ""; - strcpy(last_name, target->GetLastName()); - strcat(name, " "); - strcat(name, last_name); - } - strcpy(olms->Title, name); - break; - } - case 2: { - if(target->GuildID()) { - char *guild_name = (char*)guild_mgr.GetGuildName(target->GuildID()); - strcpy(olms->Title, guild_name); - } - else { - strcpy(olms->Title, "No Guild"); - } - break; - } - default: { - strcpy(olms->Title, Title); - break; - } - } - - memcpy(olms->Text, buffer, len+1); - - olms->Buttons = Buttons; - - sprintf(olms->ButtonName0, "%s", ButtonName0); - sprintf(olms->ButtonName1, "%s", ButtonName1); - - if(Duration > 0) - olms->Duration = Duration * 1000; - else - olms->Duration = 0xffffffff; - - olms->PopupID = PopupID; - olms->NegativeID = NegativeID; - - FastQueuePacket(&app); -} - -void Client::KeyRingLoad() -{ - std::string query = StringFormat("SELECT item_id FROM keyring " - "WHERE char_id = '%i' ORDER BY item_id", character_id); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - std::cerr << "Error in Client::KeyRingLoad query '" << query << "' " << results.ErrorMessage() << std::endl; - return; - } - - for (auto row = results.begin(); row != results.end(); ++row) - keyring.push_back(atoi(row[0])); - -} - -void Client::KeyRingAdd(uint32 item_id) -{ - if(0==item_id) - return; - - bool found = KeyRingCheck(item_id); - if (found) - return; - - std::string query = StringFormat("INSERT INTO keyring(char_id, item_id) VALUES(%i, %i)", character_id, item_id); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - std::cerr << "Error in Doors::HandleClick query '" << query << "' " << results.ErrorMessage() << std::endl; - return; - } - - Message(4,"Added to keyring."); - - keyring.push_back(item_id); -} - -bool Client::KeyRingCheck(uint32 item_id) -{ - for(std::list::iterator iter = keyring.begin(); - iter != keyring.end(); - ++iter) - { - if(*iter == item_id) - return true; - } - return false; -} - -void Client::KeyRingList() -{ - Message(4,"Keys on Keyring:"); - const Item_Struct *item = 0; - for(std::list::iterator iter = keyring.begin(); - iter != keyring.end(); - ++iter) - { - if ((item = database.GetItem(*iter))!=nullptr) { - Message(4,item->Name); - } - } -} - -bool Client::IsDiscovered(uint32 itemid) { - - std::string query = StringFormat("SELECT count(*) FROM discovered_items WHERE item_id = '%lu'", itemid); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - std::cerr << "Error in IsDiscovered query '" << query << "' " << results.ErrorMessage() << std::endl; - return false; - } - - auto row = results.begin(); - if (!atoi(row[0])) - return false; - - return true; -} - -void Client::DiscoverItem(uint32 itemid) { - - std::string query = StringFormat("INSERT INTO discovered_items " - "SET item_id = %lu, char_name = '%s', " - "discovered_date = UNIX_TIMESTAMP(), account_status = %i", - itemid, GetName(), Admin()); - auto results = database.QueryDatabase(query); - - parse->EventPlayer(EVENT_DISCOVER_ITEM, this, "", itemid); -} - -void Client::UpdateLFP() { - - Group *g = GetGroup(); - - if(g && !g->IsLeader(this)) { - database.SetLFP(CharacterID(), false); - worldserver.StopLFP(CharacterID()); - LFP = false; - return; - } - - GroupLFPMemberEntry LFPMembers[MAX_GROUP_MEMBERS]; - - for(unsigned int i=0; iGetZoneID(); - - if(g) { - // Fill the LFPMembers array with the rest of the group members, excluding ourself - // We don't fill in the class, level or zone, because we may not be able to determine - // them if the other group members are not in this zone. World will fill in this information - // for us, if it can. - int NextFreeSlot = 1; - for(unsigned int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if((g->membername[i][0] != '\0') && strcasecmp(g->membername[i], LFPMembers[0].Name)) - strcpy(LFPMembers[NextFreeSlot++].Name, g->membername[i]); - } - } - worldserver.UpdateLFP(CharacterID(), LFPMembers); -} - -bool Client::GroupFollow(Client* inviter) { - - if (inviter) - { - isgrouped = true; - Raid* raid = entity_list.GetRaidByClient(inviter); - Raid* iraid = entity_list.GetRaidByClient(this); - - //inviter has a raid don't do group stuff instead do raid stuff! - if (raid) - { - // Suspend the merc while in a raid (maybe a rule could be added for this) - if (GetMerc()) - GetMerc()->Suspend(); - - uint32 groupToUse = 0xFFFFFFFF; - for (int x = 0; x < MAX_RAID_MEMBERS; x++) - { - if (raid->members[x].member) - { - //this assumes the inviter is in the zone - if (raid->members[x].member == inviter){ - groupToUse = raid->members[x].GroupNumber; - break; - } - } - } - if (iraid == raid) - { - //both in same raid - uint32 ngid = raid->GetGroup(inviter->GetName()); - if (raid->GroupCount(ngid) < 6) - { - raid->MoveMember(GetName(), ngid); - raid->SendGroupDisband(this); - //raid->SendRaidGroupAdd(GetName(), ngid); - //raid->SendGroupUpdate(this); - raid->GroupUpdate(ngid); //break - } - return false; - } - if (raid->RaidCount() < MAX_RAID_MEMBERS) - { - if (raid->GroupCount(groupToUse) < 6) - { - raid->SendRaidCreate(this); - raid->SendMakeLeaderPacketTo(raid->leadername, this); - raid->AddMember(this, groupToUse); - raid->SendBulkRaid(this); - //raid->SendRaidGroupAdd(GetName(), groupToUse); - //raid->SendGroupUpdate(this); - raid->GroupUpdate(groupToUse); //break - if (raid->IsLocked()) - { - raid->SendRaidLockTo(this); - } - return false; - } - else - { - raid->SendRaidCreate(this); - raid->SendMakeLeaderPacketTo(raid->leadername, this); - raid->AddMember(this); - raid->SendBulkRaid(this); - if (raid->IsLocked()) - { - raid->SendRaidLockTo(this); - } - return false; - } - } - } - - Group* group = entity_list.GetGroupByClient(inviter); - - if (!group) - { - //Make new group - group = new Group(inviter); - - if (!group) - { - return false; - } - - entity_list.AddGroup(group); - - if (group->GetID() == 0) - { - Message(13, "Unable to get new group id. Cannot create group."); - inviter->Message(13, "Unable to get new group id. Cannot create group."); - return false; - } - - //now we have a group id, can set inviter's id - database.SetGroupID(inviter->GetName(), group->GetID(), inviter->CharacterID(), false); - database.SetGroupLeaderName(group->GetID(), inviter->GetName()); - group->UpdateGroupAAs(); - - //Invite the inviter into the group first.....dont ask - if (inviter->GetClientVersion() < EQClientSoD) - { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct)); - GroupJoin_Struct* outgj = (GroupJoin_Struct*)outapp->pBuffer; - strcpy(outgj->membername, inviter->GetName()); - strcpy(outgj->yourname, inviter->GetName()); - outgj->action = groupActInviteInitial; // 'You have formed the group'. - group->GetGroupAAs(&outgj->leader_aas); - inviter->QueuePacket(outapp); - safe_delete(outapp); - } - else - { - // SoD and later - inviter->SendGroupCreatePacket(); - inviter->SendGroupLeaderChangePacket(inviter->GetName()); - inviter->SendGroupJoinAcknowledge(); - } - - } - - if (!group) - { - return false; - } - - // Remove merc from old group before adding client to the new one - if (GetMerc() && GetMerc()->HasGroup()) - { - GetMerc()->RemoveMercFromGroup(GetMerc(), GetMerc()->GetGroup()); - } - - if (!group->AddMember(this)) - { - // If failed to add client to new group, regroup with merc - if (GetMerc()) - { - GetMerc()->MercJoinClientGroup(); - } - else - { - isgrouped = false; - } - return false; - } - - if (GetClientVersion() >= EQClientSoD) - { - SendGroupJoinAcknowledge(); - } - - // Temporary hack for SoD, as things seem to work quite differently - if (inviter->IsClient() && inviter->GetClientVersion() >= EQClientSoD) - { - database.RefreshGroupFromDB(inviter); - } - - // Add the merc back into the new group if possible - if (GetMerc()) - { - GetMerc()->MercJoinClientGroup(); - } - - if (inviter->IsLFP()) - { - // If the player who invited us to a group is LFP, have them update world now that we have joined their group. - inviter->UpdateLFP(); - } - - database.RefreshGroupFromDB(this); - group->SendHPPacketsTo(this); - //send updates to clients out of zone... - group->SendGroupJoinOOZ(this); - return true; - } - return false; -} - -uint16 Client::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 { - - uint8 type = m_inv.GetItem(MainPrimary)->GetItem()->ItemType; //is this the best way to do this? - - switch (type) - { - case ItemType1HSlash: // 1H Slashing - { - skill = Skill1HSlashing; - break; - } - case ItemType2HSlash: // 2H Slashing - { - skill = Skill2HSlashing; - break; - } - case ItemType1HPiercing: // Piercing - { - skill = Skill1HPiercing; - break; - } - case ItemType1HBlunt: // 1H Blunt - { - skill = Skill1HBlunt; - break; - } - case ItemType2HBlunt: // 2H Blunt - { - skill = Skill2HBlunt; - break; - } - case ItemType2HPiercing: // 2H Piercing - { - skill = Skill1HPiercing; // change to Skill2HPiercing once activated - break; - } - case ItemTypeMartial: // Hand to Hand - { - skill = SkillHandtoHand; - break; - } - default: // All other types default to Hand to Hand - { - skill = SkillHandtoHand; - break; - } - } - } - - return GetSkill(skill); -} - -uint32 Client::GetTotalATK() -{ - uint32 AttackRating = 0; - uint32 WornCap = itembonuses.ATK; - - if(IsClient()) { - AttackRating = ((WornCap * 1.342) + (GetSkill(SkillOffense) * 1.345) + ((GetSTR() - 66) * 0.9) + (GetPrimarySkillValue() * 2.69)); - AttackRating += aabonuses.ATK + GroupLeadershipAAOffenseEnhancement(); - - if (AttackRating < 10) - AttackRating = 10; - } - else - AttackRating = GetATK(); - - AttackRating += spellbonuses.ATK; - - return AttackRating; -} - -uint32 Client::GetATKRating() -{ - uint32 AttackRating = 0; - if(IsClient()) { - AttackRating = (GetSkill(SkillOffense) * 1.345) + ((GetSTR() - 66) * 0.9) + (GetPrimarySkillValue() * 2.69); - - if (AttackRating < 10) - AttackRating = 10; - } - return AttackRating; -} - -void Client::VoiceMacroReceived(uint32 Type, char *Target, uint32 MacroNumber) { - - uint32 GroupOrRaidID = 0; - - switch(Type) { - - case VoiceMacroGroup: { - - Group* g = GetGroup(); - - if(g) - GroupOrRaidID = g->GetID(); - else - return; - - break; - } - - case VoiceMacroRaid: { - - Raid* r = GetRaid(); - - if(r) - GroupOrRaidID = r->GetID(); - else - return; - - break; - } - } - - if(!worldserver.SendVoiceMacro(this, Type, Target, MacroNumber, GroupOrRaidID)) - Message(0, "Error: World server disconnected"); -} - -void Client::ClearGroupAAs() { - for(unsigned int i = 0; i < MAX_GROUP_LEADERSHIP_AA_ARRAY; i++) - m_pp.leader_abilities.ranks[i] = 0; - - m_pp.group_leadership_points = 0; - m_pp.raid_leadership_points = 0; - m_pp.group_leadership_exp = 0; - m_pp.raid_leadership_exp = 0; - - Save(); - database.SaveCharacterLeadershipAA(this->CharacterID(), &m_pp); -} - -void Client::UpdateGroupAAs(int32 points, uint32 type) { - switch(type) { - case 0: { m_pp.group_leadership_points += points; break; } - case 1: { m_pp.raid_leadership_points += points; break; } - } - SendLeadershipEXPUpdate(); -} - -bool Client::IsLeadershipEXPOn() { - - if(!m_pp.leadAAActive) - return false; - - Group *g = GetGroup(); - - if (g && g->IsLeader(this) && g->GroupCount() > 2) - return true; - - Raid *r = GetRaid(); - - if (!r) - return false; - - // raid leaders can only gain raid AA XP - if (r->IsLeader(this)) { - if (r->RaidCount() > 17) - return true; - else - return false; - } - - uint32 gid = r->GetGroup(this); - - if (gid > 11) // not in a group - return false; - - if (r->IsGroupLeader(GetName()) && r->GroupCount(gid) > 2) - return true; - - return false; - -} - -int Client::GetAggroCount() { - return AggroCount; -} - -void Client::IncrementAggroCount() { - - // This method is called when a client is added to a mob's hate list. It turns the clients aggro flag on so - // rest state regen is stopped, and for SoF, it sends the opcode to show the crossed swords in-combat indicator. - // - // - AggroCount++; - - if(!RuleI(Character, RestRegenPercent)) - return; - - // If we already had aggro before this method was called, the combat indicator should already be up for SoF clients, - // so we don't need to send it again. - // - if(AggroCount > 1) - return; - - // Pause the rest timer - if (AggroCount == 1) - SavedRaidRestTimer = rest_timer.GetRemainingTime(); - - if(GetClientVersion() >= EQClientSoF) { - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_RestState, 1); - char *Buffer = (char *)outapp->pBuffer; - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0x01); - QueuePacket(outapp); - safe_delete(outapp); - } - -} - -void Client::DecrementAggroCount() { - - // This should be called when a client is removed from a mob's hate list (it dies or is memblurred). - // It checks whether any other mob is aggro on the player, and if not, starts the rest timer. - // For SoF, the opcode to start the rest state countdown timer in the UI is sent. - // - - // If we didn't have aggro before, this method should not have been called. - if(!AggroCount) - return; - - AggroCount--; - - if(!RuleI(Character, RestRegenPercent)) - return; - - // Something else is still aggro on us, can't rest yet. - if(AggroCount) return; - - uint32 time_until_rest; - if (GetEngagedRaidTarget()) { - time_until_rest = RuleI(Character, RestRegenRaidTimeToActivate) * 1000; - SetEngagedRaidTarget(false); - } else { - if (SavedRaidRestTimer > (RuleI(Character, RestRegenTimeToActivate) * 1000)) { - time_until_rest = SavedRaidRestTimer; - SavedRaidRestTimer = 0; - } else { - time_until_rest = RuleI(Character, RestRegenTimeToActivate) * 1000; - } - } - - rest_timer.Start(time_until_rest); - - if(GetClientVersion() >= EQClientSoF) { - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_RestState, 5); - char *Buffer = (char *)outapp->pBuffer; - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0x00); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, (uint32)(time_until_rest / 1000)); - QueuePacket(outapp); - safe_delete(outapp); - } -} - -void Client::SendPVPStats() -{ - // This sends the data to the client to populate the PVP Stats Window. - // - // When the PVP Stats window is opened, no opcode is sent. Therefore this method should be called - // from Client::CompleteConnect, and also when the player makes a PVP kill. - // - EQApplicationPacket *outapp = new EQApplicationPacket(OP_PVPStats, sizeof(PVPStats_Struct)); - PVPStats_Struct *pvps = (PVPStats_Struct *)outapp->pBuffer; - - pvps->Kills = m_pp.PVPKills; - pvps->Deaths = m_pp.PVPDeaths; - pvps->PVPPointsAvailable = m_pp.PVPCurrentPoints; - pvps->TotalPVPPoints = m_pp.PVPCareerPoints; - pvps->BestKillStreak = m_pp.PVPBestKillStreak; - pvps->WorstDeathStreak = m_pp.PVPWorstDeathStreak; - pvps->CurrentKillStreak = m_pp.PVPCurrentKillStreak; - - // TODO: Record and send other PVP Stats - - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::SendCrystalCounts() -{ - EQApplicationPacket *outapp = new EQApplicationPacket(OP_CrystalCountUpdate, sizeof(CrystalCountUpdate_Struct)); - CrystalCountUpdate_Struct *ccus = (CrystalCountUpdate_Struct *)outapp->pBuffer; - - ccus->CurrentRadiantCrystals = GetRadiantCrystals(); - ccus->CurrentEbonCrystals = GetEbonCrystals(); - ccus->CareerRadiantCrystals = m_pp.careerRadCrystals; - ccus->CareerEbonCrystals = m_pp.careerEbonCrystals; - - - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::SendDisciplineTimers() -{ - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_DisciplineTimer, sizeof(DisciplineTimer_Struct)); - DisciplineTimer_Struct *dts = (DisciplineTimer_Struct *)outapp->pBuffer; - - for(unsigned int i = 0; i < MAX_DISCIPLINE_TIMERS; ++i) - { - uint32 RemainingTime = p_timers.GetRemainingTime(pTimerDisciplineReuseStart + i); - - if(RemainingTime > 0) - { - dts->TimerID = i; - dts->Duration = RemainingTime; - QueuePacket(outapp); - } - } - - safe_delete(outapp); -} - -void Client::SendRespawnBinds() -{ - // This sends the data to the client to populate the Respawn from Death Window. - // - // This should be sent after OP_Death for SoF clients - // Client will respond with a 4 byte packet that includes the number of the selection made - // - - //If no options have been given, default to Bind + Rez - if (respawn_options.empty()) - { - BindStruct* b = &m_pp.binds[0]; - RespawnOption opt; - opt.name = "Bind Location"; - opt.zone_id = b->zoneId; - opt.instance_id = b->instance_id; - opt.x = b->x; - opt.y = b->y; - opt.z = b->z; - opt.heading = b->heading; - respawn_options.push_front(opt); - } - //Rez is always added at the end - RespawnOption rez; - rez.name = "Resurrect"; - rez.zone_id = zone->GetZoneID(); - rez.instance_id = zone->GetInstanceID(); - rez.x = GetX(); - rez.y = GetY(); - rez.z = GetZ(); - rez.heading = GetHeading(); - respawn_options.push_back(rez); - - int num_options = respawn_options.size(); - uint32 PacketLength = 17 + (26 * num_options); //Header size + per-option invariant size - - std::list::iterator itr; - RespawnOption* opt; - - //Find string size for each option - for (itr = respawn_options.begin(); itr != respawn_options.end(); ++itr) - { - opt = &(*itr); - PacketLength += opt->name.size() + 1; //+1 for cstring - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RespawnWindow, PacketLength); - char* buffer = (char*)outapp->pBuffer; - - //Packet header - VARSTRUCT_ENCODE_TYPE(uint32, buffer, initial_respawn_selection); //initial selection (from 0) - VARSTRUCT_ENCODE_TYPE(uint32, buffer, RuleI(Character, RespawnFromHoverTimer) * 1000); - VARSTRUCT_ENCODE_TYPE(uint32, buffer, 0); //unknown - VARSTRUCT_ENCODE_TYPE(uint32, buffer, num_options); //number of options to display - - //Individual options - int count = 0; - for (itr = respawn_options.begin(); itr != respawn_options.end(); ++itr) - { - opt = &(*itr); - VARSTRUCT_ENCODE_TYPE(uint32, buffer, count++); //option num (from 0) - VARSTRUCT_ENCODE_TYPE(uint32, buffer, opt->zone_id); - VARSTRUCT_ENCODE_TYPE(float, buffer, opt->x); - VARSTRUCT_ENCODE_TYPE(float, buffer, opt->y); - VARSTRUCT_ENCODE_TYPE(float, buffer, opt->z); - VARSTRUCT_ENCODE_TYPE(float, buffer, opt->heading); - VARSTRUCT_ENCODE_STRING(buffer, opt->name.c_str()); - VARSTRUCT_ENCODE_TYPE(uint8, buffer, (count == num_options)); //is this one Rez (the last option)? - } - - QueuePacket(outapp); - safe_delete(outapp); - return; -} - -void Client::HandleLDoNOpen(NPC *target) -{ - if(target) - { - if(target->GetClass() != LDON_TREASURE) - { - LogFile->write(EQEMuLog::Debug, "%s tried to open %s but %s was not a treasure chest.", - GetName(), target->GetName(), target->GetName()); - return; - } - - if(DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) - { - LogFile->write(EQEMuLog::Debug, "%s tried to open %s but %s was out of range", - GetName(), target->GetName(), target->GetName()); - Message(13, "Treasure chest out of range."); - return; - } - - if(target->IsLDoNTrapped()) - { - if(target->GetLDoNTrapSpellID() != 0) - { - Message_StringID(13, LDON_ACCIDENT_SETOFF2); - target->SpellFinished(target->GetLDoNTrapSpellID(), this, 10, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff); - target->SetLDoNTrapSpellID(0); - target->SetLDoNTrapped(false); - target->SetLDoNTrapDetected(false); - } - else - { - target->SetLDoNTrapSpellID(0); - target->SetLDoNTrapped(false); - target->SetLDoNTrapDetected(false); - } - } - - if(target->IsLDoNLocked()) - { - Message_StringID(MT_Skills, LDON_STILL_LOCKED, target->GetCleanName()); - return; - } - else - { - target->AddToHateList(this, 0, 500000, false, false, false); - if(target->GetLDoNTrapType() != 0) - { - if(GetRaid()) - { - GetRaid()->SplitExp(target->GetLevel()*target->GetLevel()*2625/10, target); - } - else if(GetGroup()) - { - GetGroup()->SplitExp(target->GetLevel()*target->GetLevel()*2625/10, target); - } - else - { - AddEXP(target->GetLevel()*target->GetLevel()*2625/10, GetLevelCon(target->GetLevel())); - } - } - target->Death(this, 1, SPELL_UNKNOWN, SkillHandtoHand); - } - } -} - -void Client::HandleLDoNSenseTraps(NPC *target, uint16 skill, uint8 type) -{ - if(target && target->GetClass() == LDON_TREASURE) - { - if(target->IsLDoNTrapped()) - { - if((target->GetLDoNTrapType() == LDoNTypeCursed || target->GetLDoNTrapType() == LDoNTypeMagical) && type != target->GetLDoNTrapType()) - { - Message_StringID(MT_Skills, LDON_CANT_DETERMINE_TRAP, target->GetCleanName()); - return; - } - - if(target->IsLDoNTrapDetected()) - { - Message_StringID(MT_Skills, LDON_CERTAIN_TRAP, target->GetCleanName()); - } - else - { - int check = LDoNChest_SkillCheck(target, skill); - switch(check) - { - case -1: - case 0: - Message_StringID(MT_Skills, LDON_DONT_KNOW_TRAPPED, target->GetCleanName()); - break; - case 1: - Message_StringID(MT_Skills, LDON_CERTAIN_TRAP, target->GetCleanName()); - target->SetLDoNTrapDetected(true); - break; - default: - break; - } - } - } - else - { - Message_StringID(MT_Skills, LDON_CERTAIN_NOT_TRAP, target->GetCleanName()); - } - } -} - -void Client::HandleLDoNDisarm(NPC *target, uint16 skill, uint8 type) -{ - if(target) - { - if(target->GetClass() == LDON_TREASURE) - { - if(!target->IsLDoNTrapped()) - { - Message_StringID(MT_Skills, LDON_WAS_NOT_TRAPPED, target->GetCleanName()); - return; - } - - if((target->GetLDoNTrapType() == LDoNTypeCursed || target->GetLDoNTrapType() == LDoNTypeMagical) && type != target->GetLDoNTrapType()) - { - Message_StringID(MT_Skills, LDON_HAVE_NOT_DISARMED, target->GetCleanName()); - return; - } - - int check = 0; - if(target->IsLDoNTrapDetected()) - { - check = LDoNChest_SkillCheck(target, skill); - } - else - { - check = LDoNChest_SkillCheck(target, skill*33/100); - } - switch(check) - { - case 1: - target->SetLDoNTrapDetected(false); - target->SetLDoNTrapped(false); - target->SetLDoNTrapSpellID(0); - Message_StringID(MT_Skills, LDON_HAVE_DISARMED, target->GetCleanName()); - break; - case 0: - Message_StringID(MT_Skills, LDON_HAVE_NOT_DISARMED, target->GetCleanName()); - break; - case -1: - Message_StringID(13, LDON_ACCIDENT_SETOFF2); - target->SpellFinished(target->GetLDoNTrapSpellID(), this, 10, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff); - target->SetLDoNTrapSpellID(0); - target->SetLDoNTrapped(false); - target->SetLDoNTrapDetected(false); - break; - } - } - } -} - -void Client::HandleLDoNPickLock(NPC *target, uint16 skill, uint8 type) -{ - if(target) - { - if(target->GetClass() == LDON_TREASURE) - { - if(target->IsLDoNTrapped()) - { - Message_StringID(13, LDON_ACCIDENT_SETOFF2); - target->SpellFinished(target->GetLDoNTrapSpellID(), this, 10, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff); - target->SetLDoNTrapSpellID(0); - target->SetLDoNTrapped(false); - target->SetLDoNTrapDetected(false); - } - - if(!target->IsLDoNLocked()) - { - Message_StringID(MT_Skills, LDON_WAS_NOT_LOCKED, target->GetCleanName()); - return; - } - - if((target->GetLDoNTrapType() == LDoNTypeCursed || target->GetLDoNTrapType() == LDoNTypeMagical) && type != target->GetLDoNTrapType()) - { - Message(MT_Skills, "You cannot unlock %s with this skill.", target->GetCleanName()); - return; - } - - int check = LDoNChest_SkillCheck(target, skill); - - switch(check) - { - case 0: - case -1: - Message_StringID(MT_Skills, LDON_PICKLOCK_FAILURE, target->GetCleanName()); - break; - case 1: - target->SetLDoNLocked(false); - Message_StringID(MT_Skills, LDON_PICKLOCK_SUCCESS, target->GetCleanName()); - break; - } - } - } -} - -int Client::LDoNChest_SkillCheck(NPC *target, int skill) -{ - if(!target) - return -1; - - int chest_difficulty = target->GetLDoNLockedSkill() == 0 ? (target->GetLevel() * 5) : target->GetLDoNLockedSkill(); - float base_difficulty = RuleR(Adventure, LDoNBaseTrapDifficulty); - - if(chest_difficulty == 0) - chest_difficulty = 5; - - float chance = ((100.0f - base_difficulty) * ((float)skill / (float)chest_difficulty)); - - if(chance > (100.0f - base_difficulty)) - { - chance = 100.0f - base_difficulty; - } - - float d100 = (float)zone->random.Real(0, 100); - - if(d100 <= chance) - return 1; - else - { - if(d100 > (chance + RuleR(Adventure, LDoNCriticalFailTrapThreshold))) - return -1; - } - - return 0; -} - -void Client::SummonAndRezzAllCorpses() -{ - PendingRezzXP = -1; - - ServerPacket *Pack = new ServerPacket(ServerOP_DepopAllPlayersCorpses, sizeof(ServerDepopAllPlayersCorpses_Struct)); - - ServerDepopAllPlayersCorpses_Struct *sdapcs = (ServerDepopAllPlayersCorpses_Struct*)Pack->pBuffer; - - sdapcs->CharacterID = CharacterID(); - sdapcs->ZoneID = zone->GetZoneID(); - sdapcs->InstanceID = zone->GetInstanceID(); - - worldserver.SendPacket(Pack); - - safe_delete(Pack); - - entity_list.RemoveAllCorpsesByCharID(CharacterID()); - - int CorpseCount = database.SummonAllCharacterCorpses(CharacterID(), zone->GetZoneID(), zone->GetInstanceID(), GetPosition()); - if(CorpseCount <= 0) - { - Message(clientMessageYellow, "You have no corpses to summnon."); - return; - } - - int RezzExp = entity_list.RezzAllCorpsesByCharID(CharacterID()); - - if(RezzExp > 0) - SetEXP(GetEXP() + RezzExp, GetAAXP(), true); - - Message(clientMessageYellow, "All your corpses have been summoned to your feet and have received a 100% resurrection."); -} - -void Client::SummonAllCorpses(const xyz_heading& position) -{ - auto summonLocation = position; - if(position.isOrigin() && position.m_Heading == 0.0f) - summonLocation = GetPosition(); - - ServerPacket *Pack = new ServerPacket(ServerOP_DepopAllPlayersCorpses, sizeof(ServerDepopAllPlayersCorpses_Struct)); - - ServerDepopAllPlayersCorpses_Struct *sdapcs = (ServerDepopAllPlayersCorpses_Struct*)Pack->pBuffer; - - sdapcs->CharacterID = CharacterID(); - sdapcs->ZoneID = zone->GetZoneID(); - sdapcs->InstanceID = zone->GetInstanceID(); - - worldserver.SendPacket(Pack); - - safe_delete(Pack); - - entity_list.RemoveAllCorpsesByCharID(CharacterID()); - - database.SummonAllCharacterCorpses(CharacterID(), zone->GetZoneID(), zone->GetInstanceID(), summonLocation); -} - -void Client::DepopAllCorpses() -{ - ServerPacket *Pack = new ServerPacket(ServerOP_DepopAllPlayersCorpses, sizeof(ServerDepopAllPlayersCorpses_Struct)); - - ServerDepopAllPlayersCorpses_Struct *sdapcs = (ServerDepopAllPlayersCorpses_Struct*)Pack->pBuffer; - - sdapcs->CharacterID = CharacterID(); - sdapcs->ZoneID = zone->GetZoneID(); - sdapcs->InstanceID = zone->GetInstanceID(); - - worldserver.SendPacket(Pack); - - safe_delete(Pack); - - entity_list.RemoveAllCorpsesByCharID(CharacterID()); -} - -void Client::DepopPlayerCorpse(uint32 dbid) -{ - ServerPacket *Pack = new ServerPacket(ServerOP_DepopPlayerCorpse, sizeof(ServerDepopPlayerCorpse_Struct)); - - ServerDepopPlayerCorpse_Struct *sdpcs = (ServerDepopPlayerCorpse_Struct*)Pack->pBuffer; - - sdpcs->DBID = dbid; - sdpcs->ZoneID = zone->GetZoneID(); - sdpcs->InstanceID = zone->GetInstanceID(); - - worldserver.SendPacket(Pack); - - safe_delete(Pack); - - entity_list.RemoveCorpseByDBID(dbid); -} - -void Client::BuryPlayerCorpses() -{ - database.BuryAllCharacterCorpses(CharacterID()); -} - -void Client::NotifyNewTitlesAvailable() -{ - EQApplicationPacket *outapp = new EQApplicationPacket(OP_NewTitlesAvailable, 0); - - QueuePacket(outapp); - - safe_delete(outapp); - -} - -void Client::SetStartZone(uint32 zoneid, float x, float y, float z) -{ - // setting city to zero allows the player to use /setstartcity to set the city themselves - if(zoneid == 0) { - m_pp.binds[4].zoneId = 0; - this->Message(15,"Your starting city has been reset. Use /setstartcity to choose a new one"); - return; - } - - // check to make sure the zone is valid - const char *target_zone_name = database.GetZoneName(zoneid); - if(target_zone_name == nullptr) - return; - - m_pp.binds[4].zoneId = zoneid; - if(zone->GetInstanceID() != 0 && zone->IsInstancePersistent()) { - m_pp.binds[4].instance_id = zone->GetInstanceID(); - } - - if (x == 0 && y == 0 && z ==0) - database.GetSafePoints(m_pp.binds[4].zoneId, 0, &m_pp.binds[4].x, &m_pp.binds[4].y, &m_pp.binds[4].z); - else { - m_pp.binds[4].x = x; - m_pp.binds[4].y = y; - m_pp.binds[4].z = z; - } -} - -uint32 Client::GetStartZone() -{ - return m_pp.binds[4].zoneId; -} - -void Client::ShowSkillsWindow() -{ - const char *WindowTitle = "Skills"; - std::string WindowText; - // using a map for easy alphabetizing of the skills list - std::map Skills; - std::map::iterator it; - - // this list of names must keep the same order as that in common/skills.h - const char* SkillName[] = {"1H Blunt","1H Slashing","2H Blunt","2H Slashing","Abjuration","Alteration","Apply Poison","Archery", - "Backstab","Bind Wound","Bash","Block","Brass Instruments","Channeling","Conjuration","Defense","Disarm","Disarm Traps","Divination", - "Dodge","Double Attack","Dragon Punch","Dual Wield","Eagle Strike","Evocation","Feign Death","Flying Kick","Forage","Hand to Hand", - "Hide","Kick","Meditate","Mend","Offense","Parry","Pick Lock","Piercing","Ripost","Round Kick","Safe Fall","Sense Heading", - "Singing","Sneak","Specialize Abjuration","Specialize Alteration","Specialize Conjuration","Specialize Divination","Specialize Evocation","Pick Pockets", - "Stringed Instruments","Swimming","Throwing","Tiger Claw","Tracking","Wind Instruments","Fishing","Make Poison","Tinkering","Research", - "Alchemy","Baking","Tailoring","Sense Traps","Blacksmithing","Fletching","Brewing","Alcohol Tolerance","Begging","Jewelry Making", - "Pottery","Percussion Instruments","Intimidation","Berserking","Taunt","Frenzy"}; - for(int i = 0; i <= (int)HIGHEST_SKILL; i++) - Skills[SkillName[i]] = (SkillUseTypes)i; - - // print out all available skills - for(it = Skills.begin(); it != Skills.end(); ++it) { - if(GetSkill(it->second) > 0 || MaxSkill(it->second) > 0) { - WindowText += it->first; - // line up the values - for (int j = 0; j < EmuConstants::ITEM_COMMON_SIZE; j++) - WindowText += " "; - WindowText += itoa(this->GetSkill(it->second)); - if (MaxSkill(it->second) > 0) { - WindowText += "/"; - WindowText += itoa(this->GetMaxSkillAfterSpecializationRules(it->second,this->MaxSkill(it->second))); - } - WindowText += "
"; - } - } - this->SendPopupToClient(WindowTitle, WindowText.c_str()); -} - - -void Client::SetShadowStepExemption(bool v) -{ - if(v == true) - { - uint32 cur_time = Timer::GetCurrentTime(); - if((cur_time - m_TimeSinceLastPositionCheck) > 1000) - { - float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck); - float runs = GetRunspeed(); - if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor))) - { - printf("%s %i moving too fast! moved: %.2f in %ims, speed %.2f\n", __FILE__, __LINE__, - m_DistanceSinceLastPositionCheck, (cur_time - m_TimeSinceLastPositionCheck), speed); - if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor))))) - { - if(IsShadowStepExempted()) - { - if(m_DistanceSinceLastPositionCheck > 800) - { - CheatDetected(MQWarpShadowStep, GetX(), GetY(), GetZ()); - } - } - else if(IsKnockBackExempted()) - { - //still potential to trigger this if you're knocked back off a - //HUGE fall that takes > 2.5 seconds - if(speed > 30.0f) - { - CheatDetected(MQWarpKnockBack, GetX(), GetY(), GetZ()); - } - } - else if(!IsPortExempted()) - { - if(!IsMQExemptedArea(zone->GetZoneID(), GetX(), GetY(), GetZ())) - { - if(speed > (runs * 2 * RuleR(Zone, MQWarpDetectionDistanceFactor))) - { - CheatDetected(MQWarp, GetX(), GetY(), GetZ()); - m_TimeSinceLastPositionCheck = cur_time; - m_DistanceSinceLastPositionCheck = 0.0f; - //Death(this, 10000000, SPELL_UNKNOWN, _1H_BLUNT); - } - else - { - CheatDetected(MQWarpLight, GetX(), GetY(), GetZ()); - } - } - } - } - } - } - m_TimeSinceLastPositionCheck = cur_time; - m_DistanceSinceLastPositionCheck = 0.0f; - } - m_ShadowStepExemption = v; -} - -void Client::SetKnockBackExemption(bool v) -{ - if(v == true) - { - uint32 cur_time = Timer::GetCurrentTime(); - if((cur_time - m_TimeSinceLastPositionCheck) > 1000) - { - float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck); - float runs = GetRunspeed(); - if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor))) - { - if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor))))) - { - printf("%s %i moving too fast! moved: %.2f in %ims, speed %.2f\n", __FILE__, __LINE__, - m_DistanceSinceLastPositionCheck, (cur_time - m_TimeSinceLastPositionCheck), speed); - if(IsShadowStepExempted()) - { - if(m_DistanceSinceLastPositionCheck > 800) - { - CheatDetected(MQWarpShadowStep, GetX(), GetY(), GetZ()); - } - } - else if(IsKnockBackExempted()) - { - //still potential to trigger this if you're knocked back off a - //HUGE fall that takes > 2.5 seconds - if(speed > 30.0f) - { - CheatDetected(MQWarpKnockBack, GetX(), GetY(), GetZ()); - } - } - else if(!IsPortExempted()) - { - if(!IsMQExemptedArea(zone->GetZoneID(), GetX(), GetY(), GetZ())) - { - if(speed > (runs * 2 * RuleR(Zone, MQWarpDetectionDistanceFactor))) - { - m_TimeSinceLastPositionCheck = cur_time; - m_DistanceSinceLastPositionCheck = 0.0f; - CheatDetected(MQWarp, GetX(), GetY(), GetZ()); - //Death(this, 10000000, SPELL_UNKNOWN, _1H_BLUNT); - } - else - { - CheatDetected(MQWarpLight, GetX(), GetY(), GetZ()); - } - } - } - } - } - } - m_TimeSinceLastPositionCheck = cur_time; - m_DistanceSinceLastPositionCheck = 0.0f; - } - m_KnockBackExemption = v; -} - -void Client::SetPortExemption(bool v) -{ - if(v == true) - { - uint32 cur_time = Timer::GetCurrentTime(); - if((cur_time - m_TimeSinceLastPositionCheck) > 1000) - { - float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck); - float runs = GetRunspeed(); - if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor))) - { - if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor))))) - { - printf("%s %i moving too fast! moved: %.2f in %ims, speed %.2f\n", __FILE__, __LINE__, - m_DistanceSinceLastPositionCheck, (cur_time - m_TimeSinceLastPositionCheck), speed); - if(IsShadowStepExempted()) - { - if(m_DistanceSinceLastPositionCheck > 800) - { - CheatDetected(MQWarpShadowStep, GetX(), GetY(), GetZ()); - } - } - else if(IsKnockBackExempted()) - { - //still potential to trigger this if you're knocked back off a - //HUGE fall that takes > 2.5 seconds - if(speed > 30.0f) - { - CheatDetected(MQWarpKnockBack, GetX(), GetY(), GetZ()); - } - } - else if(!IsPortExempted()) - { - if(!IsMQExemptedArea(zone->GetZoneID(), GetX(), GetY(), GetZ())) - { - if(speed > (runs * 2 * RuleR(Zone, MQWarpDetectionDistanceFactor))) - { - m_TimeSinceLastPositionCheck = cur_time; - m_DistanceSinceLastPositionCheck = 0.0f; - CheatDetected(MQWarp, GetX(), GetY(), GetZ()); - //Death(this, 10000000, SPELL_UNKNOWN, _1H_BLUNT); - } - else - { - CheatDetected(MQWarpLight, GetX(), GetY(), GetZ()); - } - } - } - } - } - } - m_TimeSinceLastPositionCheck = cur_time; - m_DistanceSinceLastPositionCheck = 0.0f; - } - m_PortExemption = v; -} - -void Client::Signal(uint32 data) -{ - char buf[32]; - snprintf(buf, 31, "%d", data); - buf[31] = '\0'; - parse->EventPlayer(EVENT_SIGNAL, this, buf, 0); -} - -const bool Client::IsMQExemptedArea(uint32 zoneID, float x, float y, float z) const -{ - float max_dist = 90000; - switch(zoneID) - { - case 2: - { - float delta = (x-(-713.6)); - delta *= delta; - float distance = delta; - delta = (y-(-160.2)); - delta *= delta; - distance += delta; - delta = (z-(-12.8)); - delta *= delta; - distance += delta; - - if(distance < max_dist) - return true; - - delta = (x-(-153.8)); - delta *= delta; - distance = delta; - delta = (y-(-30.3)); - delta *= delta; - distance += delta; - delta = (z-(8.2)); - delta *= delta; - distance += delta; - - if(distance < max_dist) - return true; - - break; - } - case 9: - { - float delta = (x-(-682.5)); - delta *= delta; - float distance = delta; - delta = (y-(147.0)); - delta *= delta; - distance += delta; - delta = (z-(-9.9)); - delta *= delta; - distance += delta; - - if(distance < max_dist) - return true; - - delta = (x-(-655.4)); - delta *= delta; - distance = delta; - delta = (y-(10.5)); - delta *= delta; - distance += delta; - delta = (z-(-51.8)); - delta *= delta; - distance += delta; - - if(distance < max_dist) - return true; - - break; - } - case 62: - case 75: - case 114: - case 209: - { - //The portals are so common in paineel/felwitheb that checking - //distances wouldn't be worth it cause unless you're porting to the - //start field you're going to be triggering this and that's a level of - //accuracy I'm willing to sacrifice - return true; - break; - } - - case 24: - { - float delta = (x-(-183.0)); - delta *= delta; - float distance = delta; - delta = (y-(-773.3)); - delta *= delta; - distance += delta; - delta = (z-(54.1)); - delta *= delta; - distance += delta; - - if(distance < max_dist) - return true; - - delta = (x-(-8.8)); - delta *= delta; - distance = delta; - delta = (y-(-394.1)); - delta *= delta; - distance += delta; - delta = (z-(41.1)); - delta *= delta; - distance += delta; - - if(distance < max_dist) - return true; - - delta = (x-(-310.3)); - delta *= delta; - distance = delta; - delta = (y-(-1411.6)); - delta *= delta; - distance += delta; - delta = (z-(-42.8)); - delta *= delta; - distance += delta; - - if(distance < max_dist) - return true; - - delta = (x-(-183.1)); - delta *= delta; - distance = delta; - delta = (y-(-1409.8)); - delta *= delta; - distance += delta; - delta = (z-(37.1)); - delta *= delta; - distance += delta; - - if(distance < max_dist) - return true; - - break; - } - - case 110: - case 34: - case 96: - case 93: - case 68: - case 84: - { - if(GetBoatID() != 0) - return true; - break; - } - default: - break; - } - return false; -} - -void Client::SendRewards() -{ - std::vector rewards; - std::string query = StringFormat("SELECT reward_id, amount " - "FROM account_rewards " - "WHERE account_id = %i " - "ORDER BY reward_id", AccountID()); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "Error in Client::SendRewards(): %s (%s)", query.c_str(), results.ErrorMessage().c_str()); - return; - } - - for (auto row = results.begin(); row != results.end(); ++row) { - ClientReward cr; - cr.id = atoi(row[0]); - cr.amount = atoi(row[1]); - rewards.push_back(cr); - } - - if(rewards.size() == 0) - return; - - EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(InternalVeteranReward) * rewards.size())); - uchar *data = vetapp->pBuffer; - for(int i = 0; i < rewards.size(); ++i) { - InternalVeteranReward *ivr = (InternalVeteranReward*)data; - ivr->claim_id = rewards[i].id; - ivr->number_available = rewards[i].amount; - auto iter = zone->VeteranRewards.begin(); - for (;iter != zone->VeteranRewards.end(); ++iter) - if((*iter).claim_id == rewards[i].id) - break; - - if(iter != zone->VeteranRewards.end()) { - InternalVeteranReward ivro = (*iter); - ivr->claim_count = ivro.claim_count; - for(int x = 0; x < ivro.claim_count; ++x) { - ivr->items[x].item_id = ivro.items[x].item_id; - ivr->items[x].charges = ivro.items[x].charges; - strcpy(ivr->items[x].item_name, ivro.items[x].item_name); - } - } - - data += sizeof(InternalVeteranReward); - } - - FastQueuePacket(&vetapp); -} - -bool Client::TryReward(uint32 claim_id) { - //Make sure we have an open spot - //Make sure we have it in our acct and count > 0 - //Make sure the entry was found - //If we meet all the criteria: - //Decrement our count by 1 if it > 1 delete if it == 1 - //Create our item in bag if necessary at the free inv slot - //save - uint32 free_slot = 0xFFFFFFFF; - - for(int i = EmuConstants::GENERAL_BEGIN; i <= EmuConstants::GENERAL_END; ++i) { - ItemInst *item = GetInv().GetItem(i); - if(!item) { - free_slot = i; - break; - } - } - - if(free_slot == 0xFFFFFFFF) - return false; - - char errbuf[MYSQL_ERRMSG_SIZE]; - std::string query = StringFormat("SELECT amount FROM account_rewards " - "WHERE account_id = %i AND reward_id = %i", - AccountID(), claim_id); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "Error in Client::TryReward(): %s (%s)", query.c_str(), results.ErrorMessage().c_str()); - return false; - } - - if (results.RowCount() == 0) - return false; - - auto row = results.begin(); - - uint32 amt = atoi(row[0]); - if(amt == 0) - return false; - - std::list::iterator iter = zone->VeteranRewards.begin(); - for (; iter != zone->VeteranRewards.end(); ++row) - if((*iter).claim_id == claim_id) - break; - - if(iter == zone->VeteranRewards.end()) - return false; - - if(amt == 1) { - query = StringFormat("DELETE FROM account_rewards " - "WHERE account_id = %i AND reward_id = %i", - AccountID(), claim_id); - auto results = database.QueryDatabase(query); - if(!results.Success()) - LogFile->write(EQEMuLog::Error, "Error in Client::TryReward(): %s (%s)", query.c_str(), results.ErrorMessage().c_str()); - } - else { - query = StringFormat("UPDATE account_rewards SET amount = (amount-1) " - "WHERE account_id = %i AND reward_id = %i", - AccountID(), claim_id); - auto results = database.QueryDatabase(query); - if(!results.Success()) - LogFile->write(EQEMuLog::Error, "Error in Client::TryReward(): %s (%s)", query.c_str(), results.ErrorMessage().c_str()); - } - - InternalVeteranReward ivr = (*iter); - ItemInst *claim = database.CreateItem(ivr.items[0].item_id, ivr.items[0].charges); - if(!claim) { - Save(); - return true; - } - - bool lore_conflict = CheckLoreConflict(claim->GetItem()); - - for(int y = 1; y < 8; y++) - if(ivr.items[y].item_id && claim->GetItem()->ItemClass == 1) { - ItemInst *item_temp = database.CreateItem(ivr.items[y].item_id, ivr.items[y].charges); - if(item_temp) { - if(CheckLoreConflict(item_temp->GetItem())) { - lore_conflict = true; - DuplicateLoreMessage(ivr.items[y].item_id); - } - claim->PutItem(y-1, *item_temp); - } - } - - if(lore_conflict) { - safe_delete(claim); - return true; - } - - PutItemInInventory(free_slot, *claim); - SendItemPacket(free_slot, claim, ItemPacketTrade); - - Save(); - return true; -} - -uint32 Client::GetLDoNPointsTheme(uint32 t) -{ - switch(t) - { - case 1: - return m_pp.ldon_points_guk; - case 2: - return m_pp.ldon_points_mir; - case 3: - return m_pp.ldon_points_mmc; - case 4: - return m_pp.ldon_points_ruj; - case 5: - return m_pp.ldon_points_tak; - default: - return 0; - } -} - -uint32 Client::GetLDoNWinsTheme(uint32 t) -{ - switch(t) - { - case 1: - return m_pp.ldon_wins_guk; - case 2: - return m_pp.ldon_wins_mir; - case 3: - return m_pp.ldon_wins_mmc; - case 4: - return m_pp.ldon_wins_ruj; - case 5: - return m_pp.ldon_wins_tak; - default: - return 0; - } -} - -uint32 Client::GetLDoNLossesTheme(uint32 t) -{ - switch(t) - { - case 1: - return m_pp.ldon_losses_guk; - case 2: - return m_pp.ldon_losses_mir; - case 3: - return m_pp.ldon_losses_mmc; - case 4: - return m_pp.ldon_losses_ruj; - case 5: - return m_pp.ldon_losses_tak; - default: - return 0; - } -} - -void Client::UpdateLDoNWins(uint32 t, int32 n) -{ - switch(t) - { - case 1: - m_pp.ldon_wins_guk = n; - break; - case 2: - m_pp.ldon_wins_mir = n; - break; - case 3: - m_pp.ldon_wins_mmc = n; - break; - case 4: - m_pp.ldon_wins_ruj = n; - break; - case 5: - m_pp.ldon_wins_tak = n; - break; - default: - return; - } -} - -void Client::UpdateLDoNLosses(uint32 t, int32 n) -{ - switch(t) - { - case 1: - m_pp.ldon_losses_guk = n; - break; - case 2: - m_pp.ldon_losses_mir = n; - break; - case 3: - m_pp.ldon_losses_mmc = n; - break; - case 4: - m_pp.ldon_losses_ruj = n; - break; - case 5: - m_pp.ldon_losses_tak = n; - break; - default: - return; - } -} - - -void Client::SuspendMinion() -{ - NPC *CurrentPet = GetPet()->CastToNPC(); - - int AALevel = GetAA(aaSuspendedMinion); - - if(AALevel == 0) - return; - - if(GetLevel() < 62) - return; - - if(!CurrentPet) - { - if(m_suspendedminion.SpellID > 0) - { - MakePoweredPet(m_suspendedminion.SpellID, spells[m_suspendedminion.SpellID].teleport_zone, - m_suspendedminion.petpower, m_suspendedminion.Name, m_suspendedminion.size); - - CurrentPet = GetPet()->CastToNPC(); - - if(!CurrentPet) - { - Message(13, "Failed to recall suspended minion."); - return; - } - - if(AALevel >= 2) - { - CurrentPet->SetPetState(m_suspendedminion.Buffs, m_suspendedminion.Items); - - CurrentPet->SendPetBuffsToClient(); - } - CurrentPet->CalcBonuses(); - - CurrentPet->SetHP(m_suspendedminion.HP); - - CurrentPet->SetMana(m_suspendedminion.Mana); - - Message_StringID(clientMessageTell, SUSPEND_MINION_UNSUSPEND, CurrentPet->GetCleanName()); - - memset(&m_suspendedminion, 0, sizeof(struct PetInfo)); - } - else - return; - - } - else - { - uint16 SpellID = CurrentPet->GetPetSpellID(); - - if(SpellID) - { - if(m_suspendedminion.SpellID > 0) - { - Message_StringID(clientMessageError,ONLY_ONE_PET); - - return; - } - else if(CurrentPet->IsEngaged()) - { - Message_StringID(clientMessageError,SUSPEND_MINION_FIGHTING); - - return; - } - else if(entity_list.Fighting(CurrentPet)) - { - Message_StringID(clientMessageBlue,SUSPEND_MINION_HAS_AGGRO); - } - else - { - m_suspendedminion.SpellID = SpellID; - - m_suspendedminion.HP = CurrentPet->GetHP();; - - m_suspendedminion.Mana = CurrentPet->GetMana(); - m_suspendedminion.petpower = CurrentPet->GetPetPower(); - m_suspendedminion.size = CurrentPet->GetSize(); - - if(AALevel >= 2) - CurrentPet->GetPetState(m_suspendedminion.Buffs, m_suspendedminion.Items, m_suspendedminion.Name); - else - strn0cpy(m_suspendedminion.Name, CurrentPet->GetName(), 64); // Name stays even at rank 1 - - Message_StringID(clientMessageTell, SUSPEND_MINION_SUSPEND, CurrentPet->GetCleanName()); - - CurrentPet->Depop(false); - - SetPetID(0); - } - } - else - { - Message_StringID(clientMessageError, ONLY_SUMMONED_PETS); - - return; - } - } -} - -void Client::AddPVPPoints(uint32 Points) -{ - m_pp.PVPCurrentPoints += Points; - m_pp.PVPCareerPoints += Points; - - Save(); - - SendPVPStats(); -} - -void Client::AddCrystals(uint32 Radiant, uint32 Ebon) -{ - m_pp.currentRadCrystals += Radiant; - m_pp.careerRadCrystals += Radiant; - m_pp.currentEbonCrystals += Ebon; - m_pp.careerEbonCrystals += Ebon; - - SaveCurrency(); - - SendCrystalCounts(); -} - -// Processes a client request to inspect a SoF+ client's equipment. -void Client::ProcessInspectRequest(Client* requestee, Client* requester) { - if(requestee && requester) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_InspectAnswer, sizeof(InspectResponse_Struct)); - InspectResponse_Struct* insr = (InspectResponse_Struct*) outapp->pBuffer; - insr->TargetID = requester->GetID(); - insr->playerid = requestee->GetID(); - - const Item_Struct* item = nullptr; - const ItemInst* inst = nullptr; - int ornamentationAugtype = RuleI(Character, OrnamentationAugmentType); - for(int16 L = 0; L <= 20; L++) { - inst = requestee->GetInv().GetItem(L); - - if(inst) { - item = inst->GetItem(); - if(item) { - if (inst && inst->GetOrnamentationAug(ornamentationAugtype)) { - const Item_Struct *aug_weap = inst->GetOrnamentationAug(ornamentationAugtype)->GetItem(); - strcpy(insr->itemnames[L], item->Name); - insr->itemicons[L] = aug_weap->Icon; - } -<<<<<<< HEAD -======= - else if (inst->GetOrnamentationIcon() && inst->GetOrnamentationIDFile()) { - strcpy(insr->itemnames[L], item->Name); - insr->itemicons[L] = inst->GetOrnamentationIcon(); - } ->>>>>>> master - else { - strcpy(insr->itemnames[L], item->Name); - insr->itemicons[L] = item->Icon; - } - } - else - insr->itemicons[L] = 0xFFFFFFFF; - } - } - - inst = requestee->GetInv().GetItem(MainPowerSource); - - if(inst) { - item = inst->GetItem(); - if(item) { - // we shouldn't do this..but, that's the way it's coded atm... - // (this type of action should be handled exclusively in the client translator) - strcpy(insr->itemnames[SoF::slots::MainPowerSource], item->Name); - insr->itemicons[SoF::slots::MainPowerSource] = item->Icon; - } - else - insr->itemicons[SoF::slots::MainPowerSource] = 0xFFFFFFFF; - } - - inst = requestee->GetInv().GetItem(MainAmmo); - - if(inst) { - item = inst->GetItem(); - if(item) { - strcpy(insr->itemnames[SoF::slots::MainAmmo], item->Name); - insr->itemicons[SoF::slots::MainAmmo] = item->Icon; - } - else - insr->itemicons[SoF::slots::MainAmmo] = 0xFFFFFFFF; - } - - strcpy(insr->text, requestee->GetInspectMessage().text); - - // There could be an OP for this..or not... (Ti clients are not processed here..this message is generated client-side) - if(requestee->IsClient() && (requestee != requester)) { requestee->Message(0, "%s is looking at your equipment...", requester->GetName()); } - - requester->QueuePacket(outapp); // Send answer to requester - safe_delete(outapp); - } -} - -void Client::GuildBankAck() -{ - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GuildBank, sizeof(GuildBankAck_Struct)); - - GuildBankAck_Struct *gbas = (GuildBankAck_Struct*) outapp->pBuffer; - - gbas->Action = GuildBankAcknowledge; - - FastQueuePacket(&outapp); -} - -void Client::GuildBankDepositAck(bool Fail) -{ - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GuildBank, sizeof(GuildBankDepositAck_Struct)); - - GuildBankDepositAck_Struct *gbdas = (GuildBankDepositAck_Struct*) outapp->pBuffer; - - gbdas->Action = GuildBankDeposit; - - gbdas->Fail = Fail ? 1 : 0; - - FastQueuePacket(&outapp); -} - -void Client::ClearGuildBank() -{ - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GuildBank, sizeof(GuildBankClear_Struct)); - - GuildBankClear_Struct *gbcs = (GuildBankClear_Struct*) outapp->pBuffer; - - gbcs->Action = GuildBankBulkItems; - gbcs->DepositAreaCount = 0; - gbcs->MainAreaCount = 0; - - FastQueuePacket(&outapp); -} - -void Client::SendGroupCreatePacket() -{ - // For SoD and later clients, this is sent the Group Leader upon initial creation of the group - // - EQApplicationPacket *outapp=new EQApplicationPacket(OP_GroupUpdateB, 32 + strlen(GetName())); - - char *Buffer = (char *)outapp->pBuffer; - // Header - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // Null Leader name - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Member 0 - VARSTRUCT_ENCODE_STRING(Buffer, GetName()); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, GetLevel()); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); - - FastQueuePacket(&outapp); -} - -void Client::SendGroupLeaderChangePacket(const char *LeaderName) -{ - // For SoD and later, send name of Group Leader to this client - - EQApplicationPacket *outapp=new EQApplicationPacket(OP_GroupLeaderChange, sizeof(GroupLeaderChange_Struct)); - - GroupLeaderChange_Struct *glcs = (GroupLeaderChange_Struct*)outapp->pBuffer; - - strn0cpy(glcs->LeaderName, LeaderName, sizeof(glcs->LeaderName)); - - FastQueuePacket(&outapp); -} - -void Client::SendGroupJoinAcknowledge() -{ - // For SoD and later, This produces the 'You have joined the group' message. - EQApplicationPacket* outapp=new EQApplicationPacket(OP_GroupAcknowledge, 4); - FastQueuePacket(&outapp); -} - -void Client::SendAdventureError(const char *error) -{ - size_t error_size = strlen(error); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureInfo, (error_size + 2)); - strn0cpy((char*)outapp->pBuffer, error, error_size); - FastQueuePacket(&outapp); -} - -void Client::SendAdventureDetails() -{ - if(adv_data) - { - ServerSendAdventureData_Struct *ad = (ServerSendAdventureData_Struct*)adv_data; - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureData, sizeof(AdventureRequestResponse_Struct)); - AdventureRequestResponse_Struct *arr = (AdventureRequestResponse_Struct*)outapp->pBuffer; - arr->unknown000 = 0xBFC40100; - arr->unknown2080 = 0x0A; - arr->risk = ad->risk; - strcpy(arr->text, ad->text); - - if(ad->time_to_enter != 0) - { - arr->timetoenter = ad->time_to_enter; - } - else - { - arr->timeleft = ad->time_left; - } - - if(ad->zone_in_id == zone->GetZoneID()) - { - arr->y = ad->x; - arr->x = ad->y; - arr->showcompass = 1; - } - FastQueuePacket(&outapp); - - SendAdventureCount(ad->count, ad->total); - } - else - { - ServerSendAdventureData_Struct *ad = (ServerSendAdventureData_Struct*)adv_data; - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureData, sizeof(AdventureRequestResponse_Struct)); - FastQueuePacket(&outapp); - } -} - -void Client::SendAdventureCount(uint32 count, uint32 total) -{ - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureUpdate, sizeof(AdventureCountUpdate_Struct)); - AdventureCountUpdate_Struct *acu = (AdventureCountUpdate_Struct*)outapp->pBuffer; - acu->current = count; - acu->total = total; - FastQueuePacket(&outapp); -} - -void Client::NewAdventure(int id, int theme, const char *text, int member_count, const char *members) -{ - size_t text_size = strlen(text); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureDetails, text_size + 2); - strn0cpy((char*)outapp->pBuffer, text, text_size); - FastQueuePacket(&outapp); - - adv_requested_id = id; - adv_requested_theme = theme; - safe_delete_array(adv_requested_data); - adv_requested_member_count = member_count; - adv_requested_data = new char[64 * member_count]; - memcpy(adv_requested_data, members, (64 * member_count)); -} - -void Client::ClearPendingAdventureData() -{ - adv_requested_id = 0; - adv_requested_theme = 0; - safe_delete_array(adv_requested_data); - adv_requested_member_count = 0; -} - -bool Client::IsOnAdventure() -{ - if(adv_data) - { - ServerSendAdventureData_Struct *ad = (ServerSendAdventureData_Struct*)adv_data; - if(ad->zone_in_id == 0) - { - return false; - } - else - { - return true; - } - } - return false; -} - -void Client::LeaveAdventure() -{ - if(!GetPendingAdventureLeave()) - { - PendingAdventureLeave(); - ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeave, 64); - strcpy((char*)pack->pBuffer, GetName()); - pack->Deflate(); - worldserver.SendPacket(pack); - delete pack; - } -} - -void Client::ClearCurrentAdventure() -{ - if(adv_data) - { - ServerSendAdventureData_Struct* ds = (ServerSendAdventureData_Struct*)adv_data; - if(ds->finished_adventures > 0) - { - ds->instance_id = 0; - ds->risk = 0; - memset(ds->text, 0, 512); - ds->time_left = 0; - ds->time_to_enter = 0; - ds->x = 0; - ds->y = 0; - ds->zone_in_id = 0; - ds->zone_in_object = 0; - } - else - { - safe_delete(adv_data); - } - - SendAdventureError("You are not currently assigned to an adventure."); - } -} - -void Client::AdventureFinish(bool win, int theme, int points) -{ - UpdateLDoNPoints(points, theme); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureFinish, sizeof(AdventureFinish_Struct)); - AdventureFinish_Struct *af = (AdventureFinish_Struct*)outapp->pBuffer; - af->win_lose = win ? 1 : 0; - af->points = points; - FastQueuePacket(&outapp); -} - -void Client::CheckLDoNHail(Mob *target) -{ - if(!zone->adv_data) - { - return; - } - - if(!target || !target->IsNPC()) - { - return; - } - - if(target->GetOwnerID() != 0) - { - return; - } - - ServerZoneAdventureDataReply_Struct* ds = (ServerZoneAdventureDataReply_Struct*)zone->adv_data; - if(ds->type != Adventure_Rescue) - { - return; - } - - if(ds->data_id != target->GetNPCTypeID()) - { - return; - } - - if(entity_list.CheckNPCsClose(target) != 0) - { - target->Say("You're here to save me? I couldn't possibly risk leaving yet. There are " - "far too many of those horrid things out there waiting to recapture me! Please get" - " rid of some more of those vermin and then we can try to leave."); - return; - } - - Mob *pet = GetPet(); - if(pet) - { - if(pet->GetPetType() == petCharmed) - { - pet->BuffFadeByEffect(SE_Charm); - } - else if(pet->GetPetType() == petNPCFollow) - { - pet->SetOwnerID(0); - } - else - { - pet->Depop(); - } - } - - SetPet(target); - target->SetOwnerID(GetID()); - target->Say("Wonderful! Someone to set me free! I feared for my life for so long," - " never knowing when they might choose to end my life. Now that you're here though" - " I can rest easy. Please help me find my way out of here as soon as you can" - " I'll stay close behind you!"); -} - -void Client::CheckEmoteHail(Mob *target, const char* message) -{ - if( - (message[0] != 'H' && - message[0] != 'h') || - message[1] != 'a' || - message[2] != 'i' || - message[3] != 'l'){ - return; - } - - if(!target || !target->IsNPC()) - { - return; - } - - if(target->GetOwnerID() != 0) - { - return; - } - uint16 emoteid = target->GetEmoteID(); - if(emoteid != 0) - target->CastToNPC()->DoNPCEmote(HAILED,emoteid); -} - -void Client::MarkSingleCompassLoc(float in_x, float in_y, float in_z, uint8 count) -{ - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_DzCompass, sizeof(ExpeditionInfo_Struct) + sizeof(ExpeditionCompassEntry_Struct) * count); - ExpeditionCompass_Struct *ecs = (ExpeditionCompass_Struct*)outapp->pBuffer; - //ecs->clientid = GetID(); - ecs->count = count; - - if (count) { - ecs->entries[0].x = in_x; - ecs->entries[0].y = in_y; - ecs->entries[0].z = in_z; - } - - FastQueuePacket(&outapp); - safe_delete(outapp); -} - -void Client::SendZonePoints() -{ - int count = 0; - LinkedListIterator iterator(zone->zone_point_list); - iterator.Reset(); - while(iterator.MoreElements()) - { - ZonePoint* data = iterator.GetData(); - if(GetClientVersionBit() & data->client_version_mask) - { - count++; - } - iterator.Advance(); - } - - uint32 zpsize = sizeof(ZonePoints) + ((count + 1) * sizeof(ZonePoint_Entry)); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendZonepoints, zpsize); - ZonePoints* zp = (ZonePoints*)outapp->pBuffer; - zp->count = count; - - int i = 0; - iterator.Reset(); - while(iterator.MoreElements()) - { - ZonePoint* data = iterator.GetData(); - if(GetClientVersionBit() & data->client_version_mask) - { - zp->zpe[i].iterator = data->number; - zp->zpe[i].x = data->target_x; - zp->zpe[i].y = data->target_y; - zp->zpe[i].z = data->target_z; - zp->zpe[i].heading = data->target_heading; - zp->zpe[i].zoneid = data->target_zone_id; - zp->zpe[i].zoneinstance = data->target_zone_instance; - i++; - } - iterator.Advance(); - } - FastQueuePacket(&outapp); -} - -void Client::SendTargetCommand(uint32 EntityID) -{ - EQApplicationPacket* outapp = new EQApplicationPacket(OP_TargetCommand, sizeof(ClientTarget_Struct)); - ClientTarget_Struct *cts = (ClientTarget_Struct*)outapp->pBuffer; - cts->new_target = EntityID; - FastQueuePacket(&outapp); -} - -void Client::LocateCorpse() -{ - Corpse *ClosestCorpse = nullptr; - if(!GetTarget()) - ClosestCorpse = entity_list.GetClosestCorpse(this, nullptr); - else if(GetTarget()->IsCorpse()) - ClosestCorpse = entity_list.GetClosestCorpse(this, GetTarget()->CastToCorpse()->GetOwnerName()); - else - ClosestCorpse = entity_list.GetClosestCorpse(this, GetTarget()->GetCleanName()); - - if(ClosestCorpse) - { - Message_StringID(MT_Spells, SENSE_CORPSE_DIRECTION); - SetHeading(CalculateHeadingToTarget(ClosestCorpse->GetX(), ClosestCorpse->GetY())); - SetTarget(ClosestCorpse); - SendTargetCommand(ClosestCorpse->GetID()); - SendPosUpdate(2); - } - else if(!GetTarget()) - Message_StringID(clientMessageError, SENSE_CORPSE_NONE); - else - Message_StringID(clientMessageError, SENSE_CORPSE_NOT_NAME); -} - -void Client::NPCSpawn(NPC *target_npc, const char *identifier, uint32 extra) -{ - if (!target_npc || !identifier) - return; - - std::string id = identifier; - for(int i = 0; i < id.length(); ++i) - { - id[i] = tolower(id[i]); - } - - if (id == "create") { - // extra tries to create the npc_type ID within the range for the current zone (zone_id * 1000) - database.NPCSpawnDB(0, zone->GetShortName(), zone->GetInstanceVersion(), this, target_npc->CastToNPC(), extra); - } - else if (id == "add") { - // extra sets the respawn timer for add - database.NPCSpawnDB(1, zone->GetShortName(), zone->GetInstanceVersion(), this, target_npc->CastToNPC(), extra); - } - else if (id == "update") { - database.NPCSpawnDB(2, zone->GetShortName(), zone->GetInstanceVersion(), this, target_npc->CastToNPC()); - } - else if (id == "remove") { - database.NPCSpawnDB(3, zone->GetShortName(), zone->GetInstanceVersion(), this, target_npc->CastToNPC()); - target_npc->Depop(false); - } - else if (id == "delete") { - database.NPCSpawnDB(4, zone->GetShortName(), zone->GetInstanceVersion(), this, target_npc->CastToNPC()); - target_npc->Depop(false); - } - else { - return; - } -} - -bool Client::IsDraggingCorpse(uint16 CorpseID) -{ - for (auto It = DraggedCorpses.begin(); It != DraggedCorpses.end(); ++It) { - if (It->second == CorpseID) - return true; - } - - return false; -} - -void Client::DragCorpses() -{ - for (auto It = DraggedCorpses.begin(); It != DraggedCorpses.end(); ++It) { - Mob *corpse = entity_list.GetMob(It->second); - - if (corpse && corpse->IsPlayerCorpse() && - (DistNoRootNoZ(*corpse) <= RuleR(Character, DragCorpseDistance))) - continue; - - if (!corpse || !corpse->IsPlayerCorpse() || - corpse->CastToCorpse()->IsBeingLooted() || - !corpse->CastToCorpse()->Summon(this, false, false)) { - Message_StringID(MT_DefaultText, CORPSEDRAG_STOP); - It = DraggedCorpses.erase(It); - } - } -} - -void Client::Doppelganger(uint16 spell_id, Mob *target, const char *name_override, int pet_count, int pet_duration) -{ - if(!target || !IsValidSpell(spell_id) || this->GetID() == target->GetID()) - return; - - PetRecord record; - if(!database.GetPetEntry(spells[spell_id].teleport_zone, &record)) - { - LogFile->write(EQEMuLog::Error, "Unknown doppelganger spell id: %d, check pets table", spell_id); - Message(13, "Unable to find data for pet %s", spells[spell_id].teleport_zone); - return; - } - - AA_SwarmPet pet; - pet.count = pet_count; - pet.duration = pet_duration; - pet.npc_id = record.npc_type; - - NPCType *made_npc = nullptr; - - const NPCType *npc_type = database.GetNPCType(pet.npc_id); - if(npc_type == nullptr) { - LogFile->write(EQEMuLog::Error, "Unknown npc type for doppelganger spell id: %d", spell_id); - Message(0,"Unable to find pet!"); - return; - } - // make a custom NPC type for this - made_npc = new NPCType; - memcpy(made_npc, npc_type, sizeof(NPCType)); - - strcpy(made_npc->name, name_override); - made_npc->level = GetLevel(); - made_npc->race = GetRace(); - made_npc->gender = GetGender(); - made_npc->size = GetSize(); - made_npc->AC = GetAC(); - made_npc->STR = GetSTR(); - made_npc->STA = GetSTA(); - made_npc->DEX = GetDEX(); - made_npc->AGI = GetAGI(); - made_npc->MR = GetMR(); - made_npc->FR = GetFR(); - made_npc->CR = GetCR(); - made_npc->DR = GetDR(); - made_npc->PR = GetPR(); - made_npc->Corrup = GetCorrup(); - // looks - made_npc->texture = GetEquipmentMaterial(MaterialChest); - made_npc->helmtexture = GetEquipmentMaterial(MaterialHead); - made_npc->haircolor = GetHairColor(); - made_npc->beardcolor = GetBeardColor(); - made_npc->eyecolor1 = GetEyeColor1(); - made_npc->eyecolor2 = GetEyeColor2(); - made_npc->hairstyle = GetHairStyle(); - made_npc->luclinface = GetLuclinFace(); - made_npc->beard = GetBeard(); - made_npc->drakkin_heritage = GetDrakkinHeritage(); - made_npc->drakkin_tattoo = GetDrakkinTattoo(); - made_npc->drakkin_details = GetDrakkinDetails(); - made_npc->d_meele_texture1 = GetEquipmentMaterial(MaterialPrimary); - made_npc->d_meele_texture2 = GetEquipmentMaterial(MaterialSecondary); - for (int i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_END; i++) { - made_npc->armor_tint[i] = GetEquipmentColor(i); - } - made_npc->loottable_id = 0; - - npc_type = made_npc; - - int summon_count = 0; - summon_count = pet.count; - - if(summon_count > MAX_SWARM_PETS) - summon_count = MAX_SWARM_PETS; - - static const xy_location swarmPetLocations[MAX_SWARM_PETS] = { - {5, 5}, {-5, 5}, {5, -5}, {-5, -5}, - {10, 10}, {-10, 10}, {10, -10}, {-10, -10}, - {8, 8}, {-8, 8}, {8, -8}, {-8, -8} - }; - - while(summon_count > 0) { - NPCType *npc_dup = nullptr; - if(made_npc != nullptr) { - npc_dup = new NPCType; - memcpy(npc_dup, made_npc, sizeof(NPCType)); - } - - NPC* npca = new NPC( - (npc_dup!=nullptr)?npc_dup:npc_type, //make sure we give the NPC the correct data pointer - 0, - GetPosition()+swarmPetLocations[summon_count], - FlyMode3); - - if(!npca->GetSwarmInfo()){ - AA_SwarmPetInfo* nSI = new AA_SwarmPetInfo; - npca->SetSwarmInfo(nSI); - npca->GetSwarmInfo()->duration = new Timer(pet_duration*1000); - } - else{ - npca->GetSwarmInfo()->duration->Start(pet_duration*1000); - } - - npca->GetSwarmInfo()->owner_id = GetID(); - - // Give the pets alittle more agro than the caster and then agro them on the target - target->AddToHateList(npca, (target->GetHateAmount(this) + 100), (target->GetDamageAmount(this) + 100)); - npca->AddToHateList(target, 1000, 1000); - npca->GetSwarmInfo()->target = target->GetID(); - - //we allocated a new NPC type object, give the NPC ownership of that memory - if(npc_dup != nullptr) - npca->GiveNPCTypeData(npc_dup); - - entity_list.AddNPC(npca); - summon_count--; - } -} - -void Client::AssignToInstance(uint16 instance_id) -{ - database.AddClientToInstance(instance_id, CharacterID()); -} - -void Client::RemoveFromInstance(uint16 instance_id) -{ - database.RemoveClientFromInstance(instance_id, CharacterID()); -} - -void Client::SendStatsWindow(Client* client, bool use_window) -{ - // Define the types of page breaks we need - std::string indP = " "; - std::string indS = "          "; - std::string indM = "                          "; - std::string indL = "                                 "; - std::string div = " | "; - - std::string color_red = ""; - std::string color_blue = ""; - std::string color_green = ""; - std::string bright_green = ""; - std::string bright_red = ""; - std::string heroic_color = " +"; - - // Set Class - std::string class_Name = itoa(GetClass()); - std::string class_List[] = { "WAR", "CLR", "PAL", "RNG", "SK", "DRU", "MNK", "BRD", "ROG", "SHM", "NEC", "WIZ", "MAG", "ENC", "BST", "BER" }; - - if(GetClass() < 17 && GetClass() > 0) { class_Name = class_List[GetClass()-1]; } - - // Race - std::string race_Name = itoa(GetRace()); - switch(GetRace()) - { - case 1: race_Name = "Human"; break; - case 2: race_Name = "Barbarian"; break; - case 3: race_Name = "Erudite"; break; - case 4: race_Name = "Wood Elf"; break; - case 5: race_Name = "High Elf"; break; - case 6: race_Name = "Dark Elf"; break; - case 7: race_Name = "Half Elf"; break; - case 8: race_Name = "Dwarf"; break; - case 9: race_Name = "Troll"; break; - case 10: race_Name = "Ogre"; break; - case 11: race_Name = "Halfing"; break; - case 12: race_Name = "Gnome"; break; - case 128: race_Name = "Iksar"; break; - case 130: race_Name = "Vah Shir"; break; - case 330: race_Name = "Froglok"; break; - case 522: race_Name = "Drakkin"; break; - default: break; - } - /*########################################################## - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - H/M/E String - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - ##########################################################*/ - std::string HME_row = ""; - //Loop Variables - /*===========================*/ - std::string cur_field = ""; - std::string total_field = ""; - std::string cur_name = ""; - std::string cur_spacing = ""; - std::string cur_color = ""; - - int hme_rows = 3; // Rows in display - int max_HME_value_len = 9; // 9 digits in the displayed value - - for(int hme_row_counter = 0; hme_row_counter < hme_rows; hme_row_counter++) - { - switch(hme_row_counter) { - case 0: { - cur_name = " H: "; - cur_field = itoa(GetHP()); - total_field = itoa(GetMaxHP()); - break; - } - case 1: { - if(CalcMaxMana() > 0) { - cur_name = " M: "; - cur_field = itoa(GetMana()); - total_field = itoa(CalcMaxMana()); - } - else { continue; } - - break; - } - case 2: { - cur_name = " E: "; - cur_field = itoa(GetEndurance()); - total_field = itoa(GetMaxEndurance()); - break; - } - default: { break; } - } - if(cur_field.compare(total_field) == 0) { cur_color = bright_green; } - else { cur_color = bright_red; } - - cur_spacing.clear(); - for(int a = cur_field.size(); a < max_HME_value_len; a++) { cur_spacing += " ."; } - - HME_row += indM + cur_name + cur_spacing + cur_color + cur_field + " / " + total_field + "
"; - } - /*########################################################## - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Regen String - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - ##########################################################*/ - std::string regen_string; - //Loop Variables - /*===========================*/ - std::string regen_row_header = ""; - std::string regen_row_color = ""; - std::string base_regen_field = ""; - std::string base_regen_spacing = ""; - std::string item_regen_field = ""; - std::string item_regen_spacing = ""; - std::string cap_regen_field = ""; - std::string cap_regen_spacing = ""; - std::string spell_regen_field = ""; - std::string spell_regen_spacing = ""; - std::string aa_regen_field = ""; - std::string aa_regen_spacing = ""; - std::string total_regen_field = ""; - int regen_rows = 3; // Number of rows - int max_regen_value_len = 5; // 5 digits in the displayed value(larger values will not get cut off, this is just a baseline) - - for(int regen_row_counter = 0; regen_row_counter < regen_rows; regen_row_counter++) - { - switch(regen_row_counter) - { - case 0: { - regen_row_header = "H: "; - regen_row_color = color_red; - - base_regen_field = itoa(LevelRegen()); - item_regen_field = itoa(itembonuses.HPRegen); - cap_regen_field = itoa(CalcHPRegenCap()); - spell_regen_field = itoa(spellbonuses.HPRegen); - aa_regen_field = itoa(aabonuses.HPRegen); - total_regen_field = itoa(CalcHPRegen()); - break; - } - case 1: { - if(CalcMaxMana() > 0) { - regen_row_header = "M: "; - regen_row_color = color_blue; - - base_regen_field = itoa(CalcBaseManaRegen()); - item_regen_field = itoa(itembonuses.ManaRegen); - cap_regen_field = itoa(CalcManaRegenCap()); - spell_regen_field = itoa(spellbonuses.ManaRegen); - aa_regen_field = itoa(aabonuses.ManaRegen); - total_regen_field = itoa(CalcManaRegen()); - } - else { continue; } - break; - } - case 2: { - regen_row_header = "E: "; - regen_row_color = color_green; - - base_regen_field = itoa(((GetLevel() * 4 / 10) + 2)); - item_regen_field = itoa(itembonuses.EnduranceRegen); - cap_regen_field = itoa(CalcEnduranceRegenCap()); - spell_regen_field = itoa(spellbonuses.EnduranceRegen); - aa_regen_field = itoa(aabonuses.EnduranceRegen); - total_regen_field = itoa(CalcEnduranceRegen()); - break; - } - default: { break; } - } - - base_regen_spacing.clear(); - item_regen_spacing.clear(); - cap_regen_spacing.clear(); - spell_regen_spacing.clear(); - aa_regen_spacing.clear(); - - for(int b = base_regen_field.size(); b < max_regen_value_len; b++) { base_regen_spacing += " ."; } - for(int b = item_regen_field.size(); b < max_regen_value_len; b++) { item_regen_spacing += " ."; } - for(int b = cap_regen_field.size(); b < max_regen_value_len; b++) { cap_regen_spacing += " ."; } - for(int b = spell_regen_field.size(); b < max_regen_value_len; b++) { spell_regen_spacing += " ."; } - for(int b = aa_regen_field.size(); b < max_regen_value_len; b++) { aa_regen_spacing += " ."; } - - regen_string += indS + regen_row_color + regen_row_header + base_regen_spacing + base_regen_field; - regen_string += div + item_regen_spacing + item_regen_field + " (" + cap_regen_field; - regen_string += ") " + cap_regen_spacing + div + spell_regen_spacing + spell_regen_field; - regen_string += div + aa_regen_spacing + aa_regen_field + div + total_regen_field + "

"; - } - /*########################################################## - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Stat String - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - ##########################################################*/ - std::string stat_field = ""; - //Loop Variables - /*===========================*/ - //first field(stat) - std::string a_stat = "";; - std::string a_stat_name = ""; - std::string a_stat_spacing = ""; - //second field(heroic stat) - std::string h_stat = ""; - std::string h_stat_spacing = ""; - //third field(resist) - std::string a_resist = ""; - std::string a_resist_name = ""; - std::string a_resist_spacing = ""; - //fourth field(heroic resist) - std::string h_resist_field = ""; - - int stat_rows = 7; // Number of rows - int max_stat_value_len = 3; // 3 digits in the displayed value - - for(int stat_row_counter = 0; stat_row_counter < stat_rows; stat_row_counter++) - { - switch(stat_row_counter) { - case 0: { - a_stat_name = " STR: "; - a_resist_name = "MR: "; - a_stat = itoa(GetSTR()); - h_stat = itoa(GetHeroicSTR()); - a_resist = itoa(GetMR()); - h_resist_field = itoa(GetHeroicMR()); - break; - } - case 1: { - a_stat_name = " STA: "; - a_resist_name = "CR: "; - a_stat = itoa(GetSTA()); - h_stat = itoa(GetHeroicSTA()); - a_resist = itoa(GetCR()); - h_resist_field = itoa(GetHeroicCR()); - break; - } - case 2: { - a_stat_name = " AGI : "; - a_resist_name = "FR: "; - a_stat = itoa(GetAGI()); - h_stat = itoa(GetHeroicAGI()); - a_resist = itoa(GetFR()); - h_resist_field = itoa(GetHeroicFR()); - break; - } - case 3: { - a_stat_name = " DEX: "; - a_resist_name = "PR: "; - a_stat = itoa(GetDEX()); - h_stat = itoa(GetHeroicDEX()); - a_resist = itoa(GetPR()); - h_resist_field = itoa(GetHeroicPR()); - break; - } - case 4: { - a_stat_name = " INT : "; - a_resist_name = "DR: "; - a_stat = itoa(GetINT()); - h_stat = itoa(GetHeroicINT()); - a_resist = itoa(GetDR()); - h_resist_field = itoa(GetHeroicDR()); - break; - } - case 5: { - a_stat_name = " WIS: "; - a_resist_name = "Cp: "; - a_stat = itoa(GetWIS()); - h_stat = itoa(GetHeroicWIS()); - a_resist = itoa(GetCorrup()); - h_resist_field = itoa(GetHeroicCorrup()); - break; - } - case 6: { - a_stat_name = " CHA: "; - a_stat = itoa(GetCHA()); - h_stat = itoa(GetHeroicCHA()); - break; - } - default: { break; } - } - - a_stat_spacing.clear(); - h_stat_spacing.clear(); - a_resist_spacing.clear(); - - for(int a = a_stat.size(); a < max_stat_value_len; a++) { a_stat_spacing += " . "; } - for(int h = h_stat.size(); h < 20; h++) { h_stat_spacing += " . "; } - for(int h = a_resist.size(); h < max_stat_value_len; h++) { a_resist_spacing += " . "; } - - stat_field += indP + a_stat_name + a_stat_spacing + a_stat + heroic_color + h_stat + "
"; - if(stat_row_counter < 6) { - stat_field += h_stat_spacing + a_resist_name + a_resist_spacing + a_resist + heroic_color + h_resist_field + "

"; - } - } - /*########################################################## - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Mod2 String - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - ##########################################################*/ - std::string mod2_field = ""; - //Loop Variables - /*===========================*/ - std::string mod2a = ""; - std::string mod2a_name = ""; - std::string mod2a_spacing = ""; - std::string mod2a_cap = ""; - std::string mod_row_spacing = ""; - std::string mod2b = ""; - std::string mod2b_name = ""; - std::string mod2b_spacing = ""; - std::string mod2b_cap = ""; - int mod2a_space_count; - int mod2b_space_count; - - int mod2_rows = 4; - int max_mod2_value_len = 3; // 3 digits in the displayed value - - for(int mod2_row_counter = 0; mod2_row_counter < mod2_rows; mod2_row_counter++) - { - switch (mod2_row_counter) - { - case 0: { - mod2a_name = "Avoidance: "; - mod2b_name = "Combat Effects: "; - mod2a = itoa(GetAvoidance()); - mod2a_cap = itoa(RuleI(Character, ItemAvoidanceCap)); - mod2b = itoa(GetCombatEffects()); - mod2b_cap = itoa(RuleI(Character, ItemCombatEffectsCap)); - mod2a_space_count = 2; - mod2b_space_count = 0; - break; - } - case 1: { - mod2a_name = "Accuracy: "; - mod2b_name = "Strike Through: "; - mod2a = itoa(GetAccuracy()); - mod2a_cap = itoa(RuleI(Character, ItemAccuracyCap)); - mod2b = itoa(GetStrikeThrough()); - mod2b_cap = itoa(RuleI(Character, ItemStrikethroughCap)); - mod2a_space_count = 3; - mod2b_space_count = 1; - break; - } - case 2: { - mod2a_name = "Shielding: "; - mod2b_name = "Spell Shielding: "; - mod2a = itoa(GetShielding()); - mod2a_cap = itoa(RuleI(Character, ItemShieldingCap)); - mod2b = itoa(GetSpellShield()); - mod2b_cap = itoa(RuleI(Character, ItemSpellShieldingCap)); - mod2a_space_count = 2; - mod2b_space_count = 1; - break; - } - case 3: { - mod2a_name = "Stun Resist: "; - mod2b_name = "DoT Shielding: "; - mod2a = itoa(GetStunResist()); - mod2a_cap = itoa(RuleI(Character, ItemStunResistCap)); - mod2b = itoa(GetDoTShield()); - mod2b_cap = itoa(RuleI(Character, ItemDoTShieldingCap)); - mod2a_space_count = 0; - mod2b_space_count = 2; - break; - } - } - - mod2a_spacing.clear(); - mod_row_spacing.clear(); - mod2b_spacing.clear(); - - for(int a = mod2a.size(); a < (max_mod2_value_len + mod2a_space_count); a++) { mod2a_spacing += " . "; } - for(int a = mod2a_cap.size(); a < 6 ; a++) { mod_row_spacing += " . "; } - for(int a = mod2b.size(); a < (max_mod2_value_len + mod2b_space_count); a++) { mod2b_spacing += " . "; } - - mod2_field += indP + mod2a_name + mod2a_spacing + mod2a + " / " + mod2a_cap + mod_row_spacing; - mod2_field += mod2b_name + mod2b_spacing + mod2b + " / " + mod2b_cap + "
"; - } - - uint32 rune_number = 0; - uint32 magic_rune_number = 0; - uint32 buff_count = GetMaxTotalSlots(); - for (int i=0; i < buff_count; i++) { - if (buffs[i].spellid != SPELL_UNKNOWN) { - if (buffs[i].melee_rune > 0) { rune_number += buffs[i].melee_rune; } - - if (buffs[i].magic_rune > 0) { magic_rune_number += buffs[i].magic_rune; } - } - } - - int shield_ac = 0; - GetRawACNoShield(shield_ac); - - std::string skill_list[] = { - "1H Blunt","1H Slashing","2H Blunt","2H Slashing","Abjuration","Alteration","Apply Poison","Archery","Backstab","Bind Wound","Bash","Block","Brass Instruments","Channeling","Conjuration", - "Defense","Disarm","Disarm Traps","Divination","Dodge","Double Attack","Dragon Punch","Dual Wield","Eagle Strike","Evocation","Feign Death","Flying Kick","Forage","Hand To Hand","Hide","Kick", - "Meditate","Mend","Offense","Parry","Pick Lock","Piercing","Riposte","Round Kick","Safe Fall","Sense Heading","Singing","Sneak","Specialize Abjuration","Specialize Alteration","Specialize Conjuration", - "Specialize Divination","Specialize Evocation","Pick Pockets","Stringed_Instruments","Swimming","Throwing","Tiger Claw","Tracking","Wind Instruments","Fishing","Make Poison","Tinkering","Research","Alchemy", - "Baking","Tailoring","Sense Traps","Blacksmithing","Fletching","Brewing","Alcohol_Tolerance","Begging","Jewelry Making","Pottery","Percussion Instruments","Intimidation","Berserking","Taunt","Frenzy" - }; - - std::string skill_mods = ""; - for(int j = 0; j <= HIGHEST_SKILL; j++) { - if(itembonuses.skillmod[j] > 0) - skill_mods += indP + skill_list[j] + " : +" + itoa(itembonuses.skillmod[j]) + "%
"; - else if(itembonuses.skillmod[j] < 0) - skill_mods += indP + skill_list[j] + " : -" + itoa(itembonuses.skillmod[j]) + "%
"; - } - - std::string skill_dmgs = ""; - for(int j = 0; j <= HIGHEST_SKILL; j++) { - if((itembonuses.SkillDamageAmount[j] + spellbonuses.SkillDamageAmount[j]) > 0) - skill_dmgs += indP + skill_list[j] + " : +" + itoa(itembonuses.SkillDamageAmount[j] + spellbonuses.SkillDamageAmount[j]) + "
"; - else if((itembonuses.SkillDamageAmount[j] + spellbonuses.SkillDamageAmount[j]) < 0) - skill_dmgs += indP + skill_list[j] + " : -" + itoa(itembonuses.SkillDamageAmount[j] + spellbonuses.SkillDamageAmount[j]) + "
"; - } - - std::string faction_item_string = ""; - char faction_buf[256]; - - for(std::map ::iterator iter = item_faction_bonuses.begin(); - iter != item_faction_bonuses.end(); - ++iter) - { - memset(&faction_buf, 0, sizeof(faction_buf)); - - if(!database.GetFactionName((int32)((*iter).first), faction_buf, sizeof(faction_buf))) - strcpy(faction_buf, "Not in DB"); - - if((*iter).second > 0) { - faction_item_string += indP + faction_buf + " : +" + itoa((*iter).second) + "
"; - } - else if((*iter).second < 0) { - faction_item_string += indP + faction_buf + " : -" + itoa((*iter).second) + "
"; - } - } - - std::string bard_info = ""; - if(GetClass() == BARD) { - bard_info = indP + "Singing: " + itoa(GetSingMod()) + "
" + - indP + "Brass: " + itoa(GetBrassMod()) + "
" + - indP + "String: " + itoa(GetStringMod()) + "
" + - indP + "Percussion: " + itoa(GetPercMod()) + "
" + - indP + "Wind: " + itoa(GetWindMod()) + "
"; - } - - std::ostringstream final_string; - final_string << - /* C/L/R */ indP << "Class: " << class_Name << indS << "Level: " << static_cast(GetLevel()) << indS << "Race: " << race_Name << "
" << - /* Runes */ indP << "Rune: " << rune_number << indL << indS << "Spell Rune: " << magic_rune_number << "
" << - /* HP/M/E */ HME_row << - /* DS */ indP << "DS: " << (itembonuses.DamageShield + spellbonuses.DamageShield*-1) << " (Spell: " << (spellbonuses.DamageShield*-1) << " + Item: " << itembonuses.DamageShield << " / " << RuleI(Character, ItemDamageShieldCap) << ")
" << - /* Atk */ indP << "ATK: " << GetTotalATK() << "
" << - /* Atk2 */ indP << "- Base: " << GetATKRating() << " | Item: " << itembonuses.ATK << " (" << RuleI(Character, ItemATKCap) << ")~Used: " << (itembonuses.ATK * 1.342) << " | Spell: " << spellbonuses.ATK << "
" << - /* AC */ indP << "AC: " << CalcAC() << "
" << - /* AC2 */ indP << "- Mit: " << GetACMit() << " | Avoid: " << GetACAvoid() << " | Spell: " << spellbonuses.AC << " | Shield: " << shield_ac << "
" << - /* Haste */ indP << "Haste: " << GetHaste() << "
" << - /* Haste2 */ indP << " - Item: " << itembonuses.haste << " + Spell: " << (spellbonuses.haste + spellbonuses.hastetype2) << " (Cap: " << RuleI(Character, HasteCap) << ") | Over: " << (spellbonuses.hastetype3 + ExtraHaste) << "

" << - /* RegenLbl */ indL << indS << "Regen
" << indS << indP << indP << " Base | Items (Cap) " << indP << " | Spell | A.A.s | Total
" << - /* Regen */ regen_string << "
" << - /* Stats */ stat_field << "

" << - /* Mod2s */ mod2_field << "
" << - /* HealAmt */ indP << "Heal Amount: " << GetHealAmt() << " / " << RuleI(Character, ItemHealAmtCap) << "
" << - /* SpellDmg*/ indP << "Spell Dmg: " << GetSpellDmg() << " / " << RuleI(Character, ItemSpellDmgCap) << "
" << - /* Clair */ indP << "Clairvoyance: " << GetClair() << " / " << RuleI(Character, ItemClairvoyanceCap) << "
" << - /* DSMit */ indP << "Dmg Shld Mit: " << GetDSMit() << " / " << RuleI(Character, ItemDSMitigationCap) << "

"; - if(GetClass() == BARD) - final_string << bard_info << "
"; - if(skill_mods.size() > 0) - final_string << skill_mods << "
"; - if(skill_dmgs.size() > 0) - final_string << skill_dmgs << "
"; - if(faction_item_string.size() > 0) - final_string << faction_item_string; - - std::string final_stats = final_string.str(); - - if(use_window) { - if(final_stats.size() < 4096) - { - uint32 Buttons = (client->GetClientVersion() < EQClientSoD) ? 0 : 1; - client->SendWindow(0, POPUPID_UPDATE_SHOWSTATSWINDOW, Buttons, "Cancel", "Update", 0, 1, this, "", "%s", final_stats.c_str()); - goto Extra_Info; - } - else { - client->Message(15, "The window has exceeded its character limit, displaying stats to chat window:"); - } - } - - client->Message(15, "~~~~~ %s %s ~~~~~", GetCleanName(), GetLastName()); - client->Message(0, " Level: %i Class: %i Race: %i DS: %i/%i Size: %1.1f Weight: %.1f/%d ", GetLevel(), GetClass(), GetRace(), GetDS(), RuleI(Character, ItemDamageShieldCap), GetSize(), (float)CalcCurrentWeight() / 10.0f, GetSTR()); - client->Message(0, " HP: %i/%i HP Regen: %i/%i",GetHP(), GetMaxHP(), CalcHPRegen(), CalcHPRegenCap()); - client->Message(0, " AC: %i ( Mit.: %i + Avoid.: %i + Spell: %i ) | Shield AC: %i", CalcAC(), GetACMit(), GetACAvoid(), spellbonuses.AC, shield_ac); - if(CalcMaxMana() > 0) - client->Message(0, " Mana: %i/%i Mana Regen: %i/%i", GetMana(), GetMaxMana(), CalcManaRegen(), CalcManaRegenCap()); - client->Message(0, " End.: %i/%i End. Regen: %i/%i",GetEndurance(), GetMaxEndurance(), CalcEnduranceRegen(), CalcEnduranceRegenCap()); - client->Message(0, " ATK: %i Worn/Spell ATK %i/%i Server Side ATK: %i", GetTotalATK(), RuleI(Character, ItemATKCap), GetATKBonus(), GetATK()); - client->Message(0, " Haste: %i / %i (Item: %i + Spell: %i + Over: %i)", GetHaste(), RuleI(Character, HasteCap), itembonuses.haste, spellbonuses.haste + spellbonuses.hastetype2, spellbonuses.hastetype3 + ExtraHaste); - client->Message(0, " STR: %i STA: %i DEX: %i AGI: %i INT: %i WIS: %i CHA: %i", GetSTR(), GetSTA(), GetDEX(), GetAGI(), GetINT(), GetWIS(), GetCHA()); - client->Message(0, " hSTR: %i hSTA: %i hDEX: %i hAGI: %i hINT: %i hWIS: %i hCHA: %i", GetHeroicSTR(), GetHeroicSTA(), GetHeroicDEX(), GetHeroicAGI(), GetHeroicINT(), GetHeroicWIS(), GetHeroicCHA()); - client->Message(0, " MR: %i PR: %i FR: %i CR: %i DR: %i Corruption: %i", GetMR(), GetPR(), GetFR(), GetCR(), GetDR(), GetCorrup()); - client->Message(0, " hMR: %i hPR: %i hFR: %i hCR: %i hDR: %i hCorruption: %i", GetHeroicMR(), GetHeroicPR(), GetHeroicFR(), GetHeroicCR(), GetHeroicDR(), GetHeroicCorrup()); - client->Message(0, " Shielding: %i Spell Shield: %i DoT Shielding: %i Stun Resist: %i Strikethrough: %i Avoidance: %i Accuracy: %i Combat Effects: %i", GetShielding(), GetSpellShield(), GetDoTShield(), GetStunResist(), GetStrikeThrough(), GetAvoidance(), GetAccuracy(), GetCombatEffects()); - client->Message(0, " Heal Amt.: %i Spell Dmg.: %i Clairvoyance: %i DS Mitigation: %i", GetHealAmt(), GetSpellDmg(), GetClair(), GetDSMit()); - if(GetClass() == BARD) - client->Message(0, " Singing: %i Brass: %i String: %i Percussion: %i Wind: %i", GetSingMod(), GetBrassMod(), GetStringMod(), GetPercMod(), GetWindMod()); - - Extra_Info: - - client->Message(0, " BaseRace: %i Gender: %i BaseGender: %i Texture: %i HelmTexture: %i", GetBaseRace(), GetGender(), GetBaseGender(), GetTexture(), GetHelmTexture()); - if (client->Admin() >= 100) { - client->Message(0, " CharID: %i EntityID: %i PetID: %i OwnerID: %i AIControlled: %i Targetted: %i", CharacterID(), GetID(), GetPetID(), GetOwnerID(), IsAIControlled(), targeted); - } -} - -void Client::SendAltCurrencies() { - if(GetClientVersion() >= EQClientSoF) { - uint32 count = zone->AlternateCurrencies.size(); - if(count == 0) { - return; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_AltCurrency, - sizeof(AltCurrencyPopulate_Struct) + sizeof(AltCurrencyPopulateEntry_Struct) * count); - AltCurrencyPopulate_Struct *altc = (AltCurrencyPopulate_Struct*)outapp->pBuffer; - altc->opcode = ALT_CURRENCY_OP_POPULATE; - altc->count = count; - - uint32 i = 0; - std::list::iterator iter = zone->AlternateCurrencies.begin(); - while(iter != zone->AlternateCurrencies.end()) { - const Item_Struct* item = database.GetItem((*iter).item_id); - altc->entries[i].currency_number = (*iter).id; - altc->entries[i].unknown00 = 1; - altc->entries[i].currency_number2 = (*iter).id; - altc->entries[i].item_id = (*iter).item_id; - if(item) { - altc->entries[i].item_icon = item->Icon; - altc->entries[i].stack_size = item->StackSize; - } else { - altc->entries[i].item_icon = 1000; - altc->entries[i].stack_size = 1000; - } - i++; - ++iter; - } - - FastQueuePacket(&outapp); - } -} - -void Client::SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount) -{ - alternate_currency[currency_id] = new_amount; - database.UpdateAltCurrencyValue(CharacterID(), currency_id, new_amount); - SendAlternateCurrencyValue(currency_id); -} - -void Client::AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method) -{ - - /* Added via Quest, rest of the logging methods may be done inline due to information available in that area of the code */ - if (method == 1){ - /* QS: PlayerLogAlternateCurrencyTransactions :: Cursor to Item Storage */ - if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ - std::string event_desc = StringFormat("Added via Quest :: Cursor to Item :: alt_currency_id:%i amount:%i in zoneid:%i instid:%i", currency_id, this->GetZoneID(), this->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); - } - } - - if(amount == 0) { - return; - } - - if(!alternate_currency_loaded) { - alternate_currency_queued_operations.push(std::make_pair(currency_id, amount)); - return; - } - - int new_value = 0; - std::map::iterator iter = alternate_currency.find(currency_id); - if(iter == alternate_currency.end()) { - new_value = amount; - } else { - new_value = (*iter).second + amount; - } - - if(new_value < 0) { - alternate_currency[currency_id] = 0; - database.UpdateAltCurrencyValue(CharacterID(), currency_id, 0); - } else { - alternate_currency[currency_id] = new_value; - database.UpdateAltCurrencyValue(CharacterID(), currency_id, new_value); - } - SendAlternateCurrencyValue(currency_id); -} - -void Client::SendAlternateCurrencyValues() -{ - std::list::iterator iter = zone->AlternateCurrencies.begin(); - while(iter != zone->AlternateCurrencies.end()) { - SendAlternateCurrencyValue((*iter).id, false); - ++iter; - } -} - -void Client::SendAlternateCurrencyValue(uint32 currency_id, bool send_if_null) -{ - uint32 value = GetAlternateCurrencyValue(currency_id); - if(value > 0 || (value == 0 && send_if_null)) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AltCurrency, sizeof(AltCurrencyUpdate_Struct)); - AltCurrencyUpdate_Struct *update = (AltCurrencyUpdate_Struct*)outapp->pBuffer; - update->opcode = 7; - strcpy(update->name, GetName()); - update->currency_number = currency_id; - update->amount = value; - update->unknown072 = 1; - FastQueuePacket(&outapp); - } -} - -uint32 Client::GetAlternateCurrencyValue(uint32 currency_id) const -{ - std::map::const_iterator iter = alternate_currency.find(currency_id); - if(iter == alternate_currency.end()) { - return 0; - } else { - return (*iter).second; - } -} - -void Client::ProcessAlternateCurrencyQueue() { - while(!alternate_currency_queued_operations.empty()) { - std::pair op = alternate_currency_queued_operations.front(); - - AddAlternateCurrencyValue(op.first, op.second); - - alternate_currency_queued_operations.pop(); - } -} - -void Client::OpenLFGuildWindow() -{ - EQApplicationPacket *outapp = new EQApplicationPacket(OP_LFGuild, 8); - - outapp->WriteUInt32(6); - - FastQueuePacket(&outapp); -} - -bool Client::IsXTarget(const Mob *m) const -{ - if(!XTargettingAvailable() || !m || (m->GetID() == 0)) - return false; - - for(int i = 0; i < GetMaxXTargets(); ++i) - { - if(XTargets[i].ID == m->GetID()) - return true; - } - return false; -} - -bool Client::IsClientXTarget(const Client *c) const -{ - if(!XTargettingAvailable() || !c) - return false; - - for(int i = 0; i < GetMaxXTargets(); ++i) - { - if(!strcasecmp(XTargets[i].Name, c->GetName())) - return true; - } - return false; -} - - -void Client::UpdateClientXTarget(Client *c) -{ - if(!XTargettingAvailable() || !c) - return; - - for(int i = 0; i < GetMaxXTargets(); ++i) - { - if(!strcasecmp(XTargets[i].Name, c->GetName())) - { - XTargets[i].ID = c->GetID(); - SendXTargetPacket(i, c); - } - } -} - -void Client::AddAutoXTarget(Mob *m) -{ - if(!XTargettingAvailable() || !XTargetAutoAddHaters) - return; - - if(IsXTarget(m)) - return; - - for(int i = 0; i < GetMaxXTargets(); ++i) - { - if((XTargets[i].Type == Auto) && (XTargets[i].ID == 0)) - { - XTargets[i].ID = m->GetID(); - SendXTargetPacket(i, m); - break; - } - } -} - -void Client::RemoveXTarget(Mob *m, bool OnlyAutoSlots) -{ - if(!XTargettingAvailable()) - return; - - bool HadFreeAutoSlotsBefore = false; - - int FreedAutoSlots = 0; - - if(m->GetID() == 0) - return; - - for(int i = 0; i < GetMaxXTargets(); ++i) - { - if(OnlyAutoSlots && (XTargets[i].Type !=Auto)) - continue; - - if(XTargets[i].ID == m->GetID()) - { - if(XTargets[i].Type == CurrentTargetNPC) - XTargets[i].Type = Auto; - - if(XTargets[i].Type == Auto) - ++FreedAutoSlots; - - XTargets[i].ID = 0; - - SendXTargetPacket(i, nullptr); - } - else - { - if((XTargets[i].Type == Auto) && (XTargets[i].ID == 0)) - HadFreeAutoSlotsBefore = true; - } - } - // If there are more mobs aggro on us than we had auto-hate slots, add one of those haters into the slot(s) we just freed up. - if(!HadFreeAutoSlotsBefore && FreedAutoSlots) - entity_list.RefreshAutoXTargets(this); -} - -void Client::UpdateXTargetType(XTargetType Type, Mob *m, const char *Name) -{ - if(!XTargettingAvailable()) - return; - - for(int i = 0; i < GetMaxXTargets(); ++i) - { - if(XTargets[i].Type == Type) - { - if(m) - XTargets[i].ID = m->GetID(); - else - XTargets[i].ID = 0; - - if(Name) - strncpy(XTargets[i].Name, Name, 64); - - SendXTargetPacket(i, m); - } - } -} - -void Client::SendXTargetPacket(uint32 Slot, Mob *m) -{ - if(!XTargettingAvailable()) - return; - - uint32 PacketSize = 18; - - if(m) - PacketSize += strlen(m->GetCleanName()); - else - { - PacketSize += strlen(XTargets[Slot].Name); - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_XTargetResponse, PacketSize); - outapp->WriteUInt32(GetMaxXTargets()); - outapp->WriteUInt32(1); - outapp->WriteUInt32(Slot); - if(m) - { - outapp->WriteUInt8(1); - } - else - { - if (strlen(XTargets[Slot].Name) && ((XTargets[Slot].Type == CurrentTargetPC) || - (XTargets[Slot].Type == GroupTank) || - (XTargets[Slot].Type == GroupAssist) || - (XTargets[Slot].Type == Puller) || - (XTargets[Slot].Type == RaidAssist1) || - (XTargets[Slot].Type == RaidAssist2) || - (XTargets[Slot].Type == RaidAssist3))) - { - outapp->WriteUInt8(2); - } - else - { - outapp->WriteUInt8(0); - } - } - outapp->WriteUInt32(XTargets[Slot].ID); - outapp->WriteString(m ? m->GetCleanName() : XTargets[Slot].Name); - FastQueuePacket(&outapp); -} - -void Client::RemoveGroupXTargets() -{ - if(!XTargettingAvailable()) - return; - - for(int i = 0; i < GetMaxXTargets(); ++i) - { - if ((XTargets[i].Type == GroupTank) || - (XTargets[i].Type == GroupAssist) || - (XTargets[i].Type == Puller) || - (XTargets[i].Type == RaidAssist1) || - (XTargets[i].Type == RaidAssist2) || - (XTargets[i].Type == RaidAssist3) || - (XTargets[i].Type == GroupMarkTarget1) || - (XTargets[i].Type == GroupMarkTarget2) || - (XTargets[i].Type == GroupMarkTarget3)) - { - XTargets[i].ID = 0; - XTargets[i].Name[0] = 0; - SendXTargetPacket(i, nullptr); - } - } -} - -void Client::RemoveAutoXTargets() -{ - if(!XTargettingAvailable()) - return; - - for(int i = 0; i < GetMaxXTargets(); ++i) - { - if(XTargets[i].Type == Auto) - { - XTargets[i].ID = 0; - XTargets[i].Name[0] = 0; - SendXTargetPacket(i, nullptr); - } - } -} - -void Client::ShowXTargets(Client *c) -{ - if(!c) - return; - - for(int i = 0; i < GetMaxXTargets(); ++i) - c->Message(0, "Xtarget Slot: %i, Type: %2i, ID: %4i, Name: %s", i, XTargets[i].Type, XTargets[i].ID, XTargets[i].Name); -} - -void Client::SetMaxXTargets(uint8 NewMax) -{ - if(!XTargettingAvailable()) - return; - - if(NewMax > XTARGET_HARDCAP) - return; - - MaxXTargets = NewMax; - - Save(0); - - for(int i = MaxXTargets; i < XTARGET_HARDCAP; ++i) - { - XTargets[i].Type = Auto; - XTargets[i].ID = 0; - XTargets[i].Name[0] = 0; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_XTargetResponse, 8); - outapp->WriteUInt32(GetMaxXTargets()); - outapp->WriteUInt32(0); - FastQueuePacket(&outapp); -} - -const char* Client::GetRacePlural(Client* client) { - - switch (client->CastToMob()->GetRace()) { - case HUMAN: - return "Humans"; break; - case BARBARIAN: - return "Barbarians"; break; - case ERUDITE: - return "Erudites"; break; - case WOOD_ELF: - return "Wood Elves"; break; - case HIGH_ELF: - return "High Elves"; break; - case DARK_ELF: - return "Dark Elves"; break; - case HALF_ELF: - return "Half Elves"; break; - case DWARF: - return "Dwarves"; break; - case TROLL: - return "Trolls"; break; - case OGRE: - return "Ogres"; break; - case HALFLING: - return "Halflings"; break; - case GNOME: - return "Gnomes"; break; - case IKSAR: - return "Iksar"; break; - case VAHSHIR: - return "Vah Shir"; break; - case FROGLOK: - return "Frogloks"; break; - case DRAKKIN: - return "Drakkin"; break; - default: - return "Races"; break; - } -} - -const char* Client::GetClassPlural(Client* client) { - - switch (client->CastToMob()->GetClass()) { - case WARRIOR: - return "Warriors"; break; - case CLERIC: - return "Clerics"; break; - case PALADIN: - return "Paladins"; break; - case RANGER: - return "Rangers"; break; - case SHADOWKNIGHT: - return "Shadowknights"; break; - case DRUID: - return "Druids"; break; - case MONK: - return "Monks"; break; - case BARD: - return "Bards"; break; - case ROGUE: - return "Rogues"; break; - case SHAMAN: - return "Shamen"; break; - case NECROMANCER: - return "Necromancers"; break; - case WIZARD: - return "Wizards"; break; - case MAGICIAN: - return "Magicians"; break; - case ENCHANTER: - return "Enchanters"; break; - case BEASTLORD: - return "Beastlords"; break; - case BERSERKER: - return "Berserkers"; break; - default: - return "Classes"; break; - } -} - - -void Client::SendWebLink(const char *website) -{ - size_t len = strlen(website) + 1; - if(website != 0 && len > 1) - { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Weblink, sizeof(Weblink_Struct) + len); - Weblink_Struct *wl = (Weblink_Struct*)outapp->pBuffer; - memcpy(wl->weblink, website, len); - wl->weblink[len] = '\0'; - - FastQueuePacket(&outapp); - } -} - -void Client::SendMercPersonalInfo() -{ - uint32 mercTypeCount = 1; - uint32 mercCount = 1; //TODO: Un-hardcode this and support multiple mercs like in later clients than SoD. - uint32 i = 0; - uint32 altCurrentType = 19; //TODO: Implement alternate currency purchases involving mercs! - - MercTemplate *mercData = &zone->merc_templates[GetMercInfo().MercTemplateID]; - - int stancecount = 0; - stancecount += zone->merc_stance_list[GetMercInfo().MercTemplateID].size(); - if(stancecount > MAX_MERC_STANCES || mercCount > MAX_MERC || mercTypeCount > MAX_MERC_GRADES) - { - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: SendMercPersonalInfo Cancelled: (%i) (%i) (%i)", stancecount, mercCount, mercTypeCount); - SendMercMerchantResponsePacket(0); - return; - } - - if(mercData) - { - if (GetClientVersion() >= EQClientRoF) - { - if (mercCount > 0) - { - EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, sizeof(MercenaryDataUpdate_Struct)); - MercenaryDataUpdate_Struct* mdus = (MercenaryDataUpdate_Struct*)outapp->pBuffer; - mdus->MercStatus = 0; - mdus->MercCount = mercCount; - mdus->MercData[i].MercID = mercData->MercTemplateID; - mdus->MercData[i].MercType = mercData->MercType; - mdus->MercData[i].MercSubType = mercData->MercSubType; - mdus->MercData[i].PurchaseCost = Merc::CalcPurchaseCost(mercData->MercTemplateID, GetLevel(), 0); - mdus->MercData[i].UpkeepCost = Merc::CalcUpkeepCost(mercData->MercTemplateID, GetLevel(), 0); - mdus->MercData[i].Status = 0; - mdus->MercData[i].AltCurrencyCost = Merc::CalcPurchaseCost(mercData->MercTemplateID, GetLevel(), altCurrentType); - mdus->MercData[i].AltCurrencyUpkeep = Merc::CalcPurchaseCost(mercData->MercTemplateID, GetLevel(), altCurrentType); - mdus->MercData[i].AltCurrencyType = altCurrentType; - mdus->MercData[i].MercUnk01 = 0; - mdus->MercData[i].TimeLeft = GetMercInfo().MercTimerRemaining; //GetMercTimer().GetRemainingTime(); - mdus->MercData[i].MerchantSlot = i + 1; - mdus->MercData[i].MercUnk02 = 1; - mdus->MercData[i].StanceCount = zone->merc_stance_list[mercData->MercTemplateID].size(); - mdus->MercData[i].MercUnk03 = 0; - mdus->MercData[i].MercUnk04 = 1; - strn0cpy(mdus->MercData[i].MercName, GetMercInfo().merc_name , sizeof(mdus->MercData[i].MercName)); - uint32 stanceindex = 0; - if (mdus->MercData[i].StanceCount != 0) - { - std::list::iterator iter = zone->merc_stance_list[mercData->MercTemplateID].begin(); - while(iter != zone->merc_stance_list[mercData->MercTemplateID].end()) - { - mdus->MercData[i].Stances[stanceindex].StanceIndex = stanceindex; - mdus->MercData[i].Stances[stanceindex].Stance = (iter->StanceID); - stanceindex++; - ++iter; - } - } - - mdus->MercData[i].MercUnk05 = 1; - FastQueuePacket(&outapp); - safe_delete(outapp); - return; - } - } - else - { - if(mercTypeCount > 0 && mercCount > 0) - { - EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataResponse, sizeof(MercenaryMerchantList_Struct)); - MercenaryMerchantList_Struct* mml = (MercenaryMerchantList_Struct*)outapp->pBuffer; - mml->MercTypeCount = mercTypeCount; //We should only have one merc entry. - mml->MercGrades[i] = 1; - mml->MercCount = mercCount; - mml->Mercs[i].MercID = mercData->MercTemplateID; - mml->Mercs[i].MercType = mercData->MercType; - mml->Mercs[i].MercSubType = mercData->MercSubType; - mml->Mercs[i].PurchaseCost = RuleB(Mercs, ChargeMercPurchaseCost) ? Merc::CalcPurchaseCost(mercData->MercTemplateID, GetLevel(), 0): 0; - mml->Mercs[i].UpkeepCost = RuleB(Mercs, ChargeMercUpkeepCost) ? Merc::CalcUpkeepCost(mercData->MercTemplateID, GetLevel(), 0): 0; - mml->Mercs[i].Status = 0; - mml->Mercs[i].AltCurrencyCost = RuleB(Mercs, ChargeMercPurchaseCost) ? Merc::CalcPurchaseCost(mercData->MercTemplateID, GetLevel(), altCurrentType): 0; - mml->Mercs[i].AltCurrencyUpkeep = RuleB(Mercs, ChargeMercUpkeepCost) ? Merc::CalcUpkeepCost(mercData->MercTemplateID, GetLevel(), altCurrentType): 0; - mml->Mercs[i].AltCurrencyType = altCurrentType; - mml->Mercs[i].MercUnk01 = 0; - mml->Mercs[i].TimeLeft = GetMercInfo().MercTimerRemaining; - mml->Mercs[i].MerchantSlot = i + 1; - mml->Mercs[i].MercUnk02 = 1; - mml->Mercs[i].StanceCount = zone->merc_stance_list[mercData->MercTemplateID].size(); - mml->Mercs[i].MercUnk03 = 0; - mml->Mercs[i].MercUnk04 = 1; - strn0cpy(mml->Mercs[i].MercName, GetMercInfo().merc_name , sizeof(mml->Mercs[i].MercName)); - int stanceindex = 0; - if(mml->Mercs[i].StanceCount != 0) - { - std::list::iterator iter = zone->merc_stance_list[mercData->MercTemplateID].begin(); - while(iter != zone->merc_stance_list[mercData->MercTemplateID].end()) - { - mml->Mercs[i].Stances[stanceindex].StanceIndex = stanceindex; - mml->Mercs[i].Stances[stanceindex].Stance = (iter->StanceID); - stanceindex++; - ++iter; - } - } - FastQueuePacket(&outapp); - safe_delete(outapp); - return; - } - } - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: SendMercPersonalInfo Send Successful"); - - SendMercMerchantResponsePacket(0); - } - else - { - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: SendMercPersonalInfo Send Failed Due to no MercData for %i", GetMercInfo().MercTemplateID); - } - -} - -void Client::SendClearMercInfo() -{ - EQApplicationPacket* outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, sizeof(NoMercenaryHired_Struct)); - NoMercenaryHired_Struct *nmhs = (NoMercenaryHired_Struct*)outapp->pBuffer; - nmhs->MercStatus = -1; - nmhs->MercCount = 0; - nmhs->MercID = 1; - FastQueuePacket(&outapp); -} - - -void Client::DuplicateLoreMessage(uint32 ItemID) -{ - if(!(ClientVersionBit & BIT_RoFAndLater)) - { - Message_StringID(0, PICK_LORE); - return; - } - - const Item_Struct *item = database.GetItem(ItemID); - - if(!item) - return; - - Message_StringID(0, PICK_LORE, item->Name); -} - -void Client::GarbleMessage(char *message, uint8 variance) -{ - // Garble message by variance% - const char alpha_list[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; // only change alpha characters for now - - for (size_t i = 0; i < strlen(message); i++) { - uint8 chance = (uint8)zone->random.Int(0, 115); // variation just over worst possible scrambling - if (isalpha(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]; - } - } -} - -// returns what Other thinks of this -FACTION_VALUE Client::GetReverseFactionCon(Mob* iOther) { - if (GetOwnerID()) { - return GetOwnerOrSelf()->GetReverseFactionCon(iOther); - } - - iOther = iOther->GetOwnerOrSelf(); - - if (iOther->GetPrimaryFaction() < 0) - return GetSpecialFactionCon(iOther); - - if (iOther->GetPrimaryFaction() == 0) - return FACTION_INDIFFERENT; - - return GetFactionLevel(CharacterID(), 0, GetRace(), GetClass(), GetDeity(), iOther->GetPrimaryFaction(), iOther); -} - -//o-------------------------------------------------------------- -//| Name: GetFactionLevel; Dec. 16, 2001 -//o-------------------------------------------------------------- -//| Notes: Gets the characters faction standing with the specified NPC. -//| Will return Indifferent on failure. -//o-------------------------------------------------------------- -FACTION_VALUE Client::GetFactionLevel(uint32 char_id, uint32 npc_id, uint32 p_race, uint32 p_class, uint32 p_deity, int32 pFaction, Mob* tnpc) -{ - if (pFaction < 0) - return GetSpecialFactionCon(tnpc); - FACTION_VALUE fac = FACTION_INDIFFERENT; - int32 tmpFactionValue; - FactionMods fmods; - - // few optimizations - if (GetFeigned()) - return FACTION_INDIFFERENT; - if (invisible_undead && tnpc && !tnpc->SeeInvisibleUndead()) - return FACTION_INDIFFERENT; - if (IsInvisible(tnpc)) - return FACTION_INDIFFERENT; - if (tnpc && tnpc->GetOwnerID() != 0) // pets con amiably to owner and indiff to rest - { - if (char_id == tnpc->GetOwner()->CastToClient()->CharacterID()) - return FACTION_AMIABLE; - else - return FACTION_INDIFFERENT; - } - - //First get the NPC's Primary faction - if(pFaction > 0) - { - //Get the faction data from the database - if(database.GetFactionData(&fmods, p_class, p_race, p_deity, pFaction)) - { - //Get the players current faction with pFaction - tmpFactionValue = GetCharacterFactionLevel(pFaction); - //Tack on any bonuses from Alliance type spell effects - tmpFactionValue += GetFactionBonus(pFaction); - tmpFactionValue += GetItemFactionBonus(pFaction); - //Return the faction to the client - fac = CalculateFaction(&fmods, tmpFactionValue); - } - } - else - { - return(FACTION_INDIFFERENT); - } - - // merchant fix - if (tnpc && tnpc->IsNPC() && tnpc->CastToNPC()->MerchantType && (fac == FACTION_THREATENLY || fac == FACTION_SCOWLS)) - fac = FACTION_DUBIOUS; - - if (tnpc != 0 && fac != FACTION_SCOWLS && tnpc->CastToNPC()->CheckAggro(this)) - fac = FACTION_THREATENLY; - - return fac; -} - -//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) -{ - 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 }; - uint8 temp[MAX_NPC_FACTIONS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - int32 mod; - int32 tmpValue; - int32 current_value; - FactionMods fm; - bool change = false; - bool repair = false; - - // Get the npc faction list - if (!database.GetNPCFactionList(npc_id, faction_id, npc_value, temp)) - return; - for (int i = 0; i < MAX_NPC_FACTIONS; i++) - { - if (faction_id[i] <= 0) - continue; - - // Get the faction modifiers - if (database.GetFactionData(&fm, char_class, char_race, char_deity, faction_id[i])) - { - // Get the characters current value with that faction - current_value = GetCharacterFactionLevel(faction_id[i]); - - if (this->itembonuses.HeroicCHA) - { - int faction_mod = itembonuses.HeroicCHA / 5; - // If our result isn't truncated, then just do that - if (npc_value[i] * faction_mod / 100 != 0) - npc_value[i] += npc_value[i] * faction_mod / 100; - // If our result is truncated, then double a mob's value every once and a while to equal what they would have got - else - { - if (zone->random.Int(0, 100) < faction_mod) - npc_value[i] *= 2; - } - } - // Set flag when to update db - if (current_value > MAX_PERSONAL_FACTION) - { - current_value = MAX_PERSONAL_FACTION; - repair = true; - } - else if (current_value < MIN_PERSONAL_FACTION) - { - current_value = MIN_PERSONAL_FACTION; - repair = true; - } - else if ((m_pp.gm != 1) && (npc_value[i] != 0) && ((current_value != MAX_PERSONAL_FACTION) || (current_value != MIN_PERSONAL_FACTION))) - change = true; - - current_value += npc_value[i]; - - if (current_value > MAX_PERSONAL_FACTION) - current_value = MAX_PERSONAL_FACTION; - else if (current_value < MIN_PERSONAL_FACTION) - current_value = MIN_PERSONAL_FACTION; - - if (change || repair) - { - database.SetCharacterFactionLevel(char_id, faction_id[i], current_value, temp[i], factionvalues); - - if (change) - { - mod = fm.base + fm.class_mod + fm.race_mod + fm.deity_mod; - tmpValue = current_value + mod + npc_value[i]; - SendFactionMessage(npc_value[i], faction_id[i], tmpValue, temp[i]); - } - } - } - } - return; -} - -void Client::SetFactionLevel2(uint32 char_id, int32 faction_id, uint8 char_class, uint8 char_race, uint8 char_deity, int32 value, uint8 temp) -{ - int32 current_value; - //Get the npc faction list - if(faction_id > 0 && value != 0) { - //Get the faction modifiers - current_value = GetCharacterFactionLevel(faction_id) + value; - if(!(database.SetCharacterFactionLevel(char_id, faction_id, current_value, temp, factionvalues))) - return; - - SendFactionMessage(value, faction_id, current_value, temp); - } - return; -} - -int32 Client::GetCharacterFactionLevel(int32 faction_id) -{ - if (faction_id <= 0) - return 0; - faction_map::iterator res; - res = factionvalues.find(faction_id); - if (res == factionvalues.end()) - return 0; - return res->second; -} - -// returns the character's faction level, adjusted for racial, class, and deity modifiers -int32 Client::GetModCharacterFactionLevel(int32 faction_id) { - int32 Modded = GetCharacterFactionLevel(faction_id); - FactionMods fm; - if (database.GetFactionData(&fm, GetClass(), GetRace(), GetDeity(), faction_id)) - Modded += fm.base + fm.class_mod + fm.race_mod + fm.deity_mod; - - return Modded; -} - -void Client::MerchantRejectMessage(Mob *merchant, int primaryfaction) -{ - int messageid = 0; - int32 tmpFactionValue = 0; - int32 lowestvalue = 0; - FactionMods fmod; - - // If a faction is involved, get the data. - if (primaryfaction > 0) { - if (database.GetFactionData(&fmod, GetClass(), GetRace(), GetDeity(), primaryfaction)) { - tmpFactionValue = GetCharacterFactionLevel(primaryfaction); - lowestvalue = std::min(tmpFactionValue, std::min(fmod.class_mod, fmod.race_mod)); - } - } - // If no primary faction or biggest influence is your faction hit - if (primaryfaction <= 0 || lowestvalue == tmpFactionValue) { - merchant->Say_StringID(zone->random.Int(WONT_SELL_DEEDS1, WONT_SELL_DEEDS6)); - } else if (lowestvalue == fmod.race_mod) { // race biggest - // Non-standard race (ex. illusioned to wolf) - if (GetRace() > PLAYER_RACE_COUNT) { - messageid = zone->random.Int(1, 3); // these aren't sequential StringIDs :( - switch (messageid) { - case 1: - messageid = WONT_SELL_NONSTDRACE1; - break; - case 2: - messageid = WONT_SELL_NONSTDRACE2; - break; - case 3: - messageid = WONT_SELL_NONSTDRACE3; - break; - default: // w/e should never happen - messageid = WONT_SELL_NONSTDRACE1; - break; - } - merchant->Say_StringID(messageid); - } else { // normal player races - messageid = zone->random.Int(1, 4); - switch (messageid) { - case 1: - messageid = WONT_SELL_RACE1; - break; - case 2: - messageid = WONT_SELL_RACE2; - break; - case 3: - messageid = WONT_SELL_RACE3; - break; - case 4: - messageid = WONT_SELL_RACE4; - break; - default: // w/e should never happen - messageid = WONT_SELL_RACE1; - break; - } - merchant->Say_StringID(messageid, itoa(GetRace())); - } - } else if (lowestvalue == fmod.class_mod) { - merchant->Say_StringID(zone->random.Int(WONT_SELL_CLASS1, WONT_SELL_CLASS5), itoa(GetClass())); - } - return; -} - -//o-------------------------------------------------------------- -//| Name: SendFactionMessage -//o-------------------------------------------------------------- -//| Purpose: Send faction change message to client -//o-------------------------------------------------------------- -void Client::SendFactionMessage(int32 tmpvalue, int32 faction_id, int32 totalvalue, uint8 temp) -{ - char name[50]; - - // default to Faction# if we couldn't get the name from the ID - if (database.GetFactionName(faction_id, name, sizeof(name)) == false) - snprintf(name, sizeof(name), "Faction%i", faction_id); - - if (tmpvalue == 0 || temp == 1 || temp == 2) - return; - else if (totalvalue >= MAX_PERSONAL_FACTION) - Message_StringID(15, FACTION_BEST, name); - else if (totalvalue <= MIN_PERSONAL_FACTION) - Message_StringID(15, FACTION_WORST, name); - else if (tmpvalue > 0 && totalvalue < MAX_PERSONAL_FACTION && !RuleB(Client, UseLiveFactionMessage)) - Message_StringID(15, FACTION_BETTER, name); - else if (tmpvalue < 0 && totalvalue > MIN_PERSONAL_FACTION && !RuleB(Client, UseLiveFactionMessage)) - Message_StringID(15, FACTION_WORSE, name); - else if (RuleB(Client, UseLiveFactionMessage)) - Message(15, "Your faction standing with %s has been adjusted by %i.", name, tmpvalue); //New Live faction message (14261) - - return; -} - -void Client::LoadAccountFlags() -{ - - accountflags.clear(); - std::string query = StringFormat("SELECT p_flag, p_value " - "FROM account_flags WHERE p_accid = '%d'", - account_id); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - std::cerr << "Error in LoadAccountFlags query '" << query << "' " << results.ErrorMessage() << std::endl; - return; - } - - for (auto row = results.begin(); row != results.end(); ++row) - accountflags[row[0]] = row[1]; -} - -void Client::SetAccountFlag(std::string flag, std::string val) { - - std::string query = StringFormat("REPLACE INTO account_flags (p_accid, p_flag, p_value) " - "VALUES( '%d', '%s', '%s')", - account_id, flag.c_str(), val.c_str()); - auto results = database.QueryDatabase(query); - if(!results.Success()) { - std::cerr << "Error in SetAccountFlags query '" << query << "' " << results.ErrorMessage() << std::endl; - return; - } - - accountflags[flag] = val; -} - -std::string Client::GetAccountFlag(std::string flag) -{ - return(accountflags[flag]); -} - -void Client::TickItemCheck() -{ - int i; - - if(zone->tick_items.empty()) { return; } - - //Scan equip slots for items - for(i = EmuConstants::EQUIPMENT_BEGIN; i <= EmuConstants::EQUIPMENT_END; i++) - { - TryItemTick(i); - } - //Scan main inventory + cursor - for(i = EmuConstants::GENERAL_BEGIN; i <= MainCursor; i++) - { - TryItemTick(i); - } - //Scan bags - for(i = EmuConstants::GENERAL_BAGS_BEGIN; i <= EmuConstants::CURSOR_BAG_END; i++) - { - TryItemTick(i); - } -} - -void Client::TryItemTick(int slot) -{ - int iid = 0; - const ItemInst* inst = m_inv[slot]; - if(inst == 0) { return; } - - iid = inst->GetID(); - - if(zone->tick_items.count(iid) > 0) - { - if( GetLevel() >= zone->tick_items[iid].level && zone->random.Int(0, 100) >= (100 - zone->tick_items[iid].chance) && (zone->tick_items[iid].bagslot || slot <= EmuConstants::EQUIPMENT_END) ) - { - ItemInst* e_inst = (ItemInst*)inst; - parse->EventItem(EVENT_ITEM_TICK, this, e_inst, nullptr, "", slot); - } - } - - //Only look at augs in main inventory - if(slot > EmuConstants::EQUIPMENT_END) { return; } - - for (int x = AUG_BEGIN; x < EmuConstants::ITEM_COMMON_SIZE; ++x) - { - ItemInst * a_inst = inst->GetAugment(x); - if(!a_inst) { continue; } - - iid = a_inst->GetID(); - - if(zone->tick_items.count(iid) > 0) - { - if( GetLevel() >= zone->tick_items[iid].level && zone->random.Int(0, 100) >= (100 - zone->tick_items[iid].chance) ) - { - ItemInst* e_inst = (ItemInst*)a_inst; - parse->EventItem(EVENT_ITEM_TICK, this, e_inst, nullptr, "", slot); - } - } - } -} - -void Client::ItemTimerCheck() -{ - int i; - for(i = EmuConstants::EQUIPMENT_BEGIN; i <= EmuConstants::EQUIPMENT_END; i++) - { - TryItemTimer(i); - } - - for(i = EmuConstants::GENERAL_BEGIN; i <= MainCursor; i++) - { - TryItemTimer(i); - } - - for(i = EmuConstants::GENERAL_BAGS_BEGIN; i <= EmuConstants::CURSOR_BAG_END; i++) - { - TryItemTimer(i); - } -} - -void Client::TryItemTimer(int slot) -{ - ItemInst* inst = m_inv.GetItem(slot); - if(!inst) { - return; - } - - auto item_timers = inst->GetTimers(); - auto it_iter = item_timers.begin(); - while(it_iter != item_timers.end()) { - if(it_iter->second.Check()) { - parse->EventItem(EVENT_TIMER, this, inst, nullptr, it_iter->first, 0); - } - ++it_iter; - } - - if(slot > EmuConstants::EQUIPMENT_END) { - return; - } - - for (int x = AUG_BEGIN; x < EmuConstants::ITEM_COMMON_SIZE; ++x) - { - ItemInst * a_inst = inst->GetAugment(x); - if(!a_inst) { - continue; - } - - auto item_timers = a_inst->GetTimers(); - auto it_iter = item_timers.begin(); - while(it_iter != item_timers.end()) { - if(it_iter->second.Check()) { - parse->EventItem(EVENT_TIMER, this, a_inst, nullptr, it_iter->first, 0); - } - ++it_iter; - } - } -} - -void Client::RefundAA() { - int cur = 0; - bool refunded = false; - - for(int x = 0; x < aaHighestID; x++) { - cur = GetAA(x); - if(cur > 0){ - SendAA_Struct* curaa = zone->FindAA(x); - if(cur){ - SetAA(x, 0); - for(int j = 0; j < cur; j++) { - m_pp.aapoints += curaa->cost + (curaa->cost_inc * j); - refunded = true; - } - } - else - { - m_pp.aapoints += cur; - SetAA(x, 0); - refunded = true; - } - } - } - - if(refunded) { - SaveAA(); - Save(); - // Kick(); - } -} - -void Client::IncrementAA(int aa_id) { - SendAA_Struct* aa2 = zone->FindAA(aa_id); - - if(aa2 == nullptr) - return; - - if(GetAA(aa_id) == aa2->max_level) - return; - - SetAA(aa_id, GetAA(aa_id) + 1); - - SaveAA(); - - SendAA(aa_id); - SendAATable(); - SendAAStats(); - CalcBonuses(); -} - -void Client::SendItemScale(ItemInst *inst) { - int slot = m_inv.GetSlotByItemInst(inst); - if(slot != -1) { - inst->ScaleItem(); - SendItemPacket(slot, inst, ItemPacketCharmUpdate); - CalcBonuses(); - } -} - -void Client::AddRespawnOption(std::string option_name, uint32 zoneid, uint16 instance_id, float x, float y, float z, float heading, bool initial_selection, int8 position) -{ - //If respawn window is already open, any changes would create an inconsistency with the client - if (IsHoveringForRespawn()) { return; } - - if (zoneid == 0) - zoneid = zone->GetZoneID(); - - //Create respawn option - RespawnOption res_opt; - res_opt.name = option_name; - res_opt.zone_id = zoneid; - res_opt.instance_id = instance_id; - res_opt.x = x; - res_opt.y = y; - res_opt.z = z; - res_opt.heading = heading; - - if (position == -1 || position >= respawn_options.size()) - { - //No position specified, or specified beyond the end, simply append - respawn_options.push_back(res_opt); - //Make this option the initial selection for the window if desired - if (initial_selection) - initial_respawn_selection = static_cast(respawn_options.size()) - 1; - } - else if (position == 0) - { - respawn_options.push_front(res_opt); - if (initial_selection) - initial_respawn_selection = 0; - } - else - { - //Insert new option between existing options - std::list::iterator itr; - uint8 pos = 0; - for (itr = respawn_options.begin(); itr != respawn_options.end(); ++itr) - { - if (pos++ == position) - { - respawn_options.insert(itr,res_opt); - //Make this option the initial selection for the window if desired - if (initial_selection) - initial_respawn_selection = pos; - return; - } - } - } -} - -bool Client::RemoveRespawnOption(std::string option_name) -{ - //If respawn window is already open, any changes would create an inconsistency with the client - if (IsHoveringForRespawn() || respawn_options.empty()) { return false; } - - bool had = false; - RespawnOption* opt; - std::list::iterator itr; - for (itr = respawn_options.begin(); itr != respawn_options.end(); ++itr) - { - opt = &(*itr); - if (opt->name.compare(option_name) == 0) - { - itr = respawn_options.erase(itr); - had = true; - //could be more with the same name, so keep going... - } - } - return had; -} - -bool Client::RemoveRespawnOption(uint8 position) -{ - //If respawn window is already open, any changes would create an inconsistency with the client - if (IsHoveringForRespawn() || respawn_options.empty()) { return false; } - - //Easy cases first... - if (position == 0) - { - respawn_options.pop_front(); - return true; - } - else if (position == (respawn_options.size() - 1)) - { - respawn_options.pop_back(); - return true; - } - - std::list::iterator itr; - uint8 pos = 0; - for (itr = respawn_options.begin(); itr != respawn_options.end(); ++itr) - { - if (pos++ == position) - { - respawn_options.erase(itr); - return true; - } - } - return false; -} - -void Client::SetHunger(int32 in_hunger) -{ - EQApplicationPacket *outapp; - outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); - Stamina_Struct* sta = (Stamina_Struct*)outapp->pBuffer; - sta->food = in_hunger; - sta->water = m_pp.thirst_level > 6000 ? 6000 : m_pp.thirst_level; - - m_pp.hunger_level = in_hunger; - - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::SetThirst(int32 in_thirst) -{ - EQApplicationPacket *outapp; - outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); - Stamina_Struct* sta = (Stamina_Struct*)outapp->pBuffer; - sta->food = m_pp.hunger_level > 6000 ? 6000 : m_pp.hunger_level; - sta->water = in_thirst; - - m_pp.thirst_level = in_thirst; - - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::SetConsumption(int32 in_hunger, int32 in_thirst) -{ - EQApplicationPacket *outapp; - outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); - Stamina_Struct* sta = (Stamina_Struct*)outapp->pBuffer; - sta->food = in_hunger; - sta->water = in_thirst; - - m_pp.hunger_level = in_hunger; - m_pp.thirst_level = in_thirst; - - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::Consume(const Item_Struct *item, uint8 type, int16 slot, bool auto_consume) -{ - if(!item) { return; } - - uint32 cons_mod = 180; - - int32 metabolism_bonus = spellbonuses.Metabolism + itembonuses.Metabolism + aabonuses.Metabolism; - - if (metabolism_bonus) - cons_mod = cons_mod * metabolism_bonus * RuleI(Character, ConsumptionMultiplier) / 10000; - else - cons_mod = cons_mod * RuleI(Character, ConsumptionMultiplier) / 100; - - if(type == ItemTypeFood) - { - int hchange = item->CastTime * cons_mod; - hchange = mod_food_value(item, hchange); - - if(hchange < 0) { return; } - - m_pp.hunger_level += hchange; - DeleteItemInInventory(slot, 1, false); - - if(!auto_consume) //no message if the client consumed for us - entity_list.MessageClose_StringID(this, true, 50, 0, EATING_MESSAGE, GetName(), item->Name); - -#if EQDEBUG >= 5 - LogFile->write(EQEMuLog::Debug, "Eating from slot:%i", (int)slot); -#endif - } - else - { - int tchange = item->CastTime * cons_mod; - tchange = mod_drink_value(item, tchange); - - if(tchange < 0) { return; } - - m_pp.thirst_level += tchange; - DeleteItemInInventory(slot, 1, false); - - if(!auto_consume) //no message if the client consumed for us - entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), item->Name); - -#if EQDEBUG >= 5 - LogFile->write(EQEMuLog::Debug, "Drinking from slot:%i", (int)slot); -#endif - } -} - -void Client::SendMarqueeMessage(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, std::string msg) -{ - if(duration == 0 || msg.length() == 0) { - return; - } - - EQApplicationPacket outapp(OP_Marquee, sizeof(ClientMarqueeMessage_Struct) + msg.length()); - ClientMarqueeMessage_Struct *cms = (ClientMarqueeMessage_Struct*)outapp.pBuffer; - - cms->type = type; - cms->unk04 = 10; - cms->priority = priority; - cms->fade_in_time = fade_in; - cms->fade_out_time = fade_out; - cms->duration = duration; - strcpy(cms->msg, msg.c_str()); - - QueuePacket(&outapp); -} - -void Client::PlayMP3(const char* fname) -{ - std::string filename = fname; - EQApplicationPacket *outapp = new EQApplicationPacket(OP_PlayMP3, filename.length() + 1); - PlayMP3_Struct* buf = (PlayMP3_Struct*)outapp->pBuffer; - strncpy(buf->filename, fname, filename.length()); - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::ExpeditionSay(const char *str, int ExpID) { - - std::string query = StringFormat("SELECT `player_name` FROM `cust_inst_players` " - "WHERE `inst_id` = %i", ExpID); - auto results = database.QueryDatabase(query); - if (!results.Success()) - return; - - if(results.RowCount() == 0) { - this->Message(14, "You say to the expedition, '%s'", str); - return; - } - - for(auto row = results.begin(); row != results.end(); ++row) { - const char* charName = row[0]; - if(strcmp(charName, this->GetCleanName()) != 0) - worldserver.SendEmoteMessage(charName, 0, 0, 14, "%s says to the expedition, '%s'", this->GetCleanName(), str); - // ChannelList->CreateChannel(ChannelName, ChannelOwner, ChannelPassword, true, atoi(row[3])); - } - - -} - -void Client::ShowNumHits() -{ - uint32 buffcount = GetMaxTotalSlots(); - for (uint32 buffslot = 0; buffslot < buffcount; buffslot++) { - const Buffs_Struct &curbuff = buffs[buffslot]; - if (curbuff.spellid != SPELL_UNKNOWN && curbuff.numhits) - Message(0, "You have %d hits left on %s", curbuff.numhits, GetSpellName(curbuff.spellid)); - } - return; -} - -float Client::GetQuiverHaste() -{ - float quiver_haste = 0; - for (int r = EmuConstants::GENERAL_BEGIN; r <= EmuConstants::GENERAL_END; r++) { - const ItemInst *pi = GetInv().GetItem(r); - if (!pi) - continue; - if (pi->IsType(ItemClassContainer) && pi->GetItem()->BagType == BagTypeQuiver) { - float temp_wr = (pi->GetItem()->BagWR / RuleI(Combat, QuiverWRHasteDiv)); - quiver_haste = std::max(temp_wr, quiver_haste); - } - } - if (quiver_haste > 0) - quiver_haste = 1.0f / (1.0f + static_cast(quiver_haste) / 100.0f); - return quiver_haste; -} - -void Client::SendColoredText(uint32 color, std::string message) -{ - // arbitrary size limit - if (message.size() > 512) // live does send this with empty strings sometimes ... - return; - EQApplicationPacket *outapp = new EQApplicationPacket(OP_ColoredText, - sizeof(ColoredText_Struct) + message.size()); - ColoredText_Struct *cts = (ColoredText_Struct *)outapp->pBuffer; - cts->color = color; - strcpy(cts->msg, message.c_str()); - QueuePacket(outapp); - safe_delete(outapp); -} - diff --git a/zone/client.h b/zone/client.h index 245e5803c..155b3d0b8 100644 --- a/zone/client.h +++ b/zone/client.h @@ -755,6 +755,7 @@ public: //AA Methods void SendAAList(); void ResetAA(); + void SendClearAA(); void SendAA(uint32 id, int seq=1); void SendPreviousAA(uint32 id, int seq=1); void BuyAA(AA_Action* action); diff --git a/zone/client_packet.cpp.orig b/zone/client_packet.cpp.orig deleted file mode 100644 index aad05b175..000000000 --- a/zone/client_packet.cpp.orig +++ /dev/null @@ -1,14039 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2009 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 -*/ - -#include "../common/debug.h" -#include -#include -#include -#include -#include -#include -#include - -#ifdef _WINDOWS - #define snprintf _snprintf - #define strncasecmp _strnicmp - #define strcasecmp _stricmp -#else - #include - #include - #include - #include -#endif - -#include "../common/crc32.h" -#include "../common/data_verification.h" -#include "../common/faction.h" -#include "../common/guilds.h" -#include "../common/rdtsc.h" -#include "../common/rulesys.h" -#include "../common/skills.h" -#include "../common/spdat.h" -#include "../common/string_util.h" -#include "../common/zone_numbers.h" -#include "event_codes.h" -#include "guild_mgr.h" -#include "merc.h" -#include "petitions.h" -#include "pets.h" -#include "queryserv.h" -#include "quest_parser_collection.h" -#include "string_ids.h" -#include "titles.h" -#include "water_map.h" -#include "worldserver.h" -#include "zone.h" - -extern QueryServ* QServ; -extern Zone* zone; -extern volatile bool ZoneLoaded; -extern WorldServer worldserver; -extern PetitionList petition_list; -extern EntityList entity_list; -typedef void (Client::*ClientPacketProc)(const EQApplicationPacket *app); - -//Use a map for connecting opcodes since it dosent get used a lot and is sparse -std::map ConnectingOpcodes; -//Use a static array for connected, for speed -ClientPacketProc ConnectedOpcodes[_maxEmuOpcode]; - -void MapOpcodes() -{ - ConnectingOpcodes.clear(); - memset(ConnectedOpcodes, 0, sizeof(ConnectedOpcodes)); - - // Now put all the opcodes into their home... - // connecting opcode handler assignments: - ConnectingOpcodes[OP_ApproveZone] = &Client::Handle_Connect_OP_ApproveZone; - ConnectingOpcodes[OP_BlockedBuffs] = &Client::Handle_OP_BlockedBuffs; - ConnectingOpcodes[OP_ClientError] = &Client::Handle_Connect_OP_ClientError; - ConnectingOpcodes[OP_ClientReady] = &Client::Handle_Connect_OP_ClientReady; - ConnectingOpcodes[OP_ClientUpdate] = &Client::Handle_Connect_OP_ClientUpdate; - ConnectingOpcodes[OP_GetGuildsList] = &Client::Handle_OP_GetGuildsList; // temporary hack - ConnectingOpcodes[OP_ReqClientSpawn] = &Client::Handle_Connect_OP_ReqClientSpawn; - ConnectingOpcodes[OP_ReqNewZone] = &Client::Handle_Connect_OP_ReqNewZone; - ConnectingOpcodes[OP_SendAAStats] = &Client::Handle_Connect_OP_SendAAStats; - ConnectingOpcodes[OP_SendAATable] = &Client::Handle_Connect_OP_SendAATable; - ConnectingOpcodes[OP_SendExpZonein] = &Client::Handle_Connect_OP_SendExpZonein; - ConnectingOpcodes[OP_SendGuildTributes] = &Client::Handle_Connect_OP_SendGuildTributes; - ConnectingOpcodes[OP_SendGuildTributes] = &Client::Handle_Connect_OP_SendGuildTributes; // I guess it didn't believe us with the first assignment? - ConnectingOpcodes[OP_SendTributes] = &Client::Handle_Connect_OP_SendTributes; - ConnectingOpcodes[OP_SetServerFilter] = &Client::Handle_Connect_OP_SetServerFilter; - ConnectingOpcodes[OP_SpawnAppearance] = &Client::Handle_Connect_OP_SpawnAppearance; - ConnectingOpcodes[OP_TGB] = &Client::Handle_Connect_OP_TGB; - ConnectingOpcodes[OP_UpdateAA] = &Client::Handle_Connect_OP_UpdateAA; - ConnectingOpcodes[OP_WearChange] = &Client::Handle_Connect_OP_WearChange; - ConnectingOpcodes[OP_WorldObjectsSent] = &Client::Handle_Connect_OP_WorldObjectsSent; - ConnectingOpcodes[OP_XTargetAutoAddHaters] = &Client::Handle_OP_XTargetAutoAddHaters; - ConnectingOpcodes[OP_XTargetRequest] = &Client::Handle_OP_XTargetRequest; - ConnectingOpcodes[OP_ZoneComplete] = &Client::Handle_Connect_OP_ZoneComplete; - ConnectingOpcodes[OP_ZoneEntry] = &Client::Handle_Connect_OP_ZoneEntry; - - // connected opcode handler assignments: - ConnectedOpcodes[OP_0x0193] = &Client::Handle_0x0193; - ConnectedOpcodes[OP_AAAction] = &Client::Handle_OP_AAAction; - ConnectedOpcodes[OP_AcceptNewTask] = &Client::Handle_OP_AcceptNewTask; - ConnectedOpcodes[OP_AdventureInfoRequest] = &Client::Handle_OP_AdventureInfoRequest; - ConnectedOpcodes[OP_AdventureLeaderboardRequest] = &Client::Handle_OP_AdventureLeaderboardRequest; - ConnectedOpcodes[OP_AdventureMerchantPurchase] = &Client::Handle_OP_AdventureMerchantPurchase; - ConnectedOpcodes[OP_AdventureMerchantRequest] = &Client::Handle_OP_AdventureMerchantRequest; - ConnectedOpcodes[OP_AdventureMerchantSell] = &Client::Handle_OP_AdventureMerchantSell; - ConnectedOpcodes[OP_AdventureRequest] = &Client::Handle_OP_AdventureRequest; - ConnectedOpcodes[OP_AdventureStatsRequest] = &Client::Handle_OP_AdventureStatsRequest; - ConnectedOpcodes[OP_AltCurrencyMerchantRequest] = &Client::Handle_OP_AltCurrencyMerchantRequest; - ConnectedOpcodes[OP_AltCurrencyPurchase] = &Client::Handle_OP_AltCurrencyPurchase; - ConnectedOpcodes[OP_AltCurrencyReclaim] = &Client::Handle_OP_AltCurrencyReclaim; - ConnectedOpcodes[OP_AltCurrencySell] = &Client::Handle_OP_AltCurrencySell; - ConnectedOpcodes[OP_AltCurrencySellSelection] = &Client::Handle_OP_AltCurrencySellSelection; - ConnectedOpcodes[OP_Animation] = &Client::Handle_OP_Animation; - ConnectedOpcodes[OP_ApplyPoison] = &Client::Handle_OP_ApplyPoison; - ConnectedOpcodes[OP_Assist] = &Client::Handle_OP_Assist; - ConnectedOpcodes[OP_AssistGroup] = &Client::Handle_OP_AssistGroup; - ConnectedOpcodes[OP_AugmentInfo] = &Client::Handle_OP_AugmentInfo; - ConnectedOpcodes[OP_AugmentItem] = &Client::Handle_OP_AugmentItem; - ConnectedOpcodes[OP_AutoAttack] = &Client::Handle_OP_AutoAttack; - ConnectedOpcodes[OP_AutoAttack2] = &Client::Handle_OP_AutoAttack2; - ConnectedOpcodes[OP_AutoFire] = &Client::Handle_OP_AutoFire; - ConnectedOpcodes[OP_Bandolier] = &Client::Handle_OP_Bandolier; - ConnectedOpcodes[OP_BankerChange] = &Client::Handle_OP_BankerChange; - ConnectedOpcodes[OP_Barter] = &Client::Handle_OP_Barter; - ConnectedOpcodes[OP_BazaarInspect] = &Client::Handle_OP_BazaarInspect; - ConnectedOpcodes[OP_BazaarSearch] = &Client::Handle_OP_BazaarSearch; - ConnectedOpcodes[OP_Begging] = &Client::Handle_OP_Begging; - ConnectedOpcodes[OP_Bind_Wound] = &Client::Handle_OP_Bind_Wound; - ConnectedOpcodes[OP_BlockedBuffs] = &Client::Handle_OP_BlockedBuffs; - ConnectedOpcodes[OP_BoardBoat] = &Client::Handle_OP_BoardBoat; - ConnectedOpcodes[OP_Buff] = &Client::Handle_OP_Buff; - ConnectedOpcodes[OP_BuffRemoveRequest] = &Client::Handle_OP_BuffRemoveRequest; - ConnectedOpcodes[OP_Bug] = &Client::Handle_OP_Bug; - ConnectedOpcodes[OP_Camp] = &Client::Handle_OP_Camp; - ConnectedOpcodes[OP_CancelTask] = &Client::Handle_OP_CancelTask; - ConnectedOpcodes[OP_CancelTrade] = &Client::Handle_OP_CancelTrade; - ConnectedOpcodes[OP_CastSpell] = &Client::Handle_OP_CastSpell; - ConnectedOpcodes[OP_ChannelMessage] = &Client::Handle_OP_ChannelMessage; - ConnectedOpcodes[OP_ClearBlockedBuffs] = &Client::Handle_OP_ClearBlockedBuffs; - ConnectedOpcodes[OP_ClearNPCMarks] = &Client::Handle_OP_ClearNPCMarks; - ConnectedOpcodes[OP_ClearSurname] = &Client::Handle_OP_ClearSurname; - ConnectedOpcodes[OP_ClickDoor] = &Client::Handle_OP_ClickDoor; - ConnectedOpcodes[OP_ClickObject] = &Client::Handle_OP_ClickObject; - ConnectedOpcodes[OP_ClickObjectAction] = &Client::Handle_OP_ClickObjectAction; - ConnectedOpcodes[OP_ClientError] = &Client::Handle_OP_ClientError; - ConnectedOpcodes[OP_ClientTimeStamp] = &Client::Handle_OP_ClientTimeStamp; - ConnectedOpcodes[OP_ClientUpdate] = &Client::Handle_OP_ClientUpdate; - ConnectedOpcodes[OP_CombatAbility] = &Client::Handle_OP_CombatAbility; - ConnectedOpcodes[OP_ConfirmDelete] = &Client::Handle_OP_ConfirmDelete; - ConnectedOpcodes[OP_Consent] = &Client::Handle_OP_Consent; - ConnectedOpcodes[OP_ConsentDeny] = &Client::Handle_OP_ConsentDeny; - ConnectedOpcodes[OP_Consider] = &Client::Handle_OP_Consider; - ConnectedOpcodes[OP_ConsiderCorpse] = &Client::Handle_OP_ConsiderCorpse; - ConnectedOpcodes[OP_Consume] = &Client::Handle_OP_Consume; - ConnectedOpcodes[OP_ControlBoat] = &Client::Handle_OP_ControlBoat; - ConnectedOpcodes[OP_CorpseDrag] = &Client::Handle_OP_CorpseDrag; - ConnectedOpcodes[OP_CorpseDrop] = &Client::Handle_OP_CorpseDrop; - ConnectedOpcodes[OP_CrashDump] = &Client::Handle_OP_CrashDump; - ConnectedOpcodes[OP_CrystalCreate] = &Client::Handle_OP_CrystalCreate; - ConnectedOpcodes[OP_CrystalReclaim] = &Client::Handle_OP_CrystalReclaim; - ConnectedOpcodes[OP_Damage] = &Client::Handle_OP_Damage; - ConnectedOpcodes[OP_Death] = &Client::Handle_OP_Death; - ConnectedOpcodes[OP_DelegateAbility] = &Client::Handle_OP_DelegateAbility; - ConnectedOpcodes[OP_DeleteItem] = &Client::Handle_OP_DeleteItem; - ConnectedOpcodes[OP_DeleteSpawn] = &Client::Handle_OP_DeleteSpawn; - ConnectedOpcodes[OP_DeleteSpell] = &Client::Handle_OP_DeleteSpell; - ConnectedOpcodes[OP_DisarmTraps] = &Client::Handle_OP_DisarmTraps; - ConnectedOpcodes[OP_DoGroupLeadershipAbility] = &Client::Handle_OP_DoGroupLeadershipAbility; - ConnectedOpcodes[OP_DuelResponse] = &Client::Handle_OP_DuelResponse; - ConnectedOpcodes[OP_DuelResponse2] = &Client::Handle_OP_DuelResponse2; - ConnectedOpcodes[OP_DumpName] = &Client::Handle_OP_DumpName; - ConnectedOpcodes[OP_Dye] = &Client::Handle_OP_Dye; - ConnectedOpcodes[OP_Emote] = &Client::Handle_OP_Emote; - ConnectedOpcodes[OP_EndLootRequest] = &Client::Handle_OP_EndLootRequest; - ConnectedOpcodes[OP_EnvDamage] = &Client::Handle_OP_EnvDamage; - ConnectedOpcodes[OP_FaceChange] = &Client::Handle_OP_FaceChange; - ConnectedOpcodes[OP_FeignDeath] = &Client::Handle_OP_FeignDeath; - ConnectedOpcodes[OP_FindPersonRequest] = &Client::Handle_OP_FindPersonRequest; - ConnectedOpcodes[OP_Fishing] = &Client::Handle_OP_Fishing; - ConnectedOpcodes[OP_FloatListThing] = &Client::Handle_OP_Ignore; - ConnectedOpcodes[OP_Forage] = &Client::Handle_OP_Forage; - ConnectedOpcodes[OP_FriendsWho] = &Client::Handle_OP_FriendsWho; - ConnectedOpcodes[OP_GetGuildMOTD] = &Client::Handle_OP_GetGuildMOTD; - ConnectedOpcodes[OP_GetGuildsList] = &Client::Handle_OP_GetGuildsList; - ConnectedOpcodes[OP_GMBecomeNPC] = &Client::Handle_OP_GMBecomeNPC; - ConnectedOpcodes[OP_GMDelCorpse] = &Client::Handle_OP_GMDelCorpse; - ConnectedOpcodes[OP_GMEmoteZone] = &Client::Handle_OP_GMEmoteZone; - ConnectedOpcodes[OP_GMEndTraining] = &Client::Handle_OP_GMEndTraining; - ConnectedOpcodes[OP_GMFind] = &Client::Handle_OP_GMFind; - ConnectedOpcodes[OP_GMGoto] = &Client::Handle_OP_GMGoto; - ConnectedOpcodes[OP_GMHideMe] = &Client::Handle_OP_GMHideMe; - ConnectedOpcodes[OP_GMKick] = &Client::Handle_OP_GMKick; - ConnectedOpcodes[OP_GMKill] = &Client::Handle_OP_GMKill; - ConnectedOpcodes[OP_GMLastName] = &Client::Handle_OP_GMLastName; - ConnectedOpcodes[OP_GMNameChange] = &Client::Handle_OP_GMNameChange; - ConnectedOpcodes[OP_GMSearchCorpse] = &Client::Handle_OP_GMSearchCorpse; - ConnectedOpcodes[OP_GMServers] = &Client::Handle_OP_GMServers; - ConnectedOpcodes[OP_GMSummon] = &Client::Handle_OP_GMSummon; - ConnectedOpcodes[OP_GMToggle] = &Client::Handle_OP_GMToggle; - ConnectedOpcodes[OP_GMTraining] = &Client::Handle_OP_GMTraining; - ConnectedOpcodes[OP_GMTrainSkill] = &Client::Handle_OP_GMTrainSkill; - ConnectedOpcodes[OP_GMZoneRequest] = &Client::Handle_OP_GMZoneRequest; - ConnectedOpcodes[OP_GMZoneRequest2] = &Client::Handle_OP_GMZoneRequest2; - ConnectedOpcodes[OP_GroundSpawn] = &Client::Handle_OP_CreateObject; - ConnectedOpcodes[OP_GroupAcknowledge] = &Client::Handle_OP_GroupAcknowledge; - ConnectedOpcodes[OP_GroupCancelInvite] = &Client::Handle_OP_GroupCancelInvite; - ConnectedOpcodes[OP_GroupDelete] = &Client::Handle_OP_GroupDelete; - ConnectedOpcodes[OP_GroupDisband] = &Client::Handle_OP_GroupDisband; - ConnectedOpcodes[OP_GroupFollow] = &Client::Handle_OP_GroupFollow; - ConnectedOpcodes[OP_GroupFollow2] = &Client::Handle_OP_GroupFollow2; - ConnectedOpcodes[OP_GroupInvite] = &Client::Handle_OP_GroupInvite; - ConnectedOpcodes[OP_GroupInvite2] = &Client::Handle_OP_GroupInvite2; - ConnectedOpcodes[OP_GroupMakeLeader] = &Client::Handle_OP_GroupMakeLeader; - ConnectedOpcodes[OP_GroupMentor] = &Client::Handle_OP_GroupMentor; - ConnectedOpcodes[OP_GroupRoles] = &Client::Handle_OP_GroupRoles; - ConnectedOpcodes[OP_GroupUpdate] = &Client::Handle_OP_GroupUpdate; - ConnectedOpcodes[OP_GuildBank] = &Client::Handle_OP_GuildBank; - ConnectedOpcodes[OP_GuildCreate] = &Client::Handle_OP_GuildCreate; - ConnectedOpcodes[OP_GuildDelete] = &Client::Handle_OP_GuildDelete; - ConnectedOpcodes[OP_GuildDemote] = &Client::Handle_OP_GuildDemote; - ConnectedOpcodes[OP_GuildInvite] = &Client::Handle_OP_GuildInvite; - ConnectedOpcodes[OP_GuildInviteAccept] = &Client::Handle_OP_GuildInviteAccept; - ConnectedOpcodes[OP_GuildLeader] = &Client::Handle_OP_GuildLeader; - ConnectedOpcodes[OP_GuildManageBanker] = &Client::Handle_OP_GuildManageBanker; - ConnectedOpcodes[OP_GuildPeace] = &Client::Handle_OP_GuildPeace; - ConnectedOpcodes[OP_GuildPromote] = &Client::Handle_OP_GuildPromote; - ConnectedOpcodes[OP_GuildPublicNote] = &Client::Handle_OP_GuildPublicNote; - ConnectedOpcodes[OP_GuildRemove] = &Client::Handle_OP_GuildRemove; - ConnectedOpcodes[OP_GuildStatus] = &Client::Handle_OP_GuildStatus; - ConnectedOpcodes[OP_GuildUpdateURLAndChannel] = &Client::Handle_OP_GuildUpdateURLAndChannel; - ConnectedOpcodes[OP_GuildWar] = &Client::Handle_OP_GuildWar; - ConnectedOpcodes[OP_Heartbeat] = &Client::Handle_OP_Heartbeat; - ConnectedOpcodes[OP_Hide] = &Client::Handle_OP_Hide; - ConnectedOpcodes[OP_HideCorpse] = &Client::Handle_OP_HideCorpse; - ConnectedOpcodes[OP_Illusion] = &Client::Handle_OP_Illusion; - ConnectedOpcodes[OP_InspectAnswer] = &Client::Handle_OP_InspectAnswer; - ConnectedOpcodes[OP_InspectMessageUpdate] = &Client::Handle_OP_InspectMessageUpdate; - ConnectedOpcodes[OP_InspectRequest] = &Client::Handle_OP_InspectRequest; - ConnectedOpcodes[OP_InstillDoubt] = &Client::Handle_OP_InstillDoubt; - ConnectedOpcodes[OP_ItemLinkClick] = &Client::Handle_OP_ItemLinkClick; - ConnectedOpcodes[OP_ItemLinkResponse] = &Client::Handle_OP_ItemLinkResponse; - ConnectedOpcodes[OP_ItemName] = &Client::Handle_OP_ItemName; - ConnectedOpcodes[OP_ItemPreview] = &Client::Handle_OP_ItemPreview; - ConnectedOpcodes[OP_ItemVerifyRequest] = &Client::Handle_OP_ItemVerifyRequest; - ConnectedOpcodes[OP_ItemViewUnknown] = &Client::Handle_OP_Ignore; - ConnectedOpcodes[OP_Jump] = &Client::Handle_OP_Jump; - ConnectedOpcodes[OP_KeyRing] = &Client::Handle_OP_KeyRing; - ConnectedOpcodes[OP_LDoNButton] = &Client::Handle_OP_LDoNButton; - ConnectedOpcodes[OP_LDoNDisarmTraps] = &Client::Handle_OP_LDoNDisarmTraps; - ConnectedOpcodes[OP_LDoNInspect] = &Client::Handle_OP_LDoNInspect; - ConnectedOpcodes[OP_LDoNOpen] = &Client::Handle_OP_LDoNOpen; - ConnectedOpcodes[OP_LDoNPickLock] = &Client::Handle_OP_LDoNPickLock; - ConnectedOpcodes[OP_LDoNSenseTraps] = &Client::Handle_OP_LDoNSenseTraps; - ConnectedOpcodes[OP_LeadershipExpToggle] = &Client::Handle_OP_LeadershipExpToggle; - ConnectedOpcodes[OP_LeaveAdventure] = &Client::Handle_OP_LeaveAdventure; - ConnectedOpcodes[OP_LeaveBoat] = &Client::Handle_OP_LeaveBoat; - ConnectedOpcodes[OP_LFGCommand] = &Client::Handle_OP_LFGCommand; - ConnectedOpcodes[OP_LFGGetMatchesRequest] = &Client::Handle_OP_LFGGetMatchesRequest; - ConnectedOpcodes[OP_LFGuild] = &Client::Handle_OP_LFGuild; - ConnectedOpcodes[OP_LFPCommand] = &Client::Handle_OP_LFPCommand; - ConnectedOpcodes[OP_LFPGetMatchesRequest] = &Client::Handle_OP_LFPGetMatchesRequest; - ConnectedOpcodes[OP_LoadSpellSet] = &Client::Handle_OP_LoadSpellSet; - ConnectedOpcodes[OP_Logout] = &Client::Handle_OP_Logout; - ConnectedOpcodes[OP_LootItem] = &Client::Handle_OP_LootItem; - ConnectedOpcodes[OP_LootRequest] = &Client::Handle_OP_LootRequest; - ConnectedOpcodes[OP_ManaChange] = &Client::Handle_OP_ManaChange; - ConnectedOpcodes[OP_MemorizeSpell] = &Client::Handle_OP_MemorizeSpell; - ConnectedOpcodes[OP_Mend] = &Client::Handle_OP_Mend; - ConnectedOpcodes[OP_MercenaryCommand] = &Client::Handle_OP_MercenaryCommand; - ConnectedOpcodes[OP_MercenaryDataRequest] = &Client::Handle_OP_MercenaryDataRequest; - ConnectedOpcodes[OP_MercenaryDataUpdateRequest] = &Client::Handle_OP_MercenaryDataUpdateRequest; - ConnectedOpcodes[OP_MercenaryDismiss] = &Client::Handle_OP_MercenaryDismiss; - ConnectedOpcodes[OP_MercenaryHire] = &Client::Handle_OP_MercenaryHire; - ConnectedOpcodes[OP_MercenarySuspendRequest] = &Client::Handle_OP_MercenarySuspendRequest; - ConnectedOpcodes[OP_MercenaryTimerRequest] = &Client::Handle_OP_MercenaryTimerRequest; - ConnectedOpcodes[OP_MoveCoin] = &Client::Handle_OP_MoveCoin; - ConnectedOpcodes[OP_MoveItem] = &Client::Handle_OP_MoveItem; - ConnectedOpcodes[OP_OpenContainer] = &Client::Handle_OP_OpenContainer; - ConnectedOpcodes[OP_OpenGuildTributeMaster] = &Client::Handle_OP_OpenGuildTributeMaster; - ConnectedOpcodes[OP_OpenInventory] = &Client::Handle_OP_OpenInventory; - ConnectedOpcodes[OP_OpenTributeMaster] = &Client::Handle_OP_OpenTributeMaster; - ConnectedOpcodes[OP_PDeletePetition] = &Client::Handle_OP_PDeletePetition; - ConnectedOpcodes[OP_PetCommands] = &Client::Handle_OP_PetCommands; - ConnectedOpcodes[OP_Petition] = &Client::Handle_OP_Petition; - ConnectedOpcodes[OP_PetitionBug] = &Client::Handle_OP_PetitionBug; - ConnectedOpcodes[OP_PetitionCheckIn] = &Client::Handle_OP_PetitionCheckIn; - ConnectedOpcodes[OP_PetitionCheckout] = &Client::Handle_OP_PetitionCheckout; - ConnectedOpcodes[OP_PetitionDelete] = &Client::Handle_OP_PetitionDelete; - ConnectedOpcodes[OP_PetitionQue] = &Client::Handle_OP_PetitionQue; - ConnectedOpcodes[OP_PetitionRefresh] = &Client::Handle_OP_PetitionRefresh; - ConnectedOpcodes[OP_PetitionResolve] = &Client::Handle_OP_PetitionResolve; - ConnectedOpcodes[OP_PetitionUnCheckout] = &Client::Handle_OP_PetitionUnCheckout; - ConnectedOpcodes[OP_PickPocket] = &Client::Handle_OP_PickPocket; - ConnectedOpcodes[OP_PopupResponse] = &Client::Handle_OP_PopupResponse; - ConnectedOpcodes[OP_PotionBelt] = &Client::Handle_OP_PotionBelt; - ConnectedOpcodes[OP_PurchaseLeadershipAA] = &Client::Handle_OP_PurchaseLeadershipAA; - ConnectedOpcodes[OP_PVPLeaderBoardDetailsRequest] = &Client::Handle_OP_PVPLeaderBoardDetailsRequest; - ConnectedOpcodes[OP_PVPLeaderBoardRequest] = &Client::Handle_OP_PVPLeaderBoardRequest; - ConnectedOpcodes[OP_RaidInvite] = &Client::Handle_OP_RaidCommand; - ConnectedOpcodes[OP_RandomReq] = &Client::Handle_OP_RandomReq; - ConnectedOpcodes[OP_ReadBook] = &Client::Handle_OP_ReadBook; - ConnectedOpcodes[OP_RecipeAutoCombine] = &Client::Handle_OP_RecipeAutoCombine; - ConnectedOpcodes[OP_RecipeDetails] = &Client::Handle_OP_RecipeDetails; - ConnectedOpcodes[OP_RecipesFavorite] = &Client::Handle_OP_RecipesFavorite; - ConnectedOpcodes[OP_RecipesSearch] = &Client::Handle_OP_RecipesSearch; - ConnectedOpcodes[OP_ReloadUI] = &Client::Handle_OP_ReloadUI; - ConnectedOpcodes[OP_RemoveBlockedBuffs] = &Client::Handle_OP_RemoveBlockedBuffs; - ConnectedOpcodes[OP_Report] = &Client::Handle_OP_Report; - ConnectedOpcodes[OP_RequestDuel] = &Client::Handle_OP_RequestDuel; - ConnectedOpcodes[OP_RequestTitles] = &Client::Handle_OP_RequestTitles; - ConnectedOpcodes[OP_RespawnWindow] = &Client::Handle_OP_RespawnWindow; - ConnectedOpcodes[OP_Rewind] = &Client::Handle_OP_Rewind; - ConnectedOpcodes[OP_RezzAnswer] = &Client::Handle_OP_RezzAnswer; - ConnectedOpcodes[OP_Sacrifice] = &Client::Handle_OP_Sacrifice; - ConnectedOpcodes[OP_SafeFallSuccess] = &Client::Handle_OP_SafeFallSuccess; - ConnectedOpcodes[OP_SafePoint] = &Client::Handle_OP_SafePoint; - ConnectedOpcodes[OP_Save] = &Client::Handle_OP_Save; - ConnectedOpcodes[OP_SaveOnZoneReq] = &Client::Handle_OP_SaveOnZoneReq; - ConnectedOpcodes[OP_SelectTribute] = &Client::Handle_OP_SelectTribute; - ConnectedOpcodes[OP_SenseHeading] = &Client::Handle_OP_Ignore; - ConnectedOpcodes[OP_SenseTraps] = &Client::Handle_OP_SenseTraps; - ConnectedOpcodes[OP_SetGuildMOTD] = &Client::Handle_OP_SetGuildMOTD; - ConnectedOpcodes[OP_SetRunMode] = &Client::Handle_OP_SetRunMode; - ConnectedOpcodes[OP_SetServerFilter] = &Client::Handle_OP_SetServerFilter; - ConnectedOpcodes[OP_SetStartCity] = &Client::Handle_OP_SetStartCity; - ConnectedOpcodes[OP_SetTitle] = &Client::Handle_OP_SetTitle; - ConnectedOpcodes[OP_Shielding] = &Client::Handle_OP_Shielding; - ConnectedOpcodes[OP_ShopEnd] = &Client::Handle_OP_ShopEnd; - ConnectedOpcodes[OP_ShopPlayerBuy] = &Client::Handle_OP_ShopPlayerBuy; - ConnectedOpcodes[OP_ShopPlayerSell] = &Client::Handle_OP_ShopPlayerSell; - ConnectedOpcodes[OP_ShopRequest] = &Client::Handle_OP_ShopRequest; - ConnectedOpcodes[OP_Sneak] = &Client::Handle_OP_Sneak; - ConnectedOpcodes[OP_SpawnAppearance] = &Client::Handle_OP_SpawnAppearance; - ConnectedOpcodes[OP_Split] = &Client::Handle_OP_Split; - ConnectedOpcodes[OP_Surname] = &Client::Handle_OP_Surname; - ConnectedOpcodes[OP_SwapSpell] = &Client::Handle_OP_SwapSpell; - ConnectedOpcodes[OP_TargetCommand] = &Client::Handle_OP_TargetCommand; - ConnectedOpcodes[OP_TargetMouse] = &Client::Handle_OP_TargetMouse; - ConnectedOpcodes[OP_TaskHistoryRequest] = &Client::Handle_OP_TaskHistoryRequest; - ConnectedOpcodes[OP_Taunt] = &Client::Handle_OP_Taunt; - ConnectedOpcodes[OP_TestBuff] = &Client::Handle_OP_TestBuff; - ConnectedOpcodes[OP_TGB] = &Client::Handle_OP_TGB; - ConnectedOpcodes[OP_Track] = &Client::Handle_OP_Track; - ConnectedOpcodes[OP_TrackTarget] = &Client::Handle_OP_TrackTarget; - ConnectedOpcodes[OP_TrackUnknown] = &Client::Handle_OP_TrackUnknown; - ConnectedOpcodes[OP_TradeAcceptClick] = &Client::Handle_OP_TradeAcceptClick; - ConnectedOpcodes[OP_TradeBusy] = &Client::Handle_OP_TradeBusy; - ConnectedOpcodes[OP_Trader] = &Client::Handle_OP_Trader; - ConnectedOpcodes[OP_TraderBuy] = &Client::Handle_OP_TraderBuy; - ConnectedOpcodes[OP_TradeRequest] = &Client::Handle_OP_TradeRequest; - ConnectedOpcodes[OP_TradeRequestAck] = &Client::Handle_OP_TradeRequestAck; - ConnectedOpcodes[OP_TraderShop] = &Client::Handle_OP_TraderShop; - ConnectedOpcodes[OP_TradeSkillCombine] = &Client::Handle_OP_TradeSkillCombine; - ConnectedOpcodes[OP_Translocate] = &Client::Handle_OP_Translocate; - ConnectedOpcodes[OP_TributeItem] = &Client::Handle_OP_TributeItem; - ConnectedOpcodes[OP_TributeMoney] = &Client::Handle_OP_TributeMoney; - ConnectedOpcodes[OP_TributeNPC] = &Client::Handle_OP_TributeNPC; - ConnectedOpcodes[OP_TributeToggle] = &Client::Handle_OP_TributeToggle; - ConnectedOpcodes[OP_TributeUpdate] = &Client::Handle_OP_TributeUpdate; - ConnectedOpcodes[OP_VetClaimRequest] = &Client::Handle_OP_VetClaimRequest; - ConnectedOpcodes[OP_VoiceMacroIn] = &Client::Handle_OP_VoiceMacroIn; - ConnectedOpcodes[OP_WearChange] = &Client::Handle_OP_WearChange; - ConnectedOpcodes[OP_WhoAllRequest] = &Client::Handle_OP_WhoAllRequest; - ConnectedOpcodes[OP_WorldUnknown001] = &Client::Handle_OP_Ignore; - ConnectedOpcodes[OP_XTargetAutoAddHaters] = &Client::Handle_OP_XTargetAutoAddHaters; - ConnectedOpcodes[OP_XTargetRequest] = &Client::Handle_OP_XTargetRequest; - ConnectedOpcodes[OP_YellForHelp] = &Client::Handle_OP_YellForHelp; - ConnectedOpcodes[OP_ZoneChange] = &Client::Handle_OP_ZoneChange; -} - -void ClearMappedOpcode(EmuOpcode op) -{ - if(op >= _maxEmuOpcode) - return; - - ConnectedOpcodes[op] = nullptr; - auto iter = ConnectingOpcodes.find(op); - if(iter != ConnectingOpcodes.end()) { - ConnectingOpcodes.erase(iter); - } -} - -// client methods -int Client::HandlePacket(const EQApplicationPacket *app) -{ - if(is_log_enabled(CLIENT__NET_IN_TRACE)) { - char buffer[64]; - app->build_header_dump(buffer); - mlog(CLIENT__NET_IN_TRACE, "Dispatch opcode: %s", buffer); - mpkt(CLIENT__NET_IN_TRACE, app); - } - - EmuOpcode opcode = app->GetOpcode(); - if (opcode == OP_AckPacket) { - return true; - } - - #if EQDEBUG >= 9 - std::cout << "Received 0x" << std::hex << std::setw(4) << std::setfill('0') << opcode << ", size=" << std::dec << app->size << std::endl; - #endif - - #ifdef SOLAR - if(0 && opcode != OP_ClientUpdate) - { - LogFile->write(EQEMuLog::Debug,"HandlePacket() OPCODE debug enabled client %s", GetName()); - std::cerr << "OPCODE: " << std::hex << std::setw(4) << std::setfill('0') << opcode << std::dec << ", size: " << app->size << std::endl; - DumpPacket(app); - } - #endif - - switch(client_state) { - case CLIENT_CONNECTING: { - if(ConnectingOpcodes.count(opcode) != 1) { - //Hate const cast but everything in lua needs to be non-const even if i make it non-mutable - std::vector args; - args.push_back(const_cast(app)); - parse->EventPlayer(EVENT_UNHANDLED_OPCODE, this, "", 1, &args); - -#if EQDEBUG >= 10 - LogFile->write(EQEMuLog::Error, "HandlePacket() Opcode error: Unexpected packet during CLIENT_CONNECTING: opcode:" - " %s (#%d eq=0x%04x), size: %i", OpcodeNames[opcode], opcode, 0, app->size); - DumpPacket(app); -#endif - break; - } - - ClientPacketProc p; - p = ConnectingOpcodes[opcode]; - - //call the processing routine - (this->*p)(app); - - //special case where connecting code needs to boot client... - if(client_state == CLIENT_KICKED) { - return(false); - } - - break; - } - case CLIENT_CONNECTED: { - ClientPacketProc p; - p = ConnectedOpcodes[opcode]; - if(p == nullptr) { - std::vector args; - args.push_back(const_cast(app)); - parse->EventPlayer(EVENT_UNHANDLED_OPCODE, this, "", 0, &args); - -#if (EQDEBUG >= 10) - char buffer[64]; - app->build_header_dump(buffer); - mlog(CLIENT__NET_ERR, "Unhandled incoming opcode: %s", buffer); - - if(app->size < 1000) - DumpPacket(app, app->size); - else{ - std::cout << "Dump limited to 1000 characters:\n"; - DumpPacket(app, 1000); - } -#endif - break; - } - - //call the processing routine - (this->*p)(app); - break; - } - case CLIENT_KICKED: - case DISCONNECTED: - case CLIENT_LINKDEAD: - break; - default: - LogFile->write(EQEMuLog::Debug, "Unknown client_state: %d\n", client_state); - break; - } - - return(true); -} - -// Finish client connecting state -void Client::CompleteConnect() -{ - UpdateWho(); - client_state = CLIENT_CONNECTED; - - hpupdate_timer.Start(); - position_timer.Start(); - autosave_timer.Start(); - SetDuelTarget(0); - SetDueling(false); - - EnteringMessages(this); - LoadZoneFlags(); - - /* Sets GM Flag if needed & Sends Petition Queue */ - UpdateAdmin(false); - - if (IsInAGuild()){ - uint8 rank = GuildRank(); - if (GetClientVersion() >= EQClientRoF) - { - switch (rank) { - case 0: { rank = 5; break; } // GUILD_MEMBER 0 - case 1: { rank = 3; break; } // GUILD_OFFICER 1 - case 2: { rank = 1; break; } // GUILD_LEADER 2 - default: { break; } // GUILD_NONE - } - } - SendAppearancePacket(AT_GuildID, GuildID(), false); - SendAppearancePacket(AT_GuildRank, rank, false); - } - for (uint32 spellInt = 0; spellInt < MAX_PP_REF_SPELLBOOK; spellInt++) { - if (m_pp.spell_book[spellInt] < 3 || m_pp.spell_book[spellInt] > 50000) - m_pp.spell_book[spellInt] = 0xFFFFFFFF; - } - //SendAATable(); - - if (GetHideMe()) Message(13, "[GM] You are currently hidden to all clients"); - - uint32 raidid = database.GetRaidID(GetName()); - Raid *raid = nullptr; - if (raidid > 0){ - raid = entity_list.GetRaidByID(raidid); - if (!raid){ - raid = new Raid(raidid); - if (raid->GetID() != 0){ - entity_list.AddRaid(raid, raidid); - raid->LoadLeadership(); // Recreating raid in new zone, get leadership from DB - } - else - raid = nullptr; - } - if (raid){ - SetRaidGrouped(true); - raid->LearnMembers(); - raid->VerifyRaid(); - raid->GetRaidDetails(); - /* - Only leader should get this; send to all for now till - I figure out correct creation; can probably also send a no longer leader packet for non leaders - but not important for now. - */ - raid->SendRaidCreate(this); - raid->SendMakeLeaderPacketTo(raid->leadername, this); - raid->SendRaidAdd(GetName(), this); - raid->SendBulkRaid(this); - raid->SendGroupUpdate(this); - raid->SendRaidMOTD(this); - if (raid->IsLeader(this)) { // We're a raid leader, lets update just in case! - raid->UpdateRaidAAs(); - raid->SendAllRaidLeadershipAA(); - } - uint32 grpID = raid->GetGroup(GetName()); - if (grpID < 12){ - raid->SendRaidGroupRemove(GetName(), grpID); - raid->SendRaidGroupAdd(GetName(), grpID); - raid->CheckGroupMentor(grpID, this); - if (raid->IsGroupLeader(GetName())) { // group leader same thing! - raid->UpdateGroupAAs(raid->GetGroup(this)); - raid->GroupUpdate(grpID, false); - } - } - raid->SendGroupLeadershipAA(this, grpID); // this may get sent an extra time ... - if (raid->IsLocked()) - raid->SendRaidLockTo(this); - } - } - - //bulk raid send in here eventually - - //reapply some buffs - uint32 buff_count = GetMaxTotalSlots(); - for (uint32 j1 = 0; j1 < buff_count; j1++) { - if (!IsValidSpell(buffs[j1].spellid)) - continue; - - const SPDat_Spell_Struct &spell = spells[buffs[j1].spellid]; - - int NimbusEffect = GetNimbusEffect(buffs[j1].spellid); - if (NimbusEffect) { - if (!IsNimbusEffectActive(NimbusEffect)) - SendSpellEffect(NimbusEffect, 500, 0, 1, 3000, true); - } - - for (int x1 = 0; x1 < EFFECT_COUNT; x1++) { - switch (spell.effectid[x1]) { - case SE_IllusionCopy: - case SE_Illusion: { - if (spell.base[x1] == -1) { - if (gender == 1) - gender = 0; - else if (gender == 0) - gender = 1; - SendIllusionPacket(GetRace(), gender, 0xFF, 0xFF); - } - else if (spell.base[x1] == -2) - { - if (GetRace() == 128 || GetRace() == 130 || GetRace() <= 12) - SendIllusionPacket(GetRace(), GetGender(), spell.max[x1], spell.max[x1]); - } - else if (spell.max[x1] > 0) - { - SendIllusionPacket(spell.base[x1], 0xFF, spell.max[x1], spell.max[x1]); - } - else - { - SendIllusionPacket(spell.base[x1], 0xFF, 0xFF, 0xFF); - } - switch (spell.base[x1]){ - case OGRE: - SendAppearancePacket(AT_Size, 9); - break; - case TROLL: - SendAppearancePacket(AT_Size, 8); - break; - case VAHSHIR: - case BARBARIAN: - SendAppearancePacket(AT_Size, 7); - break; - case HALF_ELF: - case WOOD_ELF: - case DARK_ELF: - case FROGLOK: - SendAppearancePacket(AT_Size, 5); - break; - case DWARF: - SendAppearancePacket(AT_Size, 4); - break; - case HALFLING: - case GNOME: - SendAppearancePacket(AT_Size, 3); - break; - default: - SendAppearancePacket(AT_Size, 6); - break; - } - break; - } - case SE_SummonHorse: { - SummonHorse(buffs[j1].spellid); - //hasmount = true; //this was false, is that the correct thing? - break; - } - case SE_Silence: - { - Silence(true); - break; - } - case SE_Amnesia: - { - Amnesia(true); - break; - } - case SE_DivineAura: - { - invulnerable = true; - break; - } - case SE_Invisibility2: - case SE_Invisibility: - { - invisible = true; - SendAppearancePacket(AT_Invis, 1); - break; - } - case SE_Levitate: - { - if (!zone->CanLevitate()) - { - if (!GetGM()) - { - SendAppearancePacket(AT_Levitate, 0); - BuffFadeByEffect(SE_Levitate); - Message(13, "You can't levitate in this zone."); - } - } - else{ - SendAppearancePacket(AT_Levitate, 2); - } - break; - } - case SE_InvisVsUndead2: - case SE_InvisVsUndead: - { - invisible_undead = true; - break; - } - case SE_InvisVsAnimals: - { - invisible_animals = true; - break; - } - case SE_AddMeleeProc: - case SE_WeaponProc: - { - AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); - break; - } - case SE_DefensiveProc: - { - AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); - break; - } - case SE_RangedProc: - { - AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); - break; - } - } - } - } - - /* Sends appearances for all mobs not doing anim_stand aka sitting, looting, playing dead */ - entity_list.SendZoneAppearance(this); - /* Sends the Nimbus particle effects (up to 3) for any mob using them */ - entity_list.SendNimbusEffects(this); - - entity_list.SendUntargetable(this); - - int x; - for (x = 0; x < 8; x++) - SendWearChange(x); - Mob *pet = GetPet(); - if (pet != nullptr) { - for (x = 0; x < 8; x++) - pet->SendWearChange(x); - } - - entity_list.SendTraders(this); - - zoneinpacket_timer.Start(); - - if (GetPet()){ - GetPet()->SendPetBuffsToClient(); - } - - if (GetGroup()) - database.RefreshGroupFromDB(this); - - if (RuleB(TaskSystem, EnableTaskSystem)) - TaskPeriodic_Timer.Start(); - else - TaskPeriodic_Timer.Disable(); - - conn_state = ClientConnectFinished; - - //enforce some rules.. - if (!CanBeInZone()) { - _log(CLIENT__ERROR, "Kicking char from zone, not allowed here"); - GoToSafeCoords(database.GetZoneID("arena"), 0); - return; - } - - if (zone) - zone->weatherSend(); - - TotalKarma = database.GetKarma(AccountID()); - SendDisciplineTimers(); - - parse->EventPlayer(EVENT_ENTER_ZONE, this, "", 0); - - /* This sub event is for if a player logs in for the first time since entering world. */ - if (firstlogon == 1){ - parse->EventPlayer(EVENT_CONNECT, this, "", 0); - /* QS: PlayerLogConnectDisconnect */ - if (RuleB(QueryServ, PlayerLogConnectDisconnect)){ - std::string event_desc = StringFormat("Connect :: Logged into zoneid:%i instid:%i", this->GetZoneID(), this->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Connect_State, this->CharacterID(), event_desc); - } - } - - if (zone) { - if (zone->GetInstanceTimer()) { - uint32 ttime = zone->GetInstanceTimer()->GetRemainingTime(); - uint32 day = (ttime / 86400000); - uint32 hour = (ttime / 3600000) % 24; - uint32 minute = (ttime / 60000) % 60; - uint32 second = (ttime / 1000) % 60; - if (day) { - Message(15, "%s(%u) will expire in %u days, %u hours, %u minutes, and %u seconds.", - zone->GetLongName(), zone->GetInstanceID(), day, hour, minute, second); - } - else if (hour) { - Message(15, "%s(%u) will expire in %u hours, %u minutes, and %u seconds.", - zone->GetLongName(), zone->GetInstanceID(), hour, minute, second); - } - else if (minute) { - Message(15, "%s(%u) will expire in %u minutes, and %u seconds.", - zone->GetLongName(), zone->GetInstanceID(), minute, second); - } - else { - Message(15, "%s(%u) will expire in in %u seconds.", - zone->GetLongName(), zone->GetInstanceID(), second); - } - } - } - - SendRewards(); - SendAltCurrencies(); - database.LoadAltCurrencyValues(CharacterID(), alternate_currency); - SendAlternateCurrencyValues(); - alternate_currency_loaded = true; - ProcessAlternateCurrencyQueue(); - - /* This needs to be set, this determines whether or not data was loaded properly before a save */ - client_data_loaded = true; - - CalcItemScale(); - DoItemEnterZone(); - - if (zone->GetZoneID() == RuleI(World, GuildBankZoneID) && GuildBanks) - GuildBanks->SendGuildBank(this); - - if (GetClientVersion() >= EQClientSoD) - entity_list.SendFindableNPCList(this); - - if (IsInAGuild()) { - SendGuildRanks(); - guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), zone->GetZoneID(), time(nullptr)); - guild_mgr.RequestOnlineGuildMembers(this->CharacterID(), this->GuildID()); - } - - /** Request adventure info **/ - ServerPacket *pack = new ServerPacket(ServerOP_AdventureDataRequest, 64); - strcpy((char*)pack->pBuffer, GetName()); - worldserver.SendPacket(pack); - delete pack; - - if (IsClient() && CastToClient()->GetClientVersionBit() & BIT_UnderfootAndLater) { - EQApplicationPacket *outapp = MakeBuffsPacket(false); - CastToClient()->FastQueuePacket(&outapp); - } - - entity_list.RefreshClientXTargets(this); - - worldserver.RequestTellQueue(GetName()); -} - -void Client::CheatDetected(CheatTypes CheatType, float x, float y, float z) -{ - //ToDo: Break warp down for special zones. Some zones have special teleportation pads or bad .map files which can trigger the detector without a legit zone request. - - switch (CheatType) - { - case MQWarp: //Some zones may still have issues. Database updates will eliminate most if not all problems. - if (RuleB(Zone, EnableMQWarpDetector) - && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) - || (RuleI(Zone, MQWarpExemptStatus)) == -1))) - { - Message(13, "Large warp detected."); - char hString[250]; - sprintf(hString, "/MQWarp with location %.2f, %.2f, %.2f", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - } - break; - case MQWarpShadowStep: - if (RuleB(Zone, EnableMQWarpDetector) - && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) - || (RuleI(Zone, MQWarpExemptStatus)) == -1))) - { - char *hString = nullptr; - MakeAnyLenString(&hString, "/MQWarp(SS) with location %.2f, %.2f, %.2f, the target was shadow step exempt but we still found this suspicious.", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - safe_delete_array(hString); - } - break; - case MQWarpKnockBack: - if (RuleB(Zone, EnableMQWarpDetector) - && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) - || (RuleI(Zone, MQWarpExemptStatus)) == -1))) - { - char *hString = nullptr; - MakeAnyLenString(&hString, "/MQWarp(KB) with location %.2f, %.2f, %.2f, the target was Knock Back exempt but we still found this suspicious.", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - safe_delete_array(hString); - } - break; - - case MQWarpLight: - if (RuleB(Zone, EnableMQWarpDetector) - && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) - || (RuleI(Zone, MQWarpExemptStatus)) == -1))) - { - if (RuleB(Zone, MarkMQWarpLT)) - { - char *hString = nullptr; - MakeAnyLenString(&hString, "/MQWarp(LT) with location %.2f, %.2f, %.2f, running fast but not fast enough to get killed, possibly: small warp, speed hack, excessive lag, marked as suspicious.", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - safe_delete_array(hString); - } - } - break; - - case MQZone: - if (RuleB(Zone, EnableMQZoneDetector) && ((this->Admin() < RuleI(Zone, MQZoneExemptStatus) || (RuleI(Zone, MQZoneExemptStatus)) == -1))) - { - char hString[250]; - sprintf(hString, "/MQZone used at %.2f, %.2f, %.2f to %.2f %.2f %.2f", GetX(), GetY(), GetZ(), x, y, z); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - } - break; - case MQZoneUnknownDest: - if (RuleB(Zone, EnableMQZoneDetector) && ((this->Admin() < RuleI(Zone, MQZoneExemptStatus) || (RuleI(Zone, MQZoneExemptStatus)) == -1))) - { - char hString[250]; - sprintf(hString, "/MQZone used at %.2f, %.2f, %.2f", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - } - break; - case MQGate: - if (RuleB(Zone, EnableMQGateDetector) && ((this->Admin() < RuleI(Zone, MQGateExemptStatus) || (RuleI(Zone, MQGateExemptStatus)) == -1))) { - Message(13, "Illegal gate request."); - char hString[250]; - sprintf(hString, "/MQGate used at %.2f, %.2f, %.2f", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - if (zone) - { - this->SetZone(this->GetZoneID(), zone->GetInstanceID()); //Prevent the player from zoning, place him back in the zone where he tried to originally /gate. - } - else - { - this->SetZone(this->GetZoneID(), 0); //Prevent the player from zoning, place him back in the zone where he tried to originally /gate. - - } - } - break; - case MQGhost: //Not currently implemented, but the framework is in place - just needs detection scenarios identified - if (RuleB(Zone, EnableMQGhostDetector) && ((this->Admin() < RuleI(Zone, MQGhostExemptStatus) || (RuleI(Zone, MQGhostExemptStatus)) == -1))) { - database.SetMQDetectionFlag(this->account_name, this->name, "/MQGhost", zone->GetShortName()); - } - break; - default: - char *hString = nullptr; - MakeAnyLenString(&hString, "Unhandled HackerDetection flag with location %.2f, %.2f, %.2f.", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - safe_delete_array(hString); - break; - } -} - -// connecting opcode handlers -/* -void Client::Handle_Connect_0x3e33(const EQApplicationPacket *app) -{ -//OP_0x0380 = 0x642c -EQApplicationPacket* outapp = new EQApplicationPacket(OP_0x0380, sizeof(uint32)); // Dunno -QueuePacket(outapp); -safe_delete(outapp); -return; -} -*/ - -void Client::Handle_Connect_OP_ApproveZone(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ApproveZone_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_ApproveZone: Expected %i, Got %i", - sizeof(ApproveZone_Struct), app->size); - return; - } - ApproveZone_Struct* azone = (ApproveZone_Struct*)app->pBuffer; - azone->approve = 1; - QueuePacket(app); - return; -} - -void Client::Handle_Connect_OP_ClientError(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ClientError_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_ClientError: Expected %i, Got %i", - sizeof(ClientError_Struct), app->size); - return; - } - // Client reporting error to server - ClientError_Struct* error = (ClientError_Struct*)app->pBuffer; - LogFile->write(EQEMuLog::Error, "Client error: %s", error->character_name); - LogFile->write(EQEMuLog::Error, "Error message: %s", error->message); - Message(13, error->message); -#if (EQDEBUG>=5) - DumpPacket(app); -#endif - return; -} - -void Client::Handle_Connect_OP_ClientReady(const EQApplicationPacket *app) -{ - conn_state = ClientReadyReceived; - - CompleteConnect(); - SendHPUpdate(); -} - -void Client::Handle_Connect_OP_ClientUpdate(const EQApplicationPacket *app) -{ - //Once we get this, the client thinks it is connected - //So give it the benefit of the doubt and move to connected - - Handle_Connect_OP_ClientReady(app); -} - -void Client::Handle_Connect_OP_ReqClientSpawn(const EQApplicationPacket *app) -{ - conn_state = ClientSpawnRequested; - - EQApplicationPacket* outapp = new EQApplicationPacket; - - // Send Zone Doors - if (entity_list.MakeDoorSpawnPacket(outapp, this)) - { - QueuePacket(outapp); - } - safe_delete(outapp); - - // Send Zone Objects - entity_list.SendZoneObjects(this); - SendZonePoints(); - // Live does this - outapp = new EQApplicationPacket(OP_SendAAStats, 0); - FastQueuePacket(&outapp); - - // Tell client they can continue we're done - outapp = new EQApplicationPacket(OP_ZoneServerReady, 0); - FastQueuePacket(&outapp); - outapp = new EQApplicationPacket(OP_SendExpZonein, 0); - FastQueuePacket(&outapp); - - if (GetClientVersion() >= EQClientRoF) - { - outapp = new EQApplicationPacket(OP_ClientReady, 0); - FastQueuePacket(&outapp); - } - - // New for Secrets of Faydwer - Used in Place of OP_SendExpZonein - outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0); - QueuePacket(outapp); - safe_delete(outapp); - - if (strncasecmp(zone->GetShortName(), "bazaar", 6) == 0) - SendBazaarWelcome(); - - conn_state = ZoneContentsSent; - - return; -} - -void Client::Handle_Connect_OP_ReqNewZone(const EQApplicationPacket *app) -{ - conn_state = NewZoneRequested; - - EQApplicationPacket* outapp; - - ///////////////////////////////////// - // New Zone Packet - outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); - NewZone_Struct* nz = (NewZone_Struct*)outapp->pBuffer; - memcpy(outapp->pBuffer, &zone->newzone_data, sizeof(NewZone_Struct)); - strcpy(nz->char_name, m_pp.name); - - FastQueuePacket(&outapp); - - return; -} - -void Client::Handle_Connect_OP_SendAAStats(const EQApplicationPacket *app) -{ - SendAATimers(); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendAAStats, 0); - QueuePacket(outapp); - safe_delete(outapp); - return; -} - -void Client::Handle_Connect_OP_SendAATable(const EQApplicationPacket *app) -{ - SendAAList(); - return; -} - -void Client::Handle_Connect_OP_SendExpZonein(const EQApplicationPacket *app) -{ - ////////////////////////////////////////////////////// - // Spawn Appearance Packet - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer; - sa->type = AT_SpawnID; // Is 0x10 used to set the player id? - sa->parameter = GetID(); // Four bytes for this parameter... - outapp->priority = 6; - QueuePacket(outapp); - safe_delete(outapp); - - // Inform the world about the client - outapp = new EQApplicationPacket(); - - CreateSpawnPacket(outapp); - outapp->priority = 6; - if (!GetHideMe()) entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - if (GetPVP()) //force a PVP update until we fix the spawn struct - SendAppearancePacket(AT_PVP, GetPVP(), true, false); - - //Send AA Exp packet: - if (GetLevel() >= 51) - SendAAStats(); - - // Send exp packets - outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); - ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; - uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1); - uint32 tmpxp2 = GetEXPForLevel(GetLevel()); - - // Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) - if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { - float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2); - eu->exp = (uint32)(330.0f * tmpxp); - outapp->priority = 6; - QueuePacket(outapp); - } - safe_delete(outapp); - - SendAATimers(); - - outapp = new EQApplicationPacket(OP_SendExpZonein, 0); - QueuePacket(outapp); - safe_delete(outapp); - - outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct)); - ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer; - strcpy(zonesendname->name, m_pp.name); - strcpy(zonesendname->name2, m_pp.name); - zonesendname->unknown0 = 0x0A; - QueuePacket(outapp); - safe_delete(outapp); - - /* this is actually the guild MOTD - outapp = new EQApplicationPacket(OP_ZoneInSendName2, sizeof(ZoneInSendName_Struct2)); - ZoneInSendName_Struct2* zonesendname2=(ZoneInSendName_Struct2*)outapp->pBuffer; - strcpy(zonesendname2->name,m_pp.name); - QueuePacket(outapp); - safe_delete(outapp);*/ - - if (IsInAGuild()) { - SendGuildMembers(); - SendGuildURL(); - SendGuildChannel(); - SendGuildLFGuildStatus(); - } - SendLFGuildStatus(); - - //No idea why live sends this if even were not in a guild - SendGuildMOTD(); - SpawnMercOnZone(); - - return; -} - -void Client::Handle_Connect_OP_SendGuildTributes(const EQApplicationPacket *app) -{ - SendGuildTributes(); - return; -} - -void Client::Handle_Connect_OP_SendTributes(const EQApplicationPacket *app) -{ - SendTributes(); - return; -} - -void Client::Handle_Connect_OP_SetServerFilter(const EQApplicationPacket *app) -{ - if (app->size != sizeof(SetServerFilter_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized OP_SetServerFilter"); - DumpPacket(app); - return; - } - SetServerFilter_Struct* filter = (SetServerFilter_Struct*)app->pBuffer; - ServerFilter(filter); - return; -} - -void Client::Handle_Connect_OP_SpawnAppearance(const EQApplicationPacket *app) -{ - return; -} - -void Client::Handle_Connect_OP_TGB(const EQApplicationPacket *app) -{ - if (app->size != sizeof(uint32)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_TGB: Expected %i, Got %i", - sizeof(uint32), app->size); - return; - } - OPTGB(app); - return; -} - -void Client::Handle_Connect_OP_UpdateAA(const EQApplicationPacket *app) -{ - SendAATable(); -} - -void Client::Handle_Connect_OP_WearChange(const EQApplicationPacket *app) -{ - //not sure what these are supposed to mean to us. - return; -} - -void Client::Handle_Connect_OP_WorldObjectsSent(const EQApplicationPacket *app) -{ - //This is a copy of SendExpZonein created for SoF due to packet order change - //This does not affect clients other than SoF - - ////////////////////////////////////////////////////// - // Spawn Appearance Packet - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer; - sa->type = AT_SpawnID; // Is 0x10 used to set the player id? - sa->parameter = GetID(); // Four bytes for this parameter... - outapp->priority = 6; - QueuePacket(outapp); - safe_delete(outapp); - - // Inform the world about the client - outapp = new EQApplicationPacket(); - - CreateSpawnPacket(outapp); - outapp->priority = 6; - if (!GetHideMe()) entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - if (GetPVP()) //force a PVP update until we fix the spawn struct - SendAppearancePacket(AT_PVP, GetPVP(), true, false); - - //Send AA Exp packet: - if (GetLevel() >= 51) - SendAAStats(); - - // Send exp packets - outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); - ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; - uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1); - uint32 tmpxp2 = GetEXPForLevel(GetLevel()); - - // Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) - if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { - float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2); - eu->exp = (uint32)(330.0f * tmpxp); - outapp->priority = 6; - QueuePacket(outapp); - } - safe_delete(outapp); - - SendAATimers(); - - // New for Secrets of Faydwer - Used in Place of OP_SendExpZonein - outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0); - QueuePacket(outapp); - safe_delete(outapp); - - outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct)); - ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer; - strcpy(zonesendname->name, m_pp.name); - strcpy(zonesendname->name2, m_pp.name); - zonesendname->unknown0 = 0x0A; - QueuePacket(outapp); - safe_delete(outapp); - - if (IsInAGuild()) { - SendGuildMembers(); - SendGuildURL(); - SendGuildChannel(); - SendGuildLFGuildStatus(); - } - SendLFGuildStatus(); - - //No idea why live sends this if even were not in a guild - SendGuildMOTD(); - - SpawnMercOnZone(); - - return; -} - -void Client::Handle_Connect_OP_ZoneComplete(const EQApplicationPacket *app) -{ - EQApplicationPacket* outapp = new EQApplicationPacket(OP_0x0347, 0); - QueuePacket(outapp); - safe_delete(outapp); - return; -} - -void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) -{ - if(app->size != sizeof(ClientZoneEntry_Struct)) - return; - ClientZoneEntry_Struct *cze = (ClientZoneEntry_Struct *) app->pBuffer; - - if(strlen(cze->char_name) > 63) - return; - - conn_state = ReceivedZoneEntry; - - ClientVersion = Connection()->ClientVersion(); - if (ClientVersion != EQClientUnknown) - ClientVersionBit = 1 << (ClientVersion - 1); - else - ClientVersionBit = 0; - - bool siv = m_inv.SetInventoryVersion(ClientVersion); - LogFile->write(EQEMuLog::Debug, "%s inventory version to %s(%i)", (siv ? "Succeeded in setting" : "Failed to set"), EQClientVersionName(ClientVersion), ClientVersion); - - /* Antighost code - tmp var is so the search doesnt find this object - */ - Client* client = entity_list.GetClientByName(cze->char_name); - if (!zone->GetAuth(ip, cze->char_name, &WID, &account_id, &character_id, &admin, lskey, &tellsoff)) { - LogFile->write(EQEMuLog::Error, "GetAuth() returned false kicking client"); - if (client != 0) { - client->Save(); - client->Kick(); - } - //ret = false; // TODO: Can we tell the client to get lost in a good way - client_state = CLIENT_KICKED; - return; - } - - strcpy(name, cze->char_name); - /* Check for Client Spoofing */ - if (client != 0) { - struct in_addr ghost_addr; - ghost_addr.s_addr = eqs->GetRemoteIP(); - - LogFile->write(EQEMuLog::Error,"Ghosting client: Account ID:%i Name:%s Character:%s IP:%s", - client->AccountID(), client->AccountName(), client->GetName(), inet_ntoa(ghost_addr)); - client->Save(); - client->Disconnect(); - } - - uint32 pplen = 0; - EQApplicationPacket* outapp = 0; - MYSQL_RES* result = 0; - bool loaditems = 0; - uint32 i; - std::string query; - unsigned long* lengths; - - uint32 cid = CharacterID(); - character_id = cid; /* Global character_id reference */ - - /* Flush and reload factions */ - database.RemoveTempFactions(this); - database.LoadCharacterFactionValues(cid, factionvalues); - - /* Load Character Account Data: Temp until I move */ - query = StringFormat("SELECT `status`, `name`, `lsaccount_id`, `gmspeed`, `revoked`, `hideme` FROM `account` WHERE `id` = %u", this->AccountID()); - auto results = database.QueryDatabase(query); - for (auto row = results.begin(); row != results.end(); ++row) { - admin = atoi(row[0]); - strncpy(account_name, row[1], 30); - lsaccountid = atoi(row[2]); - gmspeed = atoi(row[3]); - revoked = atoi(row[4]); - gmhideme = atoi(row[5]); - if (account_creation){ account_creation = atoul(row[6]); } - } - - /* Load Character Data */ - query = StringFormat("SELECT `lfp`, `lfg`, `xtargets`, `firstlogon`, `guild_id`, `rank` FROM `character_data` LEFT JOIN `guild_members` ON `id` = `char_id` WHERE `id` = %i", cid); - results = database.QueryDatabase(query); - for (auto row = results.begin(); row != results.end(); ++row) { - if (row[4] && atoi(row[4]) > 0){ - guild_id = atoi(row[4]); - if (row[5] != nullptr){ guildrank = atoi(row[5]); } - else{ guildrank = GUILD_RANK_NONE; } - } - - if (LFP){ LFP = atoi(row[0]); } - if (LFG){ LFG = atoi(row[1]); } - if (firstlogon){ firstlogon = atoi(row[3]); } - } - - if (RuleB(Character, SharedBankPlat)) - m_pp.platinum_shared = database.GetSharedPlatinum(this->AccountID()); - - loaditems = database.GetInventory(cid, &m_inv); /* Load Character Inventory */ - database.LoadCharacterBandolier(cid, &m_pp); /* Load Character Bandolier */ - database.LoadCharacterBindPoint(cid, &m_pp); /* Load Character Bind */ - database.LoadCharacterMaterialColor(cid, &m_pp); /* Load Character Material */ - database.LoadCharacterPotions(cid, &m_pp); /* Load Character Potion Belt */ - database.LoadCharacterCurrency(cid, &m_pp); /* Load Character Currency into PP */ - database.LoadCharacterData(cid, &m_pp, &m_epp); /* Load Character Data from DB into PP as well as E_PP */ - database.LoadCharacterSkills(cid, &m_pp); /* Load Character Skills */ - database.LoadCharacterInspectMessage(cid, &m_inspect_message); /* Load Character Inspect Message */ - database.LoadCharacterSpellBook(cid, &m_pp); /* Load Character Spell Book */ - database.LoadCharacterMemmedSpells(cid, &m_pp); /* Load Character Memorized Spells */ - database.LoadCharacterDisciplines(cid, &m_pp); /* Load Character Disciplines */ - database.LoadCharacterLanguages(cid, &m_pp); /* Load Character Languages */ - database.LoadCharacterLeadershipAA(cid, &m_pp); /* Load Character Leadership AA's */ - database.LoadCharacterTribute(cid, &m_pp); /* Load CharacterTribute */ - - /* Load AdventureStats */ - AdventureStats_Struct as; - if(database.GetAdventureStats(cid, &as)) - { - m_pp.ldon_wins_guk = as.success.guk; - m_pp.ldon_wins_mir = as.success.mir; - m_pp.ldon_wins_mmc = as.success.mmc; - m_pp.ldon_wins_ruj = as.success.ruj; - m_pp.ldon_wins_tak = as.success.tak; - m_pp.ldon_losses_guk = as.failure.guk; - m_pp.ldon_losses_mir = as.failure.mir; - m_pp.ldon_losses_mmc = as.failure.mmc; - m_pp.ldon_losses_ruj = as.failure.ruj; - m_pp.ldon_losses_tak = as.failure.tak; - } - - /* Set item material tint */ - for (int i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_END; i++) - if (m_pp.item_tint[i].rgb.use_tint == 1 || m_pp.item_tint[i].rgb.use_tint == 255) - m_pp.item_tint[i].rgb.use_tint = 0xFF; - - if (level){ level = m_pp.level; } - - /* If GM, not trackable */ - if (gmhideme) { trackable = false; } - /* Set Con State for Reporting */ - conn_state = PlayerProfileLoaded; - - m_pp.zone_id = zone->GetZoneID(); - m_pp.zoneInstance = zone->GetInstanceID(); - - /* Set Total Seconds Played */ - TotalSecondsPlayed = m_pp.timePlayedMin * 60; - /* Set Max AA XP */ - max_AAXP = RuleI(AA, ExpPerPoint); - /* If we can maintain intoxication across zones, check for it */ - if (!RuleB(Character, MaintainIntoxicationAcrossZones)) - m_pp.intoxication = 0; - - strcpy(name, m_pp.name); - strcpy(lastname, m_pp.last_name); - /* If PP is set to weird coordinates */ - if ((m_pp.x == -1 && m_pp.y == -1 && m_pp.z == -1) || (m_pp.x == -2 && m_pp.y == -2 && m_pp.z == -2)) { - auto safePoint = zone->GetSafePoint(); - m_pp.x = safePoint.m_X; - m_pp.y = safePoint.m_Y; - m_pp.z = safePoint.m_Z; - } - /* If too far below ground, then fix */ - // float ground_z = GetGroundZ(m_pp.x, m_pp.y, m_pp.z); - // if (m_pp.z < (ground_z - 500)) - // m_pp.z = ground_z; - - /* Set Mob variables for spawn */ - class_ = m_pp.class_; - level = m_pp.level; - m_Position.m_X = m_pp.x; - m_Position.m_Y = m_pp.y; - m_Position.m_Z = m_pp.z; - m_Position.m_Heading = m_pp.heading; - race = m_pp.race; - base_race = m_pp.race; - gender = m_pp.gender; - base_gender = m_pp.gender; - deity = m_pp.deity; - haircolor = m_pp.haircolor; - beardcolor = m_pp.beardcolor; - eyecolor1 = m_pp.eyecolor1; - eyecolor2 = m_pp.eyecolor2; - hairstyle = m_pp.hairstyle; - luclinface = m_pp.face; - beard = m_pp.beard; - drakkin_heritage = m_pp.drakkin_heritage; - drakkin_tattoo = m_pp.drakkin_tattoo; - drakkin_details = m_pp.drakkin_details; - - /* If GM not set in DB, and does not meet min status to be GM, reset */ - if (m_pp.gm && admin < minStatusToBeGM) - m_pp.gm = 0; - - /* Load Guild */ - if (!IsInAGuild()) { m_pp.guild_id = GUILD_NONE; } - else { - m_pp.guild_id = GuildID(); - if (zone->GetZoneID() == RuleI(World, GuildBankZoneID)) - GuildBanker = (guild_mgr.IsGuildLeader(GuildID(), CharacterID()) || guild_mgr.GetBankerFlag(CharacterID())); - } - m_pp.guildbanker = GuildBanker; - - switch (race) - { - case OGRE: - size = 9; break; - case TROLL: - size = 8; break; - case VAHSHIR: case BARBARIAN: - size = 7; break; - case HUMAN: case HIGH_ELF: case ERUDITE: case IKSAR: case DRAKKIN: - size = 6; break; - case HALF_ELF: - size = 5.5; break; - case WOOD_ELF: case DARK_ELF: case FROGLOK: - size = 5; break; - case DWARF: - size = 4; break; - case HALFLING: - size = 3.5; break; - case GNOME: - size = 3; break; - default: - size = 0; - } - - /* Check for Invalid points */ - if (m_pp.ldon_points_guk < 0 || m_pp.ldon_points_guk > 2000000000){ m_pp.ldon_points_guk = 0; } - if (m_pp.ldon_points_mir < 0 || m_pp.ldon_points_mir > 2000000000){ m_pp.ldon_points_mir = 0; } - if (m_pp.ldon_points_mmc < 0 || m_pp.ldon_points_mmc > 2000000000){ m_pp.ldon_points_mmc = 0; } - if (m_pp.ldon_points_ruj < 0 || m_pp.ldon_points_ruj > 2000000000){ m_pp.ldon_points_ruj = 0; } - if (m_pp.ldon_points_tak < 0 || m_pp.ldon_points_tak > 2000000000){ m_pp.ldon_points_tak = 0; } - if (m_pp.ldon_points_available < 0 || m_pp.ldon_points_available > 2000000000){ m_pp.ldon_points_available = 0; } - - /* Set Swimming Skill 100 by default if under 100 */ - if (GetSkill(SkillSwimming) < 100) - SetSkill(SkillSwimming, 100); - - /* Initialize AA's : Move to function eventually */ - 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 " - "FROM " - "`character_alternate_abilities` " - "WHERE `id` = %u ORDER BY `slot`", this->CharacterID()); - results = database.QueryDatabase(query); i = 0; - for (auto row = results.begin(); row != results.end(); ++row) { - 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]); - } - for (uint32 a = 0; a < MAX_PP_AA_ARRAY; a++){ - uint32 id = aa[a]->AA; - //watch for invalid AA IDs - if (id == aaNone) - continue; - if (id >= aaHighestID) { - aa[a]->AA = aaNone; - aa[a]->value = 0; - continue; - } - if (aa[a]->value == 0) { - aa[a]->AA = aaNone; - continue; - } - if (aa[a]->value > HIGHEST_AA_VALUE) { - aa[a]->AA = aaNone; - aa[a]->value = 0; - continue; - } - - if (aa[a]->value > 1) /* hack in some stuff for sony's new AA method (where each level of each aa.has a seperate ID) */ - aa_points[(id - aa[a]->value + 1)] = aa[a]->value; - else - aa_points[id] = aa[a]->value; - } - - if (SPDAT_RECORDS > 0) { - for (uint32 z = 0; z= (uint32)SPDAT_RECORDS) - UnmemSpell(z, false); - } - - database.LoadBuffs(this); - uint32 max_slots = GetMaxBuffSlots(); - 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].slotid = 2; - m_pp.buffs[i].player_id = 0x2211; - m_pp.buffs[i].level = buffs[i].casterlevel; - m_pp.buffs[i].effect = 0; - m_pp.buffs[i].duration = buffs[i].ticsremaining; - m_pp.buffs[i].counters = buffs[i].counters; - } - else { - m_pp.buffs[i].spellid = SPELLBOOK_UNKNOWN; - m_pp.buffs[i].bard_modifier = 10; - m_pp.buffs[i].slotid = 0; - m_pp.buffs[i].player_id = 0; - m_pp.buffs[i].level = 0; - m_pp.buffs[i].effect = 0; - m_pp.buffs[i].duration = 0; - m_pp.buffs[i].counters = 0; - } - } - } - - /* Load Character Key Ring */ - KeyRingLoad(); - - /* Send Group Members via PP */ - uint32 groupid = database.GetGroupID(GetName()); - Group* group = nullptr; - if (groupid > 0){ - group = entity_list.GetGroupByID(groupid); - if (!group) { //nobody from our is here... start a new group - group = new Group(groupid); - if (group->GetID() != 0) - entity_list.AddGroup(group, groupid); - else //error loading group members... - { - delete group; - group = nullptr; - } - } //else, somebody from our group is already here... - - if (!group) - database.SetGroupID(GetName(), 0, CharacterID(), false); //cannot re-establish group, kill it - - } - else { //no group id - //clear out the group junk in our PP - uint32 xy = 0; - for (xy = 0; xy < MAX_GROUP_MEMBERS; xy++) - memset(m_pp.groupMembers[xy], 0, 64); - } - - if (group){ - // If the group leader is not set, pull the group leader infomrmation from the database. - if (!group->GetLeader()){ - char ln[64]; - char MainTankName[64]; - char AssistName[64]; - char PullerName[64]; - char NPCMarkerName[64]; - char mentoree_name[64]; - int mentor_percent; - GroupLeadershipAA_Struct GLAA; - memset(ln, 0, 64); - strcpy(ln, database.GetGroupLeadershipInfo(group->GetID(), ln, MainTankName, AssistName, PullerName, NPCMarkerName, mentoree_name, &mentor_percent, &GLAA)); - Client *c = entity_list.GetClientByName(ln); - if (c) - group->SetLeader(c); - - group->SetMainTank(MainTankName); - group->SetMainAssist(AssistName); - group->SetPuller(PullerName); - group->SetNPCMarker(NPCMarkerName); - group->SetGroupAAs(&GLAA); - group->SetGroupMentor(mentor_percent, mentoree_name); - - //group->NotifyMainTank(this, 1); - //group->NotifyMainAssist(this, 1); - //group->NotifyPuller(this, 1); - - // If we are the leader, force an update of our group AAs to other members in the zone, in case - // we purchased a new one while out-of-zone. - if (group->IsLeader(this)) - group->SendLeadershipAAUpdate(); - - } - group->UpdatePlayer(this); - LFG = false; - } - -#ifdef BOTS - Bot::LoadAndSpawnAllZonedBots(this); -#endif - - CalcBonuses(); - if (RuleB(Zone, EnableLoggedOffReplenishments) && - time(nullptr) - m_pp.lastlogin >= RuleI(Zone, MinOfflineTimeToReplenishments)) { - m_pp.cur_hp = GetMaxHP(); - m_pp.mana = GetMaxMana(); - m_pp.endurance = GetMaxEndurance(); - } - - if (m_pp.cur_hp <= 0) - m_pp.cur_hp = GetMaxHP(); - - SetHP(m_pp.cur_hp); - Mob::SetMana(m_pp.mana); // mob function doesn't send the packet - SetEndurance(m_pp.endurance); - - /* Update LFP in case any (or all) of our group disbanded while we were zoning. */ - if (IsLFP()) { UpdateLFP(); } - - /* Get Expansions from variables table and ship via PP */ - char val[20] = { 0 }; - if (database.GetVariable("Expansions", val, 20)){ m_pp.expansions = atoi(val); } - else{ m_pp.expansions = 0x3FF; } - - p_timers.SetCharID(CharacterID()); - if (!p_timers.Load(&database)) { - LogFile->write(EQEMuLog::Error, "Unable to load ability timers from the database for %s (%i)!", GetCleanName(), CharacterID()); - } - - /* Load Spell Slot Refresh from Currently Memoried Spells */ - for (unsigned int i = 0; i < MAX_PP_MEMSPELL; ++i) - if (IsValidSpell(m_pp.mem_spells[i])) - m_pp.spellSlotRefresh[i] = p_timers.GetRemainingTime(pTimerSpellStart + m_pp.mem_spells[i]) * 1000; - - /* Ability slot refresh send SK/PAL */ - if (m_pp.class_ == SHADOWKNIGHT || m_pp.class_ == PALADIN) { - uint32 abilitynum = 0; - if (m_pp.class_ == SHADOWKNIGHT){ abilitynum = pTimerHarmTouch; } - else{ abilitynum = pTimerLayHands; } - - uint32 remaining = p_timers.GetRemainingTime(abilitynum); - if (remaining > 0 && remaining < 15300) - m_pp.abilitySlotRefresh = remaining * 1000; - else - m_pp.abilitySlotRefresh = 0; - } - -#ifdef _EQDEBUG - printf("Dumping inventory on load:\n"); - m_inv.dumpEntireInventory(); -#endif - - /* Reset to max so they dont drown on zone in if its underwater */ - m_pp.air_remaining = 60; - /* Check for PVP Zone status*/ - if (zone->IsPVPZone()) - m_pp.pvp = 1; - /* Time entitled on Account: Move to account */ - m_pp.timeentitledonaccount = database.GetTotalTimeEntitledOnAccount(AccountID()) / 1440; - /* Reset rest timer if the durations have been lowered in the database */ - if ((m_pp.RestTimer > RuleI(Character, RestRegenTimeToActivate)) && (m_pp.RestTimer > RuleI(Character, RestRegenRaidTimeToActivate))) - m_pp.RestTimer = 0; - - /* This checksum should disappear once dynamic structs are in... each struct strategy will do it */ - CRC32::SetEQChecksum((unsigned char*)&m_pp, sizeof(PlayerProfile_Struct)-4); - - outapp = new EQApplicationPacket(OP_PlayerProfile, sizeof(PlayerProfile_Struct)); - - /* The entityid field in the Player Profile is used by the Client in relation to Group Leadership AA */ - m_pp.entityid = GetID(); - memcpy(outapp->pBuffer, &m_pp, outapp->size); - outapp->priority = 6; - FastQueuePacket(&outapp); - - if (m_pp.RestTimer) - rest_timer.Start(m_pp.RestTimer * 1000); - - database.LoadPetInfo(this); - /* - This was moved before the spawn packets are sent - in hopes that it adds more consistency... - Remake pet - */ - if (m_petinfo.SpellID > 1 && !GetPet() && m_petinfo.SpellID <= SPDAT_RECORDS) { - MakePoweredPet(m_petinfo.SpellID, spells[m_petinfo.SpellID].teleport_zone, m_petinfo.petpower, m_petinfo.Name, m_petinfo.size); - if (GetPet() && GetPet()->IsNPC()) { - NPC *pet = GetPet()->CastToNPC(); - pet->SetPetState(m_petinfo.Buffs, m_petinfo.Items); - pet->CalcBonuses(); - pet->SetHP(m_petinfo.HP); - pet->SetMana(m_petinfo.Mana); - } - m_petinfo.SpellID = 0; - } - /* Moved here so it's after where we load the pet data. */ - if (!GetAA(aaPersistentMinion)) - memset(&m_suspendedminion, 0, sizeof(PetInfo)); - - /* Server Zone Entry Packet */ - outapp = new EQApplicationPacket(OP_ZoneEntry, sizeof(ServerZoneEntry_Struct)); - ServerZoneEntry_Struct* sze = (ServerZoneEntry_Struct*)outapp->pBuffer; - - FillSpawnStruct(&sze->player, CastToMob()); - sze->player.spawn.curHp = 1; - sze->player.spawn.NPC = 0; - sze->player.spawn.z += 6; //arbitrary lift, seems to help spawning under zone. - outapp->priority = 6; - FastQueuePacket(&outapp); - - /* Zone Spawns Packet */ - entity_list.SendZoneSpawnsBulk(this); - entity_list.SendZoneCorpsesBulk(this); - entity_list.SendZonePVPUpdates(this); //hack until spawn struct is fixed. - - /* 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); - outapp->priority = 6; - FastQueuePacket(&outapp); - - /* Tribute Packets */ - DoTributeUpdate(); - if (m_pp.tribute_active) { - //restart the tribute timer where we left off - tribute_timer.Start(m_pp.tribute_time_remaining); - } - - /* - Character Inventory Packet - this is not quite where live sends inventory, they do it after tribute - */ - if (loaditems) { /* Dont load if a length error occurs */ - BulkSendInventoryItems(); - /* Send stuff on the cursor which isnt sent in bulk */ - iter_queue it; - for (it = m_inv.cursor_begin(); it != m_inv.cursor_end(); ++it) { - /* First item cursor is sent in bulk inventory packet */ - if (it == m_inv.cursor_begin()) - continue; - const ItemInst *inst = *it; - SendItemPacket(MainCursor, inst, ItemPacketSummonItem); - } - } - - /* Task Packets */ - LoadClientTaskState(); - - if (GetClientVersion() >= EQClientRoF) { - outapp = new EQApplicationPacket(OP_ReqNewZone, 0); - Handle_Connect_OP_ReqNewZone(outapp); - safe_delete(outapp); - } - - if (ClientVersionBit & BIT_UnderfootAndLater) { - outapp = new EQApplicationPacket(OP_XTargetResponse, 8); - outapp->WriteUInt32(GetMaxXTargets()); - outapp->WriteUInt32(0); - FastQueuePacket(&outapp); - } - - /* - Weather Packet - This shouldent be moved, this seems to be what the client - uses to advance to the next state (sending ReqNewZone) - */ - outapp = new EQApplicationPacket(OP_Weather, 12); - Weather_Struct *ws = (Weather_Struct *)outapp->pBuffer; - ws->val1 = 0x000000FF; - if (zone->zone_weather == 1){ ws->type = 0x31; } // Rain - if (zone->zone_weather == 2) { - outapp->pBuffer[8] = 0x01; - ws->type = 0x02; - } - outapp->priority = 6; - QueuePacket(outapp); - safe_delete(outapp); - - SetAttackTimer(); - conn_state = ZoneInfoSent; - - return; -} - -// connected opcode handlers -void Client::Handle_0x0193(const EQApplicationPacket *app) -{ - // Not sure what this opcode does. It started being sent when OP_ClientUpdate was - // changed to pump OP_ClientUpdate back out instead of OP_MobUpdate - // 2 bytes: 00 00 - - return; -} - -void Client::Handle_OP_AAAction(const EQApplicationPacket *app) -{ - mlog(AA__IN, "Received OP_AAAction"); - mpkt(AA__IN, app); - - if (app->size != sizeof(AA_Action)){ - printf("Error! OP_AAAction size didnt match!\n"); - return; - } - AA_Action* action = (AA_Action*)app->pBuffer; - - if (action->action == aaActionActivate) {//AA Hotkey - mlog(AA__MESSAGE, "Activating AA %d", action->ability); - ActivateAA((aaID)action->ability); - } - else if (action->action == aaActionBuy) { - BuyAA(action); - } - else if (action->action == aaActionDisableEXP){ //Turn Off AA Exp - if (m_epp.perAA > 0) - Message_StringID(0, AA_OFF); - m_epp.perAA = 0; - SendAAStats(); - } - else if (action->action == aaActionSetEXP) { - if (m_epp.perAA == 0) - Message_StringID(0, AA_ON); - m_epp.perAA = action->exp_value; - if (m_epp.perAA<0 || m_epp.perAA>100) m_epp.perAA = 0; // stop exploit with sanity check - // send an update - SendAAStats(); - SendAATable(); - } - else { - printf("Unknown AA action: %u %u 0x%x %d\n", action->action, action->ability, action->unknown08, action->exp_value); - } - - return; -} - -void Client::Handle_OP_AcceptNewTask(const EQApplicationPacket *app) -{ - - if (app->size != sizeof(AcceptNewTask_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AcceptNewTask expected %i got %i", - sizeof(AcceptNewTask_Struct), app->size); - DumpPacket(app); - return; - } - AcceptNewTask_Struct *ant = (AcceptNewTask_Struct*)app->pBuffer; - - if (ant->task_id > 0 && RuleB(TaskSystem, EnableTaskSystem) && taskstate) - taskstate->AcceptNewTask(this, ant->task_id, ant->task_master_id); -} - -void Client::Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app) -{ - if (app->size < sizeof(EntityId_Struct)) - { - LogFile->write(EQEMuLog::Error, "Handle_OP_AdventureInfoRequest had a packet that was too small."); - return; - } - EntityId_Struct* ent = (EntityId_Struct*)app->pBuffer; - Mob * m = entity_list.GetMob(ent->entity_id); - if (m && m->IsNPC()) - { - std::map::iterator it; - it = zone->adventure_entry_list_flavor.find(m->CastToNPC()->GetAdventureTemplate()); - if (it != zone->adventure_entry_list_flavor.end()) - { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureInfo, (it->second.size() + 2)); - strn0cpy((char*)outapp->pBuffer, it->second.c_str(), it->second.size()); - FastQueuePacket(&outapp); - } - else - { - if (m->CastToNPC()->GetAdventureTemplate() != 0) - { - std::string text = "Choose your difficulty and preferred adventure type."; - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureInfo, (text.size() + 2)); - strn0cpy((char*)outapp->pBuffer, text.c_str(), text.size()); - FastQueuePacket(&outapp); - } - } - } -} - -void Client::Handle_OP_AdventureLeaderboardRequest(const EQApplicationPacket *app) -{ - if (app->size < sizeof(AdventureLeaderboardRequest_Struct)) - { - return; - } - - if (adventure_leaderboard_timer) - { - return; - } - - adventure_leaderboard_timer = new Timer(4000); - ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeaderboard, sizeof(ServerLeaderboardRequest_Struct)); - ServerLeaderboardRequest_Struct *lr = (ServerLeaderboardRequest_Struct*)pack->pBuffer; - strcpy(lr->player, GetName()); - - AdventureLeaderboardRequest_Struct *lrs = (AdventureLeaderboardRequest_Struct*)app->pBuffer; - lr->type = 1 + (lrs->theme * 2) + lrs->type; - worldserver.SendPacket(pack); - delete pack; -} - -void Client::Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Adventure_Purchase_Struct)) - { - LogFile->write(EQEMuLog::Error, "OP size error: OP_AdventureMerchantPurchase expected:%i got:%i", sizeof(Adventure_Purchase_Struct), app->size); - return; - } - - Adventure_Purchase_Struct* aps = (Adventure_Purchase_Struct*)app->pBuffer; - /* - Get item apc->itemid (can check NPC if thats necessary), ldon point theme check only if theme is not 0 (I am not sure what 1-5 are though for themes) - if(ldon_points_available >= item ldonpointcost) - { - give item (67 00 00 00 for the packettype using opcode 0x02c5) - ldon_points_available -= ldonpointcost; - } - */ - uint32 merchantid = 0; - Mob* tmp = entity_list.GetMob(aps->npcid); - if (tmp == 0 || !tmp->IsNPC() || ((tmp->GetClass() != ADVENTUREMERCHANT) && - (tmp->GetClass() != DISCORD_MERCHANT) && (tmp->GetClass() != NORRATHS_KEEPERS_MERCHANT) && (tmp->GetClass() != DARK_REIGN_MERCHANT))) - return; - - //you have to be somewhat close to them to be properly using them - if (DistNoRoot(*tmp) > USE_NPC_RANGE2) - return; - - merchantid = tmp->CastToNPC()->MerchantType; - - const Item_Struct* item = nullptr; - bool found = false; - std::list merlist = zone->merchanttable[merchantid]; - std::list::const_iterator itr; - - for (itr = merlist.begin(); itr != merlist.end(); ++itr){ - MerchantList ml = *itr; - if (GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tmp->GetPrimaryFaction(); - if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if (!item) - continue; - if (item->ID == aps->itemid) { //This check to make sure that the item is actually on the NPC, people attempt to inject packets to get items summoned... - found = true; - break; - } - } - if (!item || !found) { - Message(13, "Error: The item you purchased does not exist!"); - return; - } - - if (aps->Type == LDoNMerchant) - { - if (m_pp.ldon_points_available < int32(item->LDoNPrice)) { - Message(13, "You cannot afford that item."); - return; - } - - if (item->LDoNTheme <= 16) - { - if (item->LDoNTheme & 16) - { - if (m_pp.ldon_points_tak < int32(item->LDoNPrice)) - { - Message(13, "You need at least %u points in tak to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else if (item->LDoNTheme & 8) - { - if (m_pp.ldon_points_ruj < int32(item->LDoNPrice)) - { - Message(13, "You need at least %u points in ruj to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else if (item->LDoNTheme & 4) - { - if (m_pp.ldon_points_mmc < int32(item->LDoNPrice)) - { - Message(13, "You need at least %u points in mmc to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else if (item->LDoNTheme & 2) - { - if (m_pp.ldon_points_mir < int32(item->LDoNPrice)) - { - Message(13, "You need at least %u points in mir to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else if (item->LDoNTheme & 1) - { - if (m_pp.ldon_points_guk < int32(item->LDoNPrice)) - { - Message(13, "You need at least %u points in guk to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - } - } - else if (aps->Type == DiscordMerchant) - { - if (GetPVPPoints() < item->LDoNPrice) - { - Message(13, "You need at least %u PVP points to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else if (aps->Type == NorrathsKeepersMerchant) - { - if (GetRadiantCrystals() < item->LDoNPrice) - { - Message(13, "You need at least %u Radiant Crystals to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else if (aps->Type == DarkReignMerchant) - { - if (GetEbonCrystals() < item->LDoNPrice) - { - Message(13, "You need at least %u Ebon Crystals to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else - { - Message(13, "Unknown Adventure Merchant type."); - return; - } - - - if (CheckLoreConflict(item)) - { - Message(15, "You can only have one of a lore item."); - return; - } - - if (aps->Type == LDoNMerchant) - { - int32 requiredpts = (int32)item->LDoNPrice*-1; - - if (!UpdateLDoNPoints(requiredpts, 6)) - return; - } - else if (aps->Type == DiscordMerchant) - { - SetPVPPoints(GetPVPPoints() - (int32)item->LDoNPrice); - SendPVPStats(); - } - else if (aps->Type == NorrathsKeepersMerchant) - { - SetRadiantCrystals(GetRadiantCrystals() - (int32)item->LDoNPrice); - SendCrystalCounts(); - } - else if (aps->Type == DarkReignMerchant) - { - SetEbonCrystals(GetEbonCrystals() - (int32)item->LDoNPrice); - SendCrystalCounts(); - } - int16 charges = 1; - if (item->MaxCharges != 0) - charges = item->MaxCharges; - - ItemInst *inst = database.CreateItem(item, charges); - if (!AutoPutLootInInventory(*inst, true, true)) - { - PutLootInInventory(MainCursor, *inst); - } - Save(1); -} - -void Client::Handle_OP_AdventureMerchantRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(AdventureMerchant_Struct)) - { - LogFile->write(EQEMuLog::Error, "OP size error: OP_AdventureMerchantRequest expected:%i got:%i", sizeof(AdventureMerchant_Struct), app->size); - return; - } - std::stringstream ss(std::stringstream::in | std::stringstream::out); - - uint8 count = 0; - AdventureMerchant_Struct* eid = (AdventureMerchant_Struct*)app->pBuffer; - uint32 merchantid = 0; - - Mob* tmp = entity_list.GetMob(eid->entity_id); - if (tmp == 0 || !tmp->IsNPC() || ((tmp->GetClass() != ADVENTUREMERCHANT) && - (tmp->GetClass() != DISCORD_MERCHANT) && (tmp->GetClass() != NORRATHS_KEEPERS_MERCHANT) && (tmp->GetClass() != DARK_REIGN_MERCHANT))) - return; - - //you have to be somewhat close to them to be properly using them - if (DistNoRoot(*tmp) > USE_NPC_RANGE2) - return; - - merchantid = tmp->CastToNPC()->MerchantType; - tmp->CastToNPC()->FaceTarget(this->CastToMob()); - - const Item_Struct *item = 0; - std::list merlist = zone->merchanttable[merchantid]; - std::list::const_iterator itr; - for (itr = merlist.begin(); itr != merlist.end() && count<255; ++itr){ - const MerchantList &ml = *itr; - if (GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tmp->GetPrimaryFaction(); - if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if (item) - { - uint32 theme; - if (item->LDoNTheme > 16) - { - theme = 0; - } - else if (item->LDoNTheme & 16) - { - theme = 5; - } - else if (item->LDoNTheme & 8) - { - theme = 4; - } - else if (item->LDoNTheme & 4) - { - theme = 3; - } - else if (item->LDoNTheme & 2) - { - theme = 2; - } - else if (item->LDoNTheme & 1) - { - theme = 1; - } - else - { - theme = 0; - } - ss << "^" << item->Name << "|"; - ss << item->ID << "|"; - ss << item->LDoNPrice << "|"; - ss << theme << "|"; - ss << "0|"; - ss << "1|"; - ss << item->Races << "|"; - ss << item->Classes; - count++; - } - } - //Count - //^Item Name,Item ID,Cost in Points,Theme (0=none),0,1,races bit map,classes bitmap - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureMerchantResponse, ss.str().size() + 2); - outapp->pBuffer[0] = count; - strn0cpy((char*)&outapp->pBuffer[1], ss.str().c_str(), ss.str().size()); - FastQueuePacket(&outapp); -} - -void Client::Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Adventure_Sell_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch on OP_AdventureMerchantSell: got %u expected %u", - app->size, sizeof(Adventure_Sell_Struct)); - DumpPacket(app); - return; - } - - Adventure_Sell_Struct *ams_in = (Adventure_Sell_Struct*)app->pBuffer; - - Mob* vendor = entity_list.GetMob(ams_in->npcid); - if (vendor == 0 || !vendor->IsNPC() || ((vendor->GetClass() != ADVENTUREMERCHANT) && - (vendor->GetClass() != NORRATHS_KEEPERS_MERCHANT) && (vendor->GetClass() != DARK_REIGN_MERCHANT))) - { - Message(13, "Vendor was not found."); - return; - } - - if (DistNoRoot(*vendor) > USE_NPC_RANGE2) - { - Message(13, "Vendor is out of range."); - return; - } - - uint32 itemid = GetItemIDAt(ams_in->slot); - - if (itemid == 0) - { - Message(13, "Found no item at that slot."); - return; - } - - const Item_Struct* item = database.GetItem(itemid); - ItemInst* inst = GetInv().GetItem(ams_in->slot); - if (!item || !inst){ - Message(13, "You seemed to have misplaced that item..."); - return; - } - - // Note that Lucy has ldonsold values of 4 and 5 for items sold by Norrath's Keepers and Dark Reign, whereas 13th Floor - // has ldonsold = 0 for these items, so some manual editing of the items DB will be required to support sell back of the - // items. - // - // The Merchant seems to have some other way of knowing whether he will accept the item, other than the ldonsold field, - // e.g. if you summon items 76036 and 76053 (good and evil versions of Spell: Ward Of Vengeance), if you are interacting - // with a Norrath's Keeper merchant and click on 76036 in your inventory, he says he will give you radiant crystals for - // it, but he will refuse for item 76053. - // - // Similarly, just giving a cloth cap an ldonsold value of 4 will not make the Merchant buy it. - // - // Note that the the Client will not allow you to sell anything back to a Discord merchant, so there is no need to handle - // that case here. - if (item->LDoNSold == 0) - { - Message(13, "The merchant does not want that item."); - return; - } - - if (item->LDoNPrice == 0) - { - Message(13, "The merchant does not want that item."); - return; - } - - int32 price = item->LDoNPrice * 70 / 100; - - if (price == 0) - { - Message(13, "The merchant does not want that item."); - return; - } - - if (RuleB(EventLog, RecordSellToMerchant)) - LogMerchant(this, vendor, ams_in->charges, price, item, false); - - if (!inst->IsStackable()) - { - DeleteItemInInventory(ams_in->slot, 0, false); - } - else - { - if (inst->GetCharges() < ams_in->charges) - { - ams_in->charges = inst->GetCharges(); - } - - if (ams_in->charges == 0) - { - Message(13, "Charge mismatch error."); - return; - } - - DeleteItemInInventory(ams_in->slot, ams_in->charges, false); - price *= ams_in->charges; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureMerchantSell, sizeof(Adventure_Sell_Struct)); - Adventure_Sell_Struct *ams = (Adventure_Sell_Struct*)outapp->pBuffer; - ams->slot = ams_in->slot; - ams->unknown000 = 1; - ams->npcid = ams->npcid; - ams->charges = ams_in->charges; - ams->sell_price = price; - FastQueuePacket(&outapp); - - switch (vendor->GetClass()) - { - case ADVENTUREMERCHANT: - { - UpdateLDoNPoints(price, 6); - break; - } - case NORRATHS_KEEPERS_MERCHANT: - { - SetRadiantCrystals(GetRadiantCrystals() + price); - break; - } - case DARK_REIGN_MERCHANT: - { - SetEbonCrystals(GetEbonCrystals() + price); - break; - } - - default: - break; - } - - Save(1); -} - -void Client::Handle_OP_AdventureRequest(const EQApplicationPacket *app) -{ - if (app->size < sizeof(AdventureRequest_Struct)) - { - LogFile->write(EQEMuLog::Error, "Handle_OP_AdventureRequest had a packet that was too small."); - return; - } - - if (IsOnAdventure()) - { - return; - } - - if (!p_timers.Expired(&database, pTimerStartAdventureTimer, false)) - { - return; - } - - if (GetPendingAdventureRequest()) - { - return; - } - - AdventureRequest_Struct* ars = (AdventureRequest_Struct*)app->pBuffer; - uint8 group_members = 0; - Raid *r = nullptr; - Group *g = nullptr; - - if (IsRaidGrouped()) - { - r = GetRaid(); - group_members = r->RaidCount(); - } - else if (IsGrouped()) - { - g = GetGroup(); - group_members = g->GroupCount(); - } - else - { - return; - } - - if (group_members < RuleI(Adventure, MinNumberForGroup) || group_members > RuleI(Adventure, MaxNumberForGroup)) - { - return; - } - - Mob* m = entity_list.GetMob(ars->entity_id); - uint32 template_id = 0; - if (m && m->IsNPC()) - { - template_id = m->CastToNPC()->GetAdventureTemplate(); - } - else - { - return; - } - - ServerPacket *packet = new ServerPacket(ServerOP_AdventureRequest, sizeof(ServerAdventureRequest_Struct)+(64 * group_members)); - ServerAdventureRequest_Struct *sar = (ServerAdventureRequest_Struct*)packet->pBuffer; - sar->member_count = group_members; - sar->risk = ars->risk; - sar->type = ars->type; - sar->template_id = template_id; - strcpy(sar->leader, GetName()); - - if (IsRaidGrouped()) - { - int i = 0; - for (int x = 0; x < 72; ++x) - { - if (i == group_members) - { - break; - } - - const char *c_name = nullptr; - c_name = r->GetClientNameByIndex(x); - if (c_name) - { - memcpy((packet->pBuffer + sizeof(ServerAdventureRequest_Struct)+(64 * i)), c_name, strlen(c_name)); - ++i; - } - } - } - else - { - int i = 0; - for (int x = 0; x < 6; ++x) - { - if (i == group_members) - { - break; - } - - const char *c_name = nullptr; - c_name = g->GetClientNameByIndex(x); - if (c_name) - { - memcpy((packet->pBuffer + sizeof(ServerAdventureRequest_Struct)+(64 * i)), c_name, strlen(c_name)); - ++i; - } - } - } - - packet->Deflate(); - worldserver.SendPacket(packet); - delete packet; - p_timers.Start(pTimerStartAdventureTimer, 5); -} - -void Client::Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app) -{ - if (adventure_stats_timer) - { - return; - } - - adventure_stats_timer = new Timer(8000); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureStatsReply, sizeof(AdventureStats_Struct)); - AdventureStats_Struct *as = (AdventureStats_Struct*)outapp->pBuffer; - - if (database.GetAdventureStats(CharacterID(), as)) - { - m_pp.ldon_wins_guk = as->success.guk; - m_pp.ldon_wins_mir = as->success.mir; - m_pp.ldon_wins_mmc = as->success.mmc; - m_pp.ldon_wins_ruj = as->success.ruj; - m_pp.ldon_wins_tak = as->success.tak; - m_pp.ldon_losses_guk = as->failure.guk; - m_pp.ldon_losses_mir = as->failure.mir; - m_pp.ldon_losses_mmc = as->failure.mmc; - m_pp.ldon_losses_ruj = as->failure.ruj; - m_pp.ldon_losses_tak = as->failure.tak; - } - - FastQueuePacket(&outapp); -} - -void Client::Handle_OP_AltCurrencyMerchantRequest(const EQApplicationPacket *app) -{ - VERIFY_PACKET_LENGTH(OP_AltCurrencyMerchantRequest, app, uint32); - - NPC* tar = entity_list.GetNPCByID(*((uint32*)app->pBuffer)); - if (tar) { - if (DistNoRoot(*tar) > USE_NPC_RANGE2) - return; - - if (tar->GetClass() != ALT_CURRENCY_MERCHANT) { - return; - } - - uint32 alt_cur_id = tar->GetAltCurrencyType(); - if (alt_cur_id == 0) { - return; - } - - std::list::iterator altc_iter = zone->AlternateCurrencies.begin(); - bool found = false; - while (altc_iter != zone->AlternateCurrencies.end()) { - if ((*altc_iter).id == alt_cur_id) { - found = true; - break; - } - ++altc_iter; - } - - if (!found) { - return; - } - - std::stringstream ss(std::stringstream::in | std::stringstream::out); - std::stringstream item_ss(std::stringstream::in | std::stringstream::out); - ss << alt_cur_id << "|1|" << alt_cur_id; - uint32 count = 0; - uint32 merchant_id = tar->MerchantType; - const Item_Struct *item = nullptr; - - std::list merlist = zone->merchanttable[merchant_id]; - std::list::const_iterator itr; - for (itr = merlist.begin(); itr != merlist.end() && count < 255; ++itr){ - const MerchantList &ml = *itr; - if (GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tar->GetPrimaryFaction(); - if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if (item) - { - item_ss << "^" << item->Name << "|"; - item_ss << item->ID << "|"; - item_ss << ml.alt_currency_cost << "|"; - item_ss << "0|"; - item_ss << "1|"; - item_ss << item->Races << "|"; - item_ss << item->Classes; - count++; - } - } - - if (count > 0) { - ss << "|" << count << item_ss.str(); - } - else { - ss << "|0"; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AltCurrencyMerchantReply, ss.str().length() + 1); - memcpy(outapp->pBuffer, ss.str().c_str(), ss.str().length()); - FastQueuePacket(&outapp); - } -} - -void Client::Handle_OP_AltCurrencyPurchase(const EQApplicationPacket *app) -{ - VERIFY_PACKET_LENGTH(OP_AltCurrencyPurchase, app, AltCurrencyPurchaseItem_Struct); - AltCurrencyPurchaseItem_Struct *purchase = (AltCurrencyPurchaseItem_Struct*)app->pBuffer; - NPC* tar = entity_list.GetNPCByID(purchase->merchant_entity_id); - if (tar) { - if (DistNoRoot(*tar) > USE_NPC_RANGE2) - return; - - if (tar->GetClass() != ALT_CURRENCY_MERCHANT) { - return; - } - - uint32 alt_cur_id = tar->GetAltCurrencyType(); - if (alt_cur_id == 0) { - return; - } - - const Item_Struct* item = nullptr; - uint32 cost = 0; - uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); - uint32 merchant_id = tar->MerchantType; - bool found = false; - std::list merlist = zone->merchanttable[merchant_id]; - std::list::const_iterator itr; - for (itr = merlist.begin(); itr != merlist.end(); ++itr) { - MerchantList ml = *itr; - if (GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tar->GetPrimaryFaction(); - if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if (!item) - continue; - - if (item->ID == purchase->item_id) { //This check to make sure that the item is actually on the NPC, people attempt to inject packets to get items summoned... - cost = ml.alt_currency_cost; - found = true; - break; - } - } - - if (!item || !found) { - Message(13, "Error: The item you purchased does not exist!"); - return; - } - - if (cost > current_currency) { - Message(13, "You cannot afford that item right now."); - return; - } - - if (CheckLoreConflict(item)) - { - Message(15, "You can only have one of a lore item."); - return; - } - - /* QS: PlayerLogAlternateCurrencyTransactions :: Merchant Purchase */ - if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ - std::string event_desc = StringFormat("Merchant Purchase :: Spent alt_currency_id:%i cost:%i for itemid:%i in zoneid:%i instid:%i", alt_cur_id, cost, item->ID, this->GetZoneID(), this->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); - } - - AddAlternateCurrencyValue(alt_cur_id, -((int32)cost)); - int16 charges = 1; - if (item->MaxCharges != 0) - charges = item->MaxCharges; - - ItemInst *inst = database.CreateItem(item, charges); - if (!AutoPutLootInInventory(*inst, true, true)) - { - PutLootInInventory(MainCursor, *inst); - } - - Save(1); - } -} - -void Client::Handle_OP_AltCurrencyReclaim(const EQApplicationPacket *app) -{ - VERIFY_PACKET_LENGTH(OP_AltCurrencyReclaim, app, AltCurrencyReclaim_Struct); - AltCurrencyReclaim_Struct *reclaim = (AltCurrencyReclaim_Struct*)app->pBuffer; - uint32 item_id = 0; - std::list::iterator iter = zone->AlternateCurrencies.begin(); - while (iter != zone->AlternateCurrencies.end()) { - if ((*iter).id == reclaim->currency_id) { - item_id = (*iter).item_id; - } - ++iter; - } - - if (item_id == 0) { - return; - } - - /* Item to Currency Storage */ - if (reclaim->reclaim_flag == 1) { - uint32 removed = NukeItem(item_id, invWhereWorn | invWherePersonal | invWhereCursor); - if (removed > 0) { - AddAlternateCurrencyValue(reclaim->currency_id, removed); - - /* QS: PlayerLogAlternateCurrencyTransactions :: Item to Currency */ - if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ - std::string event_desc = StringFormat("Reclaim :: Item to Currency :: alt_currency_id:%i amount:%i to currency tab in zoneid:%i instid:%i", reclaim->currency_id, removed, this->GetZoneID(), this->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); - } - } - } - /* Cursor to Item storage */ - else { - uint32 max_currency = GetAlternateCurrencyValue(reclaim->currency_id); - - /* If you input more than you have currency wise, just give the max of the currency you currently have */ - if (reclaim->count > max_currency) { - SummonItem(item_id, max_currency); - SetAlternateCurrencyValue(reclaim->currency_id, 0); - } - else { - SummonItem(item_id, reclaim->count, 0, 0, 0, 0, 0, false, MainCursor); - AddAlternateCurrencyValue(reclaim->currency_id, -((int32)reclaim->count)); - } - /* QS: PlayerLogAlternateCurrencyTransactions :: Cursor to Item Storage */ - if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ - std::string event_desc = StringFormat("Reclaim :: Cursor to Item :: alt_currency_id:%i amount:-%i in zoneid:%i instid:%i", reclaim->currency_id, reclaim->count, this->GetZoneID(), this->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); - } - } -} - -void Client::Handle_OP_AltCurrencySell(const EQApplicationPacket *app) -{ - VERIFY_PACKET_LENGTH(OP_AltCurrencySell, app, AltCurrencySellItem_Struct); - EQApplicationPacket *outapp = app->Copy(); - AltCurrencySellItem_Struct *sell = (AltCurrencySellItem_Struct*)outapp->pBuffer; - - NPC* tar = entity_list.GetNPCByID(sell->merchant_entity_id); - if (tar) { - if (DistNoRoot(*tar) > USE_NPC_RANGE2) - return; - - if (tar->GetClass() != ALT_CURRENCY_MERCHANT) { - return; - } - - uint32 alt_cur_id = tar->GetAltCurrencyType(); - if (alt_cur_id == 0) { - return; - } - - ItemInst* inst = GetInv().GetItem(sell->slot_id); - if (!inst) { - return; - } - - if (!RuleB(Merchant, EnableAltCurrencySell)) { - return; - } - - const Item_Struct* item = nullptr; - uint32 cost = 0; - uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); - uint32 merchant_id = tar->MerchantType; - uint32 npc_id = tar->GetNPCTypeID(); - bool found = false; - std::list merlist = zone->merchanttable[merchant_id]; - std::list::const_iterator itr; - for (itr = merlist.begin(); itr != merlist.end(); ++itr) { - MerchantList ml = *itr; - if (GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tar->GetPrimaryFaction(); - if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if (!item) - continue; - - if (item->ID == inst->GetItem()->ID) { - cost = ml.alt_currency_cost; - found = true; - break; - } - } - - if (!found) { - return; - } - - if (!inst->IsStackable()) - { - DeleteItemInInventory(sell->slot_id, 0, false); - } - else - { - if (inst->GetCharges() < sell->charges) - { - sell->charges = inst->GetCharges(); - } - - if (sell->charges == 0) - { - Message(13, "Charge mismatch error."); - return; - } - - DeleteItemInInventory(sell->slot_id, sell->charges, false); - cost *= sell->charges; - } - - sell->cost = cost; - - /* QS: PlayerLogAlternateCurrencyTransactions :: Sold to Merchant*/ - if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ - std::string event_desc = StringFormat("Sold to Merchant :: itemid:%u npcid:%u alt_currency_id:%u cost:%u in zoneid:%u instid:%i", item->ID, npc_id, alt_cur_id, cost, this->GetZoneID(), this->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); - } - - FastQueuePacket(&outapp); - AddAlternateCurrencyValue(alt_cur_id, cost); - Save(1); - } -} - -void Client::Handle_OP_AltCurrencySellSelection(const EQApplicationPacket *app) -{ - VERIFY_PACKET_LENGTH(OP_AltCurrencySellSelection, app, AltCurrencySelectItem_Struct); - - AltCurrencySelectItem_Struct *select = (AltCurrencySelectItem_Struct*)app->pBuffer; - NPC* tar = entity_list.GetNPCByID(select->merchant_entity_id); - if (tar) { - if (DistNoRoot(*tar) > USE_NPC_RANGE2) - return; - - if (tar->GetClass() != ALT_CURRENCY_MERCHANT) { - return; - } - - uint32 alt_cur_id = tar->GetAltCurrencyType(); - if (alt_cur_id == 0) { - return; - } - - ItemInst *inst = m_inv.GetItem(select->slot_id); - if (!inst) { - return; - } - - const Item_Struct* item = nullptr; - uint32 cost = 0; - uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); - uint32 merchant_id = tar->MerchantType; - - if (RuleB(Merchant, EnableAltCurrencySell)) { - bool found = false; - std::list merlist = zone->merchanttable[merchant_id]; - std::list::const_iterator itr; - for (itr = merlist.begin(); itr != merlist.end(); ++itr) { - MerchantList ml = *itr; - if (GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tar->GetPrimaryFaction(); - if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if (!item) - continue; - - if (item->ID == inst->GetItem()->ID) { - cost = ml.alt_currency_cost; - found = true; - break; - } - } - - if (!found) { - cost = 0; - } - } - else { - cost = 0; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AltCurrencySellSelection, sizeof(AltCurrencySelectItemReply_Struct)); - AltCurrencySelectItemReply_Struct *reply = (AltCurrencySelectItemReply_Struct*)outapp->pBuffer; - reply->unknown004 = 0xFF; - reply->unknown005 = 0xFF; - reply->unknown006 = 0xFF; - reply->unknown007 = 0xFF; - strcpy(reply->item_name, inst->GetItem()->Name); - reply->cost = cost; - FastQueuePacket(&outapp); - } -} - -void Client::Handle_OP_Animation(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Animation_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized " - "OP_Animation: got %d, expected %d", app->size, - sizeof(Animation_Struct)); - DumpPacket(app); - return; - } - - Animation_Struct *s = (Animation_Struct *)app->pBuffer; - - //might verify spawn ID, but it wouldent affect anything - - DoAnim(s->action, s->value); - - return; -} - -void Client::Handle_OP_ApplyPoison(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ApplyPoison_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_ApplyPoison, size=%i, expected %i", app->size, sizeof(ApplyPoison_Struct)); - DumpPacket(app); - return; - } - uint32 ApplyPoisonSuccessResult = 0; - ApplyPoison_Struct* ApplyPoisonData = (ApplyPoison_Struct*)app->pBuffer; - const ItemInst* PrimaryWeapon = GetInv().GetItem(MainPrimary); - const ItemInst* SecondaryWeapon = GetInv().GetItem(MainSecondary); - const ItemInst* PoisonItemInstance = GetInv()[ApplyPoisonData->inventorySlot]; - - bool IsPoison = PoisonItemInstance && (PoisonItemInstance->GetItem()->ItemType == ItemTypePoison); - - if (!IsPoison) - { - mlog(SPELLS__CASTING_ERR, "Item used to cast spell effect from a poison item was missing from inventory slot %d " - "after casting, or is not a poison!", ApplyPoisonData->inventorySlot); - - Message(0, "Error: item not found for inventory slot #%i or is not a poison", ApplyPoisonData->inventorySlot); - } - else if (GetClass() == ROGUE) - { - if ((PrimaryWeapon && PrimaryWeapon->GetItem()->ItemType == ItemType1HPiercing) || - (SecondaryWeapon && SecondaryWeapon->GetItem()->ItemType == ItemType1HPiercing)) { - float SuccessChance = (GetSkill(SkillApplyPoison) + GetLevel()) / 400.0f; - double ChanceRoll = zone->random.Real(0, 1); - - CheckIncreaseSkill(SkillApplyPoison, nullptr, 10); - - if (ChanceRoll < SuccessChance) { - ApplyPoisonSuccessResult = 1; - // NOTE: Someone may want to tweak the chance to proc the poison effect that is added to the weapon here. - // My thinking was that DEX should be apart of the calculation. - AddProcToWeapon(PoisonItemInstance->GetItem()->Proc.Effect, false, (GetDEX() / 100) + 103); - } - - DeleteItemInInventory(ApplyPoisonData->inventorySlot, 1, true); - - LogFile->write(EQEMuLog::Debug, "Chance to Apply Poison was %f. Roll was %f. Result is %u.", SuccessChance, ChanceRoll, ApplyPoisonSuccessResult); - } - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_ApplyPoison, nullptr, sizeof(ApplyPoison_Struct)); - ApplyPoison_Struct* ApplyPoisonResult = (ApplyPoison_Struct*)outapp->pBuffer; - ApplyPoisonResult->success = ApplyPoisonSuccessResult; - ApplyPoisonResult->inventorySlot = ApplyPoisonData->inventorySlot; - - FastQueuePacket(&outapp); -} - -void Client::Handle_OP_Assist(const EQApplicationPacket *app) -{ - if (app->size != sizeof(EntityId_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Assist expected %i got %i", sizeof(EntityId_Struct), app->size); - return; - } - - EntityId_Struct* eid = (EntityId_Struct*)app->pBuffer; - Entity* entity = entity_list.GetID(eid->entity_id); - - EQApplicationPacket* outapp = app->Copy(); - eid = (EntityId_Struct*)outapp->pBuffer; - if (RuleB(Combat, AssistNoTargetSelf)) - eid->entity_id = GetID(); - if (entity && entity->IsMob()) { - Mob *assistee = entity->CastToMob(); - if (assistee->GetTarget()) { - Mob *new_target = assistee->GetTarget(); - if (new_target && (GetGM() || - Dist(*assistee) <= TARGETING_RANGE)) { - SetAssistExemption(true); - eid->entity_id = new_target->GetID(); - } - } - } - - FastQueuePacket(&outapp); - return; -} - -void Client::Handle_OP_AssistGroup(const EQApplicationPacket *app) -{ - if (app->size != sizeof(EntityId_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AssistGroup expected %i got %i", sizeof(EntityId_Struct), app->size); - return; - } - QueuePacket(app); - return; -} - -void Client::Handle_OP_AugmentInfo(const EQApplicationPacket *app) -{ - // This packet is sent by the client when an Augment item information window is opened. - // Some clients this seems to nuke the charm text (ex. Adventurer's Stone) - - if (app->size != sizeof(AugmentInfo_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AugmentInfo expected %i got %i", - sizeof(AugmentInfo_Struct), app->size); - DumpPacket(app); - return; - } - - AugmentInfo_Struct* AugInfo = (AugmentInfo_Struct*)app->pBuffer; - const Item_Struct * item = database.GetItem(AugInfo->itemid); - - if (item) { - strn0cpy(AugInfo->augment_info, item->Name, 64); - AugInfo->itemid = 0; - QueuePacket(app); - } -} - -void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) -{ - if (app->size != sizeof(AugmentItem_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for AugmentItem_Struct: Expected: %i, Got: %i", - sizeof(AugmentItem_Struct), app->size); - return; - } - - // Delegate to tradeskill object to perform combine - AugmentItem_Struct* in_augment = (AugmentItem_Struct*)app->pBuffer; - bool deleteItems = false; - if (GetClientVersion() >= EQClientRoF) - { - ItemInst *itemOneToPush = nullptr, *itemTwoToPush = nullptr; - - //Message(15, "%i %i %i %i %i %i", in_augment->container_slot, in_augment->augment_slot, in_augment->container_index, in_augment->augment_index, in_augment->augment_action, in_augment->dest_inst_id); - - // Adding augment - if (in_augment->augment_action == 0) - { - ItemInst *tobe_auged, *auged_with = nullptr; - int8 slot = -1; - Inventory& user_inv = GetInv(); - - uint16 slot_id = in_augment->container_slot; - uint16 aug_slot_id = in_augment->augment_slot; - //Message(13, "%i AugSlot", aug_slot_id); - if (slot_id == INVALID_INDEX || aug_slot_id == INVALID_INDEX) - { - Message(13, "Error: Invalid Aug Index."); - return; - } - - tobe_auged = user_inv.GetItem(slot_id); - auged_with = user_inv.GetItem(MainCursor); - - if (tobe_auged && auged_with) - { - if (((tobe_auged->IsAugmentSlotAvailable(auged_with->GetAugmentType(), in_augment->augment_index)) != -1) && - (tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots))) - { - tobe_auged->PutAugment(in_augment->augment_index, *auged_with); - - ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_index); - if (aug) { - std::vector args; - args.push_back(aug); - parse->EventItem(EVENT_AUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); - - args.assign(1, tobe_auged); - parse->EventItem(EVENT_AUGMENT_INSERT, this, aug, nullptr, "", in_augment->augment_index, &args); - } - else - { - Message(13, "Error: Could not find augmentation at index %i. Aborting.", in_augment->augment_index); - return; - } - - itemOneToPush = tobe_auged->Clone(); - // Must push items after the items in inventory are deleted - necessary due to lore items... - if (itemOneToPush) - { - DeleteItemInInventory(slot_id, 0, true); - DeleteItemInInventory(MainCursor, 0, true); - if (PutItemInInventory(slot_id, *itemOneToPush, true)) - { - CalcBonuses(); - //Message(13, "Sucessfully added an augment to your item!"); - return; - } - else - { - Message(13, "Error: No available slot for end result. Please free up some bag space."); - } - } - else - { - Message(13, "Error in cloning item for augment. Aborted."); - } - - } - else - { - Message(13, "Error: No available slot for augment in that item."); - } - } - } - else if (in_augment->augment_action == 1) - { - ItemInst *tobe_auged, *auged_with = nullptr; - int8 slot = -1; - Inventory& user_inv = GetInv(); - - uint16 slot_id = in_augment->container_slot; - uint16 aug_slot_id = in_augment->augment_slot; //it's actually solvent slot - if (slot_id == INVALID_INDEX || aug_slot_id == INVALID_INDEX) - { - Message(13, "Error: Invalid Aug Index."); - return; - } - - tobe_auged = user_inv.GetItem(slot_id); - auged_with = user_inv.GetItem(aug_slot_id); - - ItemInst *old_aug = nullptr; - if (!auged_with) - return; - const uint32 id = auged_with->GetID(); - ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_index); - if (aug) { - std::vector args; - args.push_back(aug); - parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); - - args.assign(1, tobe_auged); - - args.push_back(false); - - parse->EventItem(EVENT_AUGMENT_REMOVE, this, aug, nullptr, "", in_augment->augment_index, &args); - } - else - { - Message(13, "Error: Could not find augmentation at index %i. Aborting."); - return; - } - old_aug = tobe_auged->RemoveAugment(in_augment->augment_index); - - itemOneToPush = tobe_auged->Clone(); - if (old_aug) - itemTwoToPush = old_aug->Clone(); - if (itemOneToPush && itemTwoToPush && auged_with) - { - DeleteItemInInventory(slot_id, 0, true); - DeleteItemInInventory(aug_slot_id, auged_with->IsStackable() ? 1 : 0, true); - if (!PutItemInInventory(slot_id, *itemOneToPush, true)) - { - Message(15, "Shouldn't happen, contact an admin!"); - } - - if (PutItemInInventory(MainCursor, *itemTwoToPush, true)) - { - CalcBonuses(); - //Message(15, "Successfully removed an augmentation!"); - } - } - } - } - else - { - Object::HandleAugmentation(this, in_augment, m_tradeskill_object); - } - return; -} - -void Client::Handle_OP_AutoAttack(const EQApplicationPacket *app) -{ - if (app->size != 4) { - LogFile->write(EQEMuLog::Error, "OP size error: OP_AutoAttack expected:4 got:%i", app->size); - return; - } - - if (app->pBuffer[0] == 0) - { - auto_attack = false; - if (IsAIControlled()) - return; - attack_timer.Disable(); - ranged_timer.Disable(); - attack_dw_timer.Disable(); - - m_AutoAttackPosition = xyz_heading::Origin(); - m_AutoAttackTargetLocation = xyz_location::Origin(); - aa_los_them_mob = nullptr; - } - else if (app->pBuffer[0] == 1) - { - auto_attack = true; - auto_fire = false; - if (IsAIControlled()) - return; - SetAttackTimer(); - - if (GetTarget()) - { - aa_los_them_mob = GetTarget(); - m_AutoAttackPosition = GetPosition(); - m_AutoAttackTargetLocation = aa_los_them_mob->GetPosition(); - los_status = CheckLosFN(aa_los_them_mob); - los_status_facing = IsFacingMob(aa_los_them_mob); - } - else - { - m_AutoAttackPosition = GetPosition(); - m_AutoAttackTargetLocation = xyz_location::Origin(); - aa_los_them_mob = nullptr; - los_status = false; - los_status_facing = false; - } - } -} - -void Client::Handle_OP_AutoAttack2(const EQApplicationPacket *app) -{ - return; -} - -void Client::Handle_OP_AutoFire(const EQApplicationPacket *app) -{ - if (app->size != sizeof(bool)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AutoFire expected %i got %i", sizeof(bool), app->size); - DumpPacket(app); - return; - } - bool *af = (bool*)app->pBuffer; - auto_fire = *af; - auto_attack = false; - SetAttackTimer(); -} - -void Client::Handle_OP_Bandolier(const EQApplicationPacket *app) -{ - - // Although there are three different structs for OP_Bandolier, they are all the same size. - // - if (app->size != sizeof(BandolierCreate_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Bandolier expected %i got %i", - sizeof(BandolierCreate_Struct), app->size); - DumpPacket(app); - return; - } - - BandolierCreate_Struct *bs = (BandolierCreate_Struct*)app->pBuffer; - - switch (bs->action) { - case BandolierCreate: - CreateBandolier(app); - break; - case BandolierRemove: - RemoveBandolier(app); - break; - case BandolierSet: - SetBandolier(app); - break; - default: - LogFile->write(EQEMuLog::Debug, "Uknown Bandolier action %i", bs->action); - - } -} - -void Client::Handle_OP_BankerChange(const EQApplicationPacket *app) -{ - if (app->size != sizeof(BankerChange_Struct) && app->size != 4) //Titanium only sends 4 Bytes for this - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_BankerChange expected %i got %i", sizeof(BankerChange_Struct), app->size); - DumpPacket(app); - return; - } - - uint32 distance = 0; - NPC *banker = entity_list.GetClosestBanker(this, distance); - - if (!banker || distance > USE_NPC_RANGE2) - { - char *hacked_string = nullptr; - MakeAnyLenString(&hacked_string, "Player tried to make use of a banker(money) but %s is non-existant or too far away (%u units).", - banker ? banker->GetName() : "UNKNOWN NPC", distance); - database.SetMQDetectionFlag(AccountName(), GetName(), hacked_string, zone->GetShortName()); - safe_delete_array(hacked_string); - return; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_BankerChange, nullptr, sizeof(BankerChange_Struct)); - BankerChange_Struct *bc = (BankerChange_Struct *)outapp->pBuffer; - - if (m_pp.platinum < 0) - m_pp.platinum = 0; - if (m_pp.gold < 0) - m_pp.gold = 0; - if (m_pp.silver < 0) - m_pp.silver = 0; - if (m_pp.copper < 0) - m_pp.copper = 0; - - if (m_pp.platinum_bank < 0) - m_pp.platinum_bank = 0; - if (m_pp.gold_bank < 0) - m_pp.gold_bank = 0; - if (m_pp.silver_bank < 0) - m_pp.silver_bank = 0; - if (m_pp.copper_bank < 0) - m_pp.copper_bank = 0; - - uint64 cp = static_cast(m_pp.copper) + - (static_cast(m_pp.silver) * 10) + - (static_cast(m_pp.gold) * 100) + - (static_cast(m_pp.platinum) * 1000); - - m_pp.copper = cp % 10; - cp /= 10; - m_pp.silver = cp % 10; - cp /= 10; - m_pp.gold = cp % 10; - cp /= 10; - m_pp.platinum = cp; - - cp = static_cast(m_pp.copper_bank) + - (static_cast(m_pp.silver_bank) * 10) + - (static_cast(m_pp.gold_bank) * 100) + - (static_cast(m_pp.platinum_bank) * 1000); - - m_pp.copper_bank = cp % 10; - cp /= 10; - m_pp.silver_bank = cp % 10; - cp /= 10; - m_pp.gold_bank = cp % 10; - cp /= 10; - m_pp.platinum_bank = cp; - - bc->copper = m_pp.copper; - bc->silver = m_pp.silver; - bc->gold = m_pp.gold; - bc->platinum = m_pp.platinum; - - bc->copper_bank = m_pp.copper_bank; - bc->silver_bank = m_pp.silver_bank; - bc->gold_bank = m_pp.gold_bank; - bc->platinum_bank = m_pp.platinum_bank; - - FastQueuePacket(&outapp); - - return; -} - -void Client::Handle_OP_Barter(const EQApplicationPacket *app) -{ - - if (app->size < 4) - { - LogFile->write(EQEMuLog::Debug, "OP_Barter packet below minimum expected size. The packet was %i bytes.", app->size); - DumpPacket(app); - return; - } - - char* Buf = (char *)app->pBuffer; - - // The first 4 bytes of the packet determine the action. A lot of Barter packets require the - // packet the client sent, sent back to it as an acknowledgement. - // - uint32 Action = VARSTRUCT_DECODE_TYPE(uint32, Buf); - - _pkt(TRADING__BARTER, app); - - switch (Action) - { - - case Barter_BuyerSearch: - { - BuyerItemSearch(app); - break; - } - - case Barter_SellerSearch: - { - BarterSearchRequest_Struct *bsr = (BarterSearchRequest_Struct*)app->pBuffer; - SendBuyerResults(bsr->SearchString, bsr->SearchID); - break; - } - - case Barter_BuyerModeOn: - { - if (!Trader) { - ToggleBuyerMode(true); - } - else { - Buf = (char *)app->pBuffer; - VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerModeOff); - Message(13, "You cannot be a Trader and Buyer at the same time."); - } - QueuePacket(app); - break; - } - - case Barter_BuyerModeOff: - { - QueuePacket(app); - ToggleBuyerMode(false); - break; - } - - case Barter_BuyerItemUpdate: - { - UpdateBuyLine(app); - break; - } - - case Barter_BuyerItemRemove: - { - BuyerRemoveItem_Struct* bris = (BuyerRemoveItem_Struct*)app->pBuffer; - database.RemoveBuyLine(CharacterID(), bris->BuySlot); - QueuePacket(app); - break; - } - - case Barter_SellItem: - { - SellToBuyer(app); - break; - } - - case Barter_BuyerInspectBegin: - { - ShowBuyLines(app); - break; - } - - case Barter_BuyerInspectEnd: - { - BuyerInspectRequest_Struct* bir = (BuyerInspectRequest_Struct*)app->pBuffer; - Client *Buyer = entity_list.GetClientByID(bir->BuyerID); - if (Buyer) - Buyer->WithCustomer(0); - - break; - } - - case Barter_BarterItemInspect: - { - BarterItemSearchLinkRequest_Struct* bislr = (BarterItemSearchLinkRequest_Struct*)app->pBuffer; - - const Item_Struct* item = database.GetItem(bislr->ItemID); - - if (!item) - Message(13, "Error: This item does not exist!"); - else - { - ItemInst* inst = database.CreateItem(item); - if (inst) - { - SendItemPacket(0, inst, ItemPacketViewLink); - safe_delete(inst); - } - } - break; - } - - case Barter_Welcome: - { - SendBazaarWelcome(); - break; - } - - case Barter_WelcomeMessageUpdate: - { - BuyerWelcomeMessageUpdate_Struct* bwmu = (BuyerWelcomeMessageUpdate_Struct*)app->pBuffer; - SetBuyerWelcomeMessage(bwmu->WelcomeMessage); - break; - } - - case Barter_BuyerItemInspect: - { - BuyerItemSearchLinkRequest_Struct* bislr = (BuyerItemSearchLinkRequest_Struct*)app->pBuffer; - - const Item_Struct* item = database.GetItem(bislr->ItemID); - - if (!item) - Message(13, "Error: This item does not exist!"); - else - { - ItemInst* inst = database.CreateItem(item); - if (inst) - { - SendItemPacket(0, inst, ItemPacketViewLink); - safe_delete(inst); - } - } - break; - } - - case Barter_Unknown23: - { - // Sent by SoD client for no discernible reason. - break; - } - - default: - Message(13, "Unrecognised Barter action."); - _log(TRADING__BARTER, "Unrecognised Barter Action %i", Action); - - } -} - -void Client::Handle_OP_BazaarInspect(const EQApplicationPacket *app) -{ - if (app->size != sizeof(BazaarInspect_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for BazaarInspect_Struct: Expected %i, Got %i", - sizeof(BazaarInspect_Struct), app->size); - return; - } - - BazaarInspect_Struct* bis = (BazaarInspect_Struct*)app->pBuffer; - - const Item_Struct* item = database.GetItem(bis->ItemID); - - if (!item) { - Message(13, "Error: This item does not exist!"); - return; - } - - ItemInst* inst = database.CreateItem(item); - - if (inst) { - SendItemPacket(0, inst, ItemPacketViewLink); - safe_delete(inst); - } - - return; -} - -void Client::Handle_OP_BazaarSearch(const EQApplicationPacket *app) -{ - _pkt(TRADING__PACKETS, app); - - if (app->size == sizeof(BazaarSearch_Struct)) { - - BazaarSearch_Struct* bss = (BazaarSearch_Struct*)app->pBuffer; - - this->SendBazaarResults(bss->TraderID, bss->Class_, bss->Race, bss->ItemStat, bss->Slot, bss->Type, - bss->Name, bss->MinPrice * 1000, bss->MaxPrice * 1000); - } - else if (app->size == sizeof(BazaarWelcome_Struct)) { - - BazaarWelcome_Struct* bws = (BazaarWelcome_Struct*)app->pBuffer; - - if (bws->Beginning.Action == BazaarWelcome) - SendBazaarWelcome(); - } - else if (app->size == sizeof(NewBazaarInspect_Struct)) { - - NewBazaarInspect_Struct *nbis = (NewBazaarInspect_Struct*)app->pBuffer; - - Client *c = entity_list.GetClientByName(nbis->Name); - if (c) { - ItemInst* inst = c->FindTraderItemBySerialNumber(nbis->SerialNumber); - if (inst) - SendItemPacket(0, inst, ItemPacketViewLink); - } - return; - } - else { - _log(TRADING__CLIENT, "Malformed BazaarSearch_Struct packe, Action %it received, ignoring..."); - LogFile->write(EQEMuLog::Error, "Malformed BazaarSearch_Struct packet received, ignoring...\n"); - } - - return; -} - -void Client::Handle_OP_Begging(const EQApplicationPacket *app) -{ - if (!p_timers.Expired(&database, pTimerBeggingPickPocket, false)) - { - Message(13, "Ability recovery time not yet met."); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Begging, sizeof(BeggingResponse_Struct)); - BeggingResponse_Struct *brs = (BeggingResponse_Struct*)outapp->pBuffer; - brs->Result = 0; - FastQueuePacket(&outapp); - return; - } - - if (!HasSkill(SkillBegging) || !GetTarget()) - return; - - if (GetTarget()->GetClass() == LDON_TREASURE) - return; - - p_timers.Start(pTimerBeggingPickPocket, 8); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Begging, sizeof(BeggingResponse_Struct)); - BeggingResponse_Struct *brs = (BeggingResponse_Struct*)outapp->pBuffer; - - brs->Result = 0; // Default, Fail. - if (GetTarget() == this) - { - FastQueuePacket(&outapp); - return; - } - - int RandomChance = zone->random.Int(0, 100); - - int ChanceToAttack = 0; - - if (GetLevel() > GetTarget()->GetLevel()) - ChanceToAttack = zone->random.Int(0, 15); - else - ChanceToAttack = zone->random.Int(((this->GetTarget()->GetLevel() - this->GetLevel()) * 10) - 5, ((this->GetTarget()->GetLevel() - this->GetLevel()) * 10)); - - if (ChanceToAttack < 0) - ChanceToAttack = -ChanceToAttack; - - if (RandomChance < ChanceToAttack) - { - GetTarget()->Attack(this); - QueuePacket(outapp); - safe_delete(outapp); - return; - } - - uint16 CurrentSkill = GetSkill(SkillBegging); - - float ChanceToBeg = ((float)(CurrentSkill / 700.0f) + 0.15f) * 100; - - if (RandomChance < ChanceToBeg) - { - brs->Amount = zone->random.Int(1, 10); - // This needs some work to determine how much money they can beg, based on skill level etc. - if (CurrentSkill < 50) - { - brs->Result = 4; // Copper - AddMoneyToPP(brs->Amount, false); - } - else - { - brs->Result = 3; // Silver - AddMoneyToPP(brs->Amount * 10, false); - } - - } - QueuePacket(outapp); - safe_delete(outapp); - CheckIncreaseSkill(SkillBegging, nullptr, -10); -} - -void Client::Handle_OP_Bind_Wound(const EQApplicationPacket *app) -{ - if (app->size != sizeof(BindWound_Struct)){ - LogFile->write(EQEMuLog::Error, "Size mismatch for Bind wound packet"); - DumpPacket(app); - } - BindWound_Struct* bind_in = (BindWound_Struct*)app->pBuffer; - Mob* bindmob = entity_list.GetMob(bind_in->to); - if (!bindmob){ - LogFile->write(EQEMuLog::Error, "Bindwound on non-exsistant mob from %s", this->GetName()); - } - else { - LogFile->write(EQEMuLog::Debug, "BindWound in: to:\'%s\' from=\'%s\'", bindmob->GetName(), GetName()); - BindWound(bindmob, true); - } - return; -} - -void Client::Handle_OP_BlockedBuffs(const EQApplicationPacket *app) -{ - if (!RuleB(Spells, EnableBlockedBuffs)) - return; - - if (app->size != sizeof(BlockedBuffs_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_BlockedBuffs expected %i got %i", - sizeof(BlockedBuffs_Struct), app->size); - - DumpPacket(app); - - return; - } - - std::set::iterator Iterator; - - BlockedBuffs_Struct *bbs = (BlockedBuffs_Struct*)app->pBuffer; - - std::set *BlockedBuffs = bbs->Pet ? &PetBlockedBuffs : &PlayerBlockedBuffs; - - if (bbs->Initialise == 1) - { - BlockedBuffs->clear(); - - for (unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) - { - if ((IsValidSpell(bbs->SpellID[i])) && IsBeneficialSpell(bbs->SpellID[i]) && !spells[bbs->SpellID[i]].no_block) - { - if (BlockedBuffs->find(bbs->SpellID[i]) == BlockedBuffs->end()) - BlockedBuffs->insert(bbs->SpellID[i]); - } - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_BlockedBuffs, sizeof(BlockedBuffs_Struct)); - - BlockedBuffs_Struct *obbs = (BlockedBuffs_Struct*)outapp->pBuffer; - - for (unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) - obbs->SpellID[i] = -1; - - obbs->Pet = bbs->Pet; - obbs->Initialise = 1; - obbs->Flags = 0x54; - obbs->Count = BlockedBuffs->size(); - - unsigned int Element = 0; - - Iterator = BlockedBuffs->begin(); - - while (Iterator != BlockedBuffs->end()) - { - obbs->SpellID[Element++] = (*Iterator); - ++Iterator; - } - - FastQueuePacket(&outapp); - return; - } - - if ((bbs->Initialise == 0) && (bbs->Count > 0)) - { - EQApplicationPacket *outapp = new EQApplicationPacket(OP_BlockedBuffs, sizeof(BlockedBuffs_Struct)); - - BlockedBuffs_Struct *obbs = (BlockedBuffs_Struct*)outapp->pBuffer; - - for (unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) - obbs->SpellID[i] = -1; - - obbs->Pet = bbs->Pet; - obbs->Initialise = 0; - obbs->Flags = 0x54; - - for (unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) - { - if (!IsValidSpell(bbs->SpellID[i]) || !IsBeneficialSpell(bbs->SpellID[i]) || spells[bbs->SpellID[i]].no_block) - continue; - - if ((BlockedBuffs->size() < BLOCKED_BUFF_COUNT) && (BlockedBuffs->find(bbs->SpellID[i]) == BlockedBuffs->end())) - BlockedBuffs->insert(bbs->SpellID[i]); - } - obbs->Count = BlockedBuffs->size(); - - Iterator = BlockedBuffs->begin(); - - unsigned int Element = 0; - - while (Iterator != BlockedBuffs->end()) - { - obbs->SpellID[Element++] = (*Iterator); - ++Iterator; - } - - FastQueuePacket(&outapp); - } -} - -void Client::Handle_OP_BoardBoat(const EQApplicationPacket *app) -{ - // this sends unclean mob name, so capped at 64 - // a_boat006 - if (app->size <= 5 || app->size > 64) { - LogFile->write(EQEMuLog::Error, "Size mismatch in OP_BoardBoad. Expected greater than 5 less than 64, got %i", app->size); - DumpPacket(app); - return; - } - - char boatname[64]; - memcpy(boatname, app->pBuffer, app->size); - boatname[63] = '\0'; - - Mob* boat = entity_list.GetMob(boatname); - if (!boat || (boat->GetRace() != CONTROLLED_BOAT && boat->GetRace() != 502)) - return; - BoatID = boat->GetID(); // set the client's BoatID to show that it's on this boat - - return; -} - -void Client::Handle_OP_Buff(const EQApplicationPacket *app) -{ - if (app->size != sizeof(SpellBuffFade_Struct)) - { - LogFile->write(EQEMuLog::Error, "Size mismatch in OP_Buff. expected %i got %i", sizeof(SpellBuffFade_Struct), app->size); - DumpPacket(app); - return; - } - - SpellBuffFade_Struct* sbf = (SpellBuffFade_Struct*)app->pBuffer; - uint32 spid = sbf->spellid; - mlog(SPELLS__BUFFS, "Client requested that buff with spell id %d be canceled.", spid); - - //something about IsDetrimentalSpell() crashes this portion of code.. - //tbh we shouldn't use it anyway since this is a simple red vs blue buff check and - //isdetrimentalspell() is much more complex - if (spid == 0xFFFF || (IsValidSpell(spid) && (spells[spid].goodEffect == 0))) - QueuePacket(app); - else - BuffFadeBySpellID(spid); - - return; -} - -void Client::Handle_OP_BuffRemoveRequest(const EQApplicationPacket *app) -{ - // In SoD, this is used for clicking off Pet Buffs only. In Underfoot, it is used both for Client and Pets - // The payload contains buffslot and EntityID only, so we must check if the EntityID is ours or our pets. - // - VERIFY_PACKET_LENGTH(OP_BuffRemoveRequest, app, BuffRemoveRequest_Struct); - - BuffRemoveRequest_Struct *brrs = (BuffRemoveRequest_Struct*)app->pBuffer; - - Mob *m = nullptr; - - if (brrs->EntityID == GetID()) - m = this; - else if (brrs->EntityID == GetPetID()) - m = GetPet(); - - if (!m) - return; - - if (brrs->SlotID > (uint32)m->GetMaxTotalSlots()) - return; - - uint16 SpellID = m->GetSpellIDFromSlot(brrs->SlotID); - - if (SpellID && IsBeneficialSpell(SpellID)) - m->BuffFadeBySlot(brrs->SlotID, true); -} - -void Client::Handle_OP_Bug(const EQApplicationPacket *app) -{ - if (app->size != sizeof(BugStruct)) - printf("Wrong size of BugStruct got %d expected %zu!\n", app->size, sizeof(BugStruct)); - else{ - BugStruct* bug = (BugStruct*)app->pBuffer; - database.UpdateBug(bug); - } - return; -} - -void Client::Handle_OP_Camp(const EQApplicationPacket *app) -{ -#ifdef BOTS - // This block is necessary to clean up any bot objects owned by a Client - Bot::BotHealRotationsClear(this); - Bot::BotOrderCampAll(this); -#endif - if (IsLFP()) - worldserver.StopLFP(CharacterID()); - - if (GetGM()) - { - OnDisconnect(true); - return; - } - camp_timer.Start(29000, true); - return; -} - -void Client::Handle_OP_CancelTask(const EQApplicationPacket *app) -{ - - if (app->size != sizeof(CancelTask_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_CancelTask expected %i got %i", - sizeof(CancelTask_Struct), app->size); - DumpPacket(app); - return; - } - CancelTask_Struct *cts = (CancelTask_Struct*)app->pBuffer; - - if (RuleB(TaskSystem, EnableTaskSystem) && taskstate) - taskstate->CancelTask(this, cts->SequenceNumber); -} - -void Client::Handle_OP_CancelTrade(const EQApplicationPacket *app) -{ - if (app->size != sizeof(CancelTrade_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_CancelTrade, size=%i, expected %i", app->size, sizeof(CancelTrade_Struct)); - return; - } - Mob* with = trade->With(); - if (with && with->IsClient()) { - CancelTrade_Struct* msg = (CancelTrade_Struct*)app->pBuffer; - - // Forward cancel packet to other client - msg->fromid = with->GetID(); - //msg->action = 1; - - with->CastToClient()->QueuePacket(app); - - // Put trade items/cash back into inventory - FinishTrade(this); - trade->Reset(); - } - else if (with){ - CancelTrade_Struct* msg = (CancelTrade_Struct*)app->pBuffer; - msg->fromid = with->GetID(); - QueuePacket(app); - FinishTrade(this); - trade->Reset(); - } - EQApplicationPacket end_trade1(OP_FinishWindow, 0); - QueuePacket(&end_trade1); - - EQApplicationPacket end_trade2(OP_FinishWindow2, 0); - QueuePacket(&end_trade2); - return; -} - -void Client::Handle_OP_CastSpell(const EQApplicationPacket *app) -{ - if (app->size != sizeof(CastSpell_Struct)) { - std::cout << "Wrong size: OP_CastSpell, size=" << app->size << ", expected " << sizeof(CastSpell_Struct) << std::endl; - return; - } - if (IsAIControlled()) { - this->Message_StringID(13, NOT_IN_CONTROL); - //Message(13, "You cant cast right now, you arent in control of yourself!"); - return; - } - - CastSpell_Struct* castspell = (CastSpell_Struct*)app->pBuffer; - - m_TargetRing = xyz_location(castspell->x_pos, castspell->y_pos, castspell->z_pos); - -#ifdef _EQDEBUG - LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[0], castspell->cs_unknown[0]); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[1], castspell->cs_unknown[1]); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[2], castspell->cs_unknown[2]); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[3], castspell->cs_unknown[3]); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: 32 %p %u", &castspell->cs_unknown, *(uint32*)castspell->cs_unknown); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: 32 %p %i", &castspell->cs_unknown, *(uint32*)castspell->cs_unknown); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: 16 %p %u %u", &castspell->cs_unknown, *(uint16*)castspell->cs_unknown, *(uint16*)castspell->cs_unknown + sizeof(uint16)); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: 16 %p %i %i", &castspell->cs_unknown, *(uint16*)castspell->cs_unknown, *(uint16*)castspell->cs_unknown + sizeof(uint16)); -#endif - LogFile->write(EQEMuLog::Debug, "OP CastSpell: slot=%d, spell=%d, target=%d, inv=%lx", castspell->slot, castspell->spell_id, castspell->target_id, (unsigned long)castspell->inventoryslot); - - std::cout << "OP_CastSpell " << castspell->slot << " spell " << castspell->spell_id << " inventory slot " << castspell->inventoryslot << "\n" << std::endl; - - /* Memorized Spell */ - if (m_pp.mem_spells[castspell->slot] && m_pp.mem_spells[castspell->slot] == castspell->spell_id){ - - uint16 spell_to_cast = 0; - if (castspell->slot < MAX_PP_MEMSPELL) { - spell_to_cast = m_pp.mem_spells[castspell->slot]; - if (spell_to_cast != castspell->spell_id) { - InterruptSpell(castspell->spell_id); //CHEATER!!! - return; - } - } - else if (castspell->slot >= MAX_PP_MEMSPELL) { - InterruptSpell(); - return; - } - - m_TargetRing = xyz_location(castspell->x_pos, castspell->y_pos, castspell->z_pos); - - CastSpell(spell_to_cast, castspell->target_id, castspell->slot); - } - /* Spell Slot or Potion Belt Slot */ - else if ((castspell->slot == USE_ITEM_SPELL_SLOT) || (castspell->slot == POTION_BELT_SPELL_SLOT)|| (castspell->slot == TARGET_RING_SPELL_SLOT)) // ITEM or POTION cast - { - //discipline, using the item spell slot - if (castspell->inventoryslot == INVALID_INDEX) { - if (!UseDiscipline(castspell->spell_id, castspell->target_id)) { - LogFile->write(EQEMuLog::Debug, "Unknown ability being used by %s, spell being cast is: %i\n", GetName(), castspell->spell_id); - InterruptSpell(castspell->spell_id); - } - return; - } - else if (m_inv.SupportsClickCasting(castspell->inventoryslot) || (castspell->slot == POTION_BELT_SPELL_SLOT) || (castspell->slot == TARGET_RING_SPELL_SLOT)) // sanity check - { - // packet field types will be reviewed as packet transistions occur -U - const ItemInst* inst = m_inv[castspell->inventoryslot]; //slot values are int16, need to check packet on this field - //bool cancast = true; - if (inst && inst->IsType(ItemClassCommon)) - { - const Item_Struct* item = inst->GetItem(); - if (item->Click.Effect != (uint32)castspell->spell_id) - { - database.SetMQDetectionFlag(account_name, name, "OP_CastSpell with item, tried to cast a different spell.", zone->GetShortName()); - InterruptSpell(castspell->spell_id); //CHEATER!! - return; - } - - if ((item->Click.Type == ET_ClickEffect) || (item->Click.Type == ET_Expendable) || (item->Click.Type == ET_EquipClick) || (item->Click.Type == ET_ClickEffect2)) - { - if (item->Click.Level2 > 0) - { - if (GetLevel() >= item->Click.Level2) - { - ItemInst* p_inst = (ItemInst*)inst; - int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", castspell->inventoryslot); - - if (i == 0) { - CastSpell(item->Click.Effect, castspell->target_id, castspell->slot, item->CastTime, 0, 0, castspell->inventoryslot); - } - else { - InterruptSpell(castspell->spell_id); - return; - } - } - else - { - database.SetMQDetectionFlag(account_name, name, "OP_CastSpell with item, did not meet req level.", zone->GetShortName()); - Message(0, "Error: level not high enough.", castspell->inventoryslot); - InterruptSpell(castspell->spell_id); - } - } - else - { - ItemInst* p_inst = (ItemInst*)inst; - int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", castspell->inventoryslot); - - if (i == 0) { - CastSpell(item->Click.Effect, castspell->target_id, castspell->slot, item->CastTime, 0, 0, castspell->inventoryslot); - } - else { - InterruptSpell(castspell->spell_id); - return; - } - } - } - else - { - Message(0, "Error: unknown item->Click.Type (0x%02x)", item->Click.Type); - } - } - else - { - Message(0, "Error: item not found in inventory slot #%i", castspell->inventoryslot); - InterruptSpell(castspell->spell_id); - } - } - else - { - Message(0, "Error: castspell->inventoryslot >= %i (0x%04x)", MainCursor, castspell->inventoryslot); - InterruptSpell(castspell->spell_id); - } - } - /* Discipline */ - else if (castspell->slot == DISCIPLINE_SPELL_SLOT) { - if (!UseDiscipline(castspell->spell_id, castspell->target_id)) { - printf("Unknown ability being used by %s, spell being cast is: %i\n", GetName(), castspell->spell_id); - InterruptSpell(castspell->spell_id); - return; - } - } - /* ABILITY cast (LoH and Harm Touch) */ - else if (castspell->slot == ABILITY_SPELL_SLOT) { - uint16 spell_to_cast = 0; - - if (castspell->spell_id == SPELL_LAY_ON_HANDS && GetClass() == PALADIN) { - if (!p_timers.Expired(&database, pTimerLayHands)) { - Message(13, "Ability recovery time not yet met."); - InterruptSpell(castspell->spell_id); - return; - } - spell_to_cast = SPELL_LAY_ON_HANDS; - p_timers.Start(pTimerLayHands, LayOnHandsReuseTime); - } - else if ((castspell->spell_id == SPELL_HARM_TOUCH - || castspell->spell_id == SPELL_HARM_TOUCH2) && GetClass() == SHADOWKNIGHT) { - if (!p_timers.Expired(&database, pTimerHarmTouch)) { - Message(13, "Ability recovery time not yet met."); - InterruptSpell(castspell->spell_id); - return; - } - - // determine which version of HT we are casting based on level - if (GetLevel() < 40) - spell_to_cast = SPELL_HARM_TOUCH; - else - spell_to_cast = SPELL_HARM_TOUCH2; - - p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime); - } - - if (spell_to_cast > 0) // if we've matched LoH or HT, cast now - CastSpell(spell_to_cast, castspell->target_id, castspell->slot); - } - return; -} - -void Client::Handle_OP_ChannelMessage(const EQApplicationPacket *app) -{ - ChannelMessage_Struct* cm = (ChannelMessage_Struct*)app->pBuffer; - - if (app->size < sizeof(ChannelMessage_Struct)) { - std::cout << "Wrong size " << app->size << ", should be " << sizeof(ChannelMessage_Struct) << "+ on 0x" << std::hex << std::setfill('0') << std::setw(4) << app->GetOpcode() << std::dec << std::endl; - return; - } - if (IsAIControlled()) { - Message(13, "You try to speak but cant move your mouth!"); - return; - } - - ChannelMessageReceived(cm->chan_num, cm->language, cm->skill_in_language, cm->message, cm->targetname); - return; -} - -void Client::Handle_OP_ClearBlockedBuffs(const EQApplicationPacket *app) -{ - if (!RuleB(Spells, EnableBlockedBuffs)) - return; - - if (app->size != 1) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_ClearBlockedBuffs expected 1 got %i", app->size); - - DumpPacket(app); - - return; - } - - bool Pet = app->pBuffer[0]; - - if (Pet) - PetBlockedBuffs.clear(); - else - PlayerBlockedBuffs.clear(); - - QueuePacket(app); -} - -void Client::Handle_OP_ClearNPCMarks(const EQApplicationPacket *app) -{ - - if (app->size != 0) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_ClearNPCMarks expected 0 got %i", - app->size); - - DumpPacket(app); - - return; - } - - Group *g = GetGroup(); - - if (g) - g->ClearAllNPCMarks(); -} - -void Client::Handle_OP_ClearSurname(const EQApplicationPacket *app) -{ - ChangeLastName(""); -} - -void Client::Handle_OP_ClickDoor(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ClickDoor_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_ClickDoor, size=%i, expected %i", app->size, sizeof(ClickDoor_Struct)); - return; - } - ClickDoor_Struct* cd = (ClickDoor_Struct*)app->pBuffer; - Doors* currentdoor = entity_list.FindDoor(cd->doorid); - if (!currentdoor) - { - Message(0, "Unable to find door, please notify a GM (DoorID: %i).", cd->doorid); - return; - } - - char buf[20]; - snprintf(buf, 19, "%u", cd->doorid); - buf[19] = '\0'; - std::vector args; - args.push_back(currentdoor); - parse->EventPlayer(EVENT_CLICK_DOOR, this, buf, 0, &args); - - currentdoor->HandleClick(this, 0); - return; -} - -void Client::Handle_OP_ClickObject(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ClickObject_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on ClickObject_Struct: Expected %i, Got %i", - sizeof(ClickObject_Struct), app->size); - return; - } - - ClickObject_Struct* click_object = (ClickObject_Struct*)app->pBuffer; - Entity* entity = entity_list.GetID(click_object->drop_id); - if (entity && entity->IsObject()) { - Object* object = entity->CastToObject(); - - object->HandleClick(this, click_object); - - std::vector args; - args.push_back(object); - - char buf[10]; - snprintf(buf, 9, "%u", click_object->drop_id); - buf[9] = '\0'; - parse->EventPlayer(EVENT_CLICK_OBJECT, this, buf, 0, &args); - } - - // Observed in RoF after OP_ClickObjectAction: - //EQApplicationPacket end_trade2(OP_FinishWindow2, 0); - //QueuePacket(&end_trade2); - return; -} - -void Client::Handle_OP_ClickObjectAction(const EQApplicationPacket *app) -{ - if (app->size == 0) { - // RoF sends this packet 0 sized when switching from auto-combine to experiment windows. - // Not completely sure if 0 sized is for this or for closing objects as commented out below - EQApplicationPacket end_trade1(OP_FinishWindow, 0); - QueuePacket(&end_trade1); - - EQApplicationPacket end_trade2(OP_FinishWindow2, 0); - QueuePacket(&end_trade2); - - return; - - // RoF sends a 0 sized packet for closing objects - /* - Object* object = GetTradeskillObject(); - if (object) { - object->CastToObject()->Close(); - } - */ - } - else - { - if (app->size != sizeof(ClickObjectAction_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_ClickObjectAction: Expected %i, Got %i", - sizeof(ClickObjectAction_Struct), app->size); - return; - } - - ClickObjectAction_Struct* oos = (ClickObjectAction_Struct*)app->pBuffer; - Entity* entity = entity_list.GetEntityObject(oos->drop_id); - if (entity && entity->IsObject()) { - Object* object = entity->CastToObject(); - if (oos->open == 0) { - object->Close(); - } - else { - LogFile->write(EQEMuLog::Error, "Unsupported action %d in OP_ClickObjectAction", oos->open); - } - } - else { - LogFile->write(EQEMuLog::Error, "Invalid object %d in OP_ClickObjectAction", oos->drop_id); - } - } - - SetTradeskillObject(nullptr); - - EQApplicationPacket end_trade1(OP_FinishWindow, 0); - QueuePacket(&end_trade1); - - EQApplicationPacket end_trade2(OP_FinishWindow2, 0); - QueuePacket(&end_trade2); - return; -} - -void Client::Handle_OP_ClientError(const EQApplicationPacket *app) -{ - ClientError_Struct* error = (ClientError_Struct*)app->pBuffer; - LogFile->write(EQEMuLog::Error, "Client error: %s", error->character_name); - LogFile->write(EQEMuLog::Error, "Error message:%s", error->message); - return; -} - -void Client::Handle_OP_ClientTimeStamp(const EQApplicationPacket *app) -{ - return; -} - -void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) -{ - if (IsAIControlled()) - return; - - if(dead) - return; - - //currently accepting two sizes, one has an extra byte on the end - if (app->size != sizeof(PlayerPositionUpdateClient_Struct) - && app->size != (sizeof(PlayerPositionUpdateClient_Struct)+1) - ) { - LogFile->write(EQEMuLog::Error, "OP size error: OP_ClientUpdate expected:%i got:%i", sizeof(PlayerPositionUpdateClient_Struct), app->size); - return; - } - PlayerPositionUpdateClient_Struct* ppu = (PlayerPositionUpdateClient_Struct*)app->pBuffer; - - if(ppu->spawn_id != GetID()) { - // check if the id is for a boat the player is controlling - if (ppu->spawn_id == BoatID) { - Mob* boat = entity_list.GetMob(BoatID); - if (boat == 0) { // if the boat ID is invalid, reset the id and abort - BoatID = 0; - return; - } - - // set the boat's position deltas - auto boatDelta = xyz_heading(ppu->delta_x, ppu->delta_y, ppu->delta_z, ppu->delta_heading); - boat->SetDelta(boatDelta); - // send an update to everyone nearby except the client controlling the boat - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); - PlayerPositionUpdateServer_Struct* ppus = (PlayerPositionUpdateServer_Struct*)outapp->pBuffer; - boat->MakeSpawnUpdate(ppus); - entity_list.QueueCloseClients(boat,outapp,true,300,this,false); - safe_delete(outapp); - // update the boat's position on the server, without sending an update - boat->GMMove(ppu->x_pos, ppu->y_pos, ppu->z_pos, EQ19toFloat(ppu->heading), false); - return; - } - else return; // if not a boat, do nothing - } - - float dist = 0; - float tmp; - tmp = m_Position.m_X - ppu->x_pos; - dist += tmp*tmp; - tmp = m_Position.m_Y - ppu->y_pos; - dist += tmp*tmp; - dist = sqrt(dist); - - //the purpose of this first block may not be readily apparent - //basically it's so people don't do a moderate warp every 2.5 seconds - //letting it even out and basically getting the job done without triggering - if(dist == 0) - { - if(m_DistanceSinceLastPositionCheck > 0.0) - { - uint32 cur_time = Timer::GetCurrentTime(); - if((cur_time - m_TimeSinceLastPositionCheck) > 0) - { - float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck); - float runs = GetRunspeed(); - if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor))) - { - if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor))))) - { - if(IsShadowStepExempted()) - { - if(m_DistanceSinceLastPositionCheck > 800) - { - CheatDetected(MQWarpShadowStep, ppu->x_pos, ppu->y_pos, ppu->z_pos); - } - } - else if(IsKnockBackExempted()) - { - //still potential to trigger this if you're knocked back off a - //HUGE fall that takes > 2.5 seconds - if(speed > 30.0f) - { - CheatDetected(MQWarpKnockBack, ppu->x_pos, ppu->y_pos, ppu->z_pos); - } - } - else if(!IsPortExempted()) - { - if(!IsMQExemptedArea(zone->GetZoneID(), ppu->x_pos, ppu->y_pos, ppu->z_pos)) - { - if(speed > (runs * 2 * RuleR(Zone, MQWarpDetectionDistanceFactor))) - { - m_TimeSinceLastPositionCheck = cur_time; - m_DistanceSinceLastPositionCheck = 0.0f; - CheatDetected(MQWarp, ppu->x_pos, ppu->y_pos, ppu->z_pos); - //Death(this, 10000000, SPELL_UNKNOWN, _1H_BLUNT); - } - else - { - CheatDetected(MQWarpLight, ppu->x_pos, ppu->y_pos, ppu->z_pos); - } - } - } - } - } - SetShadowStepExemption(false); - SetKnockBackExemption(false); - SetPortExemption(false); - m_TimeSinceLastPositionCheck = cur_time; - m_DistanceSinceLastPositionCheck = 0.0f; - m_CheatDetectMoved = false; - } - } - else - { - m_TimeSinceLastPositionCheck = Timer::GetCurrentTime(); - m_CheatDetectMoved = false; - } - } - else - { - m_DistanceSinceLastPositionCheck += dist; - m_CheatDetectMoved = true; - if(m_TimeSinceLastPositionCheck == 0) - { - m_TimeSinceLastPositionCheck = Timer::GetCurrentTime(); - } - else - { - uint32 cur_time = Timer::GetCurrentTime(); - if((cur_time - m_TimeSinceLastPositionCheck) > 2500) - { - float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck); - float runs = GetRunspeed(); - if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor))) - { - if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor))))) - { - if(IsShadowStepExempted()) - { - if(m_DistanceSinceLastPositionCheck > 800) - { - //if(!IsMQExemptedArea(zone->GetZoneID(), ppu->x_pos, ppu->y_pos, ppu->z_pos)) - //{ - CheatDetected(MQWarpShadowStep, ppu->x_pos, ppu->y_pos, ppu->z_pos); - //Death(this, 10000000, SPELL_UNKNOWN, _1H_BLUNT); - //} - } - } - else if(IsKnockBackExempted()) - { - //still potential to trigger this if you're knocked back off a - //HUGE fall that takes > 2.5 seconds - if(speed > 30.0f) - { - CheatDetected(MQWarpKnockBack, ppu->x_pos, ppu->y_pos, ppu->z_pos); - } - } - else if(!IsPortExempted()) - { - if(!IsMQExemptedArea(zone->GetZoneID(), ppu->x_pos, ppu->y_pos, ppu->z_pos)) - { - if(speed > (runs * 2 * RuleR(Zone, MQWarpDetectionDistanceFactor))) - { - m_TimeSinceLastPositionCheck = cur_time; - m_DistanceSinceLastPositionCheck = 0.0f; - CheatDetected(MQWarp, ppu->x_pos, ppu->y_pos, ppu->z_pos); - //Death(this, 10000000, SPELL_UNKNOWN, _1H_BLUNT); - } - else - { - CheatDetected(MQWarpLight, ppu->x_pos, ppu->y_pos, ppu->z_pos); - } - } - } - } - } - SetShadowStepExemption(false); - SetKnockBackExemption(false); - SetPortExemption(false); - m_TimeSinceLastPositionCheck = cur_time; - m_DistanceSinceLastPositionCheck = 0.0f; - } - } - - if(IsDraggingCorpse()) - DragCorpses(); - } - - //Check to see if PPU should trigger an update to the rewind position. - float rewind_x_diff = 0; - float rewind_y_diff = 0; - - rewind_x_diff = ppu->x_pos - m_RewindLocation.m_X; - rewind_x_diff *= rewind_x_diff; - rewind_y_diff = ppu->y_pos - m_RewindLocation.m_Y; - rewind_y_diff *= rewind_y_diff; - - //We only need to store updated values if the player has moved. - //If the player has moved more than units for x or y, then we'll store - //his pre-PPU x and y for /rewind, in case he gets stuck. - if ((rewind_x_diff > 750) || (rewind_y_diff > 750)) - m_RewindLocation = m_Position; - - //If the PPU was a large jump, such as a cross zone gate or Call of Hero, - //just update rewind coords to the new ppu coords. This will prevent exploitation. - - if ((rewind_x_diff > 5000) || (rewind_y_diff > 5000)) - m_RewindLocation = xyz_location(ppu->x_pos, ppu->y_pos, ppu->z_pos); - - if(proximity_timer.Check()) { - entity_list.ProcessMove(this, xyz_location(ppu->x_pos, ppu->y_pos, ppu->z_pos)); - if(RuleB(TaskSystem, EnableTaskSystem) && RuleB(TaskSystem,EnableTaskProximity)) - ProcessTaskProximities(ppu->x_pos, ppu->y_pos, ppu->z_pos); - - m_Proximity = xyz_location(ppu->x_pos, ppu->y_pos, ppu->z_pos); - } - - // Update internal state - m_Delta = xyz_heading(ppu->delta_x, ppu->delta_y, ppu->delta_z, ppu->delta_heading); - -<<<<<<< HEAD - if(IsTracking() && ((m_Position.m_X!=ppu->x_pos) || (m_Position.m_Y!=ppu->y_pos))){ - if(MakeRandomFloat(0, 100) < 70)//should be good -======= - if(IsTracking() && ((x_pos!=ppu->x_pos) || (y_pos!=ppu->y_pos))){ - if(zone->random.Real(0, 100) < 70)//should be good ->>>>>>> master - CheckIncreaseSkill(SkillTracking, nullptr, -20); - } - - // Break Hide if moving without sneaking and set rewind timer if moved - if(ppu->y_pos != m_Position.m_Y || ppu->x_pos != m_Position.m_X){ - if((hidden || improved_hidden) && !sneaking){ - hidden = false; - improved_hidden = false; - if(!invisible) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x03; - sa_out->parameter = 0; - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - } - } - rewind_timer.Start(30000, true); - } - - // Outgoing client packet - float tmpheading = EQ19toFloat(ppu->heading); - - if (!FCMP(ppu->y_pos, m_Position.m_Y) || !FCMP(ppu->x_pos, m_Position.m_X) || !FCMP(tmpheading, m_Position.m_Heading) || ppu->animation != animation) - { - m_Position.m_X = ppu->x_pos; - m_Position.m_Y = ppu->y_pos; - m_Position.m_Z = ppu->z_pos; - m_Position.m_Heading = tmpheading; - animation = ppu->animation; - - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); - PlayerPositionUpdateServer_Struct* ppu = (PlayerPositionUpdateServer_Struct*)outapp->pBuffer; - MakeSpawnUpdate(ppu); - if (gmhideme) - entity_list.QueueClientsStatus(this,outapp,true,Admin(),250); - else - entity_list.QueueCloseClients(this,outapp,true,300,nullptr,false); - safe_delete(outapp); - } - - if(zone->watermap && zone->watermap->InLiquid(m_Position)) - CheckIncreaseSkill(SkillSwimming, nullptr, -17); - - return; -} - -/* -void Client::Handle_OP_CloseContainer(const EQApplicationPacket *app) -{ -if (app->size != sizeof(CloseContainer_Struct)) { -LogFile->write(EQEMuLog::Error, "Invalid size on CloseContainer_Struct: Expected %i, Got %i", -sizeof(CloseContainer_Struct), app->size); -return; -} - -SetTradeskillObject(nullptr); - -ClickObjectAck_Struct* oos = (ClickObjectAck_Struct*)app->pBuffer; -Entity* entity = entity_list.GetEntityObject(oos->drop_id); -if (entity && entity->IsObject()) { -Object* object = entity->CastToObject(); -object->Close(); -} -return; -} -*/ - -void Client::Handle_OP_CombatAbility(const EQApplicationPacket *app) -{ - if (app->size != sizeof(CombatAbility_Struct)) { - std::cout << "Wrong size on OP_CombatAbility. Got: " << app->size << ", Expected: " << sizeof(CombatAbility_Struct) << std::endl; - return; - } - OPCombatAbility(app); - return; -} - -void Client::Handle_OP_ConfirmDelete(const EQApplicationPacket* app) -{ - return; -} - -void Client::Handle_OP_Consent(const EQApplicationPacket *app) -{ - if(app->size<64){ - Consent_Struct* c = (Consent_Struct*)app->pBuffer; - if(strcmp(c->name, GetName()) != 0) { - ServerPacket* pack = new ServerPacket(ServerOP_Consent, sizeof(ServerOP_Consent_Struct)); - ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; - strcpy(scs->grantname, c->name); - strcpy(scs->ownername, GetName()); - scs->message_string_id = 0; - scs->permission = 1; - scs->zone_id = zone->GetZoneID(); - scs->instance_id = zone->GetInstanceID(); - //consent_list.push_back(scs->grantname); - worldserver.SendPacket(pack); - safe_delete(pack); - } - else { - Message_StringID(0, CONSENT_YOURSELF); - } - } - return; -} - -void Client::Handle_OP_ConsentDeny(const EQApplicationPacket *app) -{ - if(app->size<64){ - Consent_Struct* c = (Consent_Struct*)app->pBuffer; - ServerPacket* pack = new ServerPacket(ServerOP_Consent, sizeof(ServerOP_Consent_Struct)); - ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; - strcpy(scs->grantname, c->name); - strcpy(scs->ownername, GetName()); - scs->message_string_id = 0; - scs->permission = 0; - scs->zone_id = zone->GetZoneID(); - scs->instance_id = zone->GetInstanceID(); - //consent_list.remove(scs->grantname); - worldserver.SendPacket(pack); - safe_delete(pack); - } - return; -} - -void Client::Handle_OP_Consider(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Consider_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in Consider expected %i got %i", sizeof(Consider_Struct), app->size); - return; - } - Consider_Struct* conin = (Consider_Struct*)app->pBuffer; - Mob* tmob = entity_list.GetMob(conin->targetid); - if (tmob == 0) - return; - - if (tmob->GetClass() == LDON_TREASURE) - { - Message(15, "%s", tmob->GetCleanName()); - return; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Consider, sizeof(Consider_Struct)); - Consider_Struct* con = (Consider_Struct*)outapp->pBuffer; - con->playerid = GetID(); - con->targetid = conin->targetid; - if (tmob->IsNPC()) - con->faction = GetFactionLevel(character_id, tmob->GetNPCTypeID(), race, class_, deity, (tmob->IsNPC()) ? tmob->CastToNPC()->GetPrimaryFaction() : 0, tmob); // Dec. 20, 2001; TODO: Send the players proper deity - else - con->faction = 1; - con->level = GetLevelCon(tmob->GetLevel()); - if (zone->IsPVPZone()) { - if (!tmob->IsNPC()) - con->pvpcon = tmob->CastToClient()->GetPVP(); - } - - // Mongrel: If we're feigned show NPC as indifferent - if (tmob->IsNPC()) - { - if (GetFeigned()) - con->faction = FACTION_INDIFFERENT; - } - - if (!(con->faction == FACTION_SCOWLS)) - { - if (tmob->IsNPC()) - { - if (tmob->CastToNPC()->IsOnHatelist(this)) - con->faction = FACTION_THREATENLY; - } - } - - if (con->faction == FACTION_APPREHENSIVE) { - con->faction = FACTION_SCOWLS; - } - else if (con->faction == FACTION_DUBIOUS) { - con->faction = FACTION_THREATENLY; - } - else if (con->faction == FACTION_SCOWLS) { - con->faction = FACTION_APPREHENSIVE; - } - else if (con->faction == FACTION_THREATENLY) { - con->faction = FACTION_DUBIOUS; - } - - mod_consider(tmob, con); - - QueuePacket(outapp); - // only wanted to check raid target once - // and need con to still be around so, do it here! - if (tmob->IsRaidTarget()) { - uint32 color = 0; - switch (con->level) { - case CON_GREEN: - color = 2; - break; - case CON_LIGHTBLUE: - color = 10; - break; - case CON_BLUE: - color = 4; - break; - case CON_WHITE: - color = 10; - break; - case CON_YELLOW: - color = 15; - break; - case CON_RED: - color = 13; - break; - } - SendColoredText(color, std::string("This creature would take an army to defeat!")); - } - safe_delete(outapp); - return; -} - -void Client::Handle_OP_ConsiderCorpse(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Consider_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in Consider corpse expected %i got %i", sizeof(Consider_Struct), app->size); - return; - } - Consider_Struct* conin = (Consider_Struct*)app->pBuffer; - Corpse* tcorpse = entity_list.GetCorpseByID(conin->targetid); - if (tcorpse && tcorpse->IsNPCCorpse()) { - uint32 min; uint32 sec; uint32 ttime; - if ((ttime = tcorpse->GetDecayTime()) != 0) { - sec = (ttime / 1000) % 60; // Total seconds - min = (ttime / 60000) % 60; // Total seconds / 60 drop .00 - char val1[20] = { 0 }; - char val2[20] = { 0 }; - Message_StringID(10, CORPSE_DECAY1, ConvertArray(min, val1), ConvertArray(sec, val2)); - } - else { - Message_StringID(10, CORPSE_DECAY_NOW); - } - } - else if (tcorpse && tcorpse->IsPlayerCorpse()) { - uint32 day, hour, min, sec, ttime; - if ((ttime = tcorpse->GetDecayTime()) != 0) { - sec = (ttime / 1000) % 60; // Total seconds - min = (ttime / 60000) % 60; // Total seconds - hour = (ttime / 3600000) % 24; // Total hours - day = ttime / 86400000; // Total Days - if (day) - Message(0, "This corpse will decay in %i days, %i hours, %i minutes and %i seconds.", day, hour, min, sec); - else if (hour) - Message(0, "This corpse will decay in %i hours, %i minutes and %i seconds.", hour, min, sec); - else - Message(0, "This corpse will decay in %i minutes and %i seconds.", min, sec); - - Message(0, "This corpse %s be resurrected.", tcorpse->IsRezzed() ? "cannot" : "can"); - /* - hour = 0; - - if((ttime = tcorpse->GetResTime()) != 0) { - sec = (ttime/1000)%60; // Total seconds - min = (ttime/60000)%60; // Total seconds - hour = (ttime/3600000)%24; // Total hours - if(hour) - Message(0, "This corpse can be resurrected for %i hours, %i minutes and %i seconds.", hour, min, sec); - else - Message(0, "This corpse can be resurrected for %i minutes and %i seconds.", min, sec); - } - else { - Message_StringID(0, CORPSE_TOO_OLD); - } - */ - } - else { - Message_StringID(10, CORPSE_DECAY_NOW); - } - } -} - -void Client::Handle_OP_Consume(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Consume_Struct)) - { - LogFile->write(EQEMuLog::Error, "OP size error: OP_Consume expected:%i got:%i", sizeof(Consume_Struct), app->size); - return; - } - Consume_Struct* pcs = (Consume_Struct*)app->pBuffer; - if (pcs->type == 0x01) - { - if (m_pp.hunger_level > 6000) - { - EQApplicationPacket *outapp; - outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); - Stamina_Struct* sta = (Stamina_Struct*)outapp->pBuffer; - sta->food = m_pp.hunger_level > 6000 ? 6000 : m_pp.hunger_level; - sta->water = m_pp.thirst_level > 6000 ? 6000 : m_pp.thirst_level; - - QueuePacket(outapp); - safe_delete(outapp); - return; - } - } - else if (pcs->type == 0x02) - { - if (m_pp.thirst_level > 6000) - { - EQApplicationPacket *outapp; - outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); - Stamina_Struct* sta = (Stamina_Struct*)outapp->pBuffer; - sta->food = m_pp.hunger_level > 6000 ? 6000 : m_pp.hunger_level; - sta->water = m_pp.thirst_level > 6000 ? 6000 : m_pp.thirst_level; - - QueuePacket(outapp); - safe_delete(outapp); - return; - } - } - - ItemInst *myitem = GetInv().GetItem(pcs->slot); - if (myitem == nullptr) { - LogFile->write(EQEMuLog::Error, "Consuming from empty slot %d", pcs->slot); - return; - } - - const Item_Struct* eat_item = myitem->GetItem(); - if (pcs->type == 0x01) { - Consume(eat_item, ItemTypeFood, pcs->slot, (pcs->auto_consumed == 0xffffffff)); - } - else if (pcs->type == 0x02) { - Consume(eat_item, ItemTypeDrink, pcs->slot, (pcs->auto_consumed == 0xffffffff)); - } - else { - LogFile->write(EQEMuLog::Error, "OP_Consume: unknown type, type:%i", (int)pcs->type); - return; - } - if (m_pp.hunger_level > 50000) - m_pp.hunger_level = 50000; - if (m_pp.thirst_level > 50000) - m_pp.thirst_level = 50000; - EQApplicationPacket *outapp; - outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); - Stamina_Struct* sta = (Stamina_Struct*)outapp->pBuffer; - sta->food = m_pp.hunger_level > 6000 ? 6000 : m_pp.hunger_level; - sta->water = m_pp.thirst_level > 6000 ? 6000 : m_pp.thirst_level; - - QueuePacket(outapp); - safe_delete(outapp); - return; -} - -void Client::Handle_OP_ControlBoat(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ControlBoat_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_ControlBoat, size=%i, expected %i", app->size, sizeof(ControlBoat_Struct)); - return; - } - ControlBoat_Struct* cbs = (ControlBoat_Struct*)app->pBuffer; - Mob* boat = entity_list.GetMob(cbs->boatId); - if (boat == 0) - return; // do nothing if the boat isn't valid - - if (!boat->IsNPC() || (boat->GetRace() != CONTROLLED_BOAT && boat->GetRace() != 502)) - { - char *hacked_string = nullptr; - MakeAnyLenString(&hacked_string, "OP_Control Boat was sent against %s which is of race %u", boat->GetName(), boat->GetRace()); - database.SetMQDetectionFlag(this->AccountName(), this->GetName(), hacked_string, zone->GetShortName()); - safe_delete_array(hacked_string); - return; - } - - if (cbs->TakeControl) { - // this uses the boat's target to indicate who has control of it. It has to check hate to make sure the boat isn't actually attacking anyone. - if ((boat->GetTarget() == 0) || (boat->GetTarget() == this && boat->GetHateAmount(this) == 0)) { - boat->SetTarget(this); - } - else { - this->Message_StringID(13, IN_USE); - return; - } - } - else - boat->SetTarget(0); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ControlBoat, 0); - FastQueuePacket(&outapp); - safe_delete(outapp); - // have the boat signal itself, so quests can be triggered by boat use - boat->CastToNPC()->SignalNPC(0); -} - -void Client::Handle_OP_CorpseDrag(const EQApplicationPacket *app) -{ - if (DraggedCorpses.size() >= (unsigned int)RuleI(Character, MaxDraggedCorpses)) - { - Message_StringID(13, CORPSEDRAG_LIMIT); - return; - } - - VERIFY_PACKET_LENGTH(OP_CorpseDrag, app, CorpseDrag_Struct); - - CorpseDrag_Struct *cds = (CorpseDrag_Struct*)app->pBuffer; - - Mob* corpse = entity_list.GetMob(cds->CorpseName); - - if (!corpse || !corpse->IsPlayerCorpse() || corpse->CastToCorpse()->IsBeingLooted()) - return; - - Client *c = entity_list.FindCorpseDragger(corpse->GetID()); - - if (c) - { - if (c == this) - Message_StringID(MT_DefaultText, CORPSEDRAG_ALREADY, corpse->GetCleanName()); - else - Message_StringID(MT_DefaultText, CORPSEDRAG_SOMEONE_ELSE, corpse->GetCleanName()); - - return; - } - - if (!corpse->CastToCorpse()->Summon(this, false, true)) - return; - - DraggedCorpses.push_back(std::pair(cds->CorpseName, corpse->GetID())); - - Message_StringID(MT_DefaultText, CORPSEDRAG_BEGIN, cds->CorpseName); -} - -void Client::Handle_OP_CorpseDrop(const EQApplicationPacket *app) -{ - if (app->size == 1) - { - Message_StringID(MT_DefaultText, CORPSEDRAG_STOPALL); - ClearDraggedCorpses(); - return; - } - - for (auto Iterator = DraggedCorpses.begin(); Iterator != DraggedCorpses.end(); ++Iterator) - { - if (!strcasecmp(Iterator->first.c_str(), (const char *)app->pBuffer)) - { - Message_StringID(MT_DefaultText, CORPSEDRAG_STOP); - Iterator = DraggedCorpses.erase(Iterator); - return; - } - } -} - -void Client::Handle_OP_CrashDump(const EQApplicationPacket *app) -{ - return; -} - -void Client::Handle_OP_CreateObject(const EQApplicationPacket *app) -{ - DropItem(MainCursor); - return; -} - -void Client::Handle_OP_CrystalCreate(const EQApplicationPacket *app) -{ - VERIFY_PACKET_LENGTH(OP_CrystalCreate, app, CrystalReclaim_Struct); - CrystalReclaim_Struct *cr = (CrystalReclaim_Struct*)app->pBuffer; - - if (cr->type == 5) { - if (cr->amount > GetEbonCrystals()) { - SummonItem(RuleI(Zone, EbonCrystalItemID), GetEbonCrystals()); - m_pp.currentEbonCrystals = 0; - m_pp.careerEbonCrystals = 0; - SaveCurrency(); - SendCrystalCounts(); - } - else { - SummonItem(RuleI(Zone, EbonCrystalItemID), cr->amount); - m_pp.currentEbonCrystals -= cr->amount; - m_pp.careerEbonCrystals -= cr->amount; - SaveCurrency(); - SendCrystalCounts(); - } - } - else if (cr->type == 4) { - if (cr->amount > GetRadiantCrystals()) { - SummonItem(RuleI(Zone, RadiantCrystalItemID), GetRadiantCrystals()); - m_pp.currentRadCrystals = 0; - m_pp.careerRadCrystals = 0; - SaveCurrency(); - SendCrystalCounts(); - } - else { - SummonItem(RuleI(Zone, RadiantCrystalItemID), cr->amount); - m_pp.currentRadCrystals -= cr->amount; - m_pp.careerRadCrystals -= cr->amount; - SaveCurrency(); - SendCrystalCounts(); - } - } -} - -void Client::Handle_OP_CrystalReclaim(const EQApplicationPacket *app) -{ - uint32 ebon = NukeItem(RuleI(Zone, EbonCrystalItemID), invWhereWorn | invWherePersonal | invWhereCursor); - uint32 radiant = NukeItem(RuleI(Zone, RadiantCrystalItemID), invWhereWorn | invWherePersonal | invWhereCursor); - if ((ebon + radiant) > 0) { - AddCrystals(radiant, ebon); - } -} - -void Client::Handle_OP_Damage(const EQApplicationPacket *app) -{ - if (app->size != sizeof(CombatDamage_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized OP_Damage: got %d, expected %d", app->size, - sizeof(CombatDamage_Struct)); - DumpPacket(app); - return; - } - - // Broadcast to other clients - CombatDamage_Struct* damage = (CombatDamage_Struct*)app->pBuffer; - //dont send to originator of falling damage packets - entity_list.QueueClients(this, app, (damage->type == DamageTypeFalling)); - return; -} - -void Client::Handle_OP_Death(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Death_Struct)) - return; - - Death_Struct* ds = (Death_Struct*)app->pBuffer; - - //I think this attack_skill value is really a value from SkillDamageTypes... - if (ds->attack_skill > HIGHEST_SKILL) { - mlog(CLIENT__ERROR, "Invalid skill in OP_Death: %d"); - return; - } - - if (GetHP() > 0) - return; - - Mob* killer = entity_list.GetMob(ds->killer_id); - Death(killer, ds->damage, ds->spell_id, (SkillUseTypes)ds->attack_skill); - return; -} - -void Client::Handle_OP_DelegateAbility(const EQApplicationPacket *app) -{ - - if (app->size != sizeof(DelegateAbility_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_DelegateAbility expected %i got %i", - sizeof(DelegateAbility_Struct), app->size); - - DumpPacket(app); - - return; - } - - DelegateAbility_Struct* das = (DelegateAbility_Struct*)app->pBuffer; - - Group *g = GetGroup(); - - if (!g) return; - - switch (das->DelegateAbility) - { - case 0: - { - g->DelegateMainAssist(das->Name); - break; - } - case 1: - { - g->DelegateMarkNPC(das->Name); - break; - } - case 2: - { - g->DelegateMainTank(das->Name); - break; - } - case 3: - { - g->DelegatePuller(das->Name); - break; - } - default: - break; - } -} - -void Client::Handle_OP_DeleteItem(const EQApplicationPacket *app) -{ - if (app->size != sizeof(DeleteItem_Struct)) { - std::cout << "Wrong size on OP_DeleteItem. Got: " << app->size << ", Expected: " << sizeof(DeleteItem_Struct) << std::endl; - return; - } - - DeleteItem_Struct* alc = (DeleteItem_Struct*)app->pBuffer; - const ItemInst *inst = GetInv().GetItem(alc->from_slot); - if (inst && inst->GetItem()->ItemType == ItemTypeAlcohol) { - entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), inst->GetItem()->Name); - CheckIncreaseSkill(SkillAlcoholTolerance, nullptr, 25); - - int16 AlcoholTolerance = GetSkill(SkillAlcoholTolerance); - int16 IntoxicationIncrease; - - if (GetClientVersion() < EQClientSoD) - IntoxicationIncrease = (200 - AlcoholTolerance) * 30 / 200 + 10; - else - IntoxicationIncrease = (270 - AlcoholTolerance) * 0.111111108 + 10; - - if (IntoxicationIncrease < 0) - IntoxicationIncrease = 1; - - m_pp.intoxication += IntoxicationIncrease; - - if (m_pp.intoxication > 200) - m_pp.intoxication = 200; - } - DeleteItemInInventory(alc->from_slot, 1); - - return; -} - -void Client::Handle_OP_DeleteSpawn(const EQApplicationPacket *app) -{ - // The client will send this with his id when he zones, maybe when he disconnects too? - //eqs->RemoveData(); // Flushing the queue of packet data to allow for proper zoning - - //just make sure this gets out - EQApplicationPacket *outapp = new EQApplicationPacket(OP_LogoutReply); - FastQueuePacket(&outapp); - - outapp = new EQApplicationPacket(OP_DeleteSpawn, sizeof(EntityId_Struct)); - EntityId_Struct* eid = (EntityId_Struct*)outapp->pBuffer; - eid->entity_id = GetID(); - - entity_list.QueueClients(this, outapp, false); - safe_delete(outapp); - - hate_list.RemoveEnt(this->CastToMob()); - - Disconnect(); - return; -} - -void Client::Handle_OP_DeleteSpell(const EQApplicationPacket *app) -{ - if (app->size != sizeof(DeleteSpell_Struct)) - return; - - EQApplicationPacket* outapp = app->Copy(); - DeleteSpell_Struct* dss = (DeleteSpell_Struct*)outapp->pBuffer; - - if (dss->spell_slot < 0 || dss->spell_slot > int(MAX_PP_SPELLBOOK)) - return; - - if (m_pp.spell_book[dss->spell_slot] != SPELLBOOK_UNKNOWN) { - m_pp.spell_book[dss->spell_slot] = SPELLBOOK_UNKNOWN; - dss->success = 1; - } - else - dss->success = 0; - - FastQueuePacket(&outapp); - return; -} - -void Client::Handle_OP_DisarmTraps(const EQApplicationPacket *app) -{ - if (!HasSkill(SkillDisarmTraps)) - return; - - if (!p_timers.Expired(&database, pTimerDisarmTraps, false)) { - Message(13, "Ability recovery time not yet met."); - return; - } - int reuse = DisarmTrapsReuseTime; - switch (GetAA(aaAdvTrapNegotiation)) { - case 1: - reuse -= 1; - break; - case 2: - reuse -= 3; - break; - case 3: - reuse -= 5; - break; - } - p_timers.Start(pTimerDisarmTraps, reuse - 1); - - Trap* trap = entity_list.FindNearbyTrap(this, 60); - if (trap && trap->detected) - { - int uskill = GetSkill(SkillDisarmTraps); - if ((zone->random.Int(0, 49) + uskill) >= (zone->random.Int(0, 49) + trap->skill)) - { - Message(MT_Skills, "You disarm a trap."); - trap->disarmed = true; - trap->chkarea_timer.Disable(); - trap->respawn_timer.Start((trap->respawn_time + zone->random.Int(0, trap->respawn_var)) * 1000); - } - else - { - if (zone->random.Int(0, 99) < 25){ - Message(MT_Skills, "You set off the trap while trying to disarm it!"); - trap->Trigger(this); - } - else{ - Message(MT_Skills, "You failed to disarm a trap."); - } - } - CheckIncreaseSkill(SkillDisarmTraps, nullptr); - return; - } - Message(MT_Skills, "You did not find any traps close enough to disarm."); - return; -} - -void Client::Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app) -{ - - if (app->size != sizeof(DoGroupLeadershipAbility_Struct)) { - - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_DoGroupLeadershipAbility expected %i got %i", - sizeof(DoGroupLeadershipAbility_Struct), app->size); - - DumpPacket(app); - - return; - } - - DoGroupLeadershipAbility_Struct* dglas = (DoGroupLeadershipAbility_Struct*)app->pBuffer; - - switch (dglas->Ability) - { - case GroupLeadershipAbility_MarkNPC: - { - if (GetTarget()) - { - Group* g = GetGroup(); - if (g) - g->MarkNPC(GetTarget(), dglas->Parameter); - } - break; - } - - case groupAAInspectBuffs: - { - Mob *Target = GetTarget(); - - if (!Target || !Target->IsClient()) - return; - - if (IsRaidGrouped()) { - Raid *raid = GetRaid(); - if (!raid) - return; - uint32 group_id = raid->GetGroup(this); - if (group_id > 11 || raid->GroupCount(group_id) < 3) - return; - Target->CastToClient()->InspectBuffs(this, raid->GetLeadershipAA(groupAAInspectBuffs, group_id)); - return; - } - - Group *g = GetGroup(); - - if (!g || (g->GroupCount() < 3)) - return; - - Target->CastToClient()->InspectBuffs(this, g->GetLeadershipAA(groupAAInspectBuffs)); - - break; - } - - default: - LogFile->write(EQEMuLog::Debug, "Got unhandled OP_DoGroupLeadershipAbility Ability: %d Parameter: %d", - dglas->Ability, dglas->Parameter); - break; - } -} - -void Client::Handle_OP_DuelResponse(const EQApplicationPacket *app) -{ - if (app->size != sizeof(DuelResponse_Struct)) - return; - DuelResponse_Struct* ds = (DuelResponse_Struct*)app->pBuffer; - Entity* entity = entity_list.GetID(ds->target_id); - Entity* initiator = entity_list.GetID(ds->entity_id); - if (!entity->IsClient() || !initiator->IsClient()) - return; - - entity->CastToClient()->SetDuelTarget(0); - entity->CastToClient()->SetDueling(false); - initiator->CastToClient()->SetDuelTarget(0); - initiator->CastToClient()->SetDueling(false); - if (GetID() == initiator->GetID()) - entity->CastToClient()->Message_StringID(10, DUEL_DECLINE, initiator->GetName()); - else - initiator->CastToClient()->Message_StringID(10, DUEL_DECLINE, entity->GetName()); - return; -} - -void Client::Handle_OP_DuelResponse2(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Duel_Struct)) - return; - - Duel_Struct* ds = (Duel_Struct*)app->pBuffer; - Entity* entity = entity_list.GetID(ds->duel_target); - Entity* initiator = entity_list.GetID(ds->duel_initiator); - - if (entity && initiator && entity == this && initiator->IsClient()) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RequestDuel, sizeof(Duel_Struct)); - Duel_Struct* ds2 = (Duel_Struct*)outapp->pBuffer; - - ds2->duel_initiator = entity->GetID(); - ds2->duel_target = entity->GetID(); - initiator->CastToClient()->QueuePacket(outapp); - - outapp->SetOpcode(OP_DuelResponse2); - ds2->duel_initiator = initiator->GetID(); - - initiator->CastToClient()->QueuePacket(outapp); - - QueuePacket(outapp); - SetDueling(true); - initiator->CastToClient()->SetDueling(true); - SetDuelTarget(ds->duel_initiator); - safe_delete(outapp); - - if (IsCasting()) - InterruptSpell(); - if (initiator->CastToClient()->IsCasting()) - initiator->CastToClient()->InterruptSpell(); - } - return; -} - -void Client::Handle_OP_DumpName(const EQApplicationPacket *app) -{ - return; -} - -void Client::Handle_OP_Dye(const EQApplicationPacket *app) -{ - if (app->size != sizeof(DyeStruct)) - printf("Wrong size of DyeStruct, Got: %i, Expected: %zu\n", app->size, sizeof(DyeStruct)); - else{ - DyeStruct* dye = (DyeStruct*)app->pBuffer; - DyeArmor(dye); - } - return; -} - -void Client::Handle_OP_Emote(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Emote_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized " - "OP_Emote: got %d, expected %d", app->size, - sizeof(Emote_Struct)); - DumpPacket(app); - return; - } - - // Calculate new packet dimensions - Emote_Struct* in = (Emote_Struct*)app->pBuffer; - in->message[1023] = '\0'; - - const char* name = GetName(); - uint32 len_name = strlen(name); - uint32 len_msg = strlen(in->message); - // crash protection -- cheater - if (len_msg > 512) { - in->message[512] = '\0'; - len_msg = 512; - } - uint32 len_packet = sizeof(in->unknown01) + len_name - + len_msg + 1; - - // Construct outgoing packet - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Emote, len_packet); - Emote_Struct* out = (Emote_Struct*)outapp->pBuffer; - out->unknown01 = in->unknown01; - memcpy(out->message, name, len_name); - memcpy(&out->message[len_name], in->message, len_msg); - - /* - if (target && target->IsClient()) { - entity_list.QueueCloseClients(this, outapp, false, 100, target); - - cptr = outapp->pBuffer + 2; - - // not sure if live does this or not. thought it was a nice feature, but would take a lot to - // clean up grammatical and other errors. Maybe with a regex parser... - replacestr((char *)cptr, target->GetName(), "you"); - replacestr((char *)cptr, " he", " you"); - replacestr((char *)cptr, " she", " you"); - replacestr((char *)cptr, " him", " you"); - replacestr((char *)cptr, " her", " you"); - target->CastToClient()->QueuePacket(outapp); - - } - else - */ - entity_list.QueueCloseClients(this, outapp, true, 100, 0, true, FilterSocials); - - safe_delete(outapp); - return; -} - -void Client::Handle_OP_EndLootRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(uint32)) { - std::cout << "Wrong size: OP_EndLootRequest, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; - return; - } - - SetLooting(0); - - Entity* entity = entity_list.GetID(*((uint16*)app->pBuffer)); - if (entity == 0) { - Message(13, "Error: OP_EndLootRequest: Corpse not found (ent = 0)"); - if (GetClientVersion() >= EQClientSoD) - Corpse::SendEndLootErrorPacket(this); - else - Corpse::SendLootReqErrorPacket(this); - return; - } - else if (!entity->IsCorpse()) { - Message(13, "Error: OP_EndLootRequest: Corpse not found (!entity->IsCorpse())"); - Corpse::SendLootReqErrorPacket(this); - return; - } - else { - entity->CastToCorpse()->EndLoot(this, app); - } - return; -} - -void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app) -{ - if (!ClientFinishedLoading()) - { - SetHP(GetHP() - 1); - return; - } - - if (app->size != sizeof(EnvDamage2_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized OP_EnvDamage: got %d, expected %d", app->size, - sizeof(EnvDamage2_Struct)); - DumpPacket(app); - return; - } - EnvDamage2_Struct* ed = (EnvDamage2_Struct*)app->pBuffer; - if (admin >= minStatusToAvoidFalling && GetGM()){ - Message(13, "Your GM status protects you from %i points of type %i environmental damage.", ed->damage, ed->dmgtype); - SetHP(GetHP() - 1);//needed or else the client wont acknowledge - return; - } - else if (GetInvul()) { - Message(13, "Your invuln status protects you from %i points of type %i environmental damage.", ed->damage, ed->dmgtype); - SetHP(GetHP() - 1);//needed or else the client wont acknowledge - return; - } - - int damage = ed->damage; - - if (ed->dmgtype == 252) { - - switch (GetAA(aaAcrobatics)) { //Don't know what acrobatics effect is yet but it should be done client side via aa effect.. till then - case 1: - damage = damage * 95 / 100; - break; - case 2: - damage = damage * 90 / 100; - break; - case 3: - damage = damage * 80 / 100; - break; - } - } - - if (damage < 0) - damage = 31337; - - else if (zone->GetZoneID() == 183 || zone->GetZoneID() == 184) - return; - else - SetHP(GetHP() - damage); - - if (GetHP() <= 0) - { - mod_client_death_env(); - - Death(0, 32000, SPELL_UNKNOWN, SkillHandtoHand); - } - SendHPUpdate(); - return; -} - -void Client::Handle_OP_FaceChange(const EQApplicationPacket *app) -{ - if (app->size != sizeof(FaceChange_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for OP_FaceChange: Expected: %i, Got: %i", - sizeof(FaceChange_Struct), app->size); - return; - } - - // Notify other clients in zone - entity_list.QueueClients(this, app, false); - - FaceChange_Struct* fc = (FaceChange_Struct*)app->pBuffer; - m_pp.haircolor = fc->haircolor; - m_pp.beardcolor = fc->beardcolor; - m_pp.eyecolor1 = fc->eyecolor1; - m_pp.eyecolor2 = fc->eyecolor2; - m_pp.hairstyle = fc->hairstyle; - m_pp.face = fc->face; - m_pp.beard = fc->beard; - m_pp.drakkin_heritage = fc->drakkin_heritage; - m_pp.drakkin_tattoo = fc->drakkin_tattoo; - m_pp.drakkin_details = fc->drakkin_details; - Save(); - Message_StringID(13, FACE_ACCEPTED); - //Message(13, "Facial features updated."); - return; -} - -void Client::Handle_OP_FeignDeath(const EQApplicationPacket *app) -{ - if (GetClass() != MONK) - return; - if (!p_timers.Expired(&database, pTimerFeignDeath, false)) { - Message(13, "Ability recovery time not yet met."); - return; - } - int reuse = FeignDeathReuseTime; - switch (GetAA(aaRapidFeign)) - { - case 1: - reuse -= 1; - break; - case 2: - reuse -= 2; - break; - case 3: - reuse -= 5; - break; - } - p_timers.Start(pTimerFeignDeath, reuse - 1); - - //BreakInvis(); - - uint16 primfeign = GetSkill(SkillFeignDeath); - uint16 secfeign = GetSkill(SkillFeignDeath); - if (primfeign > 100) { - primfeign = 100; - secfeign = secfeign - 100; - secfeign = secfeign / 2; - } - else - secfeign = 0; - - uint16 totalfeign = primfeign + secfeign; - if (zone->random.Real(0, 160) > totalfeign) { - SetFeigned(false); - entity_list.MessageClose_StringID(this, false, 200, 10, STRING_FEIGNFAILED, GetName()); - } - else { - SetFeigned(true); - } - - CheckIncreaseSkill(SkillFeignDeath, nullptr, 5); - return; -} - -void Client::Handle_OP_FindPersonRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(FindPersonRequest_Struct)) - printf("Error in FindPersonRequest_Struct. Expected size of: %zu, but got: %i\n", sizeof(FindPersonRequest_Struct), app->size); - else { - FindPersonRequest_Struct* t = (FindPersonRequest_Struct*)app->pBuffer; - - std::vector points; - Mob* target = entity_list.GetMob(t->npc_id); - - if (target == nullptr) { - //empty length packet == not found. - EQApplicationPacket outapp(OP_FindPersonReply, 0); - QueuePacket(&outapp); - return; - } - - if (!RuleB(Pathing, Find) && RuleB(Bazaar, EnableWarpToTrader) && target->IsClient() && (target->CastToClient()->Trader || - target->CastToClient()->Buyer)) { - Message(15, "Moving you to Trader %s", target->GetName()); - MovePC(zone->GetZoneID(), zone->GetInstanceID(), target->GetX(), target->GetY(), target->GetZ(), 0.0f); - } - - if (!RuleB(Pathing, Find) || !zone->pathing) - { - //fill in the path array... - // - points.resize(2); - points[0].x = GetX(); - points[0].y = GetY(); - points[0].z = GetZ(); - points[1].x = target->GetX(); - points[1].y = target->GetY(); - points[1].z = target->GetZ(); - } - else - { - Map::Vertex Start(GetX(), GetY(), GetZ() + (GetSize() < 6.0 ? 6 : GetSize()) * HEAD_POSITION); - Map::Vertex End(target->GetX(), target->GetY(), target->GetZ() + (target->GetSize() < 6.0 ? 6 : target->GetSize()) * HEAD_POSITION); - - if (!zone->zonemap->LineIntersectsZone(Start, End, 1.0f, nullptr) && zone->pathing->NoHazards(Start, End)) - { - points.resize(2); - points[0].x = Start.x; - points[0].y = Start.y; - points[0].z = Start.z; - - points[1].x = End.x; - points[1].y = End.y; - points[1].z = End.z; - - } - else - { - std::list pathlist = zone->pathing->FindRoute(Start, End); - - if (pathlist.size() == 0) - { - EQApplicationPacket outapp(OP_FindPersonReply, 0); - QueuePacket(&outapp); - return; - } - - //the client seems to have issues with packets larger than this - if (pathlist.size() > 36) - { - EQApplicationPacket outapp(OP_FindPersonReply, 0); - QueuePacket(&outapp); - return; - } - - // Live appears to send the points in this order: - // Final destination. - // Current Position. - // rest of the points. - FindPerson_Point p; - - int PointNumber = 0; - - bool LeadsToTeleporter = false; - - Map::Vertex v = zone->pathing->GetPathNodeCoordinates(pathlist.back()); - - p.x = v.x; - p.y = v.y; - p.z = v.z; - points.push_back(p); - - p.x = GetX(); - p.y = GetY(); - p.z = GetZ(); - points.push_back(p); - - for (std::list::iterator Iterator = pathlist.begin(); Iterator != pathlist.end(); ++Iterator) - { - if ((*Iterator) == -1) // Teleporter - { - LeadsToTeleporter = true; - break; - } - - Map::Vertex v = zone->pathing->GetPathNodeCoordinates((*Iterator), false); - p.x = v.x; - p.y = v.y; - p.z = v.z; - points.push_back(p); - ++PointNumber; - } - - if (!LeadsToTeleporter) - { - p.x = target->GetX(); - p.y = target->GetY(); - p.z = target->GetZ(); - - points.push_back(p); - } - - } - } - - SendPathPacket(points); - } - return; -} - -void Client::Handle_OP_Fishing(const EQApplicationPacket *app) -{ - if (!p_timers.Expired(&database, pTimerFishing, false)) { - Message(13, "Ability recovery time not yet met."); - return; - } - - if (CanFish()) { - parse->EventPlayer(EVENT_FISH_START, this, "", 0); - - //these will trigger GoFish() after a delay if we're able to actually fish, and if not, we won't stop the client from trying again immediately (although we may need to tell it to repop the button) - p_timers.Start(pTimerFishing, FishingReuseTime - 1); - fishing_timer.Start(); - } - return; - // Changes made based on Bobs work on foraging. Now can set items in the forage database table to - // forage for. -} - -void Client::Handle_OP_Forage(const EQApplicationPacket *app) -{ - - if (!p_timers.Expired(&database, pTimerForaging, false)) { - Message(13, "Ability recovery time not yet met."); - return; - } - p_timers.Start(pTimerForaging, ForagingReuseTime - 1); - - ForageItem(); - - return; -} - -void Client::Handle_OP_FriendsWho(const EQApplicationPacket *app) -{ - char *FriendsString = (char*)app->pBuffer; - FriendsWho(FriendsString); - return; -} - -void Client::Handle_OP_GetGuildMOTD(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GetGuildMOTD"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - SendGuildMOTD(true); - - if (IsInAGuild()) - { - SendGuildURL(); - SendGuildChannel(); - } -} - -void Client::Handle_OP_GetGuildsList(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GetGuildsList"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - SendGuildList(); -} - -void Client::Handle_OP_GMBecomeNPC(const EQApplicationPacket *app) -{ - if (this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/becomenpc"); - return; - } - if (app->size != sizeof(BecomeNPC_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMBecomeNPC, size=%i, expected %i", app->size, sizeof(BecomeNPC_Struct)); - return; - } - //entity_list.QueueClients(this, app, false); - BecomeNPC_Struct* bnpc = (BecomeNPC_Struct*)app->pBuffer; - - Mob* cli = (Mob*)entity_list.GetMob(bnpc->id); - if (cli == 0) - return; - - if (cli->IsClient()) - cli->CastToClient()->QueuePacket(app); - cli->SendAppearancePacket(AT_NPCName, 1, true); - cli->CastToClient()->SetBecomeNPC(true); - cli->CastToClient()->SetBecomeNPCLevel(bnpc->maxlevel); - cli->Message_StringID(0, TOGGLE_OFF); - cli->CastToClient()->tellsoff = true; - //TODO: Make this toggle a BecomeNPC flag so that it gets updated when people zone in as well; Make combat work with this. - return; -} - -void Client::Handle_OP_GMDelCorpse(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMDelCorpse_Struct)) - return; - if (this->Admin() < commandEditPlayerCorpses) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/delcorpse"); - return; - } - GMDelCorpse_Struct* dc = (GMDelCorpse_Struct *)app->pBuffer; - Mob* corpse = entity_list.GetMob(dc->corpsename); - if (corpse == 0) { - return; - } - if (corpse->IsCorpse() != true) { - return; - } - corpse->CastToCorpse()->Delete(); - std::cout << name << " deleted corpse " << dc->corpsename << std::endl; - Message(13, "Corpse %s deleted.", dc->corpsename); - return; -} - -void Client::Handle_OP_GMEmoteZone(const EQApplicationPacket *app) -{ - if (this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/emote"); - return; - } - if (app->size != sizeof(GMEmoteZone_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMEmoteZone, size=%i, expected %i", app->size, sizeof(GMEmoteZone_Struct)); - return; - } - GMEmoteZone_Struct* gmez = (GMEmoteZone_Struct*)app->pBuffer; - char* newmessage = 0; - if (strstr(gmez->text, "^") == 0) - entity_list.Message(0, 15, gmez->text); - else{ - for (newmessage = strtok((char*)gmez->text, "^"); newmessage != nullptr; newmessage = strtok(nullptr, "^")) - entity_list.Message(0, 15, newmessage); - } - return; -} - -void Client::Handle_OP_GMEndTraining(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMTrainEnd_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GMEndTraining expected %i got %i", sizeof(GMTrainEnd_Struct), app->size); - DumpPacket(app); - return; - } - OPGMEndTraining(app); - return; -} - -void Client::Handle_OP_GMFind(const EQApplicationPacket *app) -{ - if (this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/find"); - return; - } - if (app->size != sizeof(GMSummon_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMFind, size=%i, expected %i", app->size, sizeof(GMSummon_Struct)); - return; - } - //Break down incoming - GMSummon_Struct* request = (GMSummon_Struct*)app->pBuffer; - //Create a new outgoing - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GMFind, sizeof(GMSummon_Struct)); - GMSummon_Struct* foundplayer = (GMSummon_Struct*)outapp->pBuffer; - //Copy the constants - strcpy(foundplayer->charname, request->charname); - strcpy(foundplayer->gmname, request->gmname); - //Check if the NPC exits intrazone... - Mob* gt = entity_list.GetMob(request->charname); - if (gt != 0) { - foundplayer->success = 1; - foundplayer->x = (int32)gt->GetX(); - foundplayer->y = (int32)gt->GetY(); - - foundplayer->z = (int32)gt->GetZ(); - foundplayer->zoneID = zone->GetZoneID(); - } - //Send the packet... - FastQueuePacket(&outapp); - return; -} - -void Client::Handle_OP_GMGoto(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMSummon_Struct)) { - std::cout << "Wrong size on OP_GMGoto. Got: " << app->size << ", Expected: " << sizeof(GMSummon_Struct) << std::endl; - return; - } - if (this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/goto"); - return; - } - GMSummon_Struct* gmg = (GMSummon_Struct*)app->pBuffer; - Mob* gt = entity_list.GetMob(gmg->charname); - if (gt != nullptr) { - this->MovePC(zone->GetZoneID(), zone->GetInstanceID(), gt->GetX(), gt->GetY(), gt->GetZ(), gt->GetHeading()); - } - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected."); - else { - ServerPacket* pack = new ServerPacket(ServerOP_GMGoto, sizeof(ServerGMGoto_Struct)); - memset(pack->pBuffer, 0, pack->size); - ServerGMGoto_Struct* wsgmg = (ServerGMGoto_Struct*)pack->pBuffer; - strcpy(wsgmg->myname, this->GetName()); - strcpy(wsgmg->gotoname, gmg->charname); - wsgmg->admin = admin; - worldserver.SendPacket(pack); - safe_delete(pack); - } - return; -} - -void Client::Handle_OP_GMHideMe(const EQApplicationPacket *app) -{ - if (this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/hideme"); - return; - } - if (app->size != sizeof(SpawnAppearance_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMHideMe, size=%i, expected %i", app->size, sizeof(SpawnAppearance_Struct)); - return; - } - SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)app->pBuffer; - Message(13, "#: %i, %i", sa->type, sa->parameter); - SetHideMe(!sa->parameter); - return; - -} - -void Client::Handle_OP_GMKick(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMKick_Struct)) - return; - if (this->Admin() < minStatusToKick) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/kick"); - return; - } - GMKick_Struct* gmk = (GMKick_Struct *)app->pBuffer; - - Client* client = entity_list.GetClientByName(gmk->name); - if (client == 0) { - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - ServerPacket* pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); - ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*)pack->pBuffer; - strcpy(skp->adminname, gmk->gmname); - strcpy(skp->name, gmk->name); - skp->adminrank = this->Admin(); - worldserver.SendPacket(pack); - safe_delete(pack); - } - } - else { - entity_list.QueueClients(this, app); - //client->Kick(); - } - return; -} - -void Client::Handle_OP_GMKill(const EQApplicationPacket *app) -{ - if (this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/kill"); - return; - } - if (app->size != sizeof(GMKill_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMKill, size=%i, expected %i", app->size, sizeof(GMKill_Struct)); - return; - } - GMKill_Struct* gmk = (GMKill_Struct *)app->pBuffer; - Mob* obj = entity_list.GetMob(gmk->name); - Client* client = entity_list.GetClientByName(gmk->name); - if (obj != 0) { - if (client != 0) { - entity_list.QueueClients(this, app); - } - else { - obj->Kill(); - } - } - else { - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - ServerPacket* pack = new ServerPacket(ServerOP_KillPlayer, sizeof(ServerKillPlayer_Struct)); - ServerKillPlayer_Struct* skp = (ServerKillPlayer_Struct*)pack->pBuffer; - strcpy(skp->gmname, gmk->gmname); - strcpy(skp->target, gmk->name); - skp->admin = this->Admin(); - worldserver.SendPacket(pack); - safe_delete(pack); - } - } - return; -} - -void Client::Handle_OP_GMLastName(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMLastName_Struct)) { - std::cout << "Wrong size on OP_GMLastName. Got: " << app->size << ", Expected: " << sizeof(GMLastName_Struct) << std::endl; - return; - } - GMLastName_Struct* gmln = (GMLastName_Struct*)app->pBuffer; - if (strlen(gmln->lastname) >= 64) { - Message(13, "/LastName: New last name too long. (max=63)"); - } - else { - Client* client = entity_list.GetClientByName(gmln->name); - if (client == 0) { - Message(13, "/LastName: %s not found", gmln->name); - } - else { - if (this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(client->account_name, client->name, "/lastname"); - return; - } - else - - client->ChangeLastName(gmln->lastname); - } - gmln->unknown[0] = 1; - gmln->unknown[1] = 1; - gmln->unknown[2] = 1; - gmln->unknown[3] = 1; - entity_list.QueueClients(this, app, false); - } - return; -} - -void Client::Handle_OP_GMNameChange(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMName_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMNameChange, size=%i, expected %i", app->size, sizeof(GMName_Struct)); - return; - } - const GMName_Struct* gmn = (const GMName_Struct *)app->pBuffer; - if (this->Admin() < minStatusToUseGMCommands){ - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/name"); - return; - } - Client* client = entity_list.GetClientByName(gmn->oldname); - LogFile->write(EQEMuLog::Status, "GM(%s) changeing players name. Old:%s New:%s", GetName(), gmn->oldname, gmn->newname); - bool usedname = database.CheckUsedName((const char*)gmn->newname); - if (client == 0) { - Message(13, "%s not found for name change. Operation failed!", gmn->oldname); - return; - } - if ((strlen(gmn->newname) > 63) || (strlen(gmn->newname) == 0)) { - Message(13, "Invalid number of characters in new name (%s).", gmn->newname); - return; - } - if (!usedname) { - Message(13, "%s is already in use. Operation failed!", gmn->newname); - return; - - } - database.UpdateName(gmn->oldname, gmn->newname); - strcpy(client->name, gmn->newname); - client->Save(); - - if (gmn->badname == 1) { - database.AddToNameFilter(gmn->oldname); - } - EQApplicationPacket* outapp = app->Copy(); - GMName_Struct* gmn2 = (GMName_Struct*)outapp->pBuffer; - gmn2->unknown[0] = 1; - gmn2->unknown[1] = 1; - gmn2->unknown[2] = 1; - entity_list.QueueClients(this, outapp, false); - safe_delete(outapp); - UpdateWho(); - return; -} - -void Client::Handle_OP_GMSearchCorpse(const EQApplicationPacket *app) -{ - // Could make this into a rule, although there is a hard limit since we are using a popup, of 4096 bytes that can - // be displayed in the window, including all the HTML formatting tags. - // - const int maxResults = 10; - - if (app->size < sizeof(GMSearchCorpse_Struct)) - { - LogFile->write(EQEMuLog::Debug, "OP_GMSearchCorpse size lower than expected: got %u expected at least %u", - app->size, sizeof(GMSearchCorpse_Struct)); - DumpPacket(app); - return; - } - - GMSearchCorpse_Struct *gmscs = (GMSearchCorpse_Struct *)app->pBuffer; - gmscs->Name[63] = '\0'; - - char *escSearchString = new char[129]; - database.DoEscapeString(escSearchString, gmscs->Name, strlen(gmscs->Name)); - - std::string query = StringFormat("SELECT charname, zoneid, x, y, z, time_of_death, rezzed, IsBurried " - "FROM character_corpses WheRE charname LIKE '%%%s%%' ORDER BY charname LIMIT %i", - escSearchString, maxResults); - safe_delete_array(escSearchString); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - Message(0, "Query failed: %s.", results.ErrorMessage().c_str()); - return; - } - - if (results.RowCount() == 0) - return; - - if (results.RowCount() == maxResults) - Message(clientMessageError, "Your search found too many results; some are not displayed."); - else - Message(clientMessageYellow, "There are %i corpse(s) that match the search string '%s'.", results.RowCount(), gmscs->Name); - - char charName[64], time_of_death[20]; - - std::string popupText = ""; - - for (auto row = results.begin(); row != results.end(); ++row) { - - strn0cpy(charName, row[0], sizeof(charName)); - - uint32 ZoneID = atoi(row[1]); - float CorpseX = atof(row[2]); - float CorpseY = atof(row[3]); - float CorpseZ = atof(row[4]); - - strn0cpy(time_of_death, row[5], sizeof(time_of_death)); - - bool corpseRezzed = atoi(row[6]); - bool corpseBuried = atoi(row[7]); - - popupText += StringFormat("", - charName, StaticGetZoneName(ZoneID), CorpseX, CorpseY, CorpseZ, time_of_death, - corpseRezzed ? "Yes" : "No", corpseBuried ? "Yes" : "No"); - - if (popupText.size() > 4000) { - Message(clientMessageError, "Unable to display all the results."); - break; - } - - } - - popupText += "
NameZoneXYZDate" - "RezzedBuried
 " - "
%s%s%8.0f%8.0f%8.0f%s%s%s
"; - - SendPopupToClient("Corpses", popupText.c_str()); - -} - -void Client::Handle_OP_GMServers(const EQApplicationPacket *app) -{ - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - ServerPacket* pack = new ServerPacket(ServerOP_ZoneStatus, strlen(this->GetName()) + 2); - memset(pack->pBuffer, (uint8)admin, 1); - strcpy((char *)&pack->pBuffer[1], this->GetName()); - worldserver.SendPacket(pack); - safe_delete(pack); - } - return; -} - -void Client::Handle_OP_GMSummon(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMSummon_Struct)) { - std::cout << "Wrong size on OP_GMSummon. Got: " << app->size << ", Expected: " << sizeof(GMSummon_Struct) << std::endl; - return; - } - OPGMSummon(app); - return; -} - -void Client::Handle_OP_GMToggle(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMToggle_Struct)) { - std::cout << "Wrong size on OP_GMToggle. Got: " << app->size << ", Expected: " << sizeof(GMToggle_Struct) << std::endl; - return; - } - if (this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/toggle"); - return; - } - GMToggle_Struct *ts = (GMToggle_Struct *)app->pBuffer; - if (ts->toggle == 0) { - this->Message_StringID(0, TOGGLE_OFF); - //Message(0, "Turning tells OFF"); - tellsoff = true; - } - else if (ts->toggle == 1) { - //Message(0, "Turning tells ON"); - this->Message_StringID(0, TOGGLE_ON); - tellsoff = false; - } - else { - Message(0, "Unkown value in /toggle packet"); - } - UpdateWho(); - return; -} - -void Client::Handle_OP_GMTraining(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMTrainee_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GMTraining expected %i got %i", sizeof(GMTrainee_Struct), app->size); - DumpPacket(app); - return; - } - OPGMTraining(app); - return; -} - -void Client::Handle_OP_GMTrainSkill(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMSkillChange_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GMTrainSkill expected %i got %i", sizeof(GMSkillChange_Struct), app->size); - DumpPacket(app); - return; - } - OPGMTrainSkill(app); - return; -} - -void Client::Handle_OP_GMZoneRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMZoneRequest_Struct)) { - std::cout << "Wrong size on OP_GMZoneRequest. Got: " << app->size << ", Expected: " << sizeof(GMZoneRequest_Struct) << std::endl; - return; - } - if (this->Admin() < minStatusToBeGM) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/zone"); - return; - } - - GMZoneRequest_Struct* gmzr = (GMZoneRequest_Struct*)app->pBuffer; - float tarx = -1, tary = -1, tarz = -1; - - int16 minstatus = 0; - uint8 minlevel = 0; - char tarzone[32]; - uint16 zid = gmzr->zone_id; - if (gmzr->zone_id == 0) - zid = zonesummon_id; - const char * zname = database.GetZoneName(zid); - if (zname == nullptr) - tarzone[0] = 0; - else - strcpy(tarzone, zname); - - // this both loads the safe points and does a sanity check on zone name - if (!database.GetSafePoints(tarzone, 0, &tarx, &tary, &tarz, &minstatus, &minlevel)) { - tarzone[0] = 0; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_GMZoneRequest, sizeof(GMZoneRequest_Struct)); - GMZoneRequest_Struct* gmzr2 = (GMZoneRequest_Struct*)outapp->pBuffer; - strcpy(gmzr2->charname, this->GetName()); - gmzr2->zone_id = gmzr->zone_id; - gmzr2->x = tarx; - gmzr2->y = tary; - gmzr2->z = tarz; - // Next line stolen from ZoneChange as well... - This gives us a nicer message than the normal "zone is down" message... - if (tarzone[0] != 0 && admin >= minstatus && GetLevel() >= minlevel) - gmzr2->success = 1; - else { - std::cout << "GetZoneSafeCoords failed. zoneid = " << gmzr->zone_id << "; czone = " << zone->GetZoneID() << std::endl; - gmzr2->success = 0; - } - - QueuePacket(outapp); - safe_delete(outapp); - return; -} - -void Client::Handle_OP_GMZoneRequest2(const EQApplicationPacket *app) -{ - if (this->Admin() < minStatusToBeGM) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/zone"); - return; - } - if (app->size < sizeof(uint32)) { - LogFile->write(EQEMuLog::Error, "OP size error: OP_GMZoneRequest2 expected:%i got:%i", sizeof(uint32), app->size); - return; - } - - uint32 zonereq = *((uint32 *)app->pBuffer); - GoToSafeCoords(zonereq, 0); - return; -} - -void Client::Handle_OP_GroupAcknowledge(const EQApplicationPacket *app) -{ - return; -} - -void Client::Handle_OP_GroupCancelInvite(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GroupCancel_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for OP_GroupCancelInvite: Expected: %i, Got: %i", - sizeof(GroupCancel_Struct), app->size); - return; - } - - GroupCancel_Struct* gf = (GroupCancel_Struct*)app->pBuffer; - Mob* inviter = entity_list.GetClientByName(gf->name1); - - if (inviter != nullptr) - { - if (inviter->IsClient()) - inviter->CastToClient()->QueuePacket(app); - } - else - { - ServerPacket* pack = new ServerPacket(ServerOP_GroupCancelInvite, sizeof(GroupCancel_Struct)); - memcpy(pack->pBuffer, gf, sizeof(GroupCancel_Struct)); - worldserver.SendPacket(pack); - safe_delete(pack); - } - - if (!GetMerc()) - { - database.SetGroupID(GetName(), 0, CharacterID(), false); - } - return; -} - -void Client::Handle_OP_GroupDelete(const EQApplicationPacket *app) -{ - //should check for leader, only they should be able to do this.. - Group* group = GetGroup(); - if (group) - group->DisbandGroup(); - - if (LFP) - UpdateLFP(); - - return; -} - -void Client::Handle_OP_GroupDisband(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GroupGeneric_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for GroupGeneric_Struct: Expected: %i, Got: %i", - sizeof(GroupGeneric_Struct), app->size); - return; - } - - LogFile->write(EQEMuLog::Debug, "Member Disband Request from %s\n", GetName()); - - GroupGeneric_Struct* gd = (GroupGeneric_Struct*)app->pBuffer; - - Raid *raid = entity_list.GetRaidByClient(this); - if (raid) - { - Mob* memberToDisband = nullptr; - - if (!raid->IsGroupLeader(GetName())) - memberToDisband = this; - else - memberToDisband = GetTarget(); - - if (!memberToDisband) - memberToDisband = entity_list.GetMob(gd->name2); - - if (!memberToDisband) - memberToDisband = this; - - if (!memberToDisband->IsClient()) - return; - - //we have a raid.. see if we're in a raid group - uint32 grp = raid->GetGroup(memberToDisband->GetName()); - bool wasGrpLdr = raid->members[raid->GetPlayerIndex(memberToDisband->GetName())].IsGroupLeader; - if (grp < 12){ - if (wasGrpLdr){ - raid->SetGroupLeader(memberToDisband->GetName(), false); - for (int x = 0; x < MAX_RAID_MEMBERS; x++) - { - if (raid->members[x].GroupNumber == grp) - { - if (strlen(raid->members[x].membername) > 0 && strcmp(raid->members[x].membername, memberToDisband->GetName()) != 0) - { - raid->SetGroupLeader(raid->members[x].membername); - break; - } - } - } - } - raid->MoveMember(memberToDisband->GetName(), 0xFFFFFFFF); - raid->GroupUpdate(grp); //break - //raid->SendRaidGroupRemove(memberToDisband->GetName(), grp); - //raid->SendGroupUpdate(memberToDisband->CastToClient()); - raid->SendGroupDisband(memberToDisband->CastToClient()); - } - //we're done - return; - } - - Group* group = GetGroup(); - - if (!group) - return; - -#ifdef BOTS - // this block is necessary to allow more control over controlling how bots are zoned or camped. - if (Bot::GroupHasBot(group)) { - if (group->IsLeader(this)) { - if ((GetTarget() == 0 || GetTarget() == this) || (group->GroupCount() < 3)) { - Bot::ProcessBotGroupDisband(this, std::string()); - } - else { - Mob* tempMember = entity_list.GetMob(gd->name2); - if (tempMember) { - if (tempMember->IsBot()) - Bot::ProcessBotGroupDisband(this, std::string(tempMember->GetCleanName())); - } - } - } - } -#endif - if (group->GroupCount() < 3) - { - group->DisbandGroup(); - if (GetMerc()) - GetMerc()->Suspend(); - } - else if (group->IsLeader(this) && GetTarget() == nullptr) - { - if (group->GroupCount() > 2 && GetMerc() && !GetMerc()->IsSuspended()) - { - group->DisbandGroup(); - GetMerc()->MercJoinClientGroup(); - } - else - { - group->DisbandGroup(); - if (GetMerc()) - GetMerc()->Suspend(); - } - } - else if (group->IsLeader(this) && GetTarget() == this) - { - LeaveGroup(); - if (GetMerc() && !GetMerc()->IsSuspended()) - { - GetMerc()->MercJoinClientGroup(); - } - } - else - { - Mob* memberToDisband = nullptr; - memberToDisband = GetTarget(); - - if (!memberToDisband) - memberToDisband = entity_list.GetMob(gd->name2); - - if (memberToDisband) - { - if (group->IsLeader(this)) - { - // the group leader can kick other members out of the group... - if (memberToDisband->IsClient()) - { - group->DelMember(memberToDisband, false); - Client* memberClient = memberToDisband->CastToClient(); - Merc* memberMerc = memberToDisband->CastToClient()->GetMerc(); - if (memberClient && memberMerc) - { - memberMerc->MercJoinClientGroup(); - } - } - else if (memberToDisband->IsMerc()) - { - memberToDisband->CastToMerc()->Suspend(); - } - } - else - { - // ...but other members can only remove themselves - group->DelMember(this, false); - - if (GetMerc() && !GetMerc()->IsSuspended()) - { - GetMerc()->MercJoinClientGroup(); - } - } - } - else - { - LogFile->write(EQEMuLog::Error, "Failed to remove player from group. Unable to find player named %s in player group", gd->name2); - } - } - if (LFP) - { - // If we are looking for players, update to show we are on our own now. - UpdateLFP(); - } - - return; -} - -void Client::Handle_OP_GroupFollow(const EQApplicationPacket *app) -{ - Handle_OP_GroupFollow2(app); -} - -void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GroupGeneric_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for OP_GroupFollow: Expected: %i, Got: %i", - sizeof(GroupGeneric_Struct), app->size); - return; - } - - if (LFP) { - // If we were looking for players to start our own group, but we accept an invitation to another - // group, turn LFP off. - database.SetLFP(CharacterID(), false); - worldserver.StopLFP(CharacterID()); - } - - GroupGeneric_Struct* gf = (GroupGeneric_Struct*)app->pBuffer; - Mob* inviter = entity_list.GetClientByName(gf->name1); - - // Inviter and Invitee are in the same zone - if (inviter != nullptr && inviter->IsClient()) - { - if (GroupFollow(inviter->CastToClient())) - { - strn0cpy(gf->name1, inviter->GetName(), sizeof(gf->name1)); - strn0cpy(gf->name2, GetName(), sizeof(gf->name2)); - inviter->CastToClient()->QueuePacket(app);//notify inviter the client accepted - } - } - else if (inviter == nullptr) - { - // Inviter is in another zone - Remove merc from group now if any - LeaveGroup(); - - ServerPacket* pack = new ServerPacket(ServerOP_GroupFollow, sizeof(ServerGroupFollow_Struct)); - ServerGroupFollow_Struct *sgfs = (ServerGroupFollow_Struct *)pack->pBuffer; - sgfs->CharacterID = CharacterID(); - strn0cpy(sgfs->gf.name1, gf->name1, sizeof(sgfs->gf.name1)); - strn0cpy(sgfs->gf.name2, gf->name2, sizeof(sgfs->gf.name2)); - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - -void Client::Handle_OP_GroupInvite(const EQApplicationPacket *app) -{ - //this seems to be the initial invite to form a group - Handle_OP_GroupInvite2(app); -} - -void Client::Handle_OP_GroupInvite2(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GroupInvite_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for OP_GroupInvite: Expected: %i, Got: %i", - sizeof(GroupInvite_Struct), app->size); - return; - } - - GroupInvite_Struct* gis = (GroupInvite_Struct*)app->pBuffer; - - Mob *Invitee = entity_list.GetMob(gis->invitee_name); - - if (Invitee == this) - { - Message_StringID(clientMessageWhite, GROUP_INVITEE_SELF); - return; - } - - if (Invitee) - { - if (Invitee->IsClient()) - { - if(Invitee->CastToClient()->MercOnlyOrNoGroup() && !Invitee->IsRaidGrouped()) - { - if (app->GetOpcode() == OP_GroupInvite2) - { - //Make a new packet using all the same information but make sure it's a fixed GroupInvite opcode so we - //Don't have to deal with GroupFollow2 crap. - EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupInvite, sizeof(GroupInvite_Struct)); - memcpy(outapp->pBuffer, app->pBuffer, outapp->size); - Invitee->CastToClient()->QueuePacket(outapp); - safe_delete(outapp); - return; - } - else - { - //The correct opcode, no reason to bother wasting time reconstructing the packet - Invitee->CastToClient()->QueuePacket(app); - } - } - } -#ifdef BOTS - else if (Invitee->IsBot()) { - Bot::ProcessBotGroupInvite(this, std::string(Invitee->GetName())); - } -#endif - } - else - { - ServerPacket* pack = new ServerPacket(ServerOP_GroupInvite, sizeof(GroupInvite_Struct)); - memcpy(pack->pBuffer, gis, sizeof(GroupInvite_Struct)); - worldserver.SendPacket(pack); - safe_delete(pack); - } - return; -} - -void Client::Handle_OP_GroupMakeLeader(const EQApplicationPacket *app) -{ - VERIFY_PACKET_LENGTH(OP_GroupMakeLeader, app, GroupMakeLeader_Struct); - - GroupMakeLeader_Struct *gmls = (GroupMakeLeader_Struct *)app->pBuffer; - - Mob* NewLeader = entity_list.GetClientByName(gmls->NewLeader); - - Group* g = GetGroup(); - - if (NewLeader && g) - { - if (g->IsLeader(this)) - g->ChangeLeader(NewLeader); - else { - LogFile->write(EQEMuLog::Debug, "Group /makeleader request originated from non-leader member: %s", GetName()); - DumpPacket(app); - } - } -} - -void Client::Handle_OP_GroupMentor(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GroupMentor_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GroupMentor, size=%i, expected %i", app->size, sizeof(GroupMentor_Struct)); - DumpPacket(app); - return; - } - GroupMentor_Struct *gms = (GroupMentor_Struct *)app->pBuffer; - gms->name[63] = '\0'; - - if (IsRaidGrouped()) { - Raid *raid = GetRaid(); - if (!raid) - return; - uint32 group_id = raid->GetGroup(this); - if (group_id > 11) - return; - if (strlen(gms->name)) - raid->SetGroupMentor(group_id, gms->percent, gms->name); - else - raid->ClearGroupMentor(group_id); - return; - } - - Group *group = GetGroup(); - if (!group) - return; - - if (strlen(gms->name)) - group->SetGroupMentor(gms->percent, gms->name); - else - group->ClearGroupMentor(); - - return; -} - -void Client::Handle_OP_GroupRoles(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GroupRole_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GroupRoles, size=%i, expected %i", app->size, sizeof(GroupRole_Struct)); - DumpPacket(app); - return; - } - GroupRole_Struct *grs = (GroupRole_Struct*)app->pBuffer; - - Group *g = GetGroup(); - - if (!g) - return; - - switch (grs->RoleNumber) - { - case 1: //Main Tank - { - if (grs->Toggle) - g->DelegateMainTank(grs->Name1, grs->Toggle); - else - g->UnDelegateMainTank(grs->Name1, grs->Toggle); - break; - } - case 2: //Main Assist - { - if (grs->Toggle) - g->DelegateMainAssist(grs->Name1, grs->Toggle); - else - g->UnDelegateMainAssist(grs->Name1, grs->Toggle); - break; - } - case 3: //Puller - { - if (grs->Toggle) - g->DelegatePuller(grs->Name1, grs->Toggle); - else - g->UnDelegatePuller(grs->Name1, grs->Toggle); - break; - } - default: - break; - } -} - -void Client::Handle_OP_GroupUpdate(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GroupUpdate_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch on OP_GroupUpdate: got %u expected %u", - app->size, sizeof(GroupUpdate_Struct)); - DumpPacket(app); - return; - } - - GroupUpdate_Struct* gu = (GroupUpdate_Struct*)app->pBuffer; - - switch (gu->action) { - case groupActMakeLeader: - { - Mob* newleader = entity_list.GetClientByName(gu->membername[0]); - Group* group = this->GetGroup(); - - if (newleader && group) { - // the client only sends this if it's the group leader, but check anyway - if (group->IsLeader(this)) - group->ChangeLeader(newleader); - else { - LogFile->write(EQEMuLog::Debug, "Group /makeleader request originated from non-leader member: %s", GetName()); - DumpPacket(app); - } - } - break; - } - - default: - { - LogFile->write(EQEMuLog::Debug, "Received unhandled OP_GroupUpdate requesting action %u", gu->action); - DumpPacket(app); - return; - } - } -} - -void Client::Handle_OP_GuildBank(const EQApplicationPacket *app) -{ - if (!GuildBanks) - return; - - if ((int)zone->GetZoneID() != RuleI(World, GuildBankZoneID)) - { - Message(13, "The Guild Bank is not available in this zone."); - - return; - } - - if (app->size < sizeof(uint32)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GuildBank, size=%i, expected %i", app->size, sizeof(uint32)); - DumpPacket(app); - return; - } - - char *Buffer = (char *)app->pBuffer; - - uint32 Action = VARSTRUCT_DECODE_TYPE(uint32, Buffer); - - if (!IsInAGuild()) - { - Message(13, "You must be in a Guild to use the Guild Bank."); - - if (Action == GuildBankDeposit) - GuildBankDepositAck(true); - else - GuildBankAck(); - - return; - } - - if (!IsGuildBanker()) - { - if ((Action != GuildBankDeposit) && (Action != GuildBankViewItem) && (Action != GuildBankWithdraw)) - { - _log(GUILDS__BANK_ERROR, "Suspected hacking attempt on guild bank from %s", GetName()); - - GuildBankAck(); - - return; - } - } - - switch (Action) - { - case GuildBankPromote: - { - if (GuildBanks->IsAreaFull(GuildID(), GuildBankMainArea)) - { - Message_StringID(13, GUILD_BANK_FULL); - - GuildBankDepositAck(true); - - return; - } - - GuildBankPromote_Struct *gbps = (GuildBankPromote_Struct*)app->pBuffer; - - int Slot = GuildBanks->Promote(GuildID(), gbps->Slot); - - if (Slot >= 0) - { - ItemInst* inst = GuildBanks->GetItem(GuildID(), GuildBankMainArea, Slot, 1); - - if (inst) - { - Message_StringID(clientMessageWhite, GUILD_BANK_TRANSFERRED, inst->GetItem()->Name); - safe_delete(inst); - } - } - else - Message(13, "Unexpected error while moving item into Guild Bank."); - - GuildBankAck(); - - break; - } - - case GuildBankViewItem: - { - GuildBankViewItem_Struct *gbvis = (GuildBankViewItem_Struct*)app->pBuffer; - - ItemInst* inst = GuildBanks->GetItem(GuildID(), gbvis->Area, gbvis->SlotID, 1); - - if (!inst) - break; - - SendItemPacket(0, inst, ItemPacketViewLink); - - safe_delete(inst); - - break; - } - - case GuildBankDeposit: // Deposit Item - { - if (GuildBanks->IsAreaFull(GuildID(), GuildBankDepositArea)) - { - Message_StringID(13, GUILD_BANK_FULL); - - GuildBankDepositAck(true); - - return; - } - - ItemInst *CursorItemInst = GetInv().GetItem(MainCursor); - - bool Allowed = true; - - if (!CursorItemInst) - { - Message(13, "No Item on the cursor."); - - GuildBankDepositAck(true); - - return; - } - - const Item_Struct* CursorItem = CursorItemInst->GetItem(); - - if (!CursorItem->NoDrop || CursorItemInst->IsInstNoDrop()) - { - Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); - - Allowed = false; - } - else if (CursorItemInst->IsNoneEmptyContainer()) - { - Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); - - Allowed = false; - } - else if (CursorItemInst->IsAugmented()) - { - Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); - - Allowed = false; - } - else if (CursorItem->NoRent == 0) - { - Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); - - Allowed = false; - } - else if (CursorItem->LoreFlag && GuildBanks->HasItem(GuildID(), CursorItem->ID)) - { - Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); - - Allowed = false; - } - - if (!Allowed) - { - GuildBankDepositAck(true); - - return; - } - - if (GuildBanks->AddItem(GuildID(), GuildBankDepositArea, CursorItem->ID, CursorItemInst->GetCharges(), GetName(), GuildBankBankerOnly, "")) - { - GuildBankDepositAck(false); - - DeleteItemInInventory(MainCursor, 0, false); - } - - break; - } - - case GuildBankPermissions: - { - GuildBankPermissions_Struct *gbps = (GuildBankPermissions_Struct*)app->pBuffer; - - if (gbps->Permissions == 1) - GuildBanks->SetPermissions(GuildID(), gbps->SlotID, gbps->Permissions, gbps->MemberName); - else - GuildBanks->SetPermissions(GuildID(), gbps->SlotID, gbps->Permissions, ""); - - GuildBankAck(); - break; - } - - case GuildBankWithdraw: - { - if (GetInv()[MainCursor]) - { - Message_StringID(13, GUILD_BANK_EMPTY_HANDS); - - GuildBankAck(); - - break; - } - - GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer; - - ItemInst* inst = GuildBanks->GetItem(GuildID(), gbwis->Area, gbwis->SlotID, gbwis->Quantity); - - if (!inst) - { - GuildBankAck(); - - break; - } - - if (!IsGuildBanker() && !GuildBanks->AllowedToWithdraw(GuildID(), gbwis->Area, gbwis->SlotID, GetName())) - { - _log(GUILDS__BANK_ERROR, "Suspected attempted hack on the guild bank from %s", GetName()); - - GuildBankAck(); - - safe_delete(inst); - - break; - } - - if (CheckLoreConflict(inst->GetItem())) - { - Message_StringID(13, DUP_LORE); - - GuildBankAck(); - - safe_delete(inst); - - break; - } - - if (gbwis->Quantity > 0) - { - PushItemOnCursor(*inst); - - SendItemPacket(MainCursor, inst, ItemPacketSummonItem); - - GuildBanks->DeleteItem(GuildID(), gbwis->Area, gbwis->SlotID, gbwis->Quantity); - } - else - { - Message(0, "Unable to withdraw 0 quantity of %s", inst->GetItem()->Name); - } - - safe_delete(inst); - - GuildBankAck(); - - break; - } - - case GuildBankSplitStacks: - { - if (GuildBanks->IsAreaFull(GuildID(), GuildBankMainArea)) - Message_StringID(13, GUILD_BANK_FULL); - else - { - GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer; - - GuildBanks->SplitStack(GuildID(), gbwis->SlotID, gbwis->Quantity); - } - - GuildBankAck(); - - break; - } - - case GuildBankMergeStacks: - { - GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer; - - GuildBanks->MergeStacks(GuildID(), gbwis->SlotID); - - GuildBankAck(); - - break; - } - - default: - { - Message(13, "Unexpected GuildBank action."); - - _log(GUILDS__BANK_ERROR, "Received unexpected guild bank action code %i from %s", Action, GetName()); - } - } -} - -void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app) -{ - if (IsInAGuild()) - { - Message(clientMessageError, "You are already in a guild!"); - return; - } - - if (!RuleB(Guild, PlayerCreationAllowed)) - { - Message(clientMessageError, "This feature is disabled on this server. Contact a GM or post on your server message boards to create a guild."); - return; - } - - if ((Admin() < RuleI(Guild, PlayerCreationRequiredStatus)) || - (GetLevel() < RuleI(Guild, PlayerCreationRequiredLevel)) || - (database.GetTotalTimeEntitledOnAccount(AccountID()) < (unsigned int)RuleI(Guild, PlayerCreationRequiredTime))) - { - Message(clientMessageError, "Your status, level or time playing on this account are insufficient to use this feature."); - return; - } - - // The Underfoot client Guild Creation window will only allow a guild name of <= around 30 characters, but the packet is 64 bytes. Sanity check the - // name anway. - // - - char *GuildName = (char *)app->pBuffer; -#ifdef DARWIN -#if __DARWIN_C_LEVEL < 200809L - if (strlen(GuildName) > 60) -#else - if (strnlen(GuildName, 64) > 60) -#endif // __DARWIN_C_LEVEL -#else - if (strnlen(GuildName, 64) > 60) -#endif // DARWIN - { - Message(clientMessageError, "Guild name too long."); - return; - } - - for (unsigned int i = 0; i < strlen(GuildName); ++i) - { - if (!isalpha(GuildName[i]) && (GuildName[i] != ' ')) - { - Message(clientMessageError, "Invalid character in Guild name."); - return; - } - } - - int32 GuildCount = guild_mgr.DoesAccountContainAGuildLeader(AccountID()); - - if (GuildCount >= RuleI(Guild, PlayerCreationLimit)) - { - Message(clientMessageError, "You cannot create this guild because this account may only be leader of %i guilds.", RuleI(Guild, PlayerCreationLimit)); - return; - } - - if (guild_mgr.GetGuildIDByName(GuildName) != GUILD_NONE) - { - Message_StringID(clientMessageError, GUILD_NAME_IN_USE); - return; - } - - uint32 NewGuildID = guild_mgr.CreateGuild(GuildName, CharacterID()); - - _log(GUILDS__ACTIONS, "%s: Creating guild %s with leader %d via UF+ GUI. It was given id %lu.", GetName(), - GuildName, CharacterID(), (unsigned long)NewGuildID); - - if (NewGuildID == GUILD_NONE) - Message(clientMessageError, "Guild creation failed."); - else - { - if (!guild_mgr.SetGuild(CharacterID(), NewGuildID, GUILD_LEADER)) - Message(clientMessageError, "Unable to set guild leader's guild in the database. Contact a GM."); - else - { - Message(clientMessageYellow, "You are now the leader of %s", GuildName); - - if (zone->GetZoneID() == RuleI(World, GuildBankZoneID) && GuildBanks) - GuildBanks->SendGuildBank(this); - SendGuildRanks(); - } - } -} - -void Client::Handle_OP_GuildDelete(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildDelete"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if (!IsInAGuild() || !guild_mgr.IsGuildLeader(GuildID(), CharacterID())) - Message(0, "You are not a guild leader or not in a guild."); - else { - mlog(GUILDS__ACTIONS, "Deleting guild %s (%d)", guild_mgr.GetGuildName(GuildID()), GuildID()); - if (!guild_mgr.DeleteGuild(GuildID())) - Message(0, "Guild delete failed."); - else { - Message(0, "Guild successfully deleted."); - } - } -} - -void Client::Handle_OP_GuildDemote(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildDemote"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if (app->size != sizeof(GuildDemoteStruct)) { - mlog(GUILDS__ERROR, "Error: app size of %i != size of GuildDemoteStruct of %i\n", app->size, sizeof(GuildDemoteStruct)); - return; - } - - if (!IsInAGuild()) - Message(0, "Error: You arent in a guild!"); - else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_DEMOTE)) - Message(0, "You dont have permission to invite."); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - GuildDemoteStruct* demote = (GuildDemoteStruct*)app->pBuffer; - - CharGuildInfo gci; - if (!guild_mgr.GetCharInfo(demote->target, gci)) { - Message(0, "Unable to find '%s'", demote->target); - return; - } - if (gci.guild_id != GuildID()) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - - if (gci.rank < 1) { - Message(0, "%s cannot be demoted any further!", demote->target); - return; - } - uint8 rank = gci.rank - 1; - - - mlog(GUILDS__ACTIONS, "Demoting %s (%d) from rank %s (%d) to %s (%d) in %s (%d)", - demote->target, gci.char_id, - guild_mgr.GetRankName(GuildID(), gci.rank), gci.rank, - guild_mgr.GetRankName(GuildID(), rank), rank, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - if (!guild_mgr.SetGuildRank(gci.char_id, rank)) { - Message(13, "Error while setting rank %d on '%s'.", rank, demote->target); - return; - } - Message(0, "Successfully demoted %s to rank %d", demote->target, rank); - } - // SendGuildMembers(GuildID(), true); - return; -} - -void Client::Handle_OP_GuildInvite(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildInvite"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if (app->size != sizeof(GuildCommand_Struct)) { - std::cout << "Wrong size: OP_GuildInvite, size=" << app->size << ", expected " << sizeof(GuildCommand_Struct) << std::endl; - return; - } - - GuildCommand_Struct* gc = (GuildCommand_Struct*)app->pBuffer; - - if (!IsInAGuild()) - Message(0, "Error: You are not in a guild!"); - else if (gc->officer > GUILD_MAX_RANK) - Message(13, "Invalid rank."); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - - //ok, the invite is also used for changing rank as well. - Mob* invitee = entity_list.GetMob(gc->othername); - - if (!invitee) { - Message(13, "Prospective guild member %s must be in zone to preform guild operations on them.", gc->othername); - return; - } - - if (invitee->IsClient()) { - Client* client = invitee->CastToClient(); - - //ok, figure out what they are trying to do. - if (client->GuildID() == GuildID()) { - //they are already in this guild, must be a promotion or demotion - if (gc->officer < client->GuildRank()) { - //demotion - if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_DEMOTE)) { - Message(13, "You dont have permission to demote."); - return; - } - - //we could send this to the member and prompt them to see if they want to - //be demoted (I guess), but I dont see a point in that. - - mlog(GUILDS__ACTIONS, "%s (%d) is demoting %s (%d) to rank %d in guild %s (%d)", - GetName(), CharacterID(), - client->GetName(), client->CharacterID(), - gc->officer, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - if (!guild_mgr.SetGuildRank(client->CharacterID(), gc->officer)) { - Message(13, "There was an error during the demotion, DB may now be inconsistent."); - return; - } - - } - else if (gc->officer > client->GuildRank()) { - //promotion - if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_PROMOTE)) { - Message(13, "You dont have permission to demote."); - return; - } - - mlog(GUILDS__ACTIONS, "%s (%d) is asking to promote %s (%d) to rank %d in guild %s (%d)", - GetName(), CharacterID(), - client->GetName(), client->CharacterID(), - gc->officer, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - //record the promotion with guild manager so we know its valid when we get the reply - guild_mgr.RecordInvite(client->CharacterID(), GuildID(), gc->officer); - - if (gc->guildeqid == 0) - gc->guildeqid = GuildID(); - - mlog(GUILDS__OUT_PACKETS, "Sending OP_GuildInvite for promotion to %s, length %d", client->GetName(), app->size); - mpkt(GUILDS__OUT_PACKET_TRACE, app); - client->QueuePacket(app); - - } - else { - Message(13, "That member is already that rank."); - return; - } - } - else if (!client->IsInAGuild()) { - //they are not in this or any other guild, this is an invite - // - if (client->GetPendingGuildInvitation()) - { - Message(13, "That person is already considering a guild invitation."); - return; - } - - if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_INVITE)) { - Message(13, "You dont have permission to invite."); - return; - } - - mlog(GUILDS__ACTIONS, "Inviting %s (%d) into guild %s (%d)", - client->GetName(), client->CharacterID(), - guild_mgr.GetGuildName(GuildID()), GuildID()); - - //record the invite with guild manager so we know its valid when we get the reply - guild_mgr.RecordInvite(client->CharacterID(), GuildID(), gc->officer); - - if (gc->guildeqid == 0) - gc->guildeqid = GuildID(); - - mlog(GUILDS__OUT_PACKETS, "Sending OP_GuildInvite for invite to %s, length %d", client->GetName(), app->size); - mpkt(GUILDS__OUT_PACKET_TRACE, app); - client->SetPendingGuildInvitation(true); - client->QueuePacket(app); - - } - else { - //they are in some other guild - Message(13, "Player is in a guild."); - return; - } - } -#ifdef BOTS - else if (invitee->IsBot()) { - // The guild system is too tightly coupled with the character_data table so we have to avoid using much of the system - Bot::ProcessGuildInvite(this, invitee->CastToBot()); - return; - } -#endif - } -} - -void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildInviteAccept"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - SetPendingGuildInvitation(false); - - if (app->size != sizeof(GuildInviteAccept_Struct)) { - std::cout << "Wrong size: OP_GuildInviteAccept, size=" << app->size << ", expected " << sizeof(GuildJoin_Struct) << std::endl; - return; - } - - GuildInviteAccept_Struct* gj = (GuildInviteAccept_Struct*)app->pBuffer; - - if (GetClientVersion() >= EQClientRoF) - { - if (gj->response > 9) - { - //dont care if the check fails (since we dont know the rank), just want to clear the entry. - guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response); - worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has declined to join the guild.", this->GetName()); - return; - } - } - if (gj->response == 5 || gj->response == 4) { - //dont care if the check fails (since we dont know the rank), just want to clear the entry. - guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response); - - worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has declined to join the guild.", this->GetName()); - - return; - } - - //uint32 tmpeq = gj->guildeqid; - if (IsInAGuild() && gj->response == GuildRank()) - Message(0, "Error: You're already in a guild!"); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - mlog(GUILDS__ACTIONS, "Guild Invite Accept: guild %d, response %d, inviter %s, person %s", - gj->guildeqid, gj->response, gj->inviter, gj->newmember); - - //we dont really care a lot about what this packet means, as long as - //it has been authorized with the guild manager - if (!guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response)) { - worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has sent an invalid response to your invite!", GetName()); - Message(13, "Invalid invite response packet!"); - return; - } - - if (gj->guildeqid == GuildID()) { - //only need to change rank. - - mlog(GUILDS__ACTIONS, "Changing guild rank of %s (%d) to rank %d in guild %s (%d)", - GetName(), CharacterID(), - gj->response, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - if (!guild_mgr.SetGuildRank(CharacterID(), gj->response)) { - Message(13, "There was an error during the rank change, DB may now be inconsistent."); - return; - } - } - else { - - mlog(GUILDS__ACTIONS, "Adding %s (%d) to guild %s (%d) at rank %d", - GetName(), CharacterID(), - guild_mgr.GetGuildName(gj->guildeqid), gj->guildeqid, - gj->response); - - //change guild and rank - - uint32 guildrank = gj->response; - - if (GetClientVersion() == EQClientRoF) - { - if (gj->response == 8) - { - guildrank = 0; - } - } - - if (!guild_mgr.SetGuild(CharacterID(), gj->guildeqid, guildrank)) { - Message(13, "There was an error during the invite, DB may now be inconsistent."); - return; - } - if (zone->GetZoneID() == RuleI(World, GuildBankZoneID) && GuildBanks) - GuildBanks->SendGuildBank(this); - } - } -} - -void Client::Handle_OP_GuildLeader(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildLeader"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if (app->size < 2) { - mlog(GUILDS__ERROR, "Invalid length %d on OP_GuildLeader", app->size); - return; - } - - app->pBuffer[app->size - 1] = 0; - GuildMakeLeader* gml = (GuildMakeLeader*)app->pBuffer; - if (!IsInAGuild()) - Message(0, "Error: You arent in a guild!"); - else if (GuildRank() != GUILD_LEADER) - Message(0, "Error: You arent the guild leader!"); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - - //NOTE: we could do cross-zone lookups here... - - Client* newleader = entity_list.GetClientByName(gml->target); - if (newleader) { - - mlog(GUILDS__ACTIONS, "Transfering leadership of %s (%d) to %s (%d)", - guild_mgr.GetGuildName(GuildID()), GuildID(), - newleader->GetName(), newleader->CharacterID()); - - if (guild_mgr.SetGuildLeader(GuildID(), newleader->CharacterID())){ - Message(0, "Successfully Transfered Leadership to %s.", gml->target); - newleader->Message(15, "%s has transfered the guild leadership into your hands.", GetName()); - } - else - Message(0, "Could not change leadership at this time."); - } - else - Message(0, "Failed to change leader, could not find target."); - } - // SendGuildMembers(GuildID(), true); - return; -} - -void Client::Handle_OP_GuildManageBanker(const EQApplicationPacket *app) -{ - - mlog(GUILDS__IN_PACKETS, "Got OP_GuildManageBanker of len %d", app->size); - mpkt(GUILDS__IN_PACKET_TRACE, app); - if (app->size != sizeof(GuildManageBanker_Struct)) { - mlog(GUILDS__ERROR, "Error: app size of %i != size of OP_GuildManageBanker of %i\n", app->size, sizeof(GuildManageBanker_Struct)); - return; - } - GuildManageBanker_Struct* gmb = (GuildManageBanker_Struct*)app->pBuffer; - - if (!IsInAGuild()) { - Message(13, "Your not in a guild!"); - return; - } - - CharGuildInfo gci; - - if (!guild_mgr.GetCharInfo(gmb->member, gci)) - { - Message(0, "Unable to find '%s'", gmb->member); - return; - } - bool IsCurrentlyABanker = guild_mgr.GetBankerFlag(gci.char_id); - - bool IsCurrentlyAnAlt = guild_mgr.GetAltFlag(gci.char_id); - - bool NewBankerStatus = gmb->enabled & 0x01; - - bool NewAltStatus = gmb->enabled & 0x02; - - if ((IsCurrentlyABanker != NewBankerStatus) && !guild_mgr.IsGuildLeader(GuildID(), CharacterID())) - { - Message(13, "Only the guild leader can assign guild bankers!"); - return; - } - - if (IsCurrentlyAnAlt != NewAltStatus) - { - bool IsAllowed = !strncasecmp(GetName(), gmb->member, strlen(GetName())) || (GuildRank() >= GUILD_OFFICER); - - if (!IsAllowed) - { - Message(13, "You are not allowed to change the alt status of %s", gmb->member); - return; - } - } - - if (gci.guild_id != GuildID()) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - - if (IsCurrentlyABanker != NewBankerStatus) - { - if (!guild_mgr.SetBankerFlag(gci.char_id, NewBankerStatus)) { - Message(13, "Error setting guild banker flag."); - return; - } - - if (NewBankerStatus) - Message(0, "%s has been made a guild banker.", gmb->member); - else - Message(0, "%s is no longer a guild banker.", gmb->member); - } - if (IsCurrentlyAnAlt != NewAltStatus) - { - if (!guild_mgr.SetAltFlag(gci.char_id, NewAltStatus)) { - Message(13, "Error setting guild alt flag."); - return; - } - - if (NewAltStatus) - Message(0, "%s has been marked as an alt.", gmb->member); - else - Message(0, "%s is no longer marked as an alt.", gmb->member); - } -} - -void Client::Handle_OP_GuildPeace(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Got OP_GuildPeace of len %d", app->size); - mpkt(GUILDS__IN_PACKET_TRACE, app); - return; -} - -void Client::Handle_OP_GuildPromote(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildPromote"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if (app->size != sizeof(GuildPromoteStruct)) { - mlog(GUILDS__ERROR, "Error: app size of %i != size of GuildDemoteStruct of %i\n", app->size, sizeof(GuildPromoteStruct)); - return; - } - - if (!IsInAGuild()) - Message(0, "Error: You arent in a guild!"); - else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_PROMOTE)) - Message(0, "You dont have permission to invite."); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - GuildPromoteStruct* promote = (GuildPromoteStruct*)app->pBuffer; - - CharGuildInfo gci; - if (!guild_mgr.GetCharInfo(promote->target, gci)) { - Message(0, "Unable to find '%s'", promote->target); - return; - } - if (gci.guild_id != GuildID()) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - - uint8 rank = gci.rank + 1; - - if (rank > GUILD_OFFICER) - return; - - - mlog(GUILDS__ACTIONS, "Promoting %s (%d) from rank %s (%d) to %s (%d) in %s (%d)", - promote->target, gci.char_id, - guild_mgr.GetRankName(GuildID(), gci.rank), gci.rank, - guild_mgr.GetRankName(GuildID(), rank), rank, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - if (!guild_mgr.SetGuildRank(gci.char_id, rank)) { - Message(13, "Error while setting rank %d on '%s'.", rank, promote->target); - return; - } - Message(0, "Successfully promoted %s to rank %d", promote->target, rank); - } - return; -} - -void Client::Handle_OP_GuildPublicNote(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildPublicNote"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if (app->size < sizeof(GuildUpdate_PublicNote)) { - // client calls for a motd on login even if they arent in a guild - printf("Error: app size of %i < size of OP_GuildPublicNote of %zu\n", app->size, sizeof(GuildUpdate_PublicNote)); - return; - } - GuildUpdate_PublicNote* gpn = (GuildUpdate_PublicNote*)app->pBuffer; - - CharGuildInfo gci; - if (!guild_mgr.GetCharInfo(gpn->target, gci)) { - Message(0, "Unable to find '%s'", gpn->target); - return; - } - if (gci.guild_id != GuildID()) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - - mlog(GUILDS__ACTIONS, "Setting public note on %s (%d) in guild %s (%d) to: %s", - gpn->target, gci.char_id, - guild_mgr.GetGuildName(GuildID()), GuildID(), - gpn->note); - - if (!guild_mgr.SetPublicNote(gci.char_id, gpn->note)) { - Message(13, "Failed to set public note on %s", gpn->target); - } - else { - Message(0, "Successfully changed public note on %s", gpn->target); - } - // SendGuildMembers(GuildID(), true); - return; -} - -void Client::Handle_OP_GuildRemove(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildRemove"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if (app->size != sizeof(GuildCommand_Struct)) { - std::cout << "Wrong size: OP_GuildRemove, size=" << app->size << ", expected " << sizeof(GuildCommand_Struct) << std::endl; - return; - } - GuildCommand_Struct* gc = (GuildCommand_Struct*)app->pBuffer; - if (!IsInAGuild()) - Message(0, "Error: You arent in a guild!"); - // we can always remove ourself, otherwise, our rank needs remove permissions - else if (strcasecmp(gc->othername, GetName()) != 0 && - !guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_REMOVE)) - Message(0, "You dont have permission to remove guild members."); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { -#ifdef BOTS - if (Bot::ProcessGuildRemoval(this, gc->othername)) - return; -#endif - uint32 char_id; - Client* client = entity_list.GetClientByName(gc->othername); - - if (client) { - if (!client->IsInGuild(GuildID())) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - char_id = client->CharacterID(); - - mlog(GUILDS__ACTIONS, "Removing %s (%d) from guild %s (%d)", - client->GetName(), client->CharacterID(), - guild_mgr.GetGuildName(GuildID()), GuildID()); - } - else { - CharGuildInfo gci; - if (!guild_mgr.GetCharInfo(gc->othername, gci)) { - Message(0, "Unable to find '%s'", gc->othername); - return; - } - if (gci.guild_id != GuildID()) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - char_id = gci.char_id; - - mlog(GUILDS__ACTIONS, "Removing remote/offline %s (%d) into guild %s (%d)", - gci.char_name.c_str(), gci.char_id, - guild_mgr.GetGuildName(GuildID()), GuildID()); - } - - if (!guild_mgr.SetGuild(char_id, GUILD_NONE, 0)) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_GuildManageRemove, sizeof(GuildManageRemove_Struct)); - GuildManageRemove_Struct* gm = (GuildManageRemove_Struct*)outapp->pBuffer; - gm->guildeqid = GuildID(); - strcpy(gm->member, gc->othername); - Message(0, "%s successfully removed from your guild.", gc->othername); - entity_list.QueueClientsGuild(this, outapp, false, GuildID()); - safe_delete(outapp); - } - else - Message(0, "Unable to remove %s from your guild.", gc->othername); - } - // SendGuildMembers(GuildID(), true); - return; -} - -void Client::Handle_OP_GuildStatus(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GuildStatus_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GuildStatus expected %i got %i", - sizeof(GuildStatus_Struct), app->size); - - DumpPacket(app); - - return; - } - GuildStatus_Struct *gss = (GuildStatus_Struct*)app->pBuffer; - - Client *c = entity_list.GetClientByName(gss->Name); - - if (!c) - { - Message_StringID(clientMessageWhite, TARGET_PLAYER_FOR_GUILD_STATUS); - return; - } - - uint32 TargetGuildID = c->GuildID(); - - if (TargetGuildID == GUILD_NONE) - { - Message_StringID(clientMessageWhite, NOT_IN_A_GUILD, c->GetName()); - return; - } - - const char *GuildName = guild_mgr.GetGuildName(TargetGuildID); - - if (!GuildName) - return; - - bool IsLeader = guild_mgr.CheckPermission(TargetGuildID, c->GuildRank(), GUILD_PROMOTE); - bool IsOfficer = guild_mgr.CheckPermission(TargetGuildID, c->GuildRank(), GUILD_INVITE); - - if ((TargetGuildID == GuildID()) && (c != this)) - { - if (IsLeader) - Message_StringID(clientMessageWhite, LEADER_OF_YOUR_GUILD, c->GetName()); - else if (IsOfficer) - Message_StringID(clientMessageWhite, OFFICER_OF_YOUR_GUILD, c->GetName()); - else - Message_StringID(clientMessageWhite, MEMBER_OF_YOUR_GUILD, c->GetName()); - - return; - } - - if (IsLeader) - Message_StringID(clientMessageWhite, LEADER_OF_X_GUILD, c->GetName(), GuildName); - else if (IsOfficer) - Message_StringID(clientMessageWhite, OFFICER_OF_X_GUILD, c->GetName(), GuildName); - else - Message_StringID(clientMessageWhite, MEMBER_OF_X_GUILD, c->GetName(), GuildName); -} - -void Client::Handle_OP_GuildUpdateURLAndChannel(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GuildUpdateURLAndChannel_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GuildUpdateURLAndChannel expected %i got %i", - sizeof(GuildUpdateURLAndChannel_Struct), app->size); - - DumpPacket(app); - - return; - } - - GuildUpdateURLAndChannel_Struct *guuacs = (GuildUpdateURLAndChannel_Struct*)app->pBuffer; - - if (!IsInAGuild()) - return; - - if (!guild_mgr.IsGuildLeader(GuildID(), CharacterID())) - { - Message(13, "Only the guild leader can change the Channel or URL.!"); - return; - } - - if (guuacs->Action == 0) - guild_mgr.SetGuildURL(GuildID(), guuacs->Text); - else - guild_mgr.SetGuildChannel(GuildID(), guuacs->Text); - -} - -void Client::Handle_OP_GuildWar(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Got OP_GuildWar of len %d", app->size); - mpkt(GUILDS__IN_PACKET_TRACE, app); - return; -} - -void Client::Handle_OP_Heartbeat(const EQApplicationPacket *app) -{ - return; -} - -void Client::Handle_OP_Hide(const EQApplicationPacket *app) -{ - if (!HasSkill(SkillHide) && GetSkill(SkillHide) == 0) - { - //Can not be able to train hide but still have it from racial though - return; //You cannot hide if you do not have hide - } - - if (!p_timers.Expired(&database, pTimerHide, false)) { - Message(13, "Ability recovery time not yet met."); - return; - } - int reuse = HideReuseTime - GetAA(209); - p_timers.Start(pTimerHide, reuse - 1); - - float hidechance = ((GetSkill(SkillHide) / 250.0f) + .25) * 100; - float random = zone->random.Real(0, 100); - CheckIncreaseSkill(SkillHide, nullptr, 5); - if (random < hidechance) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x03; - sa_out->parameter = 1; - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - if (GetAA(aaShroudofStealth)){ - improved_hidden = true; - hidden = true; - } - else - hidden = true; - } - if (GetClass() == ROGUE){ - EQApplicationPacket *outapp = new EQApplicationPacket(OP_SimpleMessage, sizeof(SimpleMessage_Struct)); - SimpleMessage_Struct *msg = (SimpleMessage_Struct *)outapp->pBuffer; - msg->color = 0x010E; - Mob *evadetar = GetTarget(); - if (!auto_attack && (evadetar && evadetar->CheckAggro(this) - && evadetar->IsNPC())) { - if (zone->random.Int(0, 260) < (int)GetSkill(SkillHide)) { - msg->string_id = EVADE_SUCCESS; - RogueEvade(evadetar); - } - else { - msg->string_id = EVADE_FAIL; - } - } - else { - if (hidden){ - msg->string_id = HIDE_SUCCESS; - } - else { - msg->string_id = HIDE_FAIL; - } - } - FastQueuePacket(&outapp); - } - return; -} - -void Client::Handle_OP_HideCorpse(const EQApplicationPacket *app) -{ - // New OPCode for SOD+ as /hidecorpse is handled serverside now. - // - if (app->size != sizeof(HideCorpse_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_HideCorpse expected %i got %i", - sizeof(HideCorpse_Struct), app->size); - - DumpPacket(app); - - return; - } - - HideCorpse_Struct *hcs = (HideCorpse_Struct*)app->pBuffer; - - if (hcs->Action == HideCorpseLooted) - return; - - if ((HideCorpseMode == HideCorpseNone) && (hcs->Action == HideCorpseNone)) - return; - - entity_list.HideCorpses(this, HideCorpseMode, hcs->Action); - - HideCorpseMode = hcs->Action; -} - -void Client::Handle_OP_Ignore(const EQApplicationPacket *app) -{ - return; -} - -void Client::Handle_OP_Illusion(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Illusion_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized OP_Illusion: got %d, expected %d", app->size, - sizeof(Illusion_Struct)); - DumpPacket(app); - return; - } - - if (!GetGM()) - { - database.SetMQDetectionFlag(this->AccountName(), this->GetName(), "OP_Illusion sent by non Game Master.", zone->GetShortName()); - return; - } - - Illusion_Struct* bnpc = (Illusion_Struct*)app->pBuffer; - //these need to be implemented - /* - texture = bnpc->texture; - helmtexture = bnpc->helmtexture; - luclinface = bnpc->luclinface; - */ - race = bnpc->race; - size = 0; - - entity_list.QueueClients(this, app); - return; -} - -void Client::Handle_OP_InspectAnswer(const EQApplicationPacket *app) -{ - - if (app->size != sizeof(InspectResponse_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_InspectAnswer, size=%i, expected %i", app->size, sizeof(InspectResponse_Struct)); - return; - } - - //Fills the app sent from client. - EQApplicationPacket* outapp = app->Copy(); - InspectResponse_Struct* insr = (InspectResponse_Struct*)outapp->pBuffer; - Mob* tmp = entity_list.GetMob(insr->TargetID); - const Item_Struct* item = nullptr; - - int ornamentationAugtype = RuleI(Character, OrnamentationAugmentType); - for (int16 L = EmuConstants::EQUIPMENT_BEGIN; L <= MainWaist; L++) { - const ItemInst* inst = GetInv().GetItem(L); - item = inst ? inst->GetItem() : nullptr; - - if (item) { - if (inst && inst->GetOrnamentationAug(ornamentationAugtype)) { - const Item_Struct *aug_weap = inst->GetOrnamentationAug(ornamentationAugtype)->GetItem(); - strcpy(insr->itemnames[L], item->Name); - insr->itemicons[L] = aug_weap->Icon; - } - else if (inst->GetOrnamentationIcon() && inst->GetOrnamentationIDFile()) { - strcpy(insr->itemnames[L], item->Name); - insr->itemicons[L] = inst->GetOrnamentationIcon(); - } - else { - strcpy(insr->itemnames[L], item->Name); - insr->itemicons[L] = item->Icon; - } - } - else { insr->itemicons[L] = 0xFFFFFFFF; } - } - - const ItemInst* inst = GetInv().GetItem(MainAmmo); - item = inst ? inst->GetItem() : nullptr; - - if (item) { - // another one..I did these, didn't I!!? - strcpy(insr->itemnames[SoF::slots::MainAmmo], item->Name); - insr->itemicons[SoF::slots::MainAmmo] = item->Icon; - } - else { insr->itemicons[SoF::slots::MainAmmo] = 0xFFFFFFFF; } - - InspectMessage_Struct* newmessage = (InspectMessage_Struct*)insr->text; - InspectMessage_Struct& playermessage = this->GetInspectMessage(); - memcpy(&playermessage, newmessage, sizeof(InspectMessage_Struct)); - database.SaveCharacterInspectMessage(this->CharacterID(), &playermessage); - - if (tmp != 0 && tmp->IsClient()) { tmp->CastToClient()->QueuePacket(outapp); } // Send answer to requester - - return; -} - -void Client::Handle_OP_InspectMessageUpdate(const EQApplicationPacket *app) -{ - - if (app->size != sizeof(InspectMessage_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_InspectMessageUpdate, size=%i, expected %i", app->size, sizeof(InspectMessage_Struct)); - return; - } - - InspectMessage_Struct* newmessage = (InspectMessage_Struct*)app->pBuffer; - InspectMessage_Struct& playermessage = this->GetInspectMessage(); - memcpy(&playermessage, newmessage, sizeof(InspectMessage_Struct)); - database.SaveCharacterInspectMessage(this->CharacterID(), &playermessage); -} - -void Client::Handle_OP_InspectRequest(const EQApplicationPacket *app) -{ - - if (app->size != sizeof(Inspect_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_InspectRequest, size=%i, expected %i", app->size, sizeof(Inspect_Struct)); - return; - } - - Inspect_Struct* ins = (Inspect_Struct*)app->pBuffer; - Mob* tmp = entity_list.GetMob(ins->TargetID); - - if (tmp != 0 && tmp->IsClient()) { - if (tmp->CastToClient()->GetClientVersion() < EQClientSoF) { tmp->CastToClient()->QueuePacket(app); } // Send request to target - // Inspecting an SoF or later client will make the server handle the request - else { ProcessInspectRequest(tmp->CastToClient(), this); } - } - -#ifdef BOTS - if (tmp != 0 && tmp->IsBot()) { Bot::ProcessBotInspectionRequest(tmp->CastToBot(), this); } -#endif - - return; -} - -void Client::Handle_OP_InstillDoubt(const EQApplicationPacket *app) -{ - //packet is empty as of 12/14/04 - - if (!p_timers.Expired(&database, pTimerInstillDoubt, false)) { - Message(13, "Ability recovery time not yet met."); - return; - } - p_timers.Start(pTimerInstillDoubt, InstillDoubtReuseTime - 1); - - InstillDoubt(GetTarget()); - return; -} - -void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ItemViewRequest_Struct)){ - LogFile->write(EQEMuLog::Error, "Wrong size on OP_ItemLinkClick. Got: %i, Expected: %i", app->size, sizeof(ItemViewRequest_Struct)); - DumpPacket(app); - return; - } - - DumpPacket(app); - ItemViewRequest_Struct* ivrs = (ItemViewRequest_Struct*)app->pBuffer; - - //todo: verify ivrs->link_hash based on a rule, in case we don't care about people being able to sniff data from the item DB - - const Item_Struct* item = database.GetItem(ivrs->item_id); - if (!item) { - if (ivrs->item_id > 500000) - { - std::string response = ""; - int sayid = ivrs->item_id - 500000; - bool silentsaylink = false; - - if (sayid > 250000) //Silent Saylink - { - sayid = sayid - 250000; - silentsaylink = true; - } - - if (sayid > 0) - { - - std::string query = StringFormat("SELECT `phrase` FROM saylink WHERE `id` = '%i'", sayid); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - Message(13, "Error: The saylink (%s) was not found in the database.", response.c_str()); - return; - } - - if (results.RowCount() != 1) { - Message(13, "Error: The saylink (%s) was not found in the database.", response.c_str()); - return; - } - - auto row = results.begin(); - response = row[0]; - - } - - if ((response).size() > 0) - { - if (!mod_saylink(response, silentsaylink)) { return; } - - if (GetTarget() && GetTarget()->IsNPC()) - { - if (silentsaylink) - { - parse->EventNPC(EVENT_SAY, GetTarget()->CastToNPC(), this, response.c_str(), 0); - parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); - } - else - { - Message(7, "You say, '%s'", response.c_str()); - ChannelMessageReceived(8, 0, 100, response.c_str()); - } - return; - } - else - { - if (silentsaylink) - { - parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); - } - else - { - Message(7, "You say, '%s'", response.c_str()); - ChannelMessageReceived(8, 0, 100, response.c_str()); - } - return; - } - } - else - { - Message(13, "Error: Say Link not found or is too long."); - return; - } - } - else { - Message(13, "Error: The item for the link you have clicked on does not exist!"); - return; - } - - } - - ItemInst* inst = database.CreateItem(item, item->MaxCharges, ivrs->augments[0], ivrs->augments[1], ivrs->augments[2], ivrs->augments[3], ivrs->augments[4]); - if (inst) { - SendItemPacket(0, inst, ItemPacketViewLink); - safe_delete(inst); - } - return; -} - -void Client::Handle_OP_ItemLinkResponse(const EQApplicationPacket *app) -{ - if (app->size != sizeof(LDONItemViewRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "OP size error: OP_ItemLinkResponse expected:%i got:%i", sizeof(LDONItemViewRequest_Struct), app->size); - return; - } - LDONItemViewRequest_Struct* item = (LDONItemViewRequest_Struct*)app->pBuffer; - ItemInst* inst = database.CreateItem(item->item_id); - if (inst) { - SendItemPacket(0, inst, ItemPacketViewLink); - safe_delete(inst); - } - return; -} - -void Client::Handle_OP_ItemName(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ItemNamePacket_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for ItemNamePacket_Struct: Expected: %i, Got: %i", - sizeof(ItemNamePacket_Struct), app->size); - return; - } - ItemNamePacket_Struct *p = (ItemNamePacket_Struct*)app->pBuffer; - const Item_Struct *item = 0; - if ((item = database.GetItem(p->item_id)) != nullptr) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ItemName, sizeof(ItemNamePacket_Struct)); - p = (ItemNamePacket_Struct*)outapp->pBuffer; - memset(p, 0, sizeof(ItemNamePacket_Struct)); - strcpy(p->name, item->Name); - FastQueuePacket(&outapp); - } - return; -} - -void Client::Handle_OP_ItemPreview(const EQApplicationPacket *app) -{ - VERIFY_PACKET_LENGTH(OP_ItemPreview, app, ItemPreview_Struct); - ItemPreview_Struct *ips = (ItemPreview_Struct *)app->pBuffer; - - const Item_Struct* item = database.GetItem(ips->itemid); - - if (item) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ItemPreview, strlen(item->Name) + strlen(item->Lore) + strlen(item->IDFile) + 898); - - int spacer; - for (spacer = 0; spacer < 16; spacer++) { - outapp->WriteUInt8(48); - } - outapp->WriteUInt16(256); - for (spacer = 0; spacer < 7; spacer++) { - outapp->WriteUInt8(0); - } - for (spacer = 0; spacer < 7; spacer++) { - outapp->WriteUInt8(255); - } - outapp->WriteUInt32(0); - outapp->WriteUInt32(1); - outapp->WriteUInt32(0); - outapp->WriteUInt8(237); // Seems to be some kind of counter? increases by 1 for each preview that you do. - outapp->WriteUInt16(2041); //F907 - for (spacer = 0; spacer < 36; spacer++) { - outapp->WriteUInt8(0); - } - for (spacer = 0; spacer < 4; spacer++) { - outapp->WriteUInt8(255); - } - for (spacer = 0; spacer < 9; spacer++) { - outapp->WriteUInt8(0); - } - for (spacer = 0; spacer < 5; spacer++) { - outapp->WriteUInt8(255); - } - for (spacer = 0; spacer < 5; spacer++) { - outapp->WriteUInt8(0); - } - outapp->WriteString(item->Name); - outapp->WriteString(item->Lore); - outapp->WriteUInt8(0); - outapp->WriteUInt32(ips->itemid); - outapp->WriteUInt32(item->Weight); - outapp->WriteUInt8(item->NoRent); - outapp->WriteUInt8(item->NoDrop); - outapp->WriteUInt8(item->Attuneable); - outapp->WriteUInt8(item->Size); - outapp->WriteUInt32(item->Slots); - outapp->WriteUInt32(item->Price); - outapp->WriteUInt32(item->Icon); - outapp->WriteUInt8(0); //Unknown? - outapp->WriteUInt8(0); //Placeable flag? - outapp->WriteUInt32(item->BenefitFlag); - outapp->WriteUInt8(item->Tradeskills); - outapp->WriteUInt8(item->CR); - outapp->WriteUInt8(item->DR); - outapp->WriteUInt8(item->PR); - outapp->WriteUInt8(item->MR); - outapp->WriteUInt8(item->FR); - outapp->WriteUInt8(item->AStr); - outapp->WriteUInt8(item->ASta); - outapp->WriteUInt8(item->AAgi); - outapp->WriteUInt8(item->ADex); - outapp->WriteUInt8(item->ACha); - outapp->WriteUInt8(item->AInt); - outapp->WriteUInt8(item->AWis); - outapp->WriteSInt32(item->HP); - outapp->WriteSInt32(item->Mana); - outapp->WriteSInt32(item->Endur); - outapp->WriteSInt32(item->AC); - outapp->WriteUInt32(item->Regen); - outapp->WriteUInt32(item->ManaRegen); - outapp->WriteSInt32(item->EnduranceRegen); - outapp->WriteUInt32(item->Classes); - outapp->WriteUInt32(item->Races); - outapp->WriteUInt32(item->Deity); - outapp->WriteUInt32(item->SkillModValue); - outapp->WriteUInt32(0); //SkillModValue - outapp->WriteUInt32(item->SkillModType); - outapp->WriteUInt32(0); //SkillModExtra - outapp->WriteUInt32(item->BaneDmgRace); - outapp->WriteUInt32(item->BaneDmgBody); - outapp->WriteUInt32(item->BaneDmgRaceAmt); - outapp->WriteUInt32(item->BaneDmgAmt); - outapp->WriteUInt8(item->Magic); - outapp->WriteUInt32(item->CastTime_); - outapp->WriteUInt32(item->ReqLevel); - outapp->WriteUInt32(item->RecLevel); - outapp->WriteUInt32(item->RecSkill); - outapp->WriteUInt32(item->BardType); - outapp->WriteUInt32(item->BardValue); - outapp->WriteUInt8(item->Light); - outapp->WriteUInt8(item->Delay); - outapp->WriteUInt8(item->ElemDmgType); - outapp->WriteUInt8(item->ElemDmgAmt); - outapp->WriteUInt8(item->Range); - outapp->WriteUInt32(item->Damage); - outapp->WriteUInt32(item->Color); - outapp->WriteUInt32(0); // Prestige - outapp->WriteUInt8(item->ItemType); - outapp->WriteUInt32(item->Material); - outapp->WriteUInt32(0); //unknown - outapp->WriteUInt32(item->EliteMaterial); - outapp->WriteUInt32(0); // unknown - outapp->WriteUInt32(0); // unknown - outapp->WriteUInt32(0); //This is unknown057 from lucy - for (spacer = 0; spacer < 77; spacer++) { //More Item stats, but some seem to be off based on packet check - outapp->WriteUInt8(0); - } - outapp->WriteUInt32(0xFFFFFFFF); //Unknown but always seen as FF FF FF FF - outapp->WriteUInt32(0); //Unknown - for (spacer = 0; spacer < 5; spacer++) { //Augment stuff - outapp->WriteUInt32(item->AugSlotType[spacer]); - outapp->WriteUInt8(item->AugSlotVisible[spacer]); - outapp->WriteUInt8(item->AugSlotUnk2[spacer]); - } - outapp->WriteUInt32(0); //New RoF 6th Aug Slot - outapp->WriteUInt8(1); //^ - outapp->WriteUInt8(0); //^^ - outapp->WriteUInt32(item->LDoNSold); - outapp->WriteUInt32(item->LDoNTheme); - outapp->WriteUInt32(item->LDoNPrice); - outapp->WriteUInt32(item->LDoNSellBackRate); - for (spacer = 0; spacer < 11; spacer++) { //unknowns - outapp->WriteUInt8(0); - } - outapp->WriteUInt32(0xFFFFFFFF); //Unknown but always seen as FF FF FF FF - outapp->WriteUInt16(0); //Unknown - outapp->WriteUInt32(item->Favor); // Tribute - for (spacer = 0; spacer < 17; spacer++) { //unknowns - outapp->WriteUInt8(0); - } - outapp->WriteUInt32(item->GuildFavor); // Tribute - outapp->WriteUInt32(0); //Unknown - outapp->WriteUInt32(0xFFFFFFFF); //Unknown but always seen as FF FF FF FF - for (spacer = 0; spacer < 11; spacer++) { //unknowns - outapp->WriteUInt8(0); - } - outapp->WriteUInt8(1); - for (spacer = 0; spacer < 25; spacer++) { //unknowns - outapp->WriteUInt8(0); - } - for (spacer = 0; spacer < 304; spacer++) { //Cast stuff and whole bunch of unknowns - outapp->WriteUInt8(0); - } - outapp->WriteUInt8(142); // Always seen not in the item structure though 8E - outapp->WriteUInt32(0); //unknown - outapp->WriteUInt32(1); // Always seen as 1 - outapp->WriteUInt32(0); //unknown - outapp->WriteUInt32(0xCDCCCC3D); // Unknown - outapp->WriteUInt32(0); - outapp->WriteUInt16(8256); //0x4020/8256 - outapp->WriteUInt16(0); - outapp->WriteUInt32(0xFFFFFFFF); //Unknown but always seen as FF FF FF FF - outapp->WriteUInt16(0); - outapp->WriteUInt32(0xFFFFFFFF); //Unknown but always seen as FF FF FF FF - outapp->WriteUInt32(0); //unknown - outapp->WriteUInt32(0); //unknown - outapp->WriteUInt16(0); //unknown - outapp->WriteUInt32(32831); //0x3F80 - for (spacer = 0; spacer < 24; spacer++) { //whole bunch of unknowns always 0's - outapp->WriteUInt8(0); - } - outapp->WriteUInt8(1); - for (spacer = 0; spacer < 6; spacer++) { //whole bunch of unknowns always 0's - outapp->WriteUInt8(0); - } - - QueuePacket(outapp); - safe_delete(outapp); - } - else - return; -} - -void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ItemVerifyRequest_Struct)) - { - LogFile->write(EQEMuLog::Error, "OP size error: OP_ItemVerifyRequest expected:%i got:%i", sizeof(ItemVerifyRequest_Struct), app->size); - return; - } - - ItemVerifyRequest_Struct* request = (ItemVerifyRequest_Struct*)app->pBuffer; - int32 slot_id; - int32 target_id; - int32 spell_id = 0; - slot_id = request->slot; - target_id = request->target; - - - EQApplicationPacket *outapp; - outapp = new EQApplicationPacket(OP_ItemVerifyReply, sizeof(ItemVerifyReply_Struct)); - ItemVerifyReply_Struct* reply = (ItemVerifyReply_Struct*)outapp->pBuffer; - reply->slot = slot_id; - reply->target = target_id; - - QueuePacket(outapp); - safe_delete(outapp); - - - if (IsAIControlled()) { - this->Message_StringID(13, NOT_IN_CONTROL); - return; - } - - if (slot_id < 0) { - LogFile->write(EQEMuLog::Debug, "Unknown slot being used by %s, slot being used is: %i", GetName(), request->slot); - return; - } - - const ItemInst* inst = m_inv[slot_id]; - if (!inst) { - Message(0, "Error: item not found in inventory slot #%i", slot_id); - DeleteItemInInventory(slot_id, 0, true); - return; - } - - const Item_Struct* item = inst->GetItem(); - if (!item) { - Message(0, "Error: item not found in inventory slot #%i", slot_id); - DeleteItemInInventory(slot_id, 0, true); - return; - } - - spell_id = item->Click.Effect; - - if - ( - spell_id > 0 && - ( - !IsValidSpell(spell_id) || - casting_spell_id || - delaytimer || - spellend_timer.Enabled() || - IsStunned() || - IsFeared() || - IsMezzed() || - DivineAura() || - (spells[spell_id].targettype == ST_Ring) || - (IsSilenced() && !IsDiscipline(spell_id)) || - (IsAmnesiad() && IsDiscipline(spell_id)) || - (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) - ) - ) - { - SendSpellBarEnable(spell_id); - return; - } - - LogFile->write(EQEMuLog::Debug, "OP ItemVerifyRequest: spell=%i, target=%i, inv=%i", spell_id, target_id, slot_id); - - if (m_inv.SupportsClickCasting(slot_id) || ((item->ItemType == ItemTypePotion || item->PotionBelt) && m_inv.SupportsPotionBeltCasting(slot_id))) // sanity check - { - ItemInst* p_inst = (ItemInst*)inst; - - parse->EventItem(EVENT_ITEM_CLICK, this, p_inst, nullptr, "", slot_id); - inst = m_inv[slot_id]; - if (!inst) - { - return; - } - - int r; - bool tryaug = false; - ItemInst* clickaug = 0; - Item_Struct* augitem = 0; - - for (r = 0; r < EmuConstants::ITEM_COMMON_SIZE; r++) { - const ItemInst* aug_i = inst->GetAugment(r); - if (!aug_i) - continue; - const Item_Struct* aug = aug_i->GetItem(); - if (!aug) - continue; - - if ((aug->Click.Type == ET_ClickEffect) || (aug->Click.Type == ET_Expendable) || (aug->Click.Type == ET_EquipClick) || (aug->Click.Type == ET_ClickEffect2)) - { - tryaug = true; - clickaug = (ItemInst*)aug_i; - augitem = (Item_Struct*)aug; - spell_id = aug->Click.Effect; - break; - } - } - - if ((spell_id <= 0) && (item->ItemType != ItemTypeFood && item->ItemType != ItemTypeDrink && item->ItemType != ItemTypeAlcohol && item->ItemType != ItemTypeSpell)) - { - LogFile->write(EQEMuLog::Debug, "Item with no effect right clicked by %s", GetName()); - } - else if (inst->IsType(ItemClassCommon)) - { - if (item->ItemType == ItemTypeSpell && (strstr((const char*)item->Name, "Tome of ") || strstr((const char*)item->Name, "Skill: "))) - { - DeleteItemInInventory(slot_id, 1, true); - TrainDiscipline(item->ID); - } - else if (item->ItemType == ItemTypeSpell) - { - return; - } - else if ((item->Click.Type == ET_ClickEffect) || (item->Click.Type == ET_Expendable) || (item->Click.Type == ET_EquipClick) || (item->Click.Type == ET_ClickEffect2)) - { - if (inst->GetCharges() == 0) - { - //Message(0, "This item is out of charges."); - Message_StringID(13, ITEM_OUT_OF_CHARGES); - return; - } - if (GetLevel() >= item->Click.Level2) - { - int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", slot_id); - inst = m_inv[slot_id]; - if (!inst) - { - return; - } - - if (i == 0) { - CastSpell(item->Click.Effect, target_id, USE_ITEM_SPELL_SLOT, item->CastTime, 0, 0, slot_id); - } - } - else - { - Message_StringID(13, ITEMS_INSUFFICIENT_LEVEL); - return; - } - } - else if (tryaug) - { - if (clickaug->GetCharges() == 0) - { - //Message(0, "This item is out of charges."); - Message_StringID(13, ITEM_OUT_OF_CHARGES); - return; - } - if (GetLevel() >= augitem->Click.Level2) - { - int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, clickaug, nullptr, "", slot_id); - inst = m_inv[slot_id]; - if (!inst) - { - return; - } - - if (i == 0) { - CastSpell(augitem->Click.Effect, target_id, USE_ITEM_SPELL_SLOT, augitem->CastTime, 0, 0, slot_id); - } - } - else - { - Message_StringID(13, ITEMS_INSUFFICIENT_LEVEL); - return; - } - } - else - { - if (GetClientVersion() >= EQClientSoD && !inst->IsEquipable(GetBaseRace(), GetClass())) - { - if (item->ItemType != ItemTypeFood && item->ItemType != ItemTypeDrink && item->ItemType != ItemTypeAlcohol) - { - LogFile->write(EQEMuLog::Debug, "Error: unknown item->Click.Type (%i)", item->Click.Type); - } - else - { - //This is food/drink - consume it - if (item->ItemType == ItemTypeFood && m_pp.hunger_level < 5000) - { - Consume(item, item->ItemType, slot_id, false); - } - else if (item->ItemType == ItemTypeDrink && m_pp.thirst_level < 5000) - { - Consume(item, item->ItemType, slot_id, false); - } - else if (item->ItemType == ItemTypeAlcohol) - { -#if EQDEBUG >= 1 - LogFile->write(EQEMuLog::Debug, "Drinking Alcohol from slot:%i", slot_id); -#endif - // This Seems to be handled in OP_DeleteItem handling - //DeleteItemInInventory(slot_id, 1, false); - //entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), item->Name); - //Should add intoxication level to the PP at some point - //CheckIncreaseSkill(ALCOHOL_TOLERANCE, nullptr, 25); - } - - if (m_pp.hunger_level > 6000) - m_pp.hunger_level = 6000; - if (m_pp.thirst_level > 6000) - m_pp.thirst_level = 6000; - - EQApplicationPacket *outapp2; - outapp2 = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); - Stamina_Struct* sta = (Stamina_Struct*)outapp2->pBuffer; - sta->food = m_pp.hunger_level; - sta->water = m_pp.thirst_level; - - QueuePacket(outapp2); - safe_delete(outapp2); - } - - } - else - { - LogFile->write(EQEMuLog::Debug, "Error: unknown item->Click.Type (%i)", item->Click.Type); - } - } - } - else - { - Message(0, "Error: item not found in inventory slot #%i", slot_id); - } - } - else - { - Message(0, "Error: Invalid inventory slot for using effects (inventory slot #%i)", slot_id); - } - - return; -} - -void Client::Handle_OP_Jump(const EQApplicationPacket *app) -{ - SetEndurance(GetEndurance() - (GetLevel()<20 ? (225 * GetLevel() / 100) : 50)); - return; -} - -void Client::Handle_OP_KeyRing(const EQApplicationPacket *app) -{ - KeyRingList(); -} - -void Client::Handle_OP_LDoNButton(const EQApplicationPacket *app) -{ - if (app->size < sizeof(bool)) - { - return; - } - - if (GetPendingAdventureCreate()) - { - return; - } - - if (IsOnAdventure()) - { - return; - } - - bool* p = (bool*)app->pBuffer; - if (*p == true) - { - ServerPacket *pack = new ServerPacket(ServerOP_AdventureRequestCreate, sizeof(ServerAdventureRequestCreate_Struct)+(64 * adv_requested_member_count)); - ServerAdventureRequestCreate_Struct *sac = (ServerAdventureRequestCreate_Struct*)pack->pBuffer; - strcpy(sac->leader, GetName()); - sac->id = adv_requested_id; - sac->theme = adv_requested_theme; - sac->member_count = adv_requested_member_count; - memcpy((pack->pBuffer + sizeof(ServerAdventureRequestCreate_Struct)), adv_requested_data, (64 * adv_requested_member_count)); - pack->Deflate(); - worldserver.SendPacket(pack); - delete pack; - PendingAdventureCreate(); - ClearPendingAdventureData(); - } - else - { - ClearPendingAdventureData(); - } -} - -void Client::Handle_OP_LDoNDisarmTraps(const EQApplicationPacket *app) -{ - Mob * target = GetTarget(); - if (target->IsNPC()) - { - if (HasSkill(SkillDisarmTraps)) - { - if (DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) - { - Message(13, "%s is too far away.", target->GetCleanName()); - return; - } - HandleLDoNDisarm(target->CastToNPC(), GetSkill(SkillDisarmTraps), LDoNTypeMechanical); - } - else - Message(13, "You do not have the disarm trap skill."); - } -} - -void Client::Handle_OP_LDoNInspect(const EQApplicationPacket *app) -{ - Mob * target = GetTarget(); - if (target && target->GetClass() == LDON_TREASURE) - Message(15, "%s", target->GetCleanName()); -} - -void Client::Handle_OP_LDoNOpen(const EQApplicationPacket *app) -{ - Mob * target = GetTarget(); - if (target && target->IsNPC()) - HandleLDoNOpen(target->CastToNPC()); -} - -void Client::Handle_OP_LDoNPickLock(const EQApplicationPacket *app) -{ - Mob * target = GetTarget(); - if (target->IsNPC()) - { - if (HasSkill(SkillPickLock)) - { - if (DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) - { - Message(13, "%s is too far away.", target->GetCleanName()); - return; - } - HandleLDoNPickLock(target->CastToNPC(), GetSkill(SkillPickLock), LDoNTypeMechanical); - } - else - Message(13, "You do not have the pick locks skill."); - } -} - -void Client::Handle_OP_LDoNSenseTraps(const EQApplicationPacket *app) -{ - Mob * target = GetTarget(); - if (target->IsNPC()) - { - if (HasSkill(SkillSenseTraps)) - { - if (DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) - { - Message(13, "%s is too far away.", target->GetCleanName()); - return; - } - HandleLDoNSenseTraps(target->CastToNPC(), GetSkill(SkillSenseTraps), LDoNTypeMechanical); - } - else - Message(13, "You do not have the sense traps skill."); - } -} - -void Client::Handle_OP_LeadershipExpToggle(const EQApplicationPacket *app) -{ - if (app->size != 1) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_LeadershipExpToggle expected %i got %i", 1, app->size); - DumpPacket(app); - return; - } - uint8 *mode = (uint8 *)app->pBuffer; - if (*mode) { - m_pp.leadAAActive = 1; - Save(); - Message_StringID(clientMessageYellow, LEADERSHIP_EXP_ON); - } - else { - m_pp.leadAAActive = 0; - Save(); - Message_StringID(clientMessageYellow, LEADERSHIP_EXP_OFF); - } -} - -void Client::Handle_OP_LeaveAdventure(const EQApplicationPacket *app) -{ - if (!IsOnAdventure()) - { - return; - } - LeaveAdventure(); -} - -void Client::Handle_OP_LeaveBoat(const EQApplicationPacket *app) -{ - Mob* boat = entity_list.GetMob(this->BoatID); // find the mob corresponding to the boat id - if (boat) { - if ((boat->GetTarget() == this) && boat->GetHateAmount(this) == 0) // if the client somehow left while still controlling the boat (and the boat isn't attacking them) - boat->SetTarget(0); // fix it to stop later problems - } - this->BoatID = 0; - return; -} - -void Client::Handle_OP_LFGCommand(const EQApplicationPacket *app) -{ - if (app->size != sizeof(LFG_Struct)) { - std::cout << "Wrong size on OP_LFGCommand. Got: " << app->size << ", Expected: " << sizeof(LFG_Struct) << std::endl; - DumpPacket(app); - return; - } - - // Process incoming packet - LFG_Struct* lfg = (LFG_Struct*)app->pBuffer; - - switch (lfg->value & 0xFF) { - case 0: - if (LFG) { - database.SetLFG(CharacterID(), false); - LFG = false; - LFGComments[0] = '\0'; - } - break; - case 1: - if (!LFG) { - LFG = true; - database.SetLFG(CharacterID(), true); - } - LFGFromLevel = lfg->FromLevel; - LFGToLevel = lfg->ToLevel; - LFGMatchFilter = lfg->MatchFilter; - strcpy(LFGComments, lfg->Comments); - break; - default: - Message(0, "Error: unknown LFG value %i", lfg->value); - } - - UpdateWho(); - - // Issue outgoing packet to notify other clients - EQApplicationPacket* outapp = new EQApplicationPacket(OP_LFGAppearance, sizeof(LFG_Appearance_Struct)); - LFG_Appearance_Struct* lfga = (LFG_Appearance_Struct*)outapp->pBuffer; - lfga->spawn_id = this->GetID(); - lfga->lfg = (uint8)LFG; - - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - return; -} - -void Client::Handle_OP_LFGGetMatchesRequest(const EQApplicationPacket *app) -{ - - if (app->size != sizeof(LFGGetMatchesRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_LFGGetMatchesRequest, size=%i, expected %i", app->size, sizeof(LFGGetMatchesRequest_Struct)); - DumpPacket(app); - return; - } - LFGGetMatchesRequest_Struct* gmrs = (LFGGetMatchesRequest_Struct*)app->pBuffer; - - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - ServerPacket* pack = new ServerPacket(ServerOP_LFGMatches, sizeof(ServerLFGMatchesRequest_Struct)); - ServerLFGMatchesRequest_Struct* smrs = (ServerLFGMatchesRequest_Struct*)pack->pBuffer; - smrs->FromID = GetID(); - smrs->QuerierLevel = GetLevel(); - strcpy(smrs->FromName, GetName()); - smrs->FromLevel = gmrs->FromLevel; - smrs->ToLevel = gmrs->ToLevel; - smrs->Classes = gmrs->Classes; - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - -void Client::Handle_OP_LFGuild(const EQApplicationPacket *app) -{ - if (app->size < 4) - return; - - uint32 Command = *((uint32 *)app->pBuffer); - - switch (Command) - { - case 0: - { - VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_PlayerToggle_Struct); - LFGuild_PlayerToggle_Struct *pts = (LFGuild_PlayerToggle_Struct *)app->pBuffer; - -#ifdef DARWIN -#if __DARWIN_C_LEVEL < 200809L - if (strlen(pts->Comment) > 256) -#else - if (strnlen(pts->Comment, 256) > 256) -#endif // __DARWIN_C_LEVEL -#else - if (strnlen(pts->Comment, 256) > 256) -#endif // DARWIN - return; - - ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + strlen(pts->Comment) + 38); - - pack->WriteUInt32(zone->GetZoneID()); - pack->WriteUInt32(zone->GetInstanceID()); - pack->WriteString(GetName()); - pack->WriteUInt32(QSG_LFGuild); - pack->WriteUInt32(QSG_LFGuild_UpdatePlayerInfo); - pack->WriteUInt32(GetBaseClass()); - pack->WriteUInt32(GetLevel()); - pack->WriteUInt32(GetAAPointsSpent()); - pack->WriteString(pts->Comment); - pack->WriteUInt32(pts->Toggle); - pack->WriteUInt32(pts->TimeZone); - - worldserver.SendPacket(pack); - safe_delete(pack); - - break; - } - case 1: - { - VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_GuildToggle_Struct); - LFGuild_GuildToggle_Struct *gts = (LFGuild_GuildToggle_Struct *)app->pBuffer; - -#ifdef DARWIN -#if __DARWIN_C_LEVEL < 200809L - if (strlen(gts->Comment) > 256) -#else - if (strnlen(gts->Comment, 256) > 256) -#endif // __DARWIN_C_LEVEL -#else - if (strnlen(gts->Comment, 256) > 256) -#endif // __DARWIN - return; - - ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + strlen(gts->Comment) + strlen(guild_mgr.GetGuildName(GuildID())) + 43); - - pack->WriteUInt32(zone->GetZoneID()); - pack->WriteUInt32(zone->GetInstanceID()); - pack->WriteString(GetName()); - pack->WriteUInt32(QSG_LFGuild); - pack->WriteUInt32(QSG_LFGuild_UpdateGuildInfo); - pack->WriteString(guild_mgr.GetGuildName(GuildID())); - pack->WriteString(gts->Comment); - pack->WriteUInt32(gts->FromLevel); - pack->WriteUInt32(gts->ToLevel); - pack->WriteUInt32(gts->Classes); - pack->WriteUInt32(gts->AACount); - pack->WriteUInt32(gts->Toggle); - pack->WriteUInt32(gts->TimeZone); - - worldserver.SendPacket(pack); - safe_delete(pack); - - break; - } - case 3: - { - VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_SearchPlayer_Struct); - - ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + 37); - - pack->WriteUInt32(zone->GetZoneID()); - pack->WriteUInt32(zone->GetInstanceID()); - pack->WriteString(GetName()); - pack->WriteUInt32(QSG_LFGuild); - pack->WriteUInt32(QSG_LFGuild_PlayerMatches); - - LFGuild_SearchPlayer_Struct *sps = (LFGuild_SearchPlayer_Struct *)app->pBuffer; - pack->WriteUInt32(sps->FromLevel); - pack->WriteUInt32(sps->ToLevel); - pack->WriteUInt32(sps->MinAA); - pack->WriteUInt32(sps->TimeZone); - pack->WriteUInt32(sps->Classes); - - worldserver.SendPacket(pack); - safe_delete(pack); - - break; - } - case 4: - { - VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_SearchGuild_Struct); - - ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + 33); - - pack->WriteUInt32(zone->GetZoneID()); - pack->WriteUInt32(zone->GetInstanceID()); - pack->WriteString(GetName()); - pack->WriteUInt32(QSG_LFGuild); - pack->WriteUInt32(QSG_LFGuild_GuildMatches); - - LFGuild_SearchGuild_Struct *sgs = (LFGuild_SearchGuild_Struct *)app->pBuffer; - - pack->WriteUInt32(sgs->Level); - pack->WriteUInt32(sgs->AAPoints); - pack->WriteUInt32(sgs->TimeZone); - pack->WriteUInt32(sgs->Class); - - worldserver.SendPacket(pack); - safe_delete(pack); - - break; - } - default: - break; - } -} - -void Client::Handle_OP_LFPCommand(const EQApplicationPacket *app) -{ - - if (app->size != sizeof(LFP_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_LFPCommand, size=%i, expected %i", app->size, sizeof(LFP_Struct)); - DumpPacket(app); - return; - } - LFP_Struct *lfp = (LFP_Struct*)app->pBuffer; - - LFP = lfp->Action != LFPOff; - database.SetLFP(CharacterID(), LFP); - - if (!LFP) { - worldserver.StopLFP(CharacterID()); - return; - } - - GroupLFPMemberEntry LFPMembers[MAX_GROUP_MEMBERS]; - - for (unsigned int i = 0; iGetZoneID(); - LFPMembers[0].GuildID = GuildID(); - - if (g) { - // This should not happen. The client checks if you are in a group and will not let you put LFP on if - // you are not the leader. - if (!g->IsLeader(this)) { - LogFile->write(EQEMuLog::Error, "Client sent LFP on for character %s who is grouped but not leader.", GetName()); - return; - } - // Fill the LFPMembers array with the rest of the group members, excluding ourself - // We don't fill in the class, level or zone, because we may not be able to determine - // them if the other group members are not in this zone. World will fill in this information - // for us, if it can. - int NextFreeSlot = 1; - for (unsigned int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (strcasecmp(g->membername[i], LFPMembers[0].Name)) - strcpy(LFPMembers[NextFreeSlot++].Name, g->membername[i]); - } - } - - - worldserver.UpdateLFP(CharacterID(), lfp->Action, lfp->MatchFilter, lfp->FromLevel, lfp->ToLevel, lfp->Classes, - lfp->Comments, LFPMembers); - - -} - -void Client::Handle_OP_LFPGetMatchesRequest(const EQApplicationPacket *app) -{ - - if (app->size != sizeof(LFPGetMatchesRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_LFPGetMatchesRequest, size=%i, expected %i", app->size, sizeof(LFPGetMatchesRequest_Struct)); - DumpPacket(app); - return; - } - LFPGetMatchesRequest_Struct* gmrs = (LFPGetMatchesRequest_Struct*)app->pBuffer; - - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - ServerPacket* pack = new ServerPacket(ServerOP_LFPMatches, sizeof(ServerLFPMatchesRequest_Struct)); - ServerLFPMatchesRequest_Struct* smrs = (ServerLFPMatchesRequest_Struct*)pack->pBuffer; - smrs->FromID = GetID(); - smrs->FromLevel = gmrs->FromLevel; - smrs->ToLevel = gmrs->ToLevel; - smrs->QuerierLevel = GetLevel(); - smrs->QuerierClass = GetClass(); - strcpy(smrs->FromName, GetName()); - worldserver.SendPacket(pack); - safe_delete(pack); - } - - return; -} - -void Client::Handle_OP_LoadSpellSet(const EQApplicationPacket *app) -{ - if (app->size != sizeof(LoadSpellSet_Struct)) { - printf("Wrong size of LoadSpellSet_Struct! Expected: %zu, Got: %i\n", sizeof(LoadSpellSet_Struct), app->size); - return; - } - int i; - LoadSpellSet_Struct* ss = (LoadSpellSet_Struct*)app->pBuffer; - for (i = 0; ispell[i] != 0xFFFFFFFF) - UnmemSpell(i, true); - } -} - -void Client::Handle_OP_Logout(const EQApplicationPacket *app) -{ - //LogFile->write(EQEMuLog::Debug, "%s sent a logout packet.", GetName()); - //we will save when we get destroyed soon anyhow - //Save(); - - SendLogoutPackets(); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_LogoutReply); - FastQueuePacket(&outapp); - - Disconnect(); - return; -} - -void Client::Handle_OP_LootItem(const EQApplicationPacket *app) -{ - if (app->size != sizeof(LootingItem_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_LootItem, size=%i, expected %i", app->size, sizeof(LootingItem_Struct)); - return; - } - /* - ** fixed the looting code so that it sends the correct opcodes - ** and now correctly removes the looted item the player selected - ** as well as gives the player the proper item. - ** Also fixed a few UI lock ups that would occur. - */ - - EQApplicationPacket* outapp = 0; - Entity* entity = entity_list.GetID(*((uint16*)app->pBuffer)); - if (entity == 0) { - Message(13, "Error: OP_LootItem: Corpse not found (ent = 0)"); - outapp = new EQApplicationPacket(OP_LootComplete, 0); - QueuePacket(outapp); - safe_delete(outapp); - return; - } - - if (entity->IsCorpse()) { - entity->CastToCorpse()->LootItem(this, app); - return; - } - else { - Message(13, "Error: Corpse not found! (!ent->IsCorpse())"); - Corpse::SendEndLootErrorPacket(this); - } - - return; -} - -void Client::Handle_OP_LootRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(uint32)) { - std::cout << "Wrong size: OP_LootRequest, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; - return; - } - - Entity* ent = entity_list.GetID(*((uint32*)app->pBuffer)); - if (ent == 0) { - Message(13, "Error: OP_LootRequest: Corpse not found (ent = 0)"); - Corpse::SendLootReqErrorPacket(this); - return; - } - if (ent->IsCorpse()) - { - SetLooting(ent->GetID()); //store the entity we are looting - Corpse *ent_corpse = ent->CastToCorpse(); - if (DistNoRootNoZ(ent_corpse->GetX(), ent_corpse->GetY()) > 625) - { - Message(13, "Corpse too far away."); - Corpse::SendLootReqErrorPacket(this); - return; - } - - if (invisible) { - BuffFadeByEffect(SE_Invisibility); - BuffFadeByEffect(SE_Invisibility2); - invisible = false; - } - if (invisible_undead) { - BuffFadeByEffect(SE_InvisVsUndead); - BuffFadeByEffect(SE_InvisVsUndead2); - invisible_undead = false; - } - if (invisible_animals){ - BuffFadeByEffect(SE_InvisVsAnimals); - invisible_animals = false; - } - if (hidden || improved_hidden){ - hidden = false; - improved_hidden = false; - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x03; - sa_out->parameter = 0; - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - } - ent->CastToCorpse()->MakeLootRequestPackets(this, app); - return; - } - else { - std::cout << "npc == 0 LOOTING FOOKED3" << std::endl; - Message(13, "Error: OP_LootRequest: Corpse not a corpse?"); - Corpse::SendLootReqErrorPacket(this); - } - return; -} - -void Client::Handle_OP_ManaChange(const EQApplicationPacket *app) -{ - if (app->size == 0) { - // i think thats the sign to stop the songs - if (IsBardSong(casting_spell_id) || bardsong != 0) - InterruptSpell(SONG_ENDS, 0x121); - else - InterruptSpell(INTERRUPT_SPELL, 0x121); - - return; - } - else // I don't think the client sends proper manachanges - { // with a length, just the 0 len ones for stopping songs - //ManaChange_Struct* p = (ManaChange_Struct*)app->pBuffer; - printf("OP_ManaChange from client:\n"); - DumpPacket(app); - } - return; -} - -/* -#if 0 // I dont think there's an op for this now, and we check this -// when the client is sitting -void Client::Handle_OP_Medding(const EQApplicationPacket *app) -{ - if (app->pBuffer[0]) - medding = true; - else - medding = false; - return; -} -#endif -*/ - -void Client::Handle_OP_MemorizeSpell(const EQApplicationPacket *app) -{ - OPMemorizeSpell(app); - return; -} - -void Client::Handle_OP_Mend(const EQApplicationPacket *app) -{ - if (!HasSkill(SkillMend)) - return; - - if (!p_timers.Expired(&database, pTimerMend, false)) { - Message(13, "Ability recovery time not yet met."); - return; - } - p_timers.Start(pTimerMend, MendReuseTime - 1); - - int mendhp = GetMaxHP() / 4; - int currenthp = GetHP(); - if (zone->random.Int(0, 199) < (int)GetSkill(SkillMend)) { - - int criticalchance = spellbonuses.CriticalMend + itembonuses.CriticalMend + aabonuses.CriticalMend; - - if (zone->random.Int(0, 99) < criticalchance){ - mendhp *= 2; - Message_StringID(4, MEND_CRITICAL); - } - SetHP(GetHP() + mendhp); - SendHPUpdate(); - Message_StringID(4, MEND_SUCCESS); - } - else { - /* the purpose of the following is to make the chance to worsen wounds much less common, - which is more consistent with the way eq live works. - according to my math, this should result in the following probability: - 0 skill - 25% chance to worsen - 20 skill - 23% chance to worsen - 50 skill - 16% chance to worsen */ - if ((GetSkill(SkillMend) <= 75) && (zone->random.Int(GetSkill(SkillMend), 100) < 75) && (zone->random.Int(1, 3) == 1)) - { - SetHP(currenthp > mendhp ? (GetHP() - mendhp) : 1); - SendHPUpdate(); - Message_StringID(4, MEND_WORSEN); - } - else - Message_StringID(4, MEND_FAIL); - } - - CheckIncreaseSkill(SkillMend, nullptr, 10); - return; -} - -void Client::Handle_OP_MercenaryCommand(const EQApplicationPacket *app) -{ - if (app->size != sizeof(MercenaryCommand_Struct)) - { - Message(13, "Size mismatch in OP_MercenaryCommand expected %i got %i", sizeof(MercenaryCommand_Struct), app->size); - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryCommand expected %i got %i", sizeof(MercenaryCommand_Struct), app->size); - DumpPacket(app); - return; - } - - MercenaryCommand_Struct* mc = (MercenaryCommand_Struct*)app->pBuffer; - uint32 merc_command = mc->MercCommand; // Seen 0 (zone in with no merc or suspended), 1 (dismiss merc), 5 (normal state), 20 (unknown), 36 (zone in with merc) - int32 option = mc->Option; // Seen -1 (zone in with no merc), 0 (setting to passive stance), 1 (normal or setting to balanced stance) - - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Command %i, Option %i received.", merc_command, option); - - if (!RuleB(Mercs, AllowMercs)) - return; - - // Handle the Command here... - // Will need a list of what every type of command is supposed to do - // Unsure if there is a server response to this packet - if (option >= 0) - { - Merc* merc = GetMerc(); - GetMercInfo().State = option; - - if (merc) - { - uint8 numStances = 0; - - //get number of available stances for the current merc - std::list mercStanceList = zone->merc_stance_list[merc->GetMercTemplateID()]; - std::list::iterator iter = mercStanceList.begin(); - while (iter != mercStanceList.end()) { - numStances++; - ++iter; - } - - MercTemplate* mercTemplate = zone->GetMercTemplate(GetMerc()->GetMercTemplateID()); - if (mercTemplate) - { - //check to see if selected option is a valid stance slot (option is the slot the stance is in, not the actual stance) - if (option >= 0 && option < numStances) - { - merc->SetStance(mercTemplate->Stances[option]); - GetMercInfo().Stance = mercTemplate->Stances[option]; - - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Set Stance: %u", merc->GetStance()); - } - } - } - } -} - -void Client::Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app) -{ - // The payload is 4 bytes. The EntityID of the Mercenary Liason which are of class 71. - if (app->size != sizeof(MercenaryMerchantShopRequest_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryDataRequest expected 4 got %i", app->size); - - DumpPacket(app); - - return; - } - - MercenaryMerchantShopRequest_Struct* mmsr = (MercenaryMerchantShopRequest_Struct*)app->pBuffer; - uint32 merchant_id = mmsr->MercMerchantID; - uint32 altCurrentType = 19; - - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Data Request for Merchant ID (%i)", merchant_id); - - //client is requesting data about currently owned mercenary - if (merchant_id == 0) { - - //send info about your current merc(s) - if (GetMercInfo().mercid) - { - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: SendMercPersonalInfo Request"); - SendMercPersonalInfo(); - } - else - { - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: SendMercPersonalInfo Not Sent - MercID (%i)", GetMercInfo().mercid); - } - } - - if (!RuleB(Mercs, AllowMercs)) { - return; - } - - NPC* tar = entity_list.GetNPCByID(merchant_id); - - if (tar) { - int mercTypeCount = 0; - int mercCount = 0; - - if (DistNoRoot(*tar) > USE_NPC_RANGE2) - return; - - if (tar->GetClass() != MERCERNARY_MASTER) { - return; - } - - mercTypeCount = tar->GetNumMercTypes(GetClientVersion()); - mercCount = tar->GetNumMercs(GetClientVersion()); - - if (mercCount > MAX_MERC) - return; - - std::list mercTypeList = tar->GetMercTypesList(GetClientVersion()); - std::list mercDataList = tar->GetMercsList(GetClientVersion()); - - int i = 0; - int StanceCount = 0; - - for (std::list::iterator mercListItr = mercDataList.begin(); mercListItr != mercDataList.end(); ++mercListItr) - { - std::list::iterator siter = zone->merc_stance_list[mercListItr->MercTemplateID].begin(); - for (siter = zone->merc_stance_list[mercListItr->MercTemplateID].begin(); siter != zone->merc_stance_list[mercListItr->MercTemplateID].end(); ++siter) - { - StanceCount++; - } - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataResponse, sizeof(MercenaryMerchantList_Struct)); - MercenaryMerchantList_Struct* mml = (MercenaryMerchantList_Struct*)outapp->pBuffer; - - mml->MercTypeCount = mercTypeCount; - if (mercTypeCount > 0) - { - for (std::list::iterator mercTypeListItr = mercTypeList.begin(); mercTypeListItr != mercTypeList.end(); ++mercTypeListItr) { - mml->MercGrades[i] = mercTypeListItr->Type; // DBStringID for Type - i++; - } - } - mml->MercCount = mercCount; - - if (mercCount > 0) - { - i = 0; - for (std::list::iterator mercListIter = mercDataList.begin(); mercListIter != mercDataList.end(); ++mercListIter) - { - mml->Mercs[i].MercID = mercListIter->MercTemplateID; - mml->Mercs[i].MercType = mercListIter->MercType; - mml->Mercs[i].MercSubType = mercListIter->MercSubType; - mml->Mercs[i].PurchaseCost = RuleB(Mercs, ChargeMercPurchaseCost) ? Merc::CalcPurchaseCost(mercListIter->MercTemplateID, GetLevel(), 0) : 0; - mml->Mercs[i].UpkeepCost = RuleB(Mercs, ChargeMercUpkeepCost) ? Merc::CalcUpkeepCost(mercListIter->MercTemplateID, GetLevel(), 0) : 0; - mml->Mercs[i].Status = 0; - mml->Mercs[i].AltCurrencyCost = RuleB(Mercs, ChargeMercPurchaseCost) ? Merc::CalcPurchaseCost(mercListIter->MercTemplateID, GetLevel(), altCurrentType) : 0; - mml->Mercs[i].AltCurrencyUpkeep = RuleB(Mercs, ChargeMercUpkeepCost) ? Merc::CalcUpkeepCost(mercListIter->MercTemplateID, GetLevel(), altCurrentType) : 0; - mml->Mercs[i].AltCurrencyType = altCurrentType; - mml->Mercs[i].MercUnk01 = 0; - mml->Mercs[i].TimeLeft = -1; - mml->Mercs[i].MerchantSlot = i + 1; - mml->Mercs[i].MercUnk02 = 1; - int mercStanceCount = 0; - std::list::iterator iter = zone->merc_stance_list[mercListIter->MercTemplateID].begin(); - for (iter = zone->merc_stance_list[mercListIter->MercTemplateID].begin(); iter != zone->merc_stance_list[mercListIter->MercTemplateID].end(); ++iter) - { - mercStanceCount++; - } - mml->Mercs[i].StanceCount = mercStanceCount; - mml->Mercs[i].MercUnk03 = 519044964; - mml->Mercs[i].MercUnk04 = 1; - //mml->Mercs[i].MercName; - int stanceindex = 0; - if (mercStanceCount > 0) - { - std::list::iterator iter2 = zone->merc_stance_list[mercListIter->MercTemplateID].begin(); - while (iter2 != zone->merc_stance_list[mercListIter->MercTemplateID].end()) - { - mml->Mercs[i].Stances[stanceindex].StanceIndex = stanceindex; - mml->Mercs[i].Stances[stanceindex].Stance = (iter2->StanceID); - stanceindex++; - ++iter2; - } - } - i++; - } - } - FastQueuePacket(&outapp); - } -} - -void Client::Handle_OP_MercenaryDataUpdateRequest(const EQApplicationPacket *app) -{ - // The payload is 0 bytes. - if (app->size != 0) - { - Message(13, "Size mismatch in OP_MercenaryDataUpdateRequest expected 0 got %i", app->size); - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryDataUpdateRequest expected 0 got %i", app->size); - DumpPacket(app); - return; - } - - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Data Update Request Received."); - - if (GetMercID()) - { - SendMercPersonalInfo(); - } -} - -void Client::Handle_OP_MercenaryDismiss(const EQApplicationPacket *app) -{ - // The payload is 0 or 1 bytes. - if (app->size > 1) - { - Message(13, "Size mismatch in OP_MercenaryDismiss expected 0 got %i", app->size); - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryDismiss expected 0 got %i", app->size); - DumpPacket(app); - return; - } - - uint8 Command = 0; - if (app->size > 0) - { - char *InBuffer = (char *)app->pBuffer; - Command = VARSTRUCT_DECODE_TYPE(uint8, InBuffer); - } - - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Dismiss Request ( %i ) Received.", Command); - - // Handle the dismiss here... - DismissMerc(GetMercInfo().mercid); - -} - -void Client::Handle_OP_MercenaryHire(const EQApplicationPacket *app) -{ - // The payload is 16 bytes. First four bytes are the Merc ID (Template ID) - if (app->size != sizeof(MercenaryMerchantRequest_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryHire expected %i got %i", sizeof(MercenaryMerchantRequest_Struct), app->size); - - DumpPacket(app); - - return; - } - - MercenaryMerchantRequest_Struct* mmrq = (MercenaryMerchantRequest_Struct*)app->pBuffer; - uint32 merc_template_id = mmrq->MercID; - uint32 merchant_id = mmrq->MercMerchantID; - uint32 merc_unk1 = mmrq->MercUnk01; - uint32 merc_unk2 = mmrq->MercUnk02; - - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Template ID (%i), Merchant ID (%i), Unknown1 (%i), Unknown2 (%i)", merc_template_id, merchant_id, merc_unk1, merc_unk2); - - //HirePending = true; - SetHoTT(0); - SendTargetCommand(0); - - if (!RuleB(Mercs, AllowMercs)) - return; - - MercTemplate* merc_template = zone->GetMercTemplate(merc_template_id); - - if (merc_template) - { - - Mob* merchant = entity_list.GetNPCByID(merchant_id); - if (!CheckCanHireMerc(merchant, merc_template_id)) - { - return; - } - - // Set time remaining to max on Hire - GetMercInfo().MercTimerRemaining = RuleI(Mercs, UpkeepIntervalMS); - - // Get merc, assign it to client & spawn - Merc* merc = Merc::LoadMerc(this, merc_template, merchant_id, false); - - if (merc) - { - SpawnMerc(merc, true); - merc->Save(); - - if (RuleB(Mercs, ChargeMercPurchaseCost)) - { - uint32 cost = Merc::CalcPurchaseCost(merc_template->MercTemplateID, GetLevel()) * 100; // Cost is in gold - TakeMoneyFromPP(cost, true); - } - - // approved hire request - SendMercMerchantResponsePacket(0); - } - else - { - //merc failed to spawn - SendMercMerchantResponsePacket(3); - } - } - else - { - //merc doesn't exist in db - SendMercMerchantResponsePacket(2); - } -} - -void Client::Handle_OP_MercenarySuspendRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(SuspendMercenary_Struct)) - { - Message(13, "Size mismatch in OP_MercenarySuspendRequest expected %i got %i", sizeof(SuspendMercenary_Struct), app->size); - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenarySuspendRequest expected %i got %i", sizeof(SuspendMercenary_Struct), app->size); - DumpPacket(app); - return; - } - - SuspendMercenary_Struct* sm = (SuspendMercenary_Struct*)app->pBuffer; - uint32 merc_suspend = sm->SuspendMerc; // Seen 30 for suspending or unsuspending - - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Suspend ( %i ) received.", merc_suspend); - - if (!RuleB(Mercs, AllowMercs)) - return; - - // Check if the merc is suspended and if so, unsuspend, otherwise suspend it - SuspendMercCommand(); -} - -void Client::Handle_OP_MercenaryTimerRequest(const EQApplicationPacket *app) -{ - // The payload is 0 bytes. - if (app->size > 1) - { - Message(13, "Size mismatch in OP_MercenaryTimerRequest expected 0 got %i", app->size); - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryTimerRequest expected 0 got %i", app->size); - DumpPacket(app); - return; - } - - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Timer Request received."); - - if (!RuleB(Mercs, AllowMercs)) { - return; - } - - // To Do: Load Mercenary Timer Data to properly populate this reply packet - // All hard set values for now - uint32 entityID = 0; - uint32 mercState = 5; - uint32 suspendedTime = 0; - if (GetMercID()) { - Merc* merc = GetMerc(); - - if (merc) { - entityID = merc->GetID(); - - if (GetMercInfo().IsSuspended) { - mercState = 1; - suspendedTime = GetMercInfo().SuspendedTime; - } - } - } - - if (entityID > 0) { - SendMercTimerPacket(entityID, mercState, suspendedTime, GetMercInfo().MercTimerRemaining, RuleI(Mercs, SuspendIntervalMS)); - } -} - -void Client::Handle_OP_MoveCoin(const EQApplicationPacket *app) -{ - if (app->size != sizeof(MoveCoin_Struct)){ - LogFile->write(EQEMuLog::Error, "Wrong size on OP_MoveCoin. Got: %i, Expected: %i", app->size, sizeof(MoveCoin_Struct)); - DumpPacket(app); - return; - } - OPMoveCoin(app); - return; -} - -void Client::Handle_OP_MoveItem(const EQApplicationPacket *app) -{ - if (!CharacterID()) - { - return; - } - - if (app->size != sizeof(MoveItem_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_MoveItem, size=%i, expected %i", app->size, sizeof(MoveItem_Struct)); - return; - } - - MoveItem_Struct* mi = (MoveItem_Struct*)app->pBuffer; - if (spellend_timer.Enabled() && casting_spell_id && !IsBardSong(casting_spell_id)) - { - if (mi->from_slot != mi->to_slot && (mi->from_slot <= EmuConstants::GENERAL_END || mi->from_slot > 39) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) - { - char *detect = nullptr; - const ItemInst *itm_from = GetInv().GetItem(mi->from_slot); - const ItemInst *itm_to = GetInv().GetItem(mi->to_slot); - MakeAnyLenString(&detect, "Player issued a move item from %u(item id %u) to %u(item id %u) while casting %u.", - mi->from_slot, - itm_from ? itm_from->GetID() : 0, - mi->to_slot, - itm_to ? itm_to->GetID() : 0, - casting_spell_id); - database.SetMQDetectionFlag(AccountName(), GetName(), detect, zone->GetShortName()); - safe_delete_array(detect); - Kick(); // Kick client to prevent client and server from getting out-of-sync inventory slots - return; - } - } - - // Illegal bagslot useage checks. Currently, user only receives a message if this check is triggered. - bool mi_hack = false; - - if (mi->from_slot >= EmuConstants::GENERAL_BAGS_BEGIN && mi->from_slot <= EmuConstants::CURSOR_BAG_END) { - if (mi->from_slot >= EmuConstants::CURSOR_BAG_BEGIN) { mi_hack = true; } - else { - int16 from_parent = m_inv.CalcSlotId(mi->from_slot); - if (!m_inv[from_parent]) { mi_hack = true; } - else if (!m_inv[from_parent]->IsType(ItemClassContainer)) { mi_hack = true; } - else if (m_inv.CalcBagIdx(mi->from_slot) >= m_inv[from_parent]->GetItem()->BagSlots) { mi_hack = true; } - } - } - - if (mi->to_slot >= EmuConstants::GENERAL_BAGS_BEGIN && mi->to_slot <= EmuConstants::CURSOR_BAG_END) { - if (mi->to_slot >= EmuConstants::CURSOR_BAG_BEGIN) { mi_hack = true; } - else { - int16 to_parent = m_inv.CalcSlotId(mi->to_slot); - if (!m_inv[to_parent]) { mi_hack = true; } - else if (!m_inv[to_parent]->IsType(ItemClassContainer)) { mi_hack = true; } - else if (m_inv.CalcBagIdx(mi->to_slot) >= m_inv[to_parent]->GetItem()->BagSlots) { mi_hack = true; } - } - } - - if (mi_hack) { Message(15, "Caution: Illegal use of inaccessable bag slots!"); } - - if (!SwapItem(mi) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) { - SwapItemResync(mi); - - bool error = false; - InterrogateInventory(this, false, true, false, error, false); - if (error) - InterrogateInventory(this, true, false, true, error); - } - - return; -} - -void Client::Handle_OP_OpenContainer(const EQApplicationPacket *app) -{ - // Does not exist in Ti client - // SoF, SoD and UF clients send a 4-byte packet indicating the 'parent' slot - // SoF, SoD and UF slots are defined by a uint32 value and currently untranslated - // RoF client sends a 12-byte packet based on the RoF::Structs::ItemSlotStruct - - // RoF structure types are defined as signed uint16 and currently untranslated - // RoF::struct.SlotType = {0 - Equipment, 1 - Bank, 2 - Shared Bank} // not tested beyond listed types - // RoF::struct.Unknown2 = 0 - // RoF::struct.MainSlot = { } - // RoF::struct.SubSlot = -1 (non-child) - // RoF::struct.AugSlot = -1 (non-child) - // RoF::struct.Unknown1 = 141 (unsure why, but always appears to be this value..combine containers not tested) - - // SideNote: Watching the slot translations, Unknown1 is showing '141' as well on certain item swaps. - // Manually looting a corpse results in a from '34' to '68' value for equipment items, '0' to '0' for inventory. -} - -void Client::Handle_OP_OpenGuildTributeMaster(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_OpenGuildTributeMaster of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - if (app->size != sizeof(StartTribute_Struct)) - printf("Error in OP_OpenGuildTributeMaster. Expected size of: %zu, but got: %i\n", sizeof(StartTribute_Struct), app->size); - else { - //Opens the guild tribute master window - StartTribute_Struct* st = (StartTribute_Struct*)app->pBuffer; - Mob* tribmast = entity_list.GetMob(st->tribute_master_id); - if (tribmast && tribmast->IsNPC() && tribmast->GetClass() == GUILD_TRIBUTE_MASTER - && DistNoRoot(*tribmast) <= USE_NPC_RANGE2) { - st->response = 1; - QueuePacket(app); - tribute_master_id = st->tribute_master_id; - DoTributeUpdate(); - } - else { - st->response = 0; - QueuePacket(app); - } - } - return; -} - -void Client::Handle_OP_OpenInventory(const EQApplicationPacket *app) -{ - // Does not exist in Ti, UF or RoF clients - // SoF and SoD both send a 4-byte packet with a uint32 value of '8' -} - -void Client::Handle_OP_OpenTributeMaster(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_OpenTributeMaster of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - if (app->size != sizeof(StartTribute_Struct)) - printf("Error in OP_OpenTributeMaster. Expected size of: %zu, but got: %i\n", sizeof(StartTribute_Struct), app->size); - else { - //Opens the tribute master window - StartTribute_Struct* st = (StartTribute_Struct*)app->pBuffer; - Mob* tribmast = entity_list.GetMob(st->tribute_master_id); - if (tribmast && tribmast->IsNPC() && tribmast->GetClass() == TRIBUTE_MASTER - && DistNoRoot(*tribmast) <= USE_NPC_RANGE2) { - st->response = 1; - QueuePacket(app); - tribute_master_id = st->tribute_master_id; - DoTributeUpdate(); - } - else { - st->response = 0; - QueuePacket(app); - } - } - return; -} - -void Client::Handle_OP_PDeletePetition(const EQApplicationPacket *app) -{ - if (app->size < 2) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_PDeletePetition, size=%i, expected %i", app->size, 2); - return; - } - if (petition_list.DeletePetitionByCharName((char*)app->pBuffer)) - Message_StringID(0, PETITION_DELETED); - else - Message_StringID(0, PETITION_NO_DELETE); - return; -} - -void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) -{ - if (app->size != sizeof(PetCommand_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetCommands, size=%i, expected %i", app->size, sizeof(PetCommand_Struct)); - return; - } - char val1[20] = { 0 }; - PetCommand_Struct* pet = (PetCommand_Struct*)app->pBuffer; - Mob* mypet = this->GetPet(); - - if (!mypet || pet->command == PET_LEADER) - { - if (pet->command == PET_LEADER) - { - if (mypet && (!GetTarget() || GetTarget() == mypet)) - { - mypet->Say_StringID(PET_LEADERIS, GetName()); - } - else if ((mypet = GetTarget())) - { - Mob *Owner = mypet->GetOwner(); - if (Owner) - mypet->Say_StringID(PET_LEADERIS, Owner->GetCleanName()); - else - mypet->Say_StringID(I_FOLLOW_NOONE); - } - } - - return; - } - - if (mypet->GetPetType() == petAnimation && (pet->command != PET_HEALTHREPORT && pet->command != PET_GETLOST) && !GetAA(aaAnimationEmpathy)) - return; - - // just let the command "/pet get lost" work for familiars - if (mypet->GetPetType() == petFamiliar && pet->command != PET_GETLOST) - return; - - uint32 PetCommand = pet->command; - - // Handle Sit/Stand toggle in UF and later. - if (GetClientVersion() >= EQClientUnderfoot) - { - if (PetCommand == PET_SITDOWN) - if (mypet->GetPetOrder() == SPO_Sit) - PetCommand = PET_STANDUP; - } - - switch (PetCommand) - { - case PET_ATTACK: { - if (!GetTarget()) - break; - if (GetTarget()->IsMezzed()) { - Message_StringID(10, CANNOT_WAKE, mypet->GetCleanName(), GetTarget()->GetCleanName()); - break; - } - if (mypet->IsFeared()) - break; //prevent pet from attacking stuff while feared - - if (!mypet->IsAttackAllowed(GetTarget())) { - mypet->Say_StringID(NOT_LEGAL_TARGET); - break; - } - - if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 2) || mypet->GetPetType() != petAnimation) { - if (GetTarget() != this && mypet->DistNoRootNoZ(*GetTarget()) <= (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. - if (mypet->GetPetOrder() != SPO_Guard) - mypet->SetPetOrder(SPO_Follow); - } - else { - mypet->SetTarget(GetTarget()); - } - } - zone->AddAggroMob(); - mypet->AddToHateList(GetTarget(), 1); - Message_StringID(MT_PetResponse, PET_ATTACKING, mypet->GetCleanName(), GetTarget()->GetCleanName()); - } - } - break; - } - case PET_BACKOFF: { - if (mypet->IsFeared()) break; //keeps pet running while feared - - if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { - mypet->Say_StringID(MT_PetResponse, PET_CALMING); - mypet->WipeHateList(); - mypet->SetTarget(nullptr); - } - break; - } - case PET_HEALTHREPORT: { - Message_StringID(MT_PetResponse, PET_REPORT_HP, ConvertArrayF(mypet->GetHPRatio(), val1)); - mypet->ShowBuffList(this); - //Message(10,"%s tells you, 'I have %d percent of my hit points left.'",mypet->GetName(),(uint8)mypet->GetHPRatio()); - break; - } - case PET_GETLOST: { - if (mypet->Charmed()) - break; - if (mypet->GetPetType() == petCharmed || !mypet->IsNPC()) { - // eqlive ignores this command - // we could just remove the charm - // and continue - mypet->BuffFadeByEffect(SE_Charm); - break; - } - else { - SetPet(nullptr); - } - - mypet->Say_StringID(MT_PetResponse, PET_GETLOST_STRING); - mypet->CastToNPC()->Depop(); - - //Oddly, the client (Titanium) will still allow "/pet get lost" command despite me adding the code below. If someone can figure that out, you can uncomment this code and use it. - /* - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 2) || mypet->GetPetType() != petAnimation) { - mypet->Say_StringID(PET_GETLOST_STRING); - mypet->CastToNPC()->Depop(); - } - */ - - break; - } - case PET_GUARDHERE: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF - - if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { - if (mypet->IsNPC()) { - mypet->SetHeld(false); - mypet->Say_StringID(MT_PetResponse, PET_GUARDINGLIFE); - mypet->SetPetOrder(SPO_Guard); - mypet->CastToNPC()->SaveGuardSpot(); - } - } - break; - } - case PET_FOLLOWME: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF - - if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { - mypet->SetHeld(false); - mypet->Say_StringID(MT_PetResponse, PET_FOLLOWING); - mypet->SetPetOrder(SPO_Follow); - mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); - } - break; - } - case PET_TAUNT: { - if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { - Message_StringID(MT_PetResponse, PET_DO_TAUNT); - mypet->CastToNPC()->SetTaunting(true); - } - break; - } - case PET_NOTAUNT: { - if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { - Message_StringID(MT_PetResponse, PET_NO_TAUNT); - mypet->CastToNPC()->SetTaunting(false); - } - break; - } - case PET_GUARDME: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF - - if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { - mypet->SetHeld(false); - mypet->Say_StringID(MT_PetResponse, PET_GUARDME_STRING); - mypet->SetPetOrder(SPO_Follow); - mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); - } - break; - } - case PET_SITDOWN: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF - - if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { - mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); - mypet->SetPetOrder(SPO_Sit); - mypet->SetRunAnimSpeed(0); - if (!mypet->UseBardSpellLogic()) //maybe we can have a bard pet - mypet->InterruptSpell(); //No cast 4 u. //i guess the pet should start casting - mypet->SendAppearancePacket(AT_Anim, ANIM_SIT); - } - break; - } - case PET_STANDUP: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF - - if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { - mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); - mypet->SetPetOrder(SPO_Follow); - mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); - } - break; - } - case PET_SLUMBER: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF - - if (mypet->GetPetType() != petAnimation) { - mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); - mypet->SetPetOrder(SPO_Sit); - mypet->SetRunAnimSpeed(0); - if (!mypet->UseBardSpellLogic()) //maybe we can have a bard pet - mypet->InterruptSpell(); //No cast 4 u. //i guess the pet should start casting - mypet->SendAppearancePacket(AT_Anim, ANIM_DEATH); - } - break; - } - case PET_HOLD: { - if (GetAA(aaPetDiscipline) && mypet->IsNPC()){ - if (mypet->IsFeared()) - break; //could be exploited like PET_BACKOFF - - mypet->Say_StringID(MT_PetResponse, PET_ON_HOLD); - mypet->WipeHateList(); - mypet->SetHeld(true); - } - break; - } - case PET_HOLD_ON: { - if (GetAA(aaPetDiscipline) && mypet->IsNPC() && !mypet->IsHeld()) { - if (mypet->IsFeared()) - break; //could be exploited like PET_BACKOFF - - mypet->Say_StringID(MT_PetResponse, PET_ON_HOLD); - mypet->WipeHateList(); - mypet->SetHeld(true); - } - break; - } - case PET_HOLD_OFF: { - if (GetAA(aaPetDiscipline) && mypet->IsNPC() && mypet->IsHeld()) - mypet->SetHeld(false); - break; - } - case PET_NOCAST: { - if (GetAA(aaAdvancedPetDiscipline) == 2 && mypet->IsNPC()) { - if (mypet->IsFeared()) - break; - if (mypet->IsNoCast()) { - Message_StringID(MT_PetResponse, PET_CASTING); - mypet->CastToNPC()->SetNoCast(false); - } - else { - Message_StringID(MT_PetResponse, PET_NOT_CASTING); - mypet->CastToNPC()->SetNoCast(true); - } - } - break; - } - case PET_FOCUS: { - if (GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { - if (mypet->IsFeared()) - break; - if (mypet->IsFocused()) { - Message_StringID(MT_PetResponse, PET_NOT_FOCUSING); - mypet->CastToNPC()->SetFocused(false); - } - else { - Message_StringID(MT_PetResponse, PET_NOW_FOCUSING); - mypet->CastToNPC()->SetFocused(true); - } - } - break; - } - case PET_FOCUS_ON: { - if (GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { - if (mypet->IsFeared()) - break; - if (!mypet->IsFocused()) { - Message_StringID(MT_PetResponse, PET_NOW_FOCUSING); - mypet->CastToNPC()->SetFocused(true); - } - } - break; - } - case PET_FOCUS_OFF: { - if (GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { - if (mypet->IsFeared()) - break; - if (mypet->IsFocused()) { - Message_StringID(MT_PetResponse, PET_NOT_FOCUSING); - mypet->CastToNPC()->SetFocused(false); - } - } - break; - } - default: - printf("Client attempted to use a unknown pet command:\n"); - break; - } -} - -void Client::Handle_OP_Petition(const EQApplicationPacket *app) -{ - if (app->size <= 1) - return; - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - /*else if(petition_list.FindPetitionByAccountName(this->AccountName())) - { - Message(0,"You already have a petition in queue, you cannot petition again until this one has been responded to or you have deleted the petition."); - return; - }*/ - else - { - if (petition_list.FindPetitionByAccountName(AccountName())) - { - Message(0, "You already have a petition in the queue, you must wait for it to be answered or use /deletepetition to delete it."); - return; - } - Petition* pet = new Petition(CharacterID()); - pet->SetAName(this->AccountName()); - pet->SetClass(this->GetClass()); - pet->SetLevel(this->GetLevel()); - pet->SetCName(this->GetName()); - pet->SetRace(this->GetRace()); - pet->SetLastGM(""); - pet->SetCName(this->GetName()); - pet->SetPetitionText((char*)app->pBuffer); - pet->SetZone(zone->GetZoneID()); - pet->SetUrgency(0); - petition_list.AddPetition(pet); - database.InsertPetitionToDB(pet); - petition_list.UpdateGMQueue(); - petition_list.UpdateZoneListQueue(); - worldserver.SendEmoteMessage(0, 0, 80, 15, "%s has made a petition. #%i", GetName(), pet->GetID()); - } - return; -} - -void Client::Handle_OP_PetitionBug(const EQApplicationPacket *app) -{ - if (app->size != sizeof(PetitionBug_Struct)) - printf("Wrong size of BugStruct! Expected: %zu, Got: %i\n", sizeof(PetitionBug_Struct), app->size); - else{ - Message(0, "Petition Bugs are not supported, please use /bug."); - } - return; -} - -void Client::Handle_OP_PetitionCheckIn(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Petition_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetitionCheckIn, size=%i, expected %i", app->size, sizeof(Petition_Struct)); - return; - } - Petition_Struct* inpet = (Petition_Struct*)app->pBuffer; - - Petition* pet = petition_list.GetPetitionByID(inpet->petnumber); - //if (inpet->urgency != pet->GetUrgency()) - pet->SetUrgency(inpet->urgency); - pet->SetLastGM(this->GetName()); - pet->SetGMText(inpet->gmtext); - - pet->SetCheckedOut(false); - petition_list.UpdatePetition(pet); - petition_list.UpdateGMQueue(); - petition_list.UpdateZoneListQueue(); - return; -} - -void Client::Handle_OP_PetitionCheckout(const EQApplicationPacket *app) -{ - if (app->size != sizeof(uint32)) { - std::cout << "Wrong size: OP_PetitionCheckout, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; - return; - } - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - uint32 getpetnum = *((uint32*)app->pBuffer); - Petition* getpet = petition_list.GetPetitionByID(getpetnum); - if (getpet != 0) { - getpet->AddCheckout(); - getpet->SetCheckedOut(true); - getpet->SendPetitionToPlayer(this->CastToClient()); - petition_list.UpdatePetition(getpet); - petition_list.UpdateGMQueue(); - petition_list.UpdateZoneListQueue(); - } - } - return; -} - -void Client::Handle_OP_PetitionDelete(const EQApplicationPacket *app) -{ - if (app->size != sizeof(PetitionUpdate_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetitionDelete, size=%i, expected %i", app->size, sizeof(PetitionUpdate_Struct)); - return; - } - EQApplicationPacket* outapp = new EQApplicationPacket(OP_PetitionUpdate, sizeof(PetitionUpdate_Struct)); - PetitionUpdate_Struct* pet = (PetitionUpdate_Struct*)outapp->pBuffer; - pet->petnumber = *((int*)app->pBuffer); - pet->color = 0x00; - pet->status = 0xFFFFFFFF; - pet->senttime = 0; - strcpy(pet->accountid, ""); - strcpy(pet->gmsenttoo, ""); - pet->quetotal = petition_list.GetTotalPetitions(); - strcpy(pet->charname, ""); - FastQueuePacket(&outapp); - - if (petition_list.DeletePetition(pet->petnumber) == -1) - std::cout << "Something is borked with: " << pet->petnumber << std::endl; - petition_list.ClearPetitions(); - petition_list.UpdateGMQueue(); - petition_list.ReadDatabase(); - petition_list.UpdateZoneListQueue(); - return; -} - -void Client::Handle_OP_PetitionQue(const EQApplicationPacket *app) -{ -#ifdef _EQDEBUG - printf("%s looking at petitions..\n", this->GetName()); -#endif - return; -} - -void Client::Handle_OP_PetitionRefresh(const EQApplicationPacket *app) -{ - // This is When Client Asks for Petition Again and Again... - // break is here because it floods the zones and causes lag if it - // Were to actually do something:P We update on our own schedule now. - return; -} - -void Client::Handle_OP_PetitionResolve(const EQApplicationPacket *app) -{ - Handle_OP_PetitionDelete(app); -} - -void Client::Handle_OP_PetitionUnCheckout(const EQApplicationPacket *app) -{ - if (app->size != sizeof(uint32)) { - std::cout << "Wrong size: OP_PetitionUnCheckout, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; - return; - } - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - uint32 getpetnum = *((uint32*)app->pBuffer); - Petition* getpet = petition_list.GetPetitionByID(getpetnum); - if (getpet != 0) { - getpet->SetCheckedOut(false); - petition_list.UpdatePetition(getpet); - petition_list.UpdateGMQueue(); - petition_list.UpdateZoneListQueue(); - } - } - return; -} - -void Client::Handle_OP_PickPocket(const EQApplicationPacket *app) -{ - if (app->size != sizeof(PickPocket_Struct)) - { - LogFile->write(EQEMuLog::Error, "Size mismatch for Pick Pocket packet"); - DumpPacket(app); - } - - if (!HasSkill(SkillPickPockets)) - { - return; - } - - if (!p_timers.Expired(&database, pTimerBeggingPickPocket, false)) - { - Message(13, "Ability recovery time not yet met."); - database.SetMQDetectionFlag(this->AccountName(), this->GetName(), "OP_PickPocket was sent again too quickly.", zone->GetShortName()); - return; - } - PickPocket_Struct* pick_in = (PickPocket_Struct*)app->pBuffer; - - Mob* victim = entity_list.GetMob(pick_in->to); - if (!victim) - return; - - p_timers.Start(pTimerBeggingPickPocket, 8); - if (victim == this){ - Message(0, "You catch yourself red-handed."); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_PickPocket, sizeof(sPickPocket_Struct)); - sPickPocket_Struct* pick_out = (sPickPocket_Struct*)outapp->pBuffer; - pick_out->coin = 0; - pick_out->from = victim->GetID(); - pick_out->to = GetID(); - pick_out->myskill = GetSkill(SkillPickPockets); - pick_out->type = 0; - //if we do not send this packet the client will lock up and require the player to relog. - QueuePacket(outapp); - safe_delete(outapp); - } - else if (victim->GetOwnerID()){ - Message(0, "You cannot steal from pets!"); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_PickPocket, sizeof(sPickPocket_Struct)); - sPickPocket_Struct* pick_out = (sPickPocket_Struct*)outapp->pBuffer; - pick_out->coin = 0; - pick_out->from = victim->GetID(); - pick_out->to = GetID(); - pick_out->myskill = GetSkill(SkillPickPockets); - pick_out->type = 0; - //if we do not send this packet the client will lock up and require the player to relog. - QueuePacket(outapp); - safe_delete(outapp); - } - else if (victim->IsNPC()){ - victim->CastToNPC()->PickPocket(this); - } - else{ - Message(0, "Stealing from clients not yet supported."); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_PickPocket, sizeof(sPickPocket_Struct)); - sPickPocket_Struct* pick_out = (sPickPocket_Struct*)outapp->pBuffer; - pick_out->coin = 0; - pick_out->from = victim->GetID(); - pick_out->to = GetID(); - pick_out->myskill = GetSkill(SkillPickPockets); - pick_out->type = 0; - //if we do not send this packet the client will lock up and require the player to relog. - QueuePacket(outapp); - safe_delete(outapp); - } -} - -void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app) -{ - - if (app->size != sizeof(PopupResponse_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PopupResponse expected %i got %i", - sizeof(PopupResponse_Struct), app->size); - DumpPacket(app); - return; - } - PopupResponse_Struct *prs = (PopupResponse_Struct*)app->pBuffer; - - // Handle any EQEmu defined popup Ids first - switch (prs->popupid) - { - case POPUPID_UPDATE_SHOWSTATSWINDOW: - if (GetTarget() && GetTarget()->IsClient()) - GetTarget()->CastToClient()->SendStatsWindow(this, true); - else - SendStatsWindow(this, true); - return; - - default: - break; - } - - char buf[16]; - sprintf(buf, "%d\0", prs->popupid); - - parse->EventPlayer(EVENT_POPUP_RESPONSE, this, buf, 0); - - Mob* Target = GetTarget(); - if (Target && Target->IsNPC()) { - parse->EventNPC(EVENT_POPUP_RESPONSE, Target->CastToNPC(), this, buf, 0); - } -} - -void Client::Handle_OP_PotionBelt(const EQApplicationPacket *app) -{ - if (app->size != sizeof(MovePotionToBelt_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PotionBelt expected %i got %i", - sizeof(MovePotionToBelt_Struct), app->size); - DumpPacket(app); - return; - } - - MovePotionToBelt_Struct *mptbs = (MovePotionToBelt_Struct*)app->pBuffer; - if(!EQEmu::ValueWithin(mptbs->SlotNumber, 0U, 3U)) { - LogFile->write(EQEMuLog::Debug, "Client::Handle_OP_PotionBelt mptbs->SlotNumber out of range."); - return; - } - - if (mptbs->Action == 0) { - const Item_Struct *BaseItem = database.GetItem(mptbs->ItemID); - if (BaseItem) { - m_pp.potionbelt.items[mptbs->SlotNumber].item_id = BaseItem->ID; - m_pp.potionbelt.items[mptbs->SlotNumber].icon = BaseItem->Icon; - strn0cpy(m_pp.potionbelt.items[mptbs->SlotNumber].item_name, BaseItem->Name, sizeof(BaseItem->Name)); - database.SaveCharacterPotionBelt(this->CharacterID(), mptbs->SlotNumber, m_pp.potionbelt.items[mptbs->SlotNumber].item_id, m_pp.potionbelt.items[mptbs->SlotNumber].icon); - } - } - else { - m_pp.potionbelt.items[mptbs->SlotNumber].item_id = 0; - m_pp.potionbelt.items[mptbs->SlotNumber].icon = 0; - strncpy(m_pp.potionbelt.items[mptbs->SlotNumber].item_name, "\0", 1); - } -} - -void Client::Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app) -{ - if (app->size != sizeof(uint32)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_LeadershipExpToggle expected %i got %i", 1, app->size); - DumpPacket(app); - return; - } - uint32 aaid = *((uint32 *)app->pBuffer); - - if (aaid >= _maxLeaderAA) - return; - - uint32 current_rank = m_pp.leader_abilities.ranks[aaid]; - if (current_rank >= MAX_LEADERSHIP_TIERS) { - Message(13, "This ability can be trained no further."); - return; - } - - uint8 cost = LeadershipAACosts[aaid][current_rank]; - if (cost == 0) { - Message(13, "This ability can be trained no further."); - return; - } - - //TODO: we need to enforce prerequisits - - if (aaid >= raidAAMarkNPC) { - //it is a raid ability. - if (cost > m_pp.raid_leadership_points) { - Message(13, "You do not have enough points to purchase this ability."); - return; - } - - //sell them the ability. - m_pp.raid_leadership_points -= cost; - m_pp.leader_abilities.ranks[aaid]++; - - database.SaveCharacterLeadershipAA(this->CharacterID(), &m_pp); - } else { - //it is a group ability. - if (cost > m_pp.group_leadership_points) { - Message(13, "You do not have enough points to purchase this ability."); - return; - } - - //sell them the ability. - m_pp.group_leadership_points -= cost; - m_pp.leader_abilities.ranks[aaid]++; - - database.SaveCharacterLeadershipAA(this->CharacterID(), &m_pp); - } - - //success, send them an update - EQApplicationPacket *outapp = new EQApplicationPacket(OP_UpdateLeadershipAA, sizeof(UpdateLeadershipAA_Struct)); - UpdateLeadershipAA_Struct *u = (UpdateLeadershipAA_Struct *)outapp->pBuffer; - u->ability_id = aaid; - u->new_rank = m_pp.leader_abilities.ranks[aaid]; - if (aaid >= raidAAMarkNPC) // raid AA - u->pointsleft = m_pp.raid_leadership_points; - else // group AA - u->pointsleft = m_pp.group_leadership_points; - FastQueuePacket(&outapp); - - // Update all group members with the new AA the leader has purchased. - if (IsRaidGrouped()) { - Raid *r = GetRaid(); - if (!r) - return; - if (aaid >= raidAAMarkNPC) { - r->UpdateRaidAAs(); - r->SendAllRaidLeadershipAA(); - } else { - uint32 gid = r->GetGroup(this); - r->UpdateGroupAAs(gid); - r->GroupUpdate(gid, false); - } - } else if (IsGrouped()) { - Group *g = GetGroup(); - if (!g) - return; - g->UpdateGroupAAs(); - g->SendLeadershipAAUpdate(); - } - -} - -void Client::Handle_OP_PVPLeaderBoardDetailsRequest(const EQApplicationPacket *app) -{ - // This opcode is sent by the client when the player right clicks a name on the PVP leaderboard and sends - // further details about the selected player, e.g. Race/Class/AAs/Guild etc. - // - if (app->size != sizeof(PVPLeaderBoardDetailsRequest_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PVPLeaderBoardDetailsRequest expected %i got %i", - sizeof(PVPLeaderBoardDetailsRequest_Struct), app->size); - - DumpPacket(app); - - return; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_PVPLeaderBoardDetailsReply, sizeof(PVPLeaderBoardDetailsReply_Struct)); - PVPLeaderBoardDetailsReply_Struct *pvplbdrs = (PVPLeaderBoardDetailsReply_Struct *)outapp->pBuffer; - - // TODO: Record and send this data. - - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app) -{ - // This Opcode is sent by the client when the Leaderboard button on the PVP Stats window is pressed. - // - // It has a single uint32 payload which is the sort method: - // - // PVPSortByKills = 0, PVPSortByPoints = 1, PVPSortByInfamy = 2 - // - if (app->size != sizeof(PVPLeaderBoardRequest_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PVPLeaderBoardRequest expected %i got %i", - sizeof(PVPLeaderBoardRequest_Struct), app->size); - - DumpPacket(app); - - return; - } - /*PVPLeaderBoardRequest_Struct *pvplbrs = (PVPLeaderBoardRequest_Struct *)app->pBuffer;*/ //unused - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_PVPLeaderBoardReply, sizeof(PVPLeaderBoard_Struct)); - /*PVPLeaderBoard_Struct *pvplb = (PVPLeaderBoard_Struct *)outapp->pBuffer;*/ //unused - - // TODO: Record and send this data. - - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) -{ - if (app->size < sizeof(RaidGeneral_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_RaidCommand, size=%i, expected at least %i", app->size, sizeof(RaidGeneral_Struct)); - DumpPacket(app); - return; - } - - RaidGeneral_Struct *ri = (RaidGeneral_Struct*)app->pBuffer; - switch (ri->action) - { - case RaidCommandInviteIntoExisting: - case RaidCommandInvite: { - Client *i = entity_list.GetClientByName(ri->player_name); - if (!i) - break; - Group *g = i->GetGroup(); - // These two messages should be generated by the client I think, just do this for now - if (i->HasRaid()) { - Message(13, "%s is already in a raid.", i->GetName()); - break; - } - if (g && !g->IsLeader(i)) { - Message(13, "You can only invite an ungrouped player or group leader to join your raid."); - break; - } - //This sends an "invite" to the client in question. - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); - RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; - strn0cpy(rg->leader_name, ri->leader_name, 64); - strn0cpy(rg->player_name, ri->player_name, 64); - - rg->parameter = 0; - rg->action = 20; - i->QueuePacket(outapp); - safe_delete(outapp); - break; - } - case RaidCommandAcceptInvite: { - Client *i = entity_list.GetClientByName(ri->player_name); - if (i){ - if (IsRaidGrouped()){ - i->Message_StringID(0, ALREADY_IN_RAID, GetName()); //group failed, must invite members not in raid... - return; - } - Raid *r = entity_list.GetRaidByClient(i); - if (r){ - r->VerifyRaid(); - Group *g = GetGroup(); - if (g){ - if (g->GroupCount() + r->RaidCount() > MAX_RAID_MEMBERS) - { - i->Message(13, "Invite failed, group invite would create a raid larger than the maximum number of members allowed."); - return; - } - } - else{ - if (1 + r->RaidCount() > MAX_RAID_MEMBERS) - { - i->Message(13, "Invite failed, member invite would create a raid larger than the maximum number of members allowed."); - return; - } - } - if (g){//add us all - uint32 freeGroup = r->GetFreeGroup(); - Client *addClient = nullptr; - for (int x = 0; x < 6; x++) - { - if (g->members[x]){ - Client *c = nullptr; - if (g->members[x]->IsClient()) - c = g->members[x]->CastToClient(); - else - continue; - - if (!addClient) - { - addClient = c; - r->SetGroupLeader(addClient->GetName()); - } - - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - if (g->IsLeader(g->members[x])) - r->AddMember(c, freeGroup, false, true); - else - r->AddMember(c, freeGroup); - r->SendBulkRaid(c); - if (r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - } - g->DisbandGroup(); - r->GroupUpdate(freeGroup); - } - else{ - r->SendRaidCreate(this); - r->SendMakeLeaderPacketTo(r->leadername, this); - r->AddMember(this); - r->SendBulkRaid(this); - if (r->IsLocked()) { - r->SendRaidLockTo(this); - } - } - } - else - { - Group *ig = i->GetGroup(); - Group *g = GetGroup(); - if (g) //if our target has a group - { - r = new Raid(i); - entity_list.AddRaid(r); - r->SetRaidDetails(); - - uint32 groupFree = r->GetFreeGroup(); //get a free group - if (ig){ //if we already have a group then cycle through adding us... - Client *addClientig = nullptr; - for (int x = 0; x < 6; x++) - { - if (ig->members[x]){ - if (!addClientig){ - if (ig->members[x]->IsClient()){ - addClientig = ig->members[x]->CastToClient(); - r->SetGroupLeader(addClientig->GetName()); - } - } - if (ig->IsLeader(ig->members[x])){ - Client *c = nullptr; - if (ig->members[x]->IsClient()) - c = ig->members[x]->CastToClient(); - else - continue; - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, groupFree, true, true, true); - r->SendBulkRaid(c); - if (r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - else{ - Client *c = nullptr; - if (ig->members[x]->IsClient()) - c = ig->members[x]->CastToClient(); - else - continue; - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, groupFree); - r->SendBulkRaid(c); - if (r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - } - } - ig->DisbandGroup(); - r->GroupUpdate(groupFree); - groupFree = r->GetFreeGroup(); - } - else{ //else just add the inviter - r->SendRaidCreate(i); - r->AddMember(i, 0xFFFFFFFF, true, false, true); - } - - Client *addClient = nullptr; - //now add the existing group - for (int x = 0; x < 6; x++) - { - if (g->members[x]){ - if (!addClient) - { - if (g->members[x]->IsClient()){ - addClient = g->members[x]->CastToClient(); - r->SetGroupLeader(addClient->GetName()); - } - } - if (g->IsLeader(g->members[x])) - { - Client *c = nullptr; - if (g->members[x]->IsClient()) - c = g->members[x]->CastToClient(); - else - continue; - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, groupFree, false, true); - r->SendBulkRaid(c); - if (r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - else - { - Client *c = nullptr; - if (g->members[x]->IsClient()) - c = g->members[x]->CastToClient(); - else - continue; - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, groupFree); - r->SendBulkRaid(c); - if (r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - } - } - g->DisbandGroup(); - r->GroupUpdate(groupFree); - } - else - { - if (ig){ - r = new Raid(i); - entity_list.AddRaid(r); - r->SetRaidDetails(); - Client *addClientig = nullptr; - for (int x = 0; x < 6; x++) - { - if (ig->members[x]) - { - if (!addClientig){ - if (ig->members[x]->IsClient()){ - addClientig = ig->members[x]->CastToClient(); - r->SetGroupLeader(addClientig->GetName()); - } - } - if (ig->IsLeader(ig->members[x])) - { - Client *c = nullptr; - if (ig->members[x]->IsClient()) - c = ig->members[x]->CastToClient(); - else - continue; - - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, 0, true, true, true); - r->SendBulkRaid(c); - if (r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - else - { - Client *c = nullptr; - if (ig->members[x]->IsClient()) - c = ig->members[x]->CastToClient(); - else - continue; - - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, 0); - r->SendBulkRaid(c); - if (r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - } - } - r->SendRaidCreate(this); - r->SendMakeLeaderPacketTo(r->leadername, this); - r->SendBulkRaid(this); - r->AddMember(this); - ig->DisbandGroup(); - r->GroupUpdate(0); - if (r->IsLocked()) { - r->SendRaidLockTo(this); - } - } - else{ - r = new Raid(i); - entity_list.AddRaid(r); - r->SetRaidDetails(); - r->SendRaidCreate(i); - r->SendRaidCreate(this); - r->SendMakeLeaderPacketTo(r->leadername, this); - r->AddMember(i, 0xFFFFFFFF, true, false, true); - r->SendBulkRaid(this); - r->AddMember(this); - if (r->IsLocked()) { - r->SendRaidLockTo(this); - } - } - } - } - } - break; - } - case RaidCommandDisband: { - Raid *r = entity_list.GetRaidByClient(this); - if (r){ - //if(this == r->GetLeader()){ - uint32 grp = r->GetGroup(ri->leader_name); - - if (grp < 12){ - uint32 i = r->GetPlayerIndex(ri->leader_name); - if (r->members[i].IsGroupLeader){ //assign group leader to someone else - for (int x = 0; x < MAX_RAID_MEMBERS; x++){ - if (strlen(r->members[x].membername) > 0 && i != x){ - if (r->members[x].GroupNumber == grp){ - r->SetGroupLeader(ri->leader_name, false); - r->SetGroupLeader(r->members[x].membername); - r->UpdateGroupAAs(grp); - break; - } - } - } - - } - if (r->members[i].IsRaidLeader){ - for (int x = 0; x < MAX_RAID_MEMBERS; x++){ - if (strlen(r->members[x].membername) > 0 && strcmp(r->members[x].membername, r->members[i].membername) != 0) - { - r->SetRaidLeader(r->members[i].membername, r->members[x].membername); - r->UpdateRaidAAs(); - r->SendAllRaidLeadershipAA(); - break; - } - } - } - } - - r->RemoveMember(ri->leader_name); - Client *c = entity_list.GetClientByName(ri->leader_name); - if (c) - r->SendGroupDisband(c); - else{ - ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupDisband, sizeof(ServerRaidGeneralAction_Struct)); - ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; - rga->rid = GetID(); - rga->zoneid = zone->GetZoneID(); - rga->instance_id = zone->GetInstanceID(); - strn0cpy(rga->playername, ri->leader_name, 64); - worldserver.SendPacket(pack); - safe_delete(pack); - } - //r->SendRaidGroupRemove(ri->leader_name, grp); - r->GroupUpdate(grp);// break - //} - } - break; - } - case RaidCommandMoveGroup: - { - Raid *r = entity_list.GetRaidByClient(this); - if (r) - { - if (ri->parameter < 12) //moving to a group - { - uint8 grpcount = r->GroupCount(ri->parameter); - - if (grpcount < 6) - { - Client *c = entity_list.GetClientByName(ri->leader_name); - uint32 oldgrp = r->GetGroup(ri->leader_name); - if (ri->parameter == oldgrp) //don't rejoin grp if we order to join same group. - break; - - if (r->members[r->GetPlayerIndex(ri->leader_name)].IsGroupLeader) - { - r->SetGroupLeader(ri->leader_name, false); - if (oldgrp < 12){ //we were the leader of our old grp - for (int x = 0; x < MAX_RAID_MEMBERS; x++) //assign a new grp leader if we can - { - if (r->members[x].GroupNumber == oldgrp) - { - if (strcmp(ri->leader_name, r->members[x].membername) != 0 && strlen(ri->leader_name) > 0) - { - r->SetGroupLeader(r->members[x].membername); - r->UpdateGroupAAs(oldgrp); - Client *cgl = entity_list.GetClientByName(r->members[x].membername); - if (cgl){ - r->SendRaidRemove(r->members[x].membername, cgl); - r->SendRaidCreate(cgl); - r->SendMakeLeaderPacketTo(r->leadername, cgl); - r->SendRaidAdd(r->members[x].membername, cgl); - r->SendBulkRaid(cgl); - if (r->IsLocked()) { - r->SendRaidLockTo(cgl); - } - } - else{ - ServerPacket *pack = new ServerPacket(ServerOP_RaidChangeGroup, sizeof(ServerRaidGeneralAction_Struct)); - ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; - rga->rid = r->GetID(); - strn0cpy(rga->playername, r->members[x].membername, 64); - rga->zoneid = zone->GetZoneID(); - rga->instance_id = zone->GetInstanceID(); - worldserver.SendPacket(pack); - safe_delete(pack); - } - break; - } - } - } - } - } - if (grpcount == 0) { - r->SetGroupLeader(ri->leader_name); - r->UpdateGroupAAs(ri->parameter); - } - - r->MoveMember(ri->leader_name, ri->parameter); - if (c){ - r->SendGroupDisband(c); - } - else{ - ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupDisband, sizeof(ServerRaidGeneralAction_Struct)); - ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; - rga->rid = r->GetID(); - rga->zoneid = zone->GetZoneID(); - rga->instance_id = zone->GetInstanceID(); - strn0cpy(rga->playername, ri->leader_name, 64); - worldserver.SendPacket(pack); - safe_delete(pack); - } - //r->SendRaidGroupAdd(ri->leader_name, ri->parameter); - //r->SendRaidGroupRemove(ri->leader_name, oldgrp); - //r->SendGroupUpdate(c); - //break - r->GroupUpdate(ri->parameter); //send group update to our new group - if (oldgrp < 12) //if our old was a group send update there too - r->GroupUpdate(oldgrp); - - //r->SendMakeGroupLeaderPacketAll(); - } - } - else //moving to ungrouped - { - Client *c = entity_list.GetClientByName(ri->leader_name); - uint32 oldgrp = r->GetGroup(ri->leader_name); - if (r->members[r->GetPlayerIndex(ri->leader_name)].IsGroupLeader){ - r->SetGroupLeader(ri->leader_name, false); - for (int x = 0; x < MAX_RAID_MEMBERS; x++) - { - if (r->members[x].GroupNumber == oldgrp && strlen(r->members[x].membername) > 0 && strcmp(r->members[x].membername, ri->leader_name) != 0) - { - r->SetGroupLeader(r->members[x].membername); - r->UpdateGroupAAs(oldgrp); - Client *cgl = entity_list.GetClientByName(r->members[x].membername); - if (cgl){ - r->SendRaidRemove(r->members[x].membername, cgl); - r->SendRaidCreate(cgl); - r->SendMakeLeaderPacketTo(r->leadername, cgl); - r->SendRaidAdd(r->members[x].membername, cgl); - r->SendBulkRaid(cgl); - if (r->IsLocked()) { - r->SendRaidLockTo(cgl); - } - } - else{ - ServerPacket *pack = new ServerPacket(ServerOP_RaidChangeGroup, sizeof(ServerRaidGeneralAction_Struct)); - ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; - rga->rid = r->GetID(); - strn0cpy(rga->playername, r->members[x].membername, 64); - rga->zoneid = zone->GetZoneID(); - rga->instance_id = zone->GetInstanceID(); - worldserver.SendPacket(pack); - safe_delete(pack); - } - break; - } - } - } - r->MoveMember(ri->leader_name, 0xFFFFFFFF); - if (c){ - r->SendGroupDisband(c); - } - else{ - ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupDisband, sizeof(ServerRaidGeneralAction_Struct)); - ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; - rga->rid = r->GetID(); - rga->zoneid = zone->GetZoneID(); - rga->instance_id = zone->GetInstanceID(); - strn0cpy(rga->playername, ri->leader_name, 64); - worldserver.SendPacket(pack); - safe_delete(pack); - } - //r->SendRaidGroupRemove(ri->leader_name, oldgrp); - r->GroupUpdate(oldgrp); - //r->SendMakeGroupLeaderPacketAll(); - } - } - break; - } - case RaidCommandRaidLock: - { - Raid *r = entity_list.GetRaidByClient(this); - if (r) - { - if (!r->IsLocked()) - r->LockRaid(true); - else - r->SendRaidLockTo(this); - } - break; - } - case RaidCommandRaidUnlock: - { - Raid *r = entity_list.GetRaidByClient(this); - if (r) - { - if (r->IsLocked()) - r->LockRaid(false); - else - r->SendRaidUnlockTo(this); - } - break; - } - case RaidCommandLootType2: - case RaidCommandLootType: - { - Raid *r = entity_list.GetRaidByClient(this); - if (r) - { - Message(15, "Loot type changed to: %d.", ri->parameter); - r->ChangeLootType(ri->parameter); - } - break; - } - - case RaidCommandAddLooter2: - case RaidCommandAddLooter: - { - Raid *r = entity_list.GetRaidByClient(this); - if (r) - { - Message(15, "Adding %s as a raid looter.", ri->leader_name); - r->AddRaidLooter(ri->leader_name); - } - break; - } - - case RaidCommandRemoveLooter2: - case RaidCommandRemoveLooter: - { - Raid *r = entity_list.GetRaidByClient(this); - if (r) - { - Message(15, "Removing %s as a raid looter.", ri->leader_name); - r->RemoveRaidLooter(ri->leader_name); - } - break; - } - - case RaidCommandMakeLeader: - { - Raid *r = entity_list.GetRaidByClient(this); - if (r) - { - if (strcmp(r->leadername, GetName()) == 0){ - r->SetRaidLeader(GetName(), ri->leader_name); - r->UpdateRaidAAs(); - r->SendAllRaidLeadershipAA(); - } - } - break; - } - - case RaidCommandSetMotd: - { - Raid *r = entity_list.GetRaidByClient(this); - if (!r) - break; - // we don't use the RaidGeneral here! - RaidMOTD_Struct *motd = (RaidMOTD_Struct *)app->pBuffer; - r->SetRaidMOTD(std::string(motd->motd)); - r->SaveRaidMOTD(); - r->SendRaidMOTDToWorld(); - break; - } - - default: { - Message(13, "Raid command (%d) NYI", ri->action); - break; - } - } -} - -void Client::Handle_OP_RandomReq(const EQApplicationPacket *app) -{ - if (app->size != sizeof(RandomReq_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_RandomReq, size=%i, expected %i", app->size, sizeof(RandomReq_Struct)); - return; - } - const RandomReq_Struct* rndq = (const RandomReq_Struct*)app->pBuffer; - uint32 randLow = rndq->low > rndq->high ? rndq->high : rndq->low; - uint32 randHigh = rndq->low > rndq->high ? rndq->low : rndq->high; - uint32 randResult; - - if (randLow == 0 && randHigh == 0) - { // defaults - randLow = 0; - randHigh = 100; - } - randResult = zone->random.Int(randLow, randHigh); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RandomReply, sizeof(RandomReply_Struct)); - RandomReply_Struct* rr = (RandomReply_Struct*)outapp->pBuffer; - rr->low = randLow; - rr->high = randHigh; - rr->result = randResult; - strcpy(rr->name, GetName()); - entity_list.QueueCloseClients(this, outapp, false, 400); - safe_delete(outapp); - return; -} - -void Client::Handle_OP_ReadBook(const EQApplicationPacket *app) -{ - if (app->size != sizeof(BookRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_ReadBook, size=%i, expected %i", app->size, sizeof(BookRequest_Struct)); - return; - } - BookRequest_Struct* book = (BookRequest_Struct*)app->pBuffer; - ReadBook(book); - if (GetClientVersion() >= EQClientSoF) - { - EQApplicationPacket EndOfBook(OP_FinishWindow, 0); - QueuePacket(&EndOfBook); - } - return; -} - -void Client::Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app) -{ - if (app->size != sizeof(RecipeAutoCombine_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for RecipeAutoCombine_Struct: Expected: %i, Got: %i", - sizeof(RecipeAutoCombine_Struct), app->size); - return; - } - - RecipeAutoCombine_Struct* rac = (RecipeAutoCombine_Struct*)app->pBuffer; - - Object::HandleAutoCombine(this, rac); - return; -} - -void Client::Handle_OP_RecipeDetails(const EQApplicationPacket *app) -{ - if (app->size < sizeof(uint32)) { - LogFile->write(EQEMuLog::Error, "Invalid size for RecipeDetails Request: Expected: %i, Got: %i", - sizeof(uint32), app->size); - return; - } - uint32 *recipe_id = (uint32*)app->pBuffer; - - SendTradeskillDetails(*recipe_id); - - return; -} - -void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app) -{ - if (app->size != sizeof(TradeskillFavorites_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for TradeskillFavorites_Struct: Expected: %i, Got: %i", - sizeof(TradeskillFavorites_Struct), app->size); - return; - } - - TradeskillFavorites_Struct* tsf = (TradeskillFavorites_Struct*)app->pBuffer; - - LogFile->write(EQEMuLog::Debug, "Requested Favorites for: %d - %d\n", tsf->object_type, tsf->some_id); - - // results show that object_type is combiner type - // some_id = 0 if world combiner, item number otherwise - - // make where clause segment for container(s) - std::string containers; - if (tsf->some_id == 0) - containers += StringFormat(" = %u ", tsf->object_type); // world combiner so no item number - else - containers += StringFormat(" in (%u, %u) ", tsf->object_type, tsf->some_id); // container in inventory - - std::string favoriteIDs; //gotta be big enough for 500 IDs - bool first = true; - //Assumes item IDs are <10 characters long - for (uint16 favoriteIndex = 0; favoriteIndex < 500; ++favoriteIndex) { - if (tsf->favorite_recipes[favoriteIndex] == 0) - continue; - - if (first) { - favoriteIDs += StringFormat("%u", tsf->favorite_recipes[favoriteIndex]); - first = false; - } - else - favoriteIDs += StringFormat(",%u", tsf->favorite_recipes[favoriteIndex]); - } - - if (first) //no favorites.... - return; - - const std::string query = StringFormat("SELECT tr.id, tr.name, tr.trivial, " - "SUM(tre.componentcount), crl.madecount,tr.tradeskill " - "FROM tradeskill_recipe AS tr " - "LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id=tre.recipe_id " - "LEFT JOIN (SELECT recipe_id, madecount " - "FROM char_recipe_list " - "WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id " - "WHERE tr.enabled <> 0 AND tr.id IN (%s) " - "AND tr.must_learn & 0x20 <> 0x20 AND " - "((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) " - "OR (tr.must_learn & 0x3 = 0)) " - "GROUP BY tr.id " - "HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 " - "LIMIT 100 ", CharacterID(), favoriteIDs.c_str(), containers.c_str()); - - TradeskillSearchResults(query, tsf->object_type, tsf->some_id); - return; -} - -void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app) -{ - if (app->size != sizeof(RecipesSearch_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for RecipesSearch_Struct: Expected: %i, Got: %i", - sizeof(RecipesSearch_Struct), app->size); - return; - } - - RecipesSearch_Struct* rss = (RecipesSearch_Struct*)app->pBuffer; - rss->query[55] = '\0'; //just to be sure. - - - LogFile->write(EQEMuLog::Debug, "Requested search recipes for: %d - %d\n", rss->object_type, rss->some_id); - - // make where clause segment for container(s) - char containers[30]; - if (rss->some_id == 0) { - // world combiner so no item number - snprintf(containers, 29, "= %u", rss->object_type); - } - else { - // container in inventory - snprintf(containers, 29, "in (%u,%u)", rss->object_type, rss->some_id); - } - - std::string searchClause; - - //omit the rlike clause if query is empty - if (rss->query[0] != 0) { - char buf[120]; //larger than 2X rss->query - database.DoEscapeString(buf, rss->query, strlen(rss->query)); - searchClause = StringFormat("name rlike '%s' AND", buf); - } - - //arbitrary limit of 200 recipes, makes sense to me. - const std::string query = StringFormat("SELECT tr.id, tr.name, tr.trivial, " - "SUM(tre.componentcount), crl.madecount,tr.tradeskill " - "FROM tradeskill_recipe AS tr " - "LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id = tre.recipe_id " - "LEFT JOIN (SELECT recipe_id, madecount " - "FROM char_recipe_list WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id " - "WHERE %s tr.trivial >= %u AND tr.trivial <= %u AND tr.enabled <> 0 " - "AND tr.must_learn & 0x20 <> 0x20 " - "AND ((tr.must_learn & 0x3 <> 0 " - "AND crl.madecount IS NOT NULL) " - "OR (tr.must_learn & 0x3 = 0)) " - "GROUP BY tr.id " - "HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 " - "LIMIT 200 ", - CharacterID(), searchClause.c_str(), - rss->mintrivial, rss->maxtrivial, containers); - TradeskillSearchResults(query, rss->object_type, rss->some_id); - return; -} - -void Client::Handle_OP_ReloadUI(const EQApplicationPacket *app) -{ - if (IsInAGuild()) - { - SendGuildRanks(); - SendGuildMembers(); - } - return; -} - -void Client::Handle_OP_RemoveBlockedBuffs(const EQApplicationPacket *app) -{ - if (!RuleB(Spells, EnableBlockedBuffs)) - return; - - if (app->size != sizeof(BlockedBuffs_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_RemoveBlockedBuffs expected %i got %i", - sizeof(BlockedBuffs_Struct), app->size); - - DumpPacket(app); - - return; - } - BlockedBuffs_Struct *bbs = (BlockedBuffs_Struct*)app->pBuffer; - - std::set *BlockedBuffs = bbs->Pet ? &PetBlockedBuffs : &PlayerBlockedBuffs; - - std::set RemovedBuffs; - - if (bbs->Count > 0) - { - std::set::iterator Iterator; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_RemoveBlockedBuffs, sizeof(BlockedBuffs_Struct)); - - BlockedBuffs_Struct *obbs = (BlockedBuffs_Struct*)outapp->pBuffer; - - for (unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) - obbs->SpellID[i] = 0; - - obbs->Pet = bbs->Pet; - obbs->Initialise = 0; - obbs->Flags = 0x5a; - - for (unsigned int i = 0; i < bbs->Count; ++i) - { - Iterator = BlockedBuffs->find(bbs->SpellID[i]); - - if (Iterator != BlockedBuffs->end()) - { - RemovedBuffs.insert(bbs->SpellID[i]); - - BlockedBuffs->erase(Iterator); - } - } - obbs->Count = RemovedBuffs.size(); - - Iterator = RemovedBuffs.begin(); - - unsigned int Element = 0; - - while (Iterator != RemovedBuffs.end()) - { - obbs->SpellID[Element++] = (*Iterator); - ++Iterator; - } - - FastQueuePacket(&outapp); - } -} - -void Client::Handle_OP_Report(const EQApplicationPacket *app) -{ - if (!CanUseReport) - { - Message_StringID(MT_System, REPORT_ONCE); - return; - } - - uint32 size = app->size; - uint32 current_point = 0; - std::string reported, reporter; - std::string current_string; - int mode = 0; - - while (current_point < size) - { - if (mode < 2) - { - if (app->pBuffer[current_point] == '|') - { - mode++; - } - else - { - if (mode == 0) - { - reported += app->pBuffer[current_point]; - } - else - { - reporter += app->pBuffer[current_point]; - } - } - current_point++; - } - else - { - if (app->pBuffer[current_point] == 0x0a) - { - current_string += '\n'; - } - else if (app->pBuffer[current_point] == 0x00) - { - CanUseReport = false; - database.AddReport(reporter, reported, current_string); - return; - } - else - { - current_string += app->pBuffer[current_point]; - } - current_point++; - } - } - - CanUseReport = false; - database.AddReport(reporter, reported, current_string); -} - -void Client::Handle_OP_RequestDuel(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Duel_Struct)) - return; - - EQApplicationPacket* outapp = app->Copy(); - Duel_Struct* ds = (Duel_Struct*)outapp->pBuffer; - uint32 duel = ds->duel_initiator; - ds->duel_initiator = ds->duel_target; - ds->duel_target = duel; - Entity* entity = entity_list.GetID(ds->duel_target); - if (GetID() != ds->duel_target && entity->IsClient() && (entity->CastToClient()->IsDueling() && entity->CastToClient()->GetDuelTarget() != 0)) { - Message_StringID(10, DUEL_CONSIDERING, entity->GetName()); - return; - } - if (IsDueling()) { - Message_StringID(10, DUEL_INPROGRESS); - return; - } - - if (GetID() != ds->duel_target && entity->IsClient() && GetDuelTarget() == 0 && !IsDueling() && !entity->CastToClient()->IsDueling() && entity->CastToClient()->GetDuelTarget() == 0) { - SetDuelTarget(ds->duel_target); - entity->CastToClient()->SetDuelTarget(GetID()); - ds->duel_target = ds->duel_initiator; - entity->CastToClient()->FastQueuePacket(&outapp); - entity->CastToClient()->SetDueling(false); - SetDueling(false); - } - else - safe_delete(outapp); - return; -} - -void Client::Handle_OP_RequestTitles(const EQApplicationPacket *app) -{ - - EQApplicationPacket *outapp = title_manager.MakeTitlesPacket(this); - - if (outapp != nullptr) - FastQueuePacket(&outapp); -} - -void Client::Handle_OP_RespawnWindow(const EQApplicationPacket *app) -{ - // This opcode is sent by the client when the player choses which bind to return to. - // The client sends just a 4 byte packet with the selection number in it - // - if (app->size != 4) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_RespawnWindow expected %i got %i", - 4, app->size); - DumpPacket(app); - return; - } - char *Buffer = (char *)app->pBuffer; - - uint32 Option = VARSTRUCT_DECODE_TYPE(uint32, Buffer); - HandleRespawnFromHover(Option); -} - -void Client::Handle_OP_Rewind(const EQApplicationPacket *app) -{ - if ((rewind_timer.GetRemainingTime() > 1 && rewind_timer.Enabled())) { - Message_StringID(MT_System, REWIND_WAIT); - } - else { - CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), m_RewindLocation.m_X, m_RewindLocation.m_Y, m_RewindLocation.m_Z, 0, 2, Rewind); - rewind_timer.Start(30000, true); - } -} - -void Client::Handle_OP_RezzAnswer(const EQApplicationPacket *app) -{ - VERIFY_PACKET_LENGTH(OP_RezzAnswer, app, Resurrect_Struct); - - const Resurrect_Struct* ra = (const Resurrect_Struct*)app->pBuffer; - - _log(SPELLS__REZ, "Received OP_RezzAnswer from client. Pendingrezzexp is %i, action is %s", - PendingRezzXP, ra->action ? "ACCEPT" : "DECLINE"); - - _pkt(SPELLS__REZ, app); - - OPRezzAnswer(ra->action, ra->spellid, ra->zone_id, ra->instance_id, ra->x, ra->y, ra->z); - - if (ra->action == 1) - { - EQApplicationPacket* outapp = app->Copy(); - // Send the OP_RezzComplete to the world server. This finds it's way to the zone that - // the rezzed corpse is in to mark the corpse as rezzed. - outapp->SetOpcode(OP_RezzComplete); - worldserver.RezzPlayer(outapp, 0, 0, OP_RezzComplete); - safe_delete(outapp); - } - return; -} - -void Client::Handle_OP_Sacrifice(const EQApplicationPacket *app) -{ - - if (app->size != sizeof(Sacrifice_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Sacrifice expected %i got %i", sizeof(Sacrifice_Struct), app->size); - DumpPacket(app); - return; - } - Sacrifice_Struct *ss = (Sacrifice_Struct*)app->pBuffer; - - if (!PendingSacrifice) { - LogFile->write(EQEMuLog::Error, "Unexpected OP_Sacrifice reply"); - DumpPacket(app); - return; - } - - if (ss->Confirm) { - Client *Caster = entity_list.GetClientByName(SacrificeCaster.c_str()); - if (Caster) Sacrifice(Caster); - } - PendingSacrifice = false; - SacrificeCaster.clear(); -} - -void Client::Handle_OP_SafeFallSuccess(const EQApplicationPacket *app) // bit of a misnomer, sent whenever safe fall is used (success of fail) -{ - if (HasSkill(SkillSafeFall)) //this should only get called if the client has safe fall, but just in case... - CheckIncreaseSkill(SkillSafeFall, nullptr); //check for skill up -} - -void Client::Handle_OP_SafePoint(const EQApplicationPacket *app) -{ - return; -} - -void Client::Handle_OP_Save(const EQApplicationPacket *app) -{ - // The payload is 192 bytes - Not sure what is contained in payload - Save(); - return; -} - -void Client::Handle_OP_SaveOnZoneReq(const EQApplicationPacket *app) -{ - Handle_OP_Save(app); -} - -void Client::Handle_OP_SelectTribute(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_SelectTribute of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - //we should enforce being near a real tribute master to change this - //but im not sure how I wanna do that right now. - if (app->size != sizeof(SelectTributeReq_Struct)) - LogFile->write(EQEMuLog::Error, "Invalid size on OP_SelectTribute packet"); - else { - SelectTributeReq_Struct *t = (SelectTributeReq_Struct *)app->pBuffer; - SendTributeDetails(t->client_id, t->tribute_id); - } - return; -} - -void Client::Handle_OP_SenseTraps(const EQApplicationPacket *app) -{ - if (!HasSkill(SkillSenseTraps)) - return; - - if (!p_timers.Expired(&database, pTimerSenseTraps, false)) { - Message(13, "Ability recovery time not yet met."); - return; - } - int reuse = SenseTrapsReuseTime; - switch (GetAA(aaAdvTrapNegotiation)) { - case 1: - reuse -= 1; - break; - case 2: - reuse -= 3; - break; - case 3: - reuse -= 5; - break; - } - p_timers.Start(pTimerSenseTraps, reuse - 1); - - Trap* trap = entity_list.FindNearbyTrap(this, 800); - - CheckIncreaseSkill(SkillSenseTraps, nullptr); - - if (trap && trap->skill > 0) { - int uskill = GetSkill(SkillSenseTraps); - if ((zone->random.Int(0, 99) + uskill) >= (zone->random.Int(0, 99) + trap->skill*0.75)) - { - auto diff = trap->m_Position - GetPosition(); - - if (diff.m_X == 0 && diff.m_Y == 0) - Message(MT_Skills, "You sense a trap right under your feet!"); - else if (diff.m_X > 10 && diff.m_Y > 10) - Message(MT_Skills, "You sense a trap to the NorthWest."); - else if (diff.m_X < -10 && diff.m_Y > 10) - Message(MT_Skills, "You sense a trap to the NorthEast."); - else if (diff.m_Y > 10) - Message(MT_Skills, "You sense a trap to the North."); - else if (diff.m_X > 10 && diff.m_Y < -10) - Message(MT_Skills, "You sense a trap to the SouthWest."); - else if (diff.m_X < -10 && diff.m_Y < -10) - Message(MT_Skills, "You sense a trap to the SouthEast."); - else if (diff.m_Y < -10) - Message(MT_Skills, "You sense a trap to the South."); - else if (diff.m_X > 10) - Message(MT_Skills, "You sense a trap to the West."); - else - Message(MT_Skills, "You sense a trap to the East."); - trap->detected = true; - - float angle = CalculateHeadingToTarget(trap->m_Position.m_X, trap->m_Position.m_Y); - - if (angle < 0) - angle = (256 + angle); - - angle *= 2; - MovePC(zone->GetZoneID(), zone->GetInstanceID(), GetX(), GetY(), GetZ(), angle); - return; - } - } - Message(MT_Skills, "You did not find any traps nearby."); - return; -} - -void Client::Handle_OP_SetGuildMOTD(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_SetGuildMOTD"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if (app->size != sizeof(GuildMOTD_Struct)) { - // client calls for a motd on login even if they arent in a guild - printf("Error: app size of %i != size of GuildMOTD_Struct of %zu\n", app->size, sizeof(GuildMOTD_Struct)); - return; - } - if (!IsInAGuild()) { - Message(13, "You are not in a guild!"); - return; - } - if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_MOTD)) { - Message(13, "You do not have permissions to edit your guild's MOTD."); - return; - } - - GuildMOTD_Struct* gmotd = (GuildMOTD_Struct*)app->pBuffer; - - mlog(GUILDS__ACTIONS, "Setting MOTD for %s (%d) to: %s - %s", - guild_mgr.GetGuildName(GuildID()), GuildID(), GetName(), gmotd->motd); - - if (!guild_mgr.SetGuildMOTD(GuildID(), gmotd->motd, GetName())) { - Message(0, "Motd update failed."); - } - - return; -} - -void Client::Handle_OP_SetRunMode(const EQApplicationPacket *app) -{ - return; -} - -void Client::Handle_OP_SetServerFilter(const EQApplicationPacket *app) -{ - if (app->size != sizeof(SetServerFilter_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized " - "OP_SetServerFilter: got %d, expected %d", app->size, - sizeof(SetServerFilter_Struct)); - DumpPacket(app); - return; - } - SetServerFilter_Struct* filter = (SetServerFilter_Struct*)app->pBuffer; - ServerFilter(filter); - return; -} - -void Client::Handle_OP_SetStartCity(const EQApplicationPacket *app) -{ - // if the character has a start city, don't let them use the command - if (m_pp.binds[4].zoneId != 0 && m_pp.binds[4].zoneId != 189) { - Message(15, "Your home city has already been set.", m_pp.binds[4].zoneId, database.GetZoneName(m_pp.binds[4].zoneId)); - return; - } - - if (app->size < 1) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_SetStartCity, size=%i, expected %i", app->size, 1); - DumpPacket(app); - return; - } - - float x(0), y(0), z(0); - uint32 zoneid = 0; - uint32 startCity = (uint32)strtol((const char*)app->pBuffer, nullptr, 10); - - std::string query = StringFormat("SELECT zone_id, bind_id, x, y, z FROM start_zones " - "WHERE player_class=%i AND player_deity=%i AND player_race=%i", - m_pp.class_, m_pp.deity, m_pp.race); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "No valid start zones found for /setstartcity"); - return; - } - - bool validCity = false; - for (auto row = results.begin(); row != results.end(); ++row) { - if (atoi(row[1]) != 0) - zoneid = atoi(row[1]); - else - zoneid = atoi(row[0]); - - if (zoneid != startCity) - continue; - - validCity = true; - x = atof(row[2]); - y = atof(row[3]); - z = atof(row[4]); - } - - if (validCity) { - Message(15, "Your home city has been set"); - SetStartZone(startCity, x, y, z); - return; - } - - query = StringFormat("SELECT zone_id, bind_id FROM start_zones " - "WHERE player_class=%i AND player_deity=%i AND player_race=%i", - m_pp.class_, m_pp.deity, m_pp.race); - results = database.QueryDatabase(query); - if (!results.Success()) - return; - - Message(15, "Use \"/startcity #\" to choose a home city from the following list:"); - - for (auto row = results.begin(); row != results.end(); ++row) { - if (atoi(row[1]) != 0) - zoneid = atoi(row[1]); - else - zoneid = atoi(row[0]); - - char* name; - database.GetZoneLongName(database.GetZoneName(zoneid), &name); - Message(15, "%d - %s", zoneid, name); - } - -} - -void Client::Handle_OP_SetTitle(const EQApplicationPacket *app) -{ - if (app->size != sizeof(SetTitle_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_SetTitle expected %i got %i", sizeof(SetTitle_Struct), app->size); - DumpPacket(app); - return; - } - - SetTitle_Struct *sts = (SetTitle_Struct *)app->pBuffer; - - std::string Title; - - if (!sts->is_suffix) - { - Title = title_manager.GetPrefix(sts->title_id); - SetAATitle(Title.c_str()); - } - else - { - Title = title_manager.GetSuffix(sts->title_id); - SetTitleSuffix(Title.c_str()); - } -} - -void Client::Handle_OP_Shielding(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Shielding_Struct)) { - LogFile->write(EQEMuLog::Error, "OP size error: OP_Shielding expected:%i got:%i", sizeof(Shielding_Struct), app->size); - return; - } - if (GetClass() != WARRIOR) - { - return; - } - - if (shield_target) - { - entity_list.MessageClose_StringID(this, false, 100, 0, - END_SHIELDING, GetName(), shield_target->GetName()); - for (int y = 0; y < 2; y++) - { - if (shield_target->shielder[y].shielder_id == GetID()) - { - shield_target->shielder[y].shielder_id = 0; - shield_target->shielder[y].shielder_bonus = 0; - } - } - } - Shielding_Struct* shield = (Shielding_Struct*)app->pBuffer; - shield_target = entity_list.GetMob(shield->target_id); - bool ack = false; - ItemInst* inst = GetInv().GetItem(MainSecondary); - if (!shield_target) - return; - if (inst) - { - const Item_Struct* shield = inst->GetItem(); - if (shield && shield->ItemType == ItemTypeShield) - { - for (int x = 0; x < 2; x++) - { - if (shield_target->shielder[x].shielder_id == 0) - { - entity_list.MessageClose_StringID(this, false, 100, 0, - START_SHIELDING, GetName(), shield_target->GetName()); - shield_target->shielder[x].shielder_id = GetID(); - int shieldbonus = shield->AC * 2; - switch (GetAA(197)) - { - case 1: - shieldbonus = shieldbonus * 115 / 100; - break; - case 2: - shieldbonus = shieldbonus * 125 / 100; - break; - case 3: - shieldbonus = shieldbonus * 150 / 100; - break; - } - shield_target->shielder[x].shielder_bonus = shieldbonus; - shield_timer.Start(); - ack = true; - break; - } - } - } - else - { - Message(0, "You must have a shield equipped to shield a target!"); - shield_target = 0; - return; - } - } - else - { - Message(0, "You must have a shield equipped to shield a target!"); - shield_target = 0; - return; - } - if (!ack) - { - Message_StringID(0, ALREADY_SHIELDED); - shield_target = 0; - return; - } - return; -} - -void Client::Handle_OP_ShopEnd(const EQApplicationPacket *app) -{ - EQApplicationPacket empty(OP_ShopEndConfirm); - QueuePacket(&empty); - //EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopEndConfirm, 2); - //outapp->pBuffer[0] = 0x0a; - //outapp->pBuffer[1] = 0x66; - //QueuePacket(outapp); - //safe_delete(outapp); - //Save(); - return; -} - -void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Merchant_Sell_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_ShopPlayerBuy: Expected %i, Got %i", - sizeof(Merchant_Sell_Struct), app->size); - return; - } - RDTSC_Timer t1; - t1.start(); - Merchant_Sell_Struct* mp = (Merchant_Sell_Struct*)app->pBuffer; -#if EQDEBUG >= 5 - LogFile->write(EQEMuLog::Debug, "%s, purchase item..", GetName()); - DumpPacket(app); -#endif - - int merchantid; - bool tmpmer_used = false; - Mob* tmp = entity_list.GetMob(mp->npcid); - - if (tmp == 0 || !tmp->IsNPC() || tmp->GetClass() != MERCHANT) - return; - - if (mp->quantity < 1) return; - - //you have to be somewhat close to them to be properly using them - if (DistNoRoot(*tmp) > USE_NPC_RANGE2) - return; - - merchantid = tmp->CastToNPC()->MerchantType; - - uint32 item_id = 0; - std::list merlist = zone->merchanttable[merchantid]; - std::list::const_iterator itr; - for (itr = merlist.begin(); itr != merlist.end(); ++itr){ - MerchantList ml = *itr; - if (GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tmp->GetPrimaryFaction(); - if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - if (mp->itemslot == ml.slot){ - item_id = ml.item; - break; - } - } - const Item_Struct* item = nullptr; - uint32 prevcharges = 0; - if (item_id == 0) { //check to see if its on the temporary table - std::list tmp_merlist = zone->tmpmerchanttable[tmp->GetNPCTypeID()]; - std::list::const_iterator tmp_itr; - TempMerchantList ml; - for (tmp_itr = tmp_merlist.begin(); tmp_itr != tmp_merlist.end(); ++tmp_itr){ - ml = *tmp_itr; - if (mp->itemslot == ml.slot){ - item_id = ml.item; - tmpmer_used = true; - prevcharges = ml.charges; - break; - } - } - } - item = database.GetItem(item_id); - if (!item){ - //error finding item, client didnt get the update packet for whatever reason, roleplay a tad - Message(15, "%s tells you 'Sorry, that item is for display purposes only.' as they take the item off the shelf.", tmp->GetCleanName()); - EQApplicationPacket* delitempacket = new EQApplicationPacket(OP_ShopDelItem, sizeof(Merchant_DelItem_Struct)); - Merchant_DelItem_Struct* delitem = (Merchant_DelItem_Struct*)delitempacket->pBuffer; - delitem->itemslot = mp->itemslot; - delitem->npcid = mp->npcid; - delitem->playerid = mp->playerid; - delitempacket->priority = 6; - entity_list.QueueCloseClients(tmp, delitempacket); //que for anyone that could be using the merchant so they see the update - safe_delete(delitempacket); - return; - } - if (CheckLoreConflict(item)) - { - Message(15, "You can only have one of a lore item."); - return; - } - if (tmpmer_used && (mp->quantity > prevcharges || item->MaxCharges > 1)) - { - if (prevcharges > item->MaxCharges && item->MaxCharges > 1) - mp->quantity = item->MaxCharges; - else - mp->quantity = prevcharges; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerBuy, sizeof(Merchant_Sell_Struct)); - Merchant_Sell_Struct* mpo = (Merchant_Sell_Struct*)outapp->pBuffer; - mpo->quantity = mp->quantity; - mpo->playerid = mp->playerid; - mpo->npcid = mp->npcid; - mpo->itemslot = mp->itemslot; - - int16 freeslotid = INVALID_INDEX; - int16 charges = 0; - if (item->Stackable || item->MaxCharges > 1) - charges = mp->quantity; - else - charges = item->MaxCharges; - - ItemInst* inst = database.CreateItem(item, charges); - - int SinglePrice = 0; - if (RuleB(Merchant, UsePriceMod)) - SinglePrice = (item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate * Client::CalcPriceMod(tmp, false)); - else - SinglePrice = (item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate); - - if (item->MaxCharges > 1) - mpo->price = SinglePrice; - else - mpo->price = SinglePrice * mp->quantity; - if (mpo->price < 0) - { - safe_delete(outapp); - safe_delete(inst); - return; - } - - // this area needs some work..two inventory insertion check failure points - // below do not return player's money..is this the intended behavior? - - if (!TakeMoneyFromPP(mpo->price)) - { - char *hacker_str = nullptr; - MakeAnyLenString(&hacker_str, "Vendor Cheat: attempted to buy %i of %i: %s that cost %d cp but only has %d pp %d gp %d sp %d cp\n", - mpo->quantity, item->ID, item->Name, - mpo->price, m_pp.platinum, m_pp.gold, m_pp.silver, m_pp.copper); - database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); - safe_delete_array(hacker_str); - safe_delete(outapp); - safe_delete(inst); - return; - } - - bool stacked = TryStacking(inst); - if (!stacked) - freeslotid = m_inv.FindFreeSlot(false, true, item->Size); - - // shouldn't we be reimbursing if these two fail? - - //make sure we are not completely full... - if (freeslotid == MainCursor) { - if (m_inv.GetItem(MainCursor) != nullptr) { - Message(13, "You do not have room for any more items."); - safe_delete(outapp); - safe_delete(inst); - return; - } - } - - if (!stacked && freeslotid == INVALID_INDEX) - { - Message(13, "You do not have room for any more items."); - safe_delete(outapp); - safe_delete(inst); - return; - } - - std::string packet; - if (!stacked && inst) { - PutItemInInventory(freeslotid, *inst); - SendItemPacket(freeslotid, inst, ItemPacketTrade); - } - else if (!stacked){ - LogFile->write(EQEMuLog::Error, "OP_ShopPlayerBuy: item->ItemClass Unknown! Type: %i", item->ItemClass); - } - QueuePacket(outapp); - if (inst && tmpmer_used){ - int32 new_charges = prevcharges - mp->quantity; - zone->SaveTempItem(merchantid, tmp->GetNPCTypeID(), item_id, new_charges); - if (new_charges <= 0){ - EQApplicationPacket* delitempacket = new EQApplicationPacket(OP_ShopDelItem, sizeof(Merchant_DelItem_Struct)); - Merchant_DelItem_Struct* delitem = (Merchant_DelItem_Struct*)delitempacket->pBuffer; - delitem->itemslot = mp->itemslot; - delitem->npcid = mp->npcid; - delitem->playerid = mp->playerid; - delitempacket->priority = 6; - entity_list.QueueClients(tmp, delitempacket); //que for anyone that could be using the merchant so they see the update - safe_delete(delitempacket); - } - else { - // Update the charges/quantity in the merchant window - inst->SetCharges(new_charges); - inst->SetPrice(SinglePrice); - inst->SetMerchantSlot(mp->itemslot); - inst->SetMerchantCount(new_charges); - - SendItemPacket(mp->itemslot, inst, ItemPacketMerchant); - } - } - safe_delete(inst); - safe_delete(outapp); - - // start QS code - // stacking purchases not supported at this time - entire process will need some work to catch them properly - if (RuleB(QueryServ, PlayerLogMerchantTransactions)) { - ServerPacket* qspack = new ServerPacket(ServerOP_QSPlayerLogMerchantTransactions, sizeof(QSMerchantLogTransaction_Struct)+sizeof(QSTransactionItems_Struct)); - QSMerchantLogTransaction_Struct* qsaudit = (QSMerchantLogTransaction_Struct*)qspack->pBuffer; - - qsaudit->zone_id = zone->GetZoneID(); - qsaudit->merchant_id = tmp->CastToNPC()->MerchantType; - qsaudit->merchant_money.platinum = 0; - qsaudit->merchant_money.gold = 0; - qsaudit->merchant_money.silver = 0; - qsaudit->merchant_money.copper = 0; - qsaudit->merchant_count = 1; - qsaudit->char_id = character_id; - qsaudit->char_money.platinum = (mpo->price / 1000); - qsaudit->char_money.gold = (mpo->price / 100) % 10; - qsaudit->char_money.silver = (mpo->price / 10) % 10; - qsaudit->char_money.copper = mpo->price % 10; - qsaudit->char_count = 0; - - qsaudit->items[0].char_slot = freeslotid == INVALID_INDEX ? 0 : freeslotid; - qsaudit->items[0].item_id = item->ID; - qsaudit->items[0].charges = mpo->quantity; - - if (freeslotid == INVALID_INDEX) { - qsaudit->items[0].aug_1 = 0; - qsaudit->items[0].aug_2 = 0; - qsaudit->items[0].aug_3 = 0; - qsaudit->items[0].aug_4 = 0; - qsaudit->items[0].aug_5 = 0; - } - else { - qsaudit->items[0].aug_1 = m_inv[freeslotid]->GetAugmentItemID(0); - qsaudit->items[0].aug_2 = m_inv[freeslotid]->GetAugmentItemID(1); - qsaudit->items[0].aug_3 = m_inv[freeslotid]->GetAugmentItemID(2); - qsaudit->items[0].aug_4 = m_inv[freeslotid]->GetAugmentItemID(3); - qsaudit->items[0].aug_5 = m_inv[freeslotid]->GetAugmentItemID(4); - } - - qspack->Deflate(); - if (worldserver.Connected()) { worldserver.SendPacket(qspack); } - safe_delete(qspack); - } - // end QS code - - if (RuleB(EventLog, RecordBuyFromMerchant)) - LogMerchant(this, tmp, mpo->quantity, mpo->price, item, true); - - if ((RuleB(Character, EnableDiscoveredItems))) - { - if (!GetGM() && !IsDiscovered(item_id)) - DiscoverItem(item_id); - } - - t1.stop(); - std::cout << "At 1: " << t1.getDuration() << std::endl; - return; -} -void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Merchant_Purchase_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_ShopPlayerSell: Expected %i, Got %i", - sizeof(Merchant_Purchase_Struct), app->size); - return; - } - RDTSC_Timer t1(true); - Merchant_Purchase_Struct* mp = (Merchant_Purchase_Struct*)app->pBuffer; - - Mob* vendor = entity_list.GetMob(mp->npcid); - - if (vendor == 0 || !vendor->IsNPC() || vendor->GetClass() != MERCHANT) - return; - - //you have to be somewhat close to them to be properly using them - if (DistNoRoot(*vendor) > USE_NPC_RANGE2) - return; - - uint32 price = 0; - uint32 itemid = GetItemIDAt(mp->itemslot); - if (itemid == 0) - return; - const Item_Struct* item = database.GetItem(itemid); - ItemInst* inst = GetInv().GetItem(mp->itemslot); - if (!item || !inst){ - Message(13, "You seemed to have misplaced that item.."); - return; - } - if (mp->quantity > 1) - { - if ((inst->GetCharges() < 0) || (mp->quantity > (uint32)inst->GetCharges())) - return; - } - - if (!item->NoDrop) { - //Message(13,"%s tells you, 'LOL NOPE'", vendor->GetName()); - return; - } - - int cost_quantity = mp->quantity; - if (inst->IsCharged()) - int cost_quantity = 1; - - if (RuleB(Merchant, UsePriceMod)) - price = (int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(vendor, true) + 0.5); // need to round up, because client does it automatically when displaying price - else - price = (int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod)) + 0.5); - AddMoneyToPP(price, false); - - if (inst->IsStackable() || inst->IsCharged()) - { - unsigned int i_quan = inst->GetCharges(); - if (mp->quantity > i_quan || inst->IsCharged()) - mp->quantity = i_quan; - } - else - mp->quantity = 1; - - if (RuleB(EventLog, RecordSellToMerchant)) - LogMerchant(this, vendor, mp->quantity, price, item, false); - - int charges = mp->quantity; - //Hack workaround so usable items with 0 charges aren't simply deleted - if (charges == 0 && item->ItemType != 11 && item->ItemType != 17 && item->ItemType != 19 && item->ItemType != 21) - charges = 1; - - int freeslot = 0; - if (charges > 0 && (freeslot = zone->SaveTempItem(vendor->CastToNPC()->MerchantType, vendor->GetNPCTypeID(), itemid, charges, true)) > 0){ - ItemInst* inst2 = inst->Clone(); - if (RuleB(Merchant, UsePriceMod)){ - inst2->SetPrice(item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate*Client::CalcPriceMod(vendor, false)); - } - else - inst2->SetPrice(item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate); - inst2->SetMerchantSlot(freeslot); - - uint32 MerchantQuantity = zone->GetTempMerchantQuantity(vendor->GetNPCTypeID(), freeslot); - - if (inst2->IsStackable()) { - inst2->SetCharges(MerchantQuantity); - } - inst2->SetMerchantCount(MerchantQuantity); - - SendItemPacket(freeslot - 1, inst2, ItemPacketMerchant); - safe_delete(inst2); - } - - // start QS code - if (RuleB(QueryServ, PlayerLogMerchantTransactions)) { - ServerPacket* qspack = new ServerPacket(ServerOP_QSPlayerLogMerchantTransactions, sizeof(QSMerchantLogTransaction_Struct)+sizeof(QSTransactionItems_Struct)); - QSMerchantLogTransaction_Struct* qsaudit = (QSMerchantLogTransaction_Struct*)qspack->pBuffer; - - qsaudit->zone_id = zone->GetZoneID(); - qsaudit->merchant_id = vendor->CastToNPC()->MerchantType; - qsaudit->merchant_money.platinum = (price / 1000); - qsaudit->merchant_money.gold = (price / 100) % 10; - qsaudit->merchant_money.silver = (price / 10) % 10; - qsaudit->merchant_money.copper = price % 10; - qsaudit->merchant_count = 0; - qsaudit->char_id = character_id; - qsaudit->char_money.platinum = 0; - qsaudit->char_money.gold = 0; - qsaudit->char_money.silver = 0; - qsaudit->char_money.copper = 0; - qsaudit->char_count = 1; - - qsaudit->items[0].char_slot = mp->itemslot; - qsaudit->items[0].item_id = itemid; - qsaudit->items[0].charges = charges; - qsaudit->items[0].aug_1 = m_inv[mp->itemslot]->GetAugmentItemID(1); - qsaudit->items[0].aug_2 = m_inv[mp->itemslot]->GetAugmentItemID(2); - qsaudit->items[0].aug_3 = m_inv[mp->itemslot]->GetAugmentItemID(3); - qsaudit->items[0].aug_4 = m_inv[mp->itemslot]->GetAugmentItemID(4); - qsaudit->items[0].aug_5 = m_inv[mp->itemslot]->GetAugmentItemID(5); - - qspack->Deflate(); - if (worldserver.Connected()) { worldserver.SendPacket(qspack); } - safe_delete(qspack); - } - // end QS code - - // Now remove the item from the player, this happens regardless of outcome - if (!inst->IsStackable()) - this->DeleteItemInInventory(mp->itemslot, 0, false); - else - this->DeleteItemInInventory(mp->itemslot, mp->quantity, false); - - //This forces the price to show up correctly for charged items. - if (inst->IsCharged()) - mp->quantity = 1; - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerSell, sizeof(Merchant_Purchase_Struct)); - Merchant_Purchase_Struct* mco = (Merchant_Purchase_Struct*)outapp->pBuffer; - mco->npcid = vendor->GetID(); - mco->itemslot = mp->itemslot; - mco->quantity = mp->quantity; - mco->price = price; - QueuePacket(outapp); - safe_delete(outapp); - SendMoneyUpdate(); - t1.start(); - Save(1); - t1.stop(); - std::cout << "Save took: " << t1.getDuration() << std::endl; - return; -} - -void Client::Handle_OP_ShopRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Merchant_Click_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_ShopRequest, size=%i, expected %i", app->size, sizeof(Merchant_Click_Struct)); - return; - } - - Merchant_Click_Struct* mc = (Merchant_Click_Struct*)app->pBuffer; - - // Send back opcode OP_ShopRequest - tells client to open merchant window. - //EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct)); - //Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer; - int merchantid = 0; - Mob* tmp = entity_list.GetMob(mc->npcid); - - if (tmp == 0 || !tmp->IsNPC() || tmp->GetClass() != MERCHANT) - return; - - //you have to be somewhat close to them to be properly using them - if (DistNoRoot(*tmp) > USE_NPC_RANGE2) - return; - - merchantid = tmp->CastToNPC()->MerchantType; - - int action = 1; - if (merchantid == 0) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct)); - Merchant_Click_Struct* mco = (Merchant_Click_Struct*)outapp->pBuffer; - mco->npcid = mc->npcid; - mco->playerid = 0; - mco->command = 1; //open... - mco->rate = 1.0; - QueuePacket(outapp); - safe_delete(outapp); - return; - } - if (tmp->IsEngaged()){ - this->Message_StringID(0, MERCHANT_BUSY); - action = 0; - } - if (GetFeigned() || IsInvisible()) - { - Message(0, "You cannot use a merchant right now."); - action = 0; - } - int primaryfaction = tmp->CastToNPC()->GetPrimaryFaction(); - int factionlvl = GetFactionLevel(CharacterID(), tmp->CastToNPC()->GetNPCTypeID(), GetRace(), GetClass(), GetDeity(), primaryfaction, tmp); - if (factionlvl >= 7) { - MerchantRejectMessage(tmp, primaryfaction); - action = 0; - } - - if (tmp->Charmed()) - action = 0; - - // 1199 I don't have time for that now. etc - if (!tmp->CastToNPC()->IsMerchantOpen()) { - tmp->Say_StringID(zone->random.Int(1199, 1202)); - action = 0; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct)); - Merchant_Click_Struct* mco = (Merchant_Click_Struct*)outapp->pBuffer; - - mco->npcid = mc->npcid; - mco->playerid = 0; - mco->command = action; // Merchant command 0x01 = open - if (RuleB(Merchant, UsePriceMod)){ - mco->rate = 1 / ((RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(tmp, true)); // works - } - else - mco->rate = 1 / (RuleR(Merchant, BuyCostMod)); - - outapp->priority = 6; - QueuePacket(outapp); - safe_delete(outapp); - - if (action == 1) - BulkSendMerchantInventory(merchantid, tmp->GetNPCTypeID()); - - return; -} - -void Client::Handle_OP_Sneak(const EQApplicationPacket *app) -{ - if (!HasSkill(SkillSneak) && GetSkill(SkillSneak) == 0) { - return; //You cannot sneak if you do not have sneak - } - - if (!p_timers.Expired(&database, pTimerSneak, false)) { - Message(13, "Ability recovery time not yet met."); - return; - } - p_timers.Start(pTimerSneak, SneakReuseTime - 1); - - bool was = sneaking; - if (sneaking){ - sneaking = false; - hidden = false; - improved_hidden = false; - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x03; - sa_out->parameter = 0; - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - } - else { - CheckIncreaseSkill(SkillSneak, nullptr, 5); - } - float hidechance = ((GetSkill(SkillSneak) / 300.0f) + .25) * 100; - float random = zone->random.Real(0, 99); - if (!was && random < hidechance) { - sneaking = true; - } - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x0F; - sa_out->parameter = sneaking; - QueuePacket(outapp); - safe_delete(outapp); - if (GetClass() == ROGUE){ - outapp = new EQApplicationPacket(OP_SimpleMessage, 12); - SimpleMessage_Struct *msg = (SimpleMessage_Struct *)outapp->pBuffer; - msg->color = 0x010E; - if (sneaking){ - msg->string_id = 347; - } - else { - msg->string_id = 348; - } - FastQueuePacket(&outapp); - } - return; -} - -void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) -{ - if (app->size != sizeof(SpawnAppearance_Struct)) { - std::cout << "Wrong size on OP_SpawnAppearance. Got: " << app->size << ", Expected: " << sizeof(SpawnAppearance_Struct) << std::endl; - return; - } - SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)app->pBuffer; - - if (sa->spawn_id != GetID()) - return; - - if (sa->type == AT_Invis) { - if (sa->parameter != 0) - { - if (!HasSkill(SkillHide) && GetSkill(SkillHide) == 0) - { - if (GetClientVersion() < EQClientSoF) - { - char *hack_str = nullptr; - MakeAnyLenString(&hack_str, "Player sent OP_SpawnAppearance with AT_Invis: %i", sa->parameter); - database.SetMQDetectionFlag(this->account_name, this->name, hack_str, zone->GetShortName()); - safe_delete_array(hack_str); - } - } - return; - } - invisible = false; - hidden = false; - improved_hidden = false; - entity_list.QueueClients(this, app, true); - return; - } - else if (sa->type == AT_Anim) { - if (IsAIControlled()) - return; - if (sa->parameter == ANIM_STAND) { - SetAppearance(eaStanding); - playeraction = 0; - SetFeigned(false); - BindWound(this, false, true); - camp_timer.Disable(); - } - else if (sa->parameter == ANIM_SIT) { - SetAppearance(eaSitting); - playeraction = 1; - if (!UseBardSpellLogic()) - InterruptSpell(); - SetFeigned(false); - BindWound(this, false, true); - } - else if (sa->parameter == ANIM_CROUCH) { - if (!UseBardSpellLogic()) - InterruptSpell(); - SetAppearance(eaCrouching); - playeraction = 2; - SetFeigned(false); - } - else if (sa->parameter == ANIM_DEATH) { // feign death too - SetAppearance(eaDead); - playeraction = 3; - InterruptSpell(); - } - else if (sa->parameter == ANIM_LOOT) { - SetAppearance(eaLooting); - playeraction = 4; - SetFeigned(false); - } - - // This is from old code - // I have no clue what it's for - /* - else if (sa->parameter == 0x05) { - // Illusion - std::cout << "Illusion packet recv'd:" << std::endl; - DumpPacket(app); - } - */ - else { - std::cerr << "Client " << name << " unknown apperance " << (int)sa->parameter << std::endl; - return; - } - - entity_list.QueueClients(this, app, true); - } - else if (sa->type == AT_Anon) { - // For Anon/Roleplay - if (sa->parameter == 1) { // Anon - m_pp.anon = 1; - } - else if ((sa->parameter == 2) || (sa->parameter == 3)) { // This is Roleplay, or anon+rp - m_pp.anon = 2; - } - else if (sa->parameter == 0) { // This is Non-Anon - m_pp.anon = 0; - } - else { - std::cerr << "Client " << name << " unknown Anon/Roleplay Switch " << (int)sa->parameter << std::endl; - return; - } - entity_list.QueueClients(this, app, true); - UpdateWho(); - } - else if ((sa->type == AT_HP) && (dead == 0)) { - return; - } - else if (sa->type == AT_AFK) { - this->AFK = (sa->parameter == 1); - entity_list.QueueClients(this, app, true); - } - else if (sa->type == AT_Split) { - m_pp.autosplit = (sa->parameter == 1); - } - else if (sa->type == AT_Sneak) { - if (sa->parameter != 0) - { - if (!HasSkill(SkillSneak)) - { - char *hack_str = nullptr; - MakeAnyLenString(&hack_str, "Player sent OP_SpawnAppearance with AT_Sneak: %i", sa->parameter); - database.SetMQDetectionFlag(this->account_name, this->name, hack_str, zone->GetShortName()); - safe_delete_array(hack_str); - } - return; - } - this->sneaking = 0; - entity_list.QueueClients(this, app, true); - } - else if (sa->type == AT_Size) - { - char *hack_str = nullptr; - MakeAnyLenString(&hack_str, "Player sent OP_SpawnAppearance with AT_Size: %i", sa->parameter); - database.SetMQDetectionFlag(this->account_name, this->name, hack_str, zone->GetShortName()); - safe_delete_array(hack_str); - } - else if (sa->type == AT_Light) // client emitting light (lightstone, shiny shield) - { - entity_list.QueueClients(this, app, false); - } - else if (sa->type == AT_Levitate) - { - // don't do anything with this, we tell the client when it's - // levitating, not the other way around - } - else if (sa->type == AT_ShowHelm) - { - m_pp.showhelm = (sa->parameter == 1); - entity_list.QueueClients(this, app, true); - } - else { - std::cout << "Unknown SpawnAppearance type: 0x" << std::hex << std::setw(4) << std::setfill('0') << sa->type << std::dec - << " value: 0x" << std::hex << std::setw(8) << std::setfill('0') << sa->parameter << std::dec << std::endl; - } - return; -} - -void Client::Handle_OP_Split(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Split_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_Split, size=%i, expected %i", app->size, sizeof(Split_Struct)); - return; - } - // The client removes the money on its own, but we have to - // update our state anyway, and make sure they had enough to begin - // with. - Split_Struct *split = (Split_Struct *)app->pBuffer; - //Per the note above, Im not exactly sure what to do on error - //to notify the client of the error... - if (!isgrouped) { - Message(13, "You can not split money if your not in a group."); - return; - } - Group *cgroup = GetGroup(); - if (cgroup == nullptr) { - //invalid group, not sure if we should say more... - Message(13, "You can not split money if your not in a group."); - return; - } - - if (!TakeMoneyFromPP(static_cast(split->copper) + - 10 * static_cast(split->silver) + - 100 * static_cast(split->gold) + - 1000 * static_cast(split->platinum))) { - Message(13, "You do not have enough money to do that split."); - return; - } - cgroup->SplitMoney(split->copper, split->silver, split->gold, split->platinum); - - return; - -} - -void Client::Handle_OP_Surname(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Surname_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in Surname expected %i got %i", sizeof(Surname_Struct), app->size); - return; - } - - if (!p_timers.Expired(&database, pTimerSurnameChange, false) && !GetGM()) - { - Message(15, "You may only change surnames once every 7 days, your /surname is currently on cooldown."); - return; - } - - if (GetLevel() < 20) - { - Message_StringID(15, SURNAME_LEVEL); - return; - } - - Surname_Struct* surname = (Surname_Struct*)app->pBuffer; - - char *c = nullptr; - bool first = true; - for (c = surname->lastname; *c; c++) - { - if (first) - { - *c = toupper(*c); - first = false; - } - else - { - *c = tolower(*c); - } - } - - if (strlen(surname->lastname) >= 20) { - Message_StringID(15, SURNAME_TOO_LONG); - return; - } - - if (!database.CheckNameFilter(surname->lastname, true)) - { - Message_StringID(15, SURNAME_REJECTED); - return; - } - - ChangeLastName(surname->lastname); - p_timers.Start(pTimerSurnameChange, 604800); - - EQApplicationPacket* outapp = app->Copy(); - outapp = app->Copy(); - surname = (Surname_Struct*)outapp->pBuffer; - surname->unknown0064 = 1; - FastQueuePacket(&outapp); - return; -} - -void Client::Handle_OP_SwapSpell(const EQApplicationPacket *app) -{ - - if (app->size != sizeof(SwapSpell_Struct)) { - std::cout << "Wrong size on OP_SwapSpell. Got: " << app->size << ", Expected: " << sizeof(SwapSpell_Struct) << std::endl; - return; - } - const SwapSpell_Struct* swapspell = (const SwapSpell_Struct*)app->pBuffer; - int swapspelltemp; - - if (swapspell->from_slot < 0 || swapspell->from_slot > MAX_PP_SPELLBOOK || swapspell->to_slot < 0 || swapspell->to_slot > MAX_PP_SPELLBOOK) - return; - - swapspelltemp = m_pp.spell_book[swapspell->from_slot]; - if (swapspelltemp < 0){ - return; - } - m_pp.spell_book[swapspell->from_slot] = m_pp.spell_book[swapspell->to_slot]; - m_pp.spell_book[swapspell->to_slot] = swapspelltemp; - - /* Save Spell Swaps */ - if (!database.SaveCharacterSpell(this->CharacterID(), m_pp.spell_book[swapspell->from_slot], swapspell->from_slot)){ - database.DeleteCharacterSpell(this->CharacterID(), m_pp.spell_book[swapspell->from_slot], swapspell->from_slot); - } - if (!database.SaveCharacterSpell(this->CharacterID(), swapspelltemp, swapspell->to_slot)){ - database.DeleteCharacterSpell(this->CharacterID(), swapspelltemp, swapspell->to_slot); - } - - QueuePacket(app); - return; -} - -void Client::Handle_OP_TargetCommand(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ClientTarget_Struct)) { - LogFile->write(EQEMuLog::Error, "OP size error: OP_TargetMouse expected:%i got:%i", sizeof(ClientTarget_Struct), app->size); - return; - } - - if (GetTarget()) - { - GetTarget()->IsTargeted(-1); - } - - // Locate and cache new target - ClientTarget_Struct* ct = (ClientTarget_Struct*)app->pBuffer; - pClientSideTarget = ct->new_target; - if (!IsAIControlled()) - { - Mob *nt = entity_list.GetMob(ct->new_target); - if (nt) - { - SetTarget(nt); - bool inspect_buffs = false; - // rank 1 gives you ability to see NPC buffs in target window (SoD+) - if (nt->IsNPC()) { - if (IsRaidGrouped()) { - Raid *raid = GetRaid(); - if (raid) { - uint32 gid = raid->GetGroup(this); - if (gid < 12 && raid->GroupCount(gid) > 2) - inspect_buffs = raid->GetLeadershipAA(groupAAInspectBuffs, gid); - } - } else { - Group *group = GetGroup(); - if (group && group->GroupCount() > 2) - inspect_buffs = group->GetLeadershipAA(groupAAInspectBuffs); - } - } - if (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); - } - else - { - SetTarget(nullptr); - SetHoTT(0); - UpdateXTargetType(TargetsTarget, nullptr); - - Group *g = GetGroup(); - - if (g && g->HasRole(this, RoleAssist)) - g->SetGroupAssistTarget(0); - - if (g && g->HasRole(this, RoleTank)) - g->SetGroupTankTarget(0); - - if (g && g->HasRole(this, RolePuller)) - g->SetGroupPullerTarget(0); - - return; - } - } - else - { - SetTarget(nullptr); - SetHoTT(0); - UpdateXTargetType(TargetsTarget, nullptr); - return; - } - - // HoTT - if (GetTarget() && GetTarget()->GetTarget()) - { - SetHoTT(GetTarget()->GetTarget()->GetID()); - UpdateXTargetType(TargetsTarget, GetTarget()->GetTarget()); - } - else - { - SetHoTT(0); - UpdateXTargetType(TargetsTarget, nullptr); - } - - Group *g = GetGroup(); - - if (g && g->HasRole(this, RoleAssist)) - g->SetGroupAssistTarget(GetTarget()); - - if (g && g->HasRole(this, RoleTank)) - g->SetGroupTankTarget(GetTarget()); - - if (g && g->HasRole(this, RolePuller)) - g->SetGroupPullerTarget(GetTarget()); - - // For /target, send reject or success packet - if (app->GetOpcode() == OP_TargetCommand) { - if (GetTarget() && !GetTarget()->CastToMob()->IsInvisible(this) && (DistNoRoot(*GetTarget()) <= TARGETING_RANGE*TARGETING_RANGE || GetGM())) { - if (GetTarget()->GetBodyType() == BT_NoTarget2 || GetTarget()->GetBodyType() == BT_Special - || GetTarget()->GetBodyType() == BT_NoTarget) - { - //Targeting something we shouldn't with /target - //but the client allows this without MQ so you don't flag it - EQApplicationPacket* outapp = new EQApplicationPacket(OP_TargetReject, sizeof(TargetReject_Struct)); - outapp->pBuffer[0] = 0x2f; - outapp->pBuffer[1] = 0x01; - outapp->pBuffer[4] = 0x0d; - if (GetTarget()) - { - SetTarget(nullptr); - } - QueuePacket(outapp); - safe_delete(outapp); - return; - } - - QueuePacket(app); - EQApplicationPacket hp_app; - GetTarget()->IsTargeted(1); - GetTarget()->CreateHPPacket(&hp_app); - QueuePacket(&hp_app, false); - } - else - { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_TargetReject, sizeof(TargetReject_Struct)); - outapp->pBuffer[0] = 0x2f; - outapp->pBuffer[1] = 0x01; - outapp->pBuffer[4] = 0x0d; - if (GetTarget()) - { - SetTarget(nullptr); - } - QueuePacket(outapp); - safe_delete(outapp); - } - } - else - { - if (GetTarget()) - { - if (GetGM()) - { - GetTarget()->IsTargeted(1); - return; - } - else if (IsAssistExempted()) - { - GetTarget()->IsTargeted(1); - SetAssistExemption(false); - return; - } - else if (GetTarget()->IsClient()) - { - //make sure this client is in our raid/group - GetTarget()->IsTargeted(1); - return; - } - else if (GetTarget()->GetBodyType() == BT_NoTarget2 || GetTarget()->GetBodyType() == BT_Special - || GetTarget()->GetBodyType() == BT_NoTarget) - { - char *hacker_str = nullptr; - MakeAnyLenString(&hacker_str, "%s attempting to target something untargetable, %s bodytype: %i\n", - GetName(), GetTarget()->GetName(), (int)GetTarget()->GetBodyType()); - database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); - safe_delete_array(hacker_str); - SetTarget((Mob*)nullptr); - return; - } - else if (IsPortExempted()) - { - GetTarget()->IsTargeted(1); - return; - } - else if (IsSenseExempted()) - { - GetTarget()->IsTargeted(1); - SetSenseExemption(false); - return; - } - else if (IsXTarget(GetTarget())) - { - GetTarget()->IsTargeted(1); - return; - } - else if (GetBindSightTarget()) - { - if (GetBindSightTarget()->DistNoRoot(*GetTarget()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip)) - { - if (DistNoRoot(*GetTarget()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip)) - { - char *hacker_str = nullptr; - MakeAnyLenString(&hacker_str, "%s attempting to target something beyond the clip plane of %.2f units," - " from (%.2f, %.2f, %.2f) to %s (%.2f, %.2f, %.2f)", GetName(), - (zone->newzone_data.maxclip*zone->newzone_data.maxclip), - GetX(), GetY(), GetZ(), GetTarget()->GetName(), GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ()); - database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); - safe_delete_array(hacker_str); - SetTarget(nullptr); - return; - } - } - } - else if (DistNoRoot(*GetTarget()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip)) - { - char *hacker_str = nullptr; - MakeAnyLenString(&hacker_str, "%s attempting to target something beyond the clip plane of %.2f units," - " from (%.2f, %.2f, %.2f) to %s (%.2f, %.2f, %.2f)", GetName(), - (zone->newzone_data.maxclip*zone->newzone_data.maxclip), - GetX(), GetY(), GetZ(), GetTarget()->GetName(), GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ()); - database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); - safe_delete_array(hacker_str); - SetTarget(nullptr); - return; - } - - GetTarget()->IsTargeted(1); - } - } - return; -} - -void Client::Handle_OP_TargetMouse(const EQApplicationPacket *app) -{ - Handle_OP_TargetCommand(app); -} - -void Client::Handle_OP_TaskHistoryRequest(const EQApplicationPacket *app) -{ - - if (app->size != sizeof(TaskHistoryRequest_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_TaskHistoryRequest expected %i got %i", - sizeof(TaskHistoryRequest_Struct), app->size); - DumpPacket(app); - return; - } - TaskHistoryRequest_Struct *ths = (TaskHistoryRequest_Struct*)app->pBuffer; - - if (RuleB(TaskSystem, EnableTaskSystem) && taskstate) - taskstate->SendTaskHistory(this, ths->TaskIndex); -} - -void Client::Handle_OP_Taunt(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ClientTarget_Struct)) { - std::cout << "Wrong size on OP_Taunt. Got: " << app->size << ", Expected: " << sizeof(ClientTarget_Struct) << std::endl; - return; - } - - if (!p_timers.Expired(&database, pTimerTaunt, false)) { - Message(13, "Ability recovery time not yet met."); - return; - } - p_timers.Start(pTimerTaunt, TauntReuseTime - 1); - - if (GetTarget() == nullptr || !GetTarget()->IsNPC()) - return; - - Taunt(GetTarget()->CastToNPC(), false); - return; -} - -void Client::Handle_OP_TestBuff(const EQApplicationPacket *app) -{ - return; -} - -void Client::Handle_OP_TGB(const EQApplicationPacket *app) -{ - OPTGB(app); - return; -} - -void Client::Handle_OP_Track(const EQApplicationPacket *app) -{ - if (GetClass() != RANGER && GetClass() != DRUID && GetClass() != BARD) - return; - - if (GetSkill(SkillTracking) == 0) - SetSkill(SkillTracking, 1); - else - CheckIncreaseSkill(SkillTracking, nullptr, 15); - - if (!entity_list.MakeTrackPacket(this)) - LogFile->write(EQEMuLog::Error, "Unable to generate OP_Track packet requested by client."); - - return; -} - -void Client::Handle_OP_TrackTarget(const EQApplicationPacket *app) -{ - int PlayerClass = GetClass(); - - if ((PlayerClass != RANGER) && (PlayerClass != DRUID) && (PlayerClass != BARD)) - return; - - if (app->size != sizeof(TrackTarget_Struct)) - { - LogFile->write(EQEMuLog::Error, "Invalid size for OP_TrackTarget: Expected: %i, Got: %i", - sizeof(TrackTarget_Struct), app->size); - return; - } - - TrackTarget_Struct *tts = (TrackTarget_Struct*)app->pBuffer; - - TrackingID = tts->EntityID; -} - -void Client::Handle_OP_TrackUnknown(const EQApplicationPacket *app) -{ - // size 0 send right after OP_Track - return; -} - -void Client::Handle_OP_TradeAcceptClick(const EQApplicationPacket *app) -{ - Mob* with = trade->With(); - trade->state = TradeAccepted; - - if (with && with->IsClient()) { - //finish trade... - // Have both accepted? - Client* other = with->CastToClient(); - other->QueuePacket(app); - - if (other->trade->state == trade->state) { - other->trade->state = TradeCompleting; - trade->state = TradeCompleting; - - // should we do this for NoDrop items as well? - if (CheckTradeLoreConflict(other) || other->CheckTradeLoreConflict(this)) { - Message_StringID(13, TRADE_CANCEL_LORE); - other->Message_StringID(13, TRADE_CANCEL_LORE); - this->FinishTrade(this); - other->FinishTrade(other); - other->trade->Reset(); - trade->Reset(); - } - else { - // Audit trade to database for both trade streams - other->trade->LogTrade(); - trade->LogTrade(); - - // start QS code - if (RuleB(QueryServ, PlayerLogTrades)) { - QSPlayerLogTrade_Struct event_entry; - std::list event_details; - - memset(&event_entry, 0, sizeof(QSPlayerLogTrade_Struct)); - - // Perform actual trade - this->FinishTrade(other, true, &event_entry, &event_details); - other->FinishTrade(this, false, &event_entry, &event_details); - - event_entry._detail_count = event_details.size(); - - ServerPacket* qs_pack = new ServerPacket(ServerOP_QSPlayerLogTrades, sizeof(QSPlayerLogTrade_Struct)+(sizeof(QSTradeItems_Struct)* event_entry._detail_count)); - QSPlayerLogTrade_Struct* qs_buf = (QSPlayerLogTrade_Struct*)qs_pack->pBuffer; - - memcpy(qs_buf, &event_entry, sizeof(QSPlayerLogTrade_Struct)); - - int offset = 0; - - for (std::list::iterator iter = event_details.begin(); iter != event_details.end(); ++iter, ++offset) { - QSTradeItems_Struct* detail = reinterpret_cast(*iter); - qs_buf->items[offset] = *detail; - safe_delete(detail); - } - - event_details.clear(); - - qs_pack->Deflate(); - - if (worldserver.Connected()) - worldserver.SendPacket(qs_pack); - - safe_delete(qs_pack); - // end QS code - } - else { - this->FinishTrade(other); - other->FinishTrade(this); - } - - other->trade->Reset(); - trade->Reset(); - } - // All done - EQApplicationPacket* outapp = new EQApplicationPacket(OP_FinishTrade, 0); - other->QueuePacket(outapp); - this->FastQueuePacket(&outapp); - } - } - // Trading with a Mob object that is not a Client. - else if (with) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_FinishTrade, 0); - QueuePacket(outapp); - safe_delete(outapp); - if (with->IsNPC()) { - // Audit trade to database for player trade stream - if (RuleB(QueryServ, PlayerLogHandins)) { - QSPlayerLogHandin_Struct event_entry; - std::list event_details; - - memset(&event_entry, 0, sizeof(QSPlayerLogHandin_Struct)); - - FinishTrade(with->CastToNPC(), false, &event_entry, &event_details); - - event_entry._detail_count = event_details.size(); - - ServerPacket* qs_pack = new ServerPacket(ServerOP_QSPlayerLogHandins, sizeof(QSPlayerLogHandin_Struct)+(sizeof(QSHandinItems_Struct)* event_entry._detail_count)); - QSPlayerLogHandin_Struct* qs_buf = (QSPlayerLogHandin_Struct*)qs_pack->pBuffer; - - memcpy(qs_buf, &event_entry, sizeof(QSPlayerLogHandin_Struct)); - - int offset = 0; - - for (std::list::iterator iter = event_details.begin(); iter != event_details.end(); ++iter, ++offset) { - QSHandinItems_Struct* detail = reinterpret_cast(*iter); - qs_buf->items[offset] = *detail; - safe_delete(detail); - } - - event_details.clear(); - - qs_pack->Deflate(); - - if (worldserver.Connected()) - worldserver.SendPacket(qs_pack); - - safe_delete(qs_pack); - } - else { - FinishTrade(with->CastToNPC()); - } - } -#ifdef BOTS - // TODO: Log Bot trades - else if (with->IsBot()) - with->CastToBot()->FinishTrade(this, Bot::BotTradeClientNormal); -#endif - trade->Reset(); - } - - - return; -} - -void Client::Handle_OP_TradeBusy(const EQApplicationPacket *app) -{ - if (app->size != sizeof(TradeBusy_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_TradeBusy, size=%i, expected %i", app->size, sizeof(TradeBusy_Struct)); - return; - } - // Trade request recipient is cancelling the trade due to being busy - // Trade requester gets message "I'm busy right now" - // Send busy message on to trade initiator if client - TradeBusy_Struct* msg = (TradeBusy_Struct*)app->pBuffer; - Mob* tradee = entity_list.GetMob(msg->to_mob_id); - - if (tradee && tradee->IsClient()) { - tradee->CastToClient()->QueuePacket(app); - } - return; -} - -void Client::Handle_OP_Trader(const EQApplicationPacket *app) -{ - // Bazaar Trader: - // - // SoF sends 1 or more unhandled OP_Trader packets of size 96 when a trade has completed. - // I don't know what they are for (yet), but it doesn't seem to matter that we ignore them. - - _pkt(TRADING__PACKETS, app); - - uint32 max_items = 80; - - /* - if (GetClientVersion() >= EQClientRoF) - max_items = 200; - */ - - //Show Items - if (app->size == sizeof(Trader_ShowItems_Struct)) - { - Trader_ShowItems_Struct* sis = (Trader_ShowItems_Struct*)app->pBuffer; - - switch (sis->Code) - { - case BazaarTrader_EndTraderMode: { - Trader_EndTrader(); - break; - } - case BazaarTrader_EndTransaction: { - - Client* c = entity_list.GetClientByID(sis->TraderID); - if (c) - c->WithCustomer(0); - else - _log(TRADING__CLIENT, "Client::Handle_OP_TraderBuy: Null Client Pointer"); - - break; - } - case BazaarTrader_ShowItems: { - Trader_ShowItems(); - break; - } - default: { - _log(TRADING__CLIENT, "Unhandled action code in OP_Trader ShowItems_Struct"); - break; - } - } - } - else if (app->size == sizeof(ClickTrader_Struct)) - { - if (Buyer) { - Trader_EndTrader(); - Message(13, "You cannot be a Trader and Buyer at the same time."); - return; - } - - ClickTrader_Struct* ints = (ClickTrader_Struct*)app->pBuffer; - - if (ints->Code == BazaarTrader_StartTraderMode) - { - GetItems_Struct* gis = GetTraderItems(); - - // Verify there are no NODROP or items with a zero price - bool TradeItemsValid = true; - - for (uint32 i = 0; i < max_items; i++) { - - if (gis->Items[i] == 0) break; - - if (ints->ItemCost[i] == 0) { - Message(13, "Item in Trader Satchel with no price. Unable to start trader mode"); - TradeItemsValid = false; - break; - } - const Item_Struct *Item = database.GetItem(gis->Items[i]); - - if (!Item) { - Message(13, "Unexpected error. Unable to start trader mode"); - TradeItemsValid = false; - break; - } - - if (Item->NoDrop == 0) { - Message(13, "NODROP Item in Trader Satchel. Unable to start trader mode"); - TradeItemsValid = false; - break; - } - } - - if (!TradeItemsValid) { - Trader_EndTrader(); - return; - } - - for (uint32 i = 0; i < max_items; i++) { - if (database.GetItem(gis->Items[i])) { - database.SaveTraderItem(this->CharacterID(), gis->Items[i], gis->SerialNumber[i], - gis->Charges[i], ints->ItemCost[i], i); - } - else { - //return; //sony doesnt memset so assume done on first bad item - break; - } - - } - safe_delete(gis); - - this->Trader_StartTrader(); - - if (GetClientVersion() >= EQClientRoF) - { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Trader, sizeof(TraderStatus_Struct)); - TraderStatus_Struct* tss = (TraderStatus_Struct*)outapp->pBuffer; - tss->Code = BazaarTrader_StartTraderMode2; - QueuePacket(outapp); - _pkt(TRADING__PACKETS, outapp); - safe_delete(outapp); - } - } - else if (app->size == sizeof(TraderStatus_Struct)) - { - TraderStatus_Struct* tss = (TraderStatus_Struct*)app->pBuffer; - - if (tss->Code == BazaarTrader_ShowItems) - { - Trader_ShowItems(); - } - } - else { - _log(TRADING__CLIENT, "Client::Handle_OP_Trader: Unknown TraderStruct code of: %i\n", - ints->Code); - - LogFile->write(EQEMuLog::Error, "Unknown TraderStruct code of: %i\n", ints->Code); - } - } - - else if (app->size == sizeof(TraderPriceUpdate_Struct)) - { - HandleTraderPriceUpdate(app); - } - else { - _log(TRADING__CLIENT, "Unknown size for OP_Trader: %i\n", app->size); - LogFile->write(EQEMuLog::Error, "Unknown size for OP_Trader: %i\n", app->size); - DumpPacket(app); - return; - } - - return; -} - -void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app) -{ - // Bazaar Trader: - // - // Client has elected to buy an item from a Trader - // - _pkt(TRADING__PACKETS, app); - - if (app->size == sizeof(TraderBuy_Struct)){ - - TraderBuy_Struct* tbs = (TraderBuy_Struct*)app->pBuffer; - - if (Client* Trader = entity_list.GetClientByID(tbs->TraderID)){ - - BuyTraderItem(tbs, Trader, app); - } - else { - _log(TRADING__CLIENT, "Client::Handle_OP_TraderBuy: Null Client Pointer"); - } - } - else { - _log(TRADING__CLIENT, "Client::Handle_OP_TraderBuy: Struct size mismatch"); - - } - return; -} - -void Client::Handle_OP_TradeRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(TradeRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_TradeRequest, size=%i, expected %i", app->size, sizeof(TradeRequest_Struct)); - return; - } - // Client requesting a trade session from an npc/client - // Trade session not started until OP_TradeRequestAck is sent - - BreakInvis(); - - // Pass trade request on to recipient - TradeRequest_Struct* msg = (TradeRequest_Struct*)app->pBuffer; - Mob* tradee = entity_list.GetMob(msg->to_mob_id); - - if (tradee && tradee->IsClient()) { - tradee->CastToClient()->QueuePacket(app); - } -#ifndef BOTS - else if (tradee && tradee->IsNPC()) { -#else - else if (tradee && (tradee->IsNPC() || tradee->IsBot())) { -#endif - //npcs always accept - trade->Start(msg->to_mob_id); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeRequestAck, sizeof(TradeRequest_Struct)); - TradeRequest_Struct* acc = (TradeRequest_Struct*)outapp->pBuffer; - acc->from_mob_id = msg->to_mob_id; - acc->to_mob_id = msg->from_mob_id; - FastQueuePacket(&outapp); - safe_delete(outapp); - } - return; -} - -void Client::Handle_OP_TradeRequestAck(const EQApplicationPacket *app) -{ - if (app->size != sizeof(TradeRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_TradeRequestAck, size=%i, expected %i", app->size, sizeof(TradeRequest_Struct)); - return; - } - // Trade request recipient is acknowledging they are able to trade - // After this, the trade session has officially started - // Send ack on to trade initiator if client - TradeRequest_Struct* msg = (TradeRequest_Struct*)app->pBuffer; - Mob* tradee = entity_list.GetMob(msg->to_mob_id); - - if (tradee && tradee->IsClient()) { - trade->Start(msg->to_mob_id); - tradee->CastToClient()->QueuePacket(app); - } - return; -} - -void Client::Handle_OP_TraderShop(const EQApplicationPacket *app) -{ - // Bazaar Trader: - // - // This is when a potential purchaser right clicks on this client who is in Trader mode to - // browse their goods. - // - _pkt(TRADING__PACKETS, app); - - TraderClick_Struct* tcs = (TraderClick_Struct*)app->pBuffer; - - if (app->size != sizeof(TraderClick_Struct)) { - - _log(TRADING__CLIENT, "Client::Handle_OP_TraderShop: Returning due to struct size mismatch"); - - return; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderShop, sizeof(TraderClick_Struct)); - - TraderClick_Struct* outtcs = (TraderClick_Struct*)outapp->pBuffer; - - Client* Customer = entity_list.GetClientByID(tcs->TraderID); - - if (Customer) - outtcs->Approval = Customer->WithCustomer(GetID()); - else { - _log(TRADING__CLIENT, "Client::Handle_OP_TraderShop: entity_list.GetClientByID(tcs->traderid)" - " returned a nullptr pointer"); - return; - } - - outtcs->TraderID = tcs->TraderID; - - outtcs->Unknown008 = 0x3f800000; - - QueuePacket(outapp); - - _pkt(TRADING__PACKETS, outapp); - - if (outtcs->Approval) { - this->BulkSendTraderInventory(Customer->CharacterID()); - Customer->Trader_CustomerBrowsing(this); - } - else - Message_StringID(clientMessageYellow, TRADER_BUSY); - - safe_delete(outapp); - - return; -} - -void Client::Handle_OP_TradeSkillCombine(const EQApplicationPacket *app) -{ - if (app->size != sizeof(NewCombine_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for NewCombine_Struct: Expected: %i, Got: %i", - sizeof(NewCombine_Struct), app->size); - return; - } - /*if (m_tradeskill_object == nullptr) { - Message(13, "Error: Server is not aware of the tradeskill container you are attempting to use"); - return; - }*/ - - //fixed this to work for non-world objects - - // Delegate to tradeskill object to perform combine - NewCombine_Struct* in_combine = (NewCombine_Struct*)app->pBuffer; - Object::HandleCombine(this, in_combine, m_tradeskill_object); - return; -} - -void Client::Handle_OP_Translocate(const EQApplicationPacket *app) -{ - - if (app->size != sizeof(Translocate_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Translocate expected %i got %i", sizeof(Translocate_Struct), app->size); - DumpPacket(app); - return; - } - Translocate_Struct *its = (Translocate_Struct*)app->pBuffer; - - if (!PendingTranslocate) - return; - - if ((RuleI(Spells, TranslocateTimeLimit) > 0) && (time(nullptr) > (TranslocateTime + RuleI(Spells, TranslocateTimeLimit)))) { - Message(13, "You did not accept the Translocate within the required time limit."); - PendingTranslocate = false; - return; - } - - if (its->Complete == 1) { - - int SpellID = PendingTranslocateData.spell_id; - int i = parse->EventSpell(EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE, nullptr, this, SpellID, 0); - - if (i == 0) - { - // If the spell has a translocate to bind effect, AND we are already in the zone the client - // is bound in, use the GoToBind method. If we send OP_Translocate in this case, the client moves itself - // to the bind coords it has from the PlayerProfile, but with the X and Y reversed. I suspect they are - // reversed in the pp, and since spells like Gate are handled serverside, this has not mattered before. - if (((SpellID == 1422) || (SpellID == 1334) || (SpellID == 3243)) && - (zone->GetZoneID() == PendingTranslocateData.zone_id && - zone->GetInstanceID() == PendingTranslocateData.instance_id)) - { - PendingTranslocate = false; - GoToBind(); - return; - } - - ////Was sending the packet back to initiate client zone... - ////but that could be abusable, so lets go through proper channels - MovePC(PendingTranslocateData.zone_id, PendingTranslocateData.instance_id, - PendingTranslocateData.x, PendingTranslocateData.y, - PendingTranslocateData.z, PendingTranslocateData.heading, 0, ZoneSolicited); - } - } - - PendingTranslocate = false; -} - -void Client::Handle_OP_TributeItem(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_TributeItem of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - //player donates an item... - if (app->size != sizeof(TributeItem_Struct)) - printf("Error in OP_TributeItem. Expected size of: %zu, but got: %i\n", sizeof(StartTribute_Struct), app->size); - else { - TributeItem_Struct* t = (TributeItem_Struct*)app->pBuffer; - - tribute_master_id = t->tribute_master_id; - //make sure they are dealing with a valid tribute master - Mob* tribmast = entity_list.GetMob(t->tribute_master_id); - if (!tribmast || !tribmast->IsNPC() || tribmast->GetClass() != TRIBUTE_MASTER) - return; - if (DistNoRoot(*tribmast) > USE_NPC_RANGE2) - return; - - t->tribute_points = TributeItem(t->slot, t->quantity); - - _log(TRIBUTE__OUT, "Sending tribute item reply with %d points", t->tribute_points); - _pkt(TRIBUTE__OUT, app); - - QueuePacket(app); - } - return; -} - -void Client::Handle_OP_TributeMoney(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_TributeMoney of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - //player donates money - if (app->size != sizeof(TributeMoney_Struct)) - printf("Error in OP_TributeMoney. Expected size of: %zu, but got: %i\n", sizeof(StartTribute_Struct), app->size); - else { - TributeMoney_Struct* t = (TributeMoney_Struct*)app->pBuffer; - - tribute_master_id = t->tribute_master_id; - //make sure they are dealing with a valid tribute master - Mob* tribmast = entity_list.GetMob(t->tribute_master_id); - if (!tribmast || !tribmast->IsNPC() || tribmast->GetClass() != TRIBUTE_MASTER) - return; - if (DistNoRoot(*tribmast) > USE_NPC_RANGE2) - return; - - t->tribute_points = TributeMoney(t->platinum); - - _log(TRIBUTE__OUT, "Sending tribute money reply with %d points", t->tribute_points); - _pkt(TRIBUTE__OUT, app); - - QueuePacket(app); - } - return; -} - -void Client::Handle_OP_TributeNPC(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_TributeNPC of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - return; -} - -void Client::Handle_OP_TributeToggle(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_TributeToggle of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - if (app->size != sizeof(uint32)) - LogFile->write(EQEMuLog::Error, "Invalid size on OP_TributeToggle packet"); - else { - uint32 *val = (uint32 *)app->pBuffer; - ToggleTribute(*val ? true : false); - } - return; -} - -void Client::Handle_OP_TributeUpdate(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_TributeUpdate of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - //sent when the client changes their tribute settings... - if (app->size != sizeof(TributeInfo_Struct)) - LogFile->write(EQEMuLog::Error, "Invalid size on OP_TributeUpdate packet"); - else { - TributeInfo_Struct *t = (TributeInfo_Struct *)app->pBuffer; - ChangeTributeSettings(t); - } - return; -} - -void Client::Handle_OP_VetClaimRequest(const EQApplicationPacket *app) -{ - if (app->size < sizeof(VeteranClaimRequest)) - { - LogFile->write(EQEMuLog::Debug, "OP_VetClaimRequest size lower than expected: got %u expected at least %u", - app->size, sizeof(VeteranClaimRequest)); - DumpPacket(app); - return; - } - - VeteranClaimRequest *vcr = (VeteranClaimRequest*)app->pBuffer; - - if (vcr->claim_id == 0xFFFFFFFF) //request update packet - { - SendRewards(); - } - else //try to claim something! - { - if (!TryReward(vcr->claim_id)) - { - Message(13, "Your claim has been rejected."); - EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaimReply)); - VeteranClaimReply * cr = (VeteranClaimReply*)vetapp->pBuffer; - strcpy(cr->name, GetName()); - cr->claim_id = vcr->claim_id; - cr->reject_field = -1; - FastQueuePacket(&vetapp); - } - else - { - EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaimReply)); - VeteranClaimReply * cr = (VeteranClaimReply*)vetapp->pBuffer; - strcpy(cr->name, GetName()); - cr->claim_id = vcr->claim_id; - cr->reject_field = 0; - FastQueuePacket(&vetapp); - } - } -} - -void Client::Handle_OP_VoiceMacroIn(const EQApplicationPacket *app) -{ - - if (app->size != sizeof(VoiceMacroIn_Struct)) { - - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_VoiceMacroIn expected %i got %i", - sizeof(VoiceMacroIn_Struct), app->size); - - DumpPacket(app); - - return; - } - - if (!RuleB(Chat, EnableVoiceMacros)) return; - - VoiceMacroIn_Struct* vmi = (VoiceMacroIn_Struct*)app->pBuffer; - - VoiceMacroReceived(vmi->Type, vmi->Target, vmi->MacroNumber); - -} - -void Client::Handle_OP_WearChange(const EQApplicationPacket *app) -{ - if (app->size != sizeof(WearChange_Struct)) { - std::cout << "Wrong size: OP_WearChange, size=" << app->size << ", expected " << sizeof(WearChange_Struct) << std::endl; - return; - } - - WearChange_Struct* wc = (WearChange_Struct*)app->pBuffer; - if (wc->spawn_id != GetID()) - return; - - // we could maybe ignore this and just send our own from moveitem - entity_list.QueueClients(this, app, true); - return; -} - -void Client::Handle_OP_WhoAllRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Who_All_Struct)) { - std::cout << "Wrong size on OP_WhoAll. Got: " << app->size << ", Expected: " << sizeof(Who_All_Struct) << std::endl; - return; - } - Who_All_Struct* whoall = (Who_All_Struct*)app->pBuffer; - - if (whoall->type == 0) // SoF only, for regular /who - entity_list.ZoneWho(this, whoall); - else - WhoAll(whoall); - return; -} - -void Client::Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app) -{ - if (app->size != 1) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_XTargetAutoAddHaters, expected 1, got %i", app->size); - DumpPacket(app); - return; - } - - XTargetAutoAddHaters = app->ReadUInt8(0); -} - -void Client::Handle_OP_XTargetRequest(const EQApplicationPacket *app) -{ - if (app->size < 12) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_XTargetRequest, expected at least 12, got %i", app->size); - DumpPacket(app); - return; - } - - uint32 Unknown000 = app->ReadUInt32(0); - - if (Unknown000 != 1) - return; - - uint32 Slot = app->ReadUInt32(4); - - if (Slot >= XTARGET_HARDCAP) - return; - - XTargetType Type = (XTargetType)app->ReadUInt32(8); - - XTargets[Slot].Type = Type; - XTargets[Slot].ID = 0; - XTargets[Slot].Name[0] = 0; - - switch (Type) - { - case Empty: - case Auto: - { - break; - } - - case CurrentTargetPC: - { - char Name[65]; - - app->ReadString(Name, 12, 64); - Client *c = entity_list.GetClientByName(Name); - if (c) - { - XTargets[Slot].ID = c->GetID(); - strncpy(XTargets[Slot].Name, c->GetName(), 64); - } - else - { - strncpy(XTargets[Slot].Name, Name, 64); - } - SendXTargetPacket(Slot, c); - - break; - } - - case CurrentTargetNPC: - { - char Name[65]; - app->ReadString(Name, 12, 64); - Mob *m = entity_list.GetMob(Name); - if (m) - { - XTargets[Slot].ID = m->GetID(); - SendXTargetPacket(Slot, m); - break; - } - } - - case TargetsTarget: - { - if (GetTarget()) - UpdateXTargetType(TargetsTarget, GetTarget()->GetTarget()); - else - UpdateXTargetType(TargetsTarget, nullptr); - - break; - } - - case GroupTank: - { - Group *g = GetGroup(); - - if (g) - { - Client *c = entity_list.GetClientByName(g->GetMainTankName()); - - if (c) - { - XTargets[Slot].ID = c->GetID(); - strncpy(XTargets[Slot].Name, c->GetName(), 64); - } - else - { - strncpy(XTargets[Slot].Name, g->GetMainTankName(), 64); - } - SendXTargetPacket(Slot, c); - } - break; - } - case GroupTankTarget: - { - Group *g = GetGroup(); - - if (g) - g->NotifyTankTarget(this); - - break; - } - - case GroupAssist: - { - Group *g = GetGroup(); - - if (g) - { - Client *c = entity_list.GetClientByName(g->GetMainAssistName()); - - if (c) - { - XTargets[Slot].ID = c->GetID(); - strncpy(XTargets[Slot].Name, c->GetName(), 64); - } - else - { - strncpy(XTargets[Slot].Name, g->GetMainAssistName(), 64); - } - SendXTargetPacket(Slot, c); - } - break; - } - - case GroupAssistTarget: - { - - Group *g = GetGroup(); - - if (g) - g->NotifyAssistTarget(this); - - break; - } - - case Puller: - { - Group *g = GetGroup(); - - if (g) - { - Client *c = entity_list.GetClientByName(g->GetPullerName()); - - if (c) - { - XTargets[Slot].ID = c->GetID(); - strncpy(XTargets[Slot].Name, c->GetName(), 64); - } - else - { - strncpy(XTargets[Slot].Name, g->GetPullerName(), 64); - } - SendXTargetPacket(Slot, c); - } - break; - } - - case PullerTarget: - { - - Group *g = GetGroup(); - - if (g) - g->NotifyPullerTarget(this); - - break; - } - - case GroupMarkTarget1: - case GroupMarkTarget2: - case GroupMarkTarget3: - { - Group *g = GetGroup(); - - if (g) - g->SendMarkedNPCsToMember(this); - - break; - } - - case RaidAssist1: - case RaidAssist2: - case RaidAssist3: - case RaidAssist1Target: - case RaidAssist2Target: - case RaidAssist3Target: - case RaidMarkTarget1: - case RaidMarkTarget2: - case RaidMarkTarget3: - { - // Not implemented yet. - break; - } - - case MyPet: - { - Mob *m = GetPet(); - if (m) - { - XTargets[Slot].ID = m->GetID(); - SendXTargetPacket(Slot, m); - - } - break; - } - case MyPetTarget: - { - Mob *m = GetPet(); - - if (m) - m = m->GetTarget(); - - if (m) - { - XTargets[Slot].ID = m->GetID(); - SendXTargetPacket(Slot, m); - - } - break; - } - - default: - LogFile->write(EQEMuLog::Debug, "Unhandled XTarget Type %i", Type); - break; - } - -} - -void Client::Handle_OP_YellForHelp(const EQApplicationPacket *app) -{ - EQApplicationPacket *outapp = new EQApplicationPacket(OP_YellForHelp, 4); - *(uint32 *)outapp->pBuffer = GetID(); - entity_list.QueueCloseClients(this, outapp, true, 100.0); - safe_delete(outapp); - return; -} - -/* -void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) -{ - return; -} -*/ diff --git a/zone/client_process.cpp.orig b/zone/client_process.cpp.orig deleted file mode 100644 index 3be80dc59..000000000 --- a/zone/client_process.cpp.orig +++ /dev/null @@ -1,2427 +0,0 @@ -/* 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 - - client_process.cpp: - Handles client login sequence and packets sent from client to zone -*/ -#include "../common/debug.h" -#include -#include -#include - -#ifdef _WINDOWS - #include - #include - #define snprintf _snprintf - #define strncasecmp _strnicmp - #define strcasecmp _stricmp -#else - #include - #include - #include - #include -#endif - -#include "../common/rulesys.h" -#include "../common/skills.h" -#include "../common/spdat.h" -#include "../common/string_util.h" -#include "event_codes.h" -#include "guild_mgr.h" -#include "map.h" -#include "petitions.h" -#include "queryserv.h" -#include "quest_parser_collection.h" -#include "string_ids.h" -#include "worldserver.h" -#include "zone.h" -#include "zonedb.h" - -extern QueryServ* QServ; -extern Zone* zone; -extern volatile bool ZoneLoaded; -extern WorldServer worldserver; -extern PetitionList petition_list; -extern EntityList entity_list; - -bool Client::Process() { - bool ret = true; - - if(Connected() || IsLD()) - { - // try to send all packets that weren't sent before - if(!IsLD() && zoneinpacket_timer.Check()) - { - SendAllPackets(); - } - - if(adventure_request_timer) - { - if(adventure_request_timer->Check()) - { - safe_delete(adventure_request_timer); - } - } - - if(adventure_create_timer) - { - if(adventure_create_timer->Check()) - { - safe_delete(adventure_create_timer); - } - } - - if(adventure_leave_timer) - { - if(adventure_leave_timer->Check()) - { - safe_delete(adventure_leave_timer); - } - } - - if(adventure_door_timer) - { - if(adventure_door_timer->Check()) - { - safe_delete(adventure_door_timer); - } - } - - if(adventure_stats_timer) - { - if(adventure_stats_timer->Check()) - { - safe_delete(adventure_stats_timer); - } - } - - if(adventure_leaderboard_timer) - { - if(adventure_leaderboard_timer->Check()) - { - safe_delete(adventure_leaderboard_timer); - } - } - - if(dead) - { - SetHP(-100); - if(RespawnFromHoverTimer.Check()) - HandleRespawnFromHover(0); - } - - if(IsTracking() && (GetClientVersion() >= EQClientSoD) && TrackingTimer.Check()) - DoTracking(); - - if(hpupdate_timer.Check()) - SendHPUpdate(); - - if(mana_timer.Check()) - SendManaUpdatePacket(); - - if(dead && dead_timer.Check()) { - database.MoveCharacterToZone(GetName(), database.GetZoneName(m_pp.binds[0].zoneId)); - - m_pp.zone_id = m_pp.binds[0].zoneId; - m_pp.zoneInstance = m_pp.binds[0].instance_id; - m_pp.x = m_pp.binds[0].x; - m_pp.y = m_pp.binds[0].y; - m_pp.z = m_pp.binds[0].z; - Save(); - - Group *mygroup = GetGroup(); - if (mygroup) - { - entity_list.MessageGroup(this,true,15,"%s died.", GetName()); - mygroup->MemberZoned(this); - } - Raid *myraid = entity_list.GetRaidByClient(this); - if (myraid) - { - myraid->MemberZoned(this); - } - return(false); - } - - if(charm_update_timer.Check()) - { - CalcItemScale(); - } - - if(TaskPeriodic_Timer.Check() && taskstate) - taskstate->TaskPeriodicChecks(this); - - if(linkdead_timer.Check()) - { - LeaveGroup(); - Save(); - if (GetMerc()) - { - GetMerc()->Save(); - GetMerc()->Depop(); - } - - Raid *myraid = entity_list.GetRaidByClient(this); - if (myraid) - { - myraid->MemberZoned(this); - } - return false; //delete client - } - - if (camp_timer.Check()) - { - LeaveGroup(); - Save(); - if (GetMerc()) - { - GetMerc()->Save(); - GetMerc()->Depop(); - } - instalog = true; - } - - if (IsStunned() && stunned_timer.Check()) { - this->stunned = false; - this->stunned_timer.Disable(); - } - - if(!m_CheatDetectMoved) - { - m_TimeSinceLastPositionCheck = Timer::GetCurrentTime(); - } - - if (bardsong_timer.Check() && bardsong != 0) { - //NOTE: this is kinda a heavy-handed check to make sure the mob still exists before - //doing the next pulse on them... - Mob *song_target; - if(bardsong_target_id == GetID()) { - song_target = this; - } else { - song_target = entity_list.GetMob(bardsong_target_id); - } - - if (song_target == nullptr) { - InterruptSpell(SONG_ENDS_ABRUPTLY, 0x121, bardsong); - } else { - if(!ApplyNextBardPulse(bardsong, song_target, bardsong_slot)) - InterruptSpell(SONG_ENDS_ABRUPTLY, 0x121, bardsong); - //SpellFinished(bardsong, bardsong_target, bardsong_slot, spells[bardsong].mana); - } - } - - if(GetMerc()) - { - UpdateMercTimer(); - } - - if(GetMercInfo().MercTemplateID != 0 && GetMercInfo().IsSuspended) - { - if(p_timers.Expired(&database, pTimerMercSuspend, false)) - { - CheckMercSuspendTimer(); - } - } - - if(IsAIControlled()) - AI_Process(); - - if (bindwound_timer.Check() && bindwound_target != 0) { - BindWound(bindwound_target, false); - } - - if(KarmaUpdateTimer) - { - if(KarmaUpdateTimer->Check(false)) - { - KarmaUpdateTimer->Start(RuleI(Chat, KarmaUpdateIntervalMS)); - database.UpdateKarma(AccountID(), ++TotalKarma); - } - } - - if(qGlobals) - { - if(qglobal_purge_timer.Check()) - { - qGlobals->PurgeExpiredGlobals(); - } - } - - bool may_use_attacks = false; - /* - Things which prevent us from attacking: - - being under AI control, the AI does attacks - - being dead - - casting a spell and bard check - - not having a target - - being stunned or mezzed - - having used a ranged weapon recently - */ - if(auto_attack) { - if(!IsAIControlled() && !dead - && !(spellend_timer.Enabled() && casting_spell_id && !IsBardSong(casting_spell_id)) - && !IsStunned() && !IsFeared() && !IsMezzed() && GetAppearance() != eaDead && !IsMeleeDisabled() - ) - may_use_attacks = true; - - if(may_use_attacks && ranged_timer.Enabled()) { - //if the range timer is enabled, we need to consider it - if(!ranged_timer.Check(false)) { - //the ranged timer has not elapsed, cannot attack. - may_use_attacks = false; - } - } - } - - if(AutoFireEnabled()){ - ItemInst *ranged = GetInv().GetItem(MainRange); - if(ranged) - { - if(ranged->GetItem() && ranged->GetItem()->ItemType == ItemTypeBow){ - if(ranged_timer.Check(false)){ - if(GetTarget() && (GetTarget()->IsNPC() || GetTarget()->IsClient())){ - if(GetTarget()->InFrontMob(this, GetTarget()->GetX(), GetTarget()->GetY())){ - if(CheckLosFN(GetTarget())){ - //client has built in los check, but auto fire does not.. done last. - RangedAttack(GetTarget()); - if (CheckDoubleRangedAttack()) - RangedAttack(GetTarget(), true); - } - else - ranged_timer.Start(); - } - else - ranged_timer.Start(); - } - else - ranged_timer.Start(); - } - } - else if(ranged->GetItem() && (ranged->GetItem()->ItemType == ItemTypeLargeThrowing || ranged->GetItem()->ItemType == ItemTypeSmallThrowing)){ - if(ranged_timer.Check(false)){ - if(GetTarget() && (GetTarget()->IsNPC() || GetTarget()->IsClient())){ - if(GetTarget()->InFrontMob(this, GetTarget()->GetX(), GetTarget()->GetY())){ - if(CheckLosFN(GetTarget())){ - //client has built in los check, but auto fire does not.. done last. - ThrowingAttack(GetTarget()); - } - else - ranged_timer.Start(); - } - else - ranged_timer.Start(); - } - else - ranged_timer.Start(); - } - } - } - } - - Mob *auto_attack_target = GetTarget(); - if (auto_attack && auto_attack_target != nullptr && may_use_attacks && attack_timer.Check()) - { - //check if change - //only check on primary attack.. sorry offhand you gotta wait! - if(aa_los_them_mob) - { - if(auto_attack_target != aa_los_them_mob || - m_AutoAttackPosition.m_X != GetX() || - m_AutoAttackPosition.m_Y != GetY() || - m_AutoAttackPosition.m_Z != GetZ() || - m_AutoAttackTargetLocation.m_X != aa_los_them_mob->GetX() || - m_AutoAttackTargetLocation.m_Y != aa_los_them_mob->GetY() || - m_AutoAttackTargetLocation.m_Z != aa_los_them_mob->GetZ()) - { - aa_los_them_mob = auto_attack_target; - m_AutoAttackPosition = GetPosition(); - m_AutoAttackTargetLocation = aa_los_them_mob->GetPosition(); - los_status = CheckLosFN(auto_attack_target); - los_status_facing = IsFacingMob(aa_los_them_mob); - } - // If only our heading changes, we can skip the CheckLosFN call - // but above we still need to update los_status_facing - if (m_AutoAttackPosition.m_Heading != GetHeading()) { - m_AutoAttackPosition.m_Heading = GetHeading(); - los_status_facing = IsFacingMob(aa_los_them_mob); - } - } - else - { - aa_los_them_mob = auto_attack_target; - m_AutoAttackPosition = GetPosition(); - m_AutoAttackTargetLocation = aa_los_them_mob->GetPosition(); - los_status = CheckLosFN(auto_attack_target); - los_status_facing = IsFacingMob(aa_los_them_mob); - } - - if (!CombatRange(auto_attack_target)) - { - Message_StringID(MT_TooFarAway,TARGET_TOO_FAR); - } - else if (auto_attack_target == this) - { - Message_StringID(MT_TooFarAway,TRY_ATTACKING_SOMEONE); - } - else if (!los_status || !los_status_facing) - { - //you can't see your target - } - else if (auto_attack_target->GetHP() > -10) // -10 so we can watch people bleed in PvP - { - if(CheckAAEffect(aaEffectRampage)) - { - entity_list.AEAttack(this, 30); - } else { - Attack(auto_attack_target, MainPrimary); // Kaiyodo - added attacking hand to arguments - } - ItemInst *wpn = GetInv().GetItem(MainPrimary); - TryWeaponProc(wpn, auto_attack_target, MainPrimary); - - bool tripleAttackSuccess = false; - if( auto_attack_target && CanThisClassDoubleAttack() ) { - - CheckIncreaseSkill(SkillDoubleAttack, auto_attack_target, -10); - if(CheckDoubleAttack()) { - //should we allow rampage on double attack? - if(CheckAAEffect(aaEffectRampage)) { - entity_list.AEAttack(this, 30); - } else { - Attack(auto_attack_target, MainPrimary, false); - } - } - - //triple attack: rangers, monks, warriors, berserkers over level 60 - if((((GetClass() == MONK || GetClass() == WARRIOR || GetClass() == RANGER || GetClass() == BERSERKER) - && GetLevel() >= 60) || GetSpecialAbility(SPECATK_TRIPLE)) - && CheckDoubleAttack(true)) - { - tripleAttackSuccess = true; - Attack(auto_attack_target, MainPrimary, false); - } - - //quad attack, does this belong here?? - if(GetSpecialAbility(SPECATK_QUAD) && CheckDoubleAttack(true)) - { - Attack(auto_attack_target, MainPrimary, false); - } - } - - //Live AA - Flurry, Rapid Strikes ect (Flurry does not require Triple Attack). - int16 flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance; - - if (auto_attack_target && flurrychance) - { - if(zone->random.Int(0, 99) < flurrychance) - { - Message_StringID(MT_NPCFlurry, YOU_FLURRY); - Attack(auto_attack_target, MainPrimary, false); - Attack(auto_attack_target, MainPrimary, false); - } - } - - int16 ExtraAttackChanceBonus = spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance + aabonuses.ExtraAttackChance; - - if (auto_attack_target && ExtraAttackChanceBonus) { - ItemInst *wpn = GetInv().GetItem(MainPrimary); - if(wpn){ - if(wpn->GetItem()->ItemType == ItemType2HSlash || - wpn->GetItem()->ItemType == ItemType2HBlunt || - wpn->GetItem()->ItemType == ItemType2HPiercing ) - { - if(zone->random.Int(0, 99) < ExtraAttackChanceBonus) - { - Attack(auto_attack_target, MainPrimary, false); - } - } - } - } - } - } - - if (GetClass() == WARRIOR || GetClass() == BERSERKER) { - if (!dead && !IsBerserk() && GetHPRatio() < RuleI(Combat, BerserkerFrenzyStart)) { - entity_list.MessageClose_StringID(this, false, 200, 0, BERSERK_START, GetName()); - berserk = true; - } - if (IsBerserk() && GetHPRatio() > RuleI(Combat, BerserkerFrenzyEnd)) { - entity_list.MessageClose_StringID(this, false, 200, 0, BERSERK_END, GetName()); - berserk = false; - } - } - - if(auto_attack && may_use_attacks && auto_attack_target != nullptr - && CanThisClassDualWield() && attack_dw_timer.Check()) - { - // Range check - if(!CombatRange(auto_attack_target)) { - // this is a duplicate message don't use it. - //Message_StringID(MT_TooFarAway,TARGET_TOO_FAR); - } - // Don't attack yourself - else if(auto_attack_target == this) { - //Message_StringID(MT_TooFarAway,TRY_ATTACKING_SOMEONE); - } - else if (!los_status || !los_status_facing) - { - //you can't see your target - } - else if(auto_attack_target->GetHP() > -10) { - float DualWieldProbability = 0.0f; - - int16 Ambidexterity = aabonuses.Ambidexterity + spellbonuses.Ambidexterity + itembonuses.Ambidexterity; - DualWieldProbability = (GetSkill(SkillDualWield) + GetLevel() + Ambidexterity) / 400.0f; // 78.0 max - int16 DWBonus = spellbonuses.DualWieldChance + itembonuses.DualWieldChance; - DualWieldProbability += DualWieldProbability*float(DWBonus)/ 100.0f; - - float random = zone->random.Real(0, 1); - CheckIncreaseSkill(SkillDualWield, auto_attack_target, -10); - if (random < DualWieldProbability){ // Max 78% of DW - if(CheckAAEffect(aaEffectRampage)) { - entity_list.AEAttack(this, 30, MainSecondary); - } else { - Attack(auto_attack_target, MainSecondary); // Single attack with offhand - } - ItemInst *wpn = GetInv().GetItem(MainSecondary); - TryWeaponProc(wpn, auto_attack_target, MainSecondary); - - if( CanThisClassDoubleAttack() && CheckDoubleAttack()) { - if(CheckAAEffect(aaEffectRampage)) { - entity_list.AEAttack(this, 30, MainSecondary); - } else { - if(auto_attack_target && auto_attack_target->GetHP() > -10) - Attack(auto_attack_target, MainSecondary); // Single attack with offhand - } - } - } - } - } - - if (position_timer.Check()) { - if (IsAIControlled()) - { - if(IsMoving()) - SendPosUpdate(2); - else - { - animation = 0; - m_Delta = xyz_heading(0.0f, 0.0f, 0.0f, m_Delta.m_Heading); - SendPosUpdate(2); - } - } - - // Send a position packet every 8 seconds - if not done, other clients - // see this char disappear after 10-12 seconds of inactivity - if (position_timer_counter >= 36) { // Approx. 4 ticks per second - entity_list.SendPositionUpdates(this, pLastUpdateWZ, 500, GetTarget(), true); - pLastUpdate = Timer::GetCurrentTime(); - pLastUpdateWZ = pLastUpdate; - position_timer_counter = 0; - } - else { - pLastUpdate = Timer::GetCurrentTime(); - position_timer_counter++; - } - } - - if(HasVirus()) { - if(viral_timer.Check()) { - viral_timer_counter++; - for(int i = 0; i < MAX_SPELL_TRIGGER*2; i+=2) { - if(viral_spells[i]) { - if(viral_timer_counter % spells[viral_spells[i]].viral_timer == 0) { - SpreadVirus(viral_spells[i], viral_spells[i+1]); - } - } - } - } - if(viral_timer_counter > 999) - viral_timer_counter = 0; - } - -<<<<<<< HEAD - if(projectile_timer.Check()) - SpellProjectileEffect(); - -======= - ProjectileAttack(); - ->>>>>>> master - if(spellbonuses.GravityEffect == 1) { - if(gravity_timer.Check()) - DoGravityEffect(); - } - - if (shield_timer.Check()) - { - if (shield_target) - { - if (!CombatRange(shield_target)) - { - entity_list.MessageClose_StringID(this, false, 100, 0, - END_SHIELDING, GetCleanName(), shield_target->GetCleanName()); - for (int y = 0; y < 2; y++) - { - if (shield_target->shielder[y].shielder_id == GetID()) - { - shield_target->shielder[y].shielder_id = 0; - shield_target->shielder[y].shielder_bonus = 0; - } - } - shield_target = 0; - shield_timer.Disable(); - } - } - else - { - shield_target = 0; - shield_timer.Disable(); - } - } - - SpellProcess(); - if (endupkeep_timer.Check() && !dead){ - DoEnduranceUpkeep(); - } - - if (tic_timer.Check() && !dead) { - CalcMaxHP(); - CalcMaxMana(); - CalcATK(); - CalcMaxEndurance(); - CalcRestState(); - DoHPRegen(); - DoManaRegen(); - DoEnduranceRegen(); - BuffProcess(); - DoStaminaUpdate(); - - if(tribute_timer.Check()) { - ToggleTribute(true); //re-activate the tribute. - } - - if (fishing_timer.Check()) { - GoFish(); - } - - if (autosave_timer.Check()) { - Save(0); - } - - if(m_pp.intoxication > 0) - { - --m_pp.intoxication; - CalcBonuses(); - } - - if(ItemTickTimer.Check()) - { - TickItemCheck(); - } - - if(ItemQuestTimer.Check()) - { - ItemTimerCheck(); - } - } - } - - if (client_state == CLIENT_KICKED) { - Save(); - OnDisconnect(true); - std::cout << "Client disconnected (cs=k): " << GetName() << std::endl; - return false; - } - - if (client_state == DISCONNECTED) { - OnDisconnect(true); - std::cout << "Client disconnected (cs=d): " << GetName() << std::endl; - database.SetMQDetectionFlag(this->AccountName(), GetName(), "/MQInstantCamp: Possible instant camp disconnect.", zone->GetShortName()); - return false; - } - - if (client_state == CLIENT_ERROR) { - OnDisconnect(true); - std::cout << "Client disconnected (cs=e): " << GetName() << std::endl; - return false; - } - - if (client_state != CLIENT_LINKDEAD && !eqs->CheckState(ESTABLISHED)) { - OnDisconnect(true); - std::cout << "Client linkdead: " << name << std::endl; - - if (GetGM()) { - if (GetMerc()) - { - GetMerc()->Save(); - GetMerc()->Depop(); - } - return false; - } - else if(!linkdead_timer.Enabled()){ - linkdead_timer.Start(RuleI(Zone,ClientLinkdeadMS)); - client_state = CLIENT_LINKDEAD; - AI_Start(CLIENT_LD_TIMEOUT); - SendAppearancePacket(AT_Linkdead, 1); - } - } - - - /************ Get all packets from packet manager out queue and process them ************/ - EQApplicationPacket *app = nullptr; - if(!eqs->CheckState(CLOSING)) - { - while(ret && (app = (EQApplicationPacket *)eqs->PopPacket())) { - if(app) - ret = HandlePacket(app); - safe_delete(app); - } - } - -#ifdef REVERSE_AGGRO - //At this point, we are still connected, everything important has taken - //place, now check to see if anybody wants to aggro us. - // only if client is not feigned - if(ret && !GetFeigned() && scanarea_timer.Check()) { - entity_list.CheckClientAggro(this); - } -#endif - - if (client_state != CLIENT_LINKDEAD && (client_state == CLIENT_ERROR || client_state == DISCONNECTED || client_state == CLIENT_KICKED || !eqs->CheckState(ESTABLISHED))) - { - //client logged out or errored out - //ResetTrade(); - if (client_state != CLIENT_KICKED) { - Save(); - } - - client_state = CLIENT_LINKDEAD; - if (zoning || instalog || GetGM()) - { - Group *mygroup = GetGroup(); - if (mygroup) - { - if (!zoning) - { - entity_list.MessageGroup(this, true, 15, "%s logged out.", GetName()); - LeaveGroup(); - } - else - { - entity_list.MessageGroup(this, true, 15, "%s left the zone.", GetName()); - mygroup->MemberZoned(this); - if (GetMerc() && GetMerc()->HasGroup()) - { - GetMerc()->RemoveMercFromGroup(GetMerc(), GetMerc()->GetGroup()); - } - } - - } - Raid *myraid = entity_list.GetRaidByClient(this); - if (myraid) - { - if (!zoning) - { - //entity_list.MessageGroup(this,true,15,"%s logged out.",GetName()); - myraid->MemberZoned(this); - } - else - { - //entity_list.MessageGroup(this,true,15,"%s left the zone.",GetName()); - myraid->MemberZoned(this); - } - } - OnDisconnect(false); - return false; - } - else - { - LinkDead(); - } - OnDisconnect(true); - } - // Feign Death 2 minutes and zone forgets you - if (forget_timer.Check()) { - forget_timer.Disable(); - entity_list.ClearZoneFeignAggro(this); - Message(0, "Your enemies have forgotten you!"); - } - - return ret; -} - -/* Just a set of actions preformed all over in Client::Process */ -void Client::OnDisconnect(bool hard_disconnect) { - if(hard_disconnect) - { - LeaveGroup(); - if (GetMerc()) - { - GetMerc()->Save(); - GetMerc()->Depop(); - } - Raid *MyRaid = entity_list.GetRaidByClient(this); - - if (MyRaid) - MyRaid->MemberZoned(this); - - parse->EventPlayer(EVENT_DISCONNECT, this, "", 0); - - /* QS: PlayerLogConnectDisconnect */ - if (RuleB(QueryServ, PlayerLogConnectDisconnect)){ - std::string event_desc = StringFormat("Disconnect :: in zoneid:%i instid:%i", this->GetZoneID(), this->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Connect_State, this->CharacterID(), event_desc); - } - } - - Mob *Other = trade->With(); - if(Other) - { - mlog(TRADING__CLIENT, "Client disconnected during a trade. Returning their items."); - FinishTrade(this); - - if(Other->IsClient()) - Other->CastToClient()->FinishTrade(Other); - - /* Reset both sides of the trade */ - trade->Reset(); - Other->trade->Reset(); - } - - database.SetFirstLogon(CharacterID(), 0); //We change firstlogon status regardless of if a player logs out to zone or not, because we only want to trigger it on their first login from world. - - /* Remove ourself from all proximities */ - ClearAllProximities(); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_LogoutReply); - FastQueuePacket(&outapp); - - Disconnect(); -} - -// Sends the client complete inventory used in character login - -// DO WE STILL NEED THE 'ITEMCOMBINED' CONDITIONAL CODE? -U - -//#ifdef ITEMCOMBINED -void Client::BulkSendInventoryItems() { - int16 slot_id = 0; - - // LINKDEAD TRADE ITEMS - // Move trade slot items back into normal inventory..need them there now for the proceeding validity checks -U - for(slot_id = EmuConstants::TRADE_BEGIN; slot_id <= EmuConstants::TRADE_END; slot_id++) { - ItemInst* inst = m_inv.PopItem(slot_id); - if(inst) { - bool is_arrow = (inst->GetItem()->ItemType == ItemTypeArrow) ? true : false; - int16 free_slot_id = m_inv.FindFreeSlot(inst->IsType(ItemClassContainer), true, inst->GetItem()->Size, is_arrow); - mlog(INVENTORY__ERROR, "Incomplete Trade Transaction: Moving %s from slot %i to %i", inst->GetItem()->Name, slot_id, free_slot_id); - PutItemInInventory(free_slot_id, *inst, false); - database.SaveInventory(character_id, nullptr, slot_id); - safe_delete(inst); - } - } - - bool deletenorent = database.NoRentExpired(GetName()); - if(deletenorent){ RemoveNoRent(false); } //client was offline for more than 30 minutes, delete no rent items - - RemoveDuplicateLore(false); - MoveSlotNotAllowed(false); - - // The previous three method calls took care of moving/removing expired/illegal item placements -U - - //TODO: this function is just retarded... it re-allocates the buffer for every - //new item. It should be changed to loop through once, gather the - //lengths, and item packet pointers into an array (fixed length), and - //then loop again to build the packet. - //EQApplicationPacket *packets[50]; - //unsigned long buflen = 0; - //unsigned long pos = 0; - //memset(packets, 0, sizeof(packets)); - //foreach item in the invendor sections - // packets[pos++] = ReturnItemPacket(...) - // buflen += temp->size - //... - //allocat the buffer - //for r from 0 to pos - // put pos[r]->pBuffer into the buffer - //for r from 0 to pos - // safe_delete(pos[r]); - - uint32 size = 0; - uint16 i = 0; - std::map ser_items; - std::map::iterator itr; - - //Inventory items - for(slot_id = MAIN_BEGIN; slot_id < EmuConstants::MAP_POSSESSIONS_SIZE; slot_id++) { - const ItemInst* inst = m_inv[slot_id]; - if(inst) { - std::string packet = inst->Serialize(slot_id); - ser_items[i++] = packet; - size += packet.length(); - } - } - - // Power Source - if(GetClientVersion() >= EQClientSoF) { - const ItemInst* inst = m_inv[MainPowerSource]; - if(inst) { - std::string packet = inst->Serialize(MainPowerSource); - ser_items[i++] = packet; - size += packet.length(); - } - } - - // Bank items - for(slot_id = EmuConstants::BANK_BEGIN; slot_id <= EmuConstants::BANK_END; slot_id++) { - const ItemInst* inst = m_inv[slot_id]; - if(inst) { - std::string packet = inst->Serialize(slot_id); - ser_items[i++] = packet; - size += packet.length(); - } - } - - // Shared Bank items - for(slot_id = EmuConstants::SHARED_BANK_BEGIN; slot_id <= EmuConstants::SHARED_BANK_END; slot_id++) { - const ItemInst* inst = m_inv[slot_id]; - if(inst) { - std::string packet = inst->Serialize(slot_id); - ser_items[i++] = packet; - size += packet.length(); - } - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_CharInventory, size); - uchar* ptr = outapp->pBuffer; - for(itr = ser_items.begin(); itr != ser_items.end(); ++itr){ - int length = itr->second.length(); - if(length > 5) { - memcpy(ptr, itr->second.c_str(), length); - ptr += length; - } - } - QueuePacket(outapp); - safe_delete(outapp); -} -/*#else -void Client::BulkSendInventoryItems() -{ - // Search all inventory buckets for items - bool deletenorent=database.NoRentExpired(GetName()); - // Worn items and Inventory items - int16 slot_id = 0; - if(deletenorent){//client was offline for more than 30 minutes, delete no rent items - RemoveNoRent(); - } - for (slot_id=EmuConstants::POSSESSIONS_BEGIN; slot_id<=EmuConstants::POSSESSIONS_END; slot_id++) { - const ItemInst* inst = m_inv[slot_id]; - if (inst){ - SendItemPacket(slot_id, inst, ItemPacketCharInventory); - } - } - // Bank items - for (slot_id=EmuConstants::BANK_BEGIN; slot_id<=EmuConstants::BANK_END; slot_id++) { // 2015... - const ItemInst* inst = m_inv[slot_id]; - if (inst){ - SendItemPacket(slot_id, inst, ItemPacketCharInventory); - } - } - - // Shared Bank items - for (slot_id=EmuConstants::SHARED_BANK_BEGIN; slot_id<=EmuConstants::SHARED_BANK_END; slot_id++) { - const ItemInst* inst = m_inv[slot_id]; - if (inst){ - SendItemPacket(slot_id, inst, ItemPacketCharInventory); - } - } - - // LINKDEAD TRADE ITEMS - // If player went LD during a trade, they have items in the trade inventory - // slots. These items are now being put into their inventory (then queue up on cursor) - for (int16 trade_slot_id=EmuConstants::TRADE_BEGIN; trade_slot_id<=EmuConstants::TRADE_END; trade_slot_id++) { - const ItemInst* inst = m_inv[slot_id]; - if (inst) { - int16 free_slot_id = m_inv.FindFreeSlot(inst->IsType(ItemClassContainer), true, inst->GetItem()->Size); - DeleteItemInInventory(trade_slot_id, 0, false); - PutItemInInventory(free_slot_id, *inst, true); - } - } -} -#endif*/ - -void Client::BulkSendMerchantInventory(int merchant_id, int npcid) { - const Item_Struct* handyitem = nullptr; - uint32 numItemSlots = 80; //The max number of items passed in the transaction. - const Item_Struct *item; - std::list merlist = zone->merchanttable[merchant_id]; - std::list::const_iterator itr; - Mob* merch = entity_list.GetMobByNpcTypeID(npcid); - if (merlist.size() == 0) { //Attempt to load the data, it might have been missed if someone spawned the merchant after the zone was loaded - zone->LoadNewMerchantData(merchant_id); - merlist = zone->merchanttable[merchant_id]; - if (merlist.size() == 0) - return; - } - std::list tmp_merlist = zone->tmpmerchanttable[npcid]; - std::list::iterator tmp_itr; - - uint32 i = 1; - uint8 handychance = 0; - for (itr = merlist.begin(); itr != merlist.end() && i <= numItemSlots; ++itr) { - MerchantList ml = *itr; - if (merch->CastToNPC()->GetMerchantProbability() > ml.probability) - continue; - - if (GetLevel() < ml.level_required) - continue; - - if (!(ml.classes_required & (1 << (GetClass() - 1)))) - continue; - - int32 fac = merch ? merch->GetPrimaryFaction() : 0; - if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) - continue; - - handychance = zone->random.Int(0, merlist.size() + tmp_merlist.size() - 1); - - item = database.GetItem(ml.item); - if (item) { - if (handychance == 0) - handyitem = item; - else - handychance--; - int charges = 1; - if (item->ItemClass == ItemClassCommon) - charges = item->MaxCharges; - ItemInst* inst = database.CreateItem(item, charges); - if (inst) { - if (RuleB(Merchant, UsePriceMod)) { - inst->SetPrice((item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate * Client::CalcPriceMod(merch, false))); - } - else - inst->SetPrice((item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate)); - inst->SetMerchantSlot(ml.slot); - inst->SetMerchantCount(-1); //unlimited - if (charges > 0) - inst->SetCharges(charges); - else - inst->SetCharges(1); - - SendItemPacket(ml.slot - 1, inst, ItemPacketMerchant); - safe_delete(inst); - } - } - // Account for merchant lists with gaps. - if (ml.slot >= i) { - if (ml.slot > i) - LogFile->write(EQEMuLog::Debug, "(WARNING) Merchantlist contains gap at slot %d. Merchant: %d, NPC: %d", i, merchant_id, npcid); - i = ml.slot + 1; - } - } - std::list origtmp_merlist = zone->tmpmerchanttable[npcid]; - tmp_merlist.clear(); - for (tmp_itr = origtmp_merlist.begin(); tmp_itr != origtmp_merlist.end() && i <= numItemSlots; ++tmp_itr) { - TempMerchantList ml = *tmp_itr; - item = database.GetItem(ml.item); - ml.slot = i; - if (item) { - if (handychance == 0) - handyitem = item; - else - handychance--; - int charges = 1; - //if(item->ItemClass==ItemClassCommon && (int16)ml.charges <= item->MaxCharges) - // charges=ml.charges; - //else - charges = item->MaxCharges; - ItemInst* inst = database.CreateItem(item, charges); - if (inst) { - if (RuleB(Merchant, UsePriceMod)) { - inst->SetPrice((item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate * Client::CalcPriceMod(merch, false))); - } - else - inst->SetPrice((item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate)); - inst->SetMerchantSlot(ml.slot); - inst->SetMerchantCount(ml.charges); - if(charges > 0) - inst->SetCharges(item->MaxCharges);//inst->SetCharges(charges); - else - inst->SetCharges(1); - SendItemPacket(ml.slot-1, inst, ItemPacketMerchant); - safe_delete(inst); - } - } - tmp_merlist.push_back(ml); - i++; - } - //this resets the slot - zone->tmpmerchanttable[npcid] = tmp_merlist; - if (merch != nullptr && handyitem) { - char handy_id[8] = { 0 }; - int greeting = zone->random.Int(0, 4); - int greet_id = 0; - switch (greeting) { - case 1: - greet_id = MERCHANT_GREETING; - break; - case 2: - greet_id = MERCHANT_HANDY_ITEM1; - break; - case 3: - greet_id = MERCHANT_HANDY_ITEM2; - break; - case 4: - greet_id = MERCHANT_HANDY_ITEM3; - break; - default: - greet_id = MERCHANT_HANDY_ITEM4; - } - sprintf(handy_id, "%i", greet_id); - - if (greet_id != MERCHANT_GREETING) - Message_StringID(10, GENERIC_STRINGID_SAY, merch->GetCleanName(), handy_id, this->GetName(), handyitem->Name); - else - Message_StringID(10, GENERIC_STRINGID_SAY, merch->GetCleanName(), handy_id, this->GetName()); - - merch->CastToNPC()->FaceTarget(this->CastToMob()); - } - -// safe_delete_array(cpi); -} - -uint8 Client::WithCustomer(uint16 NewCustomer){ - - if(NewCustomer == 0) { - CustomerID = 0; - return 0; - } - - if(CustomerID == 0) { - CustomerID = NewCustomer; - return 1; - } - - // Check that the player browsing our wares hasn't gone away. - - Client* c = entity_list.GetClientByID(CustomerID); - - if(!c) { - _log(TRADING__CLIENT, "Previous customer has gone away."); - CustomerID = NewCustomer; - return 1; - } - - return 0; -} - -void Client::OPRezzAnswer(uint32 Action, uint32 SpellID, uint16 ZoneID, uint16 InstanceID, float x, float y, float z) -{ - if(PendingRezzXP < 0) { - // pendingrezexp is set to -1 if we are not expecting an OP_RezzAnswer - _log(SPELLS__REZ, "Unexpected OP_RezzAnswer. Ignoring it."); - Message(13, "You have already been resurrected.\n"); - return; - } - - if (Action == 1) - { - // Mark the corpse as rezzed in the database, just in case the corpse has buried, or the zone the - // corpse is in has shutdown since the rez spell was cast. - database.MarkCorpseAsRezzed(PendingRezzDBID); - _log(SPELLS__REZ, "Player %s got a %i Rezz, spellid %i in zone%i, instance id %i", - this->name, (uint16)spells[SpellID].base[0], - SpellID, ZoneID, InstanceID); - - this->BuffFadeNonPersistDeath(); - int SpellEffectDescNum = GetSpellEffectDescNum(SpellID); - // Rez spells with Rez effects have this DescNum (first is Titanium, second is 6.2 Client) - if((SpellEffectDescNum == 82) || (SpellEffectDescNum == 39067)) { - SetMana(0); - SetHP(GetMaxHP()/5); - SpellOnTarget(756, this); // Rezz effects - } - else { - SetMana(GetMaxMana()); - SetHP(GetMaxHP()); - } - if(spells[SpellID].base[0] < 100 && spells[SpellID].base[0] > 0 && PendingRezzXP > 0) - { - SetEXP(((int)(GetEXP()+((float)((PendingRezzXP / 100) * spells[SpellID].base[0])))), - GetAAXP(),true); - } - else if (spells[SpellID].base[0] == 100 && PendingRezzXP > 0) { - SetEXP((GetEXP() + PendingRezzXP), GetAAXP(), true); - } - - //Was sending the packet back to initiate client zone... - //but that could be abusable, so lets go through proper channels - MovePC(ZoneID, InstanceID, x, y, z, GetHeading(), 0, ZoneSolicited); - entity_list.RefreshClientXTargets(this); - } - PendingRezzXP = -1; - PendingRezzSpellID = 0; -} - -void Client::OPTGB(const EQApplicationPacket *app) -{ - if(!app) return; - if(!app->pBuffer) return; - - uint32 tgb_flag = *(uint32 *)app->pBuffer; - if(tgb_flag == 2) - Message_StringID(0, TGB() ? TGB_ON : TGB_OFF); - else - tgb = tgb_flag; -} - -void Client::OPMemorizeSpell(const EQApplicationPacket* app) -{ - if(app->size != sizeof(MemorizeSpell_Struct)) - { - LogFile->write(EQEMuLog::Error,"Wrong size on OP_MemorizeSpell. Got: %i, Expected: %i", app->size, sizeof(MemorizeSpell_Struct)); - DumpPacket(app); - return; - } - - const MemorizeSpell_Struct* memspell = (const MemorizeSpell_Struct*) app->pBuffer; - - if(!IsValidSpell(memspell->spell_id)) - { - Message(13, "Unexpected error: spell id out of range"); - return; - } - - if - ( - GetClass() > 16 || - GetLevel() < spells[memspell->spell_id].classes[GetClass()-1] - ) - { - char val1[20]={0}; - Message_StringID(13,SPELL_LEVEL_TO_LOW,ConvertArray(spells[memspell->spell_id].classes[GetClass()-1],val1),spells[memspell->spell_id].name); - //Message(13, "Unexpected error: Class cant use this spell at your level!"); - return; - } - - switch(memspell->scribing) - { - case memSpellScribing: { // scribing spell to book - const ItemInst* inst = m_inv[MainCursor]; - - if(inst && inst->IsType(ItemClassCommon)) - { - const Item_Struct* item = inst->GetItem(); - - if(item && item->Scroll.Effect == (int32)(memspell->spell_id)) - { - ScribeSpell(memspell->spell_id, memspell->slot); - DeleteItemInInventory(MainCursor, 1, true); - } - else - Message(0,"Scribing spell: inst exists but item does not or spell ids do not match."); - } - else - Message(0,"Scribing a spell without an inst on your cursor?"); - break; - } - case memSpellMemorize: { // memming spell - if(HasSpellScribed(memspell->spell_id)) - { - MemSpell(memspell->spell_id, memspell->slot); - } - else - { - database.SetMQDetectionFlag(AccountName(), GetName(), "OP_MemorizeSpell but we don't have this spell scribed...", zone->GetShortName()); - } - break; - } - case memSpellForget: { // unmemming spell - UnmemSpell(memspell->slot); - break; - } - } - - Save(); -} - -void Client::BreakInvis() -{ - if (invisible) - { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x03; - sa_out->parameter = 0; - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - invisible = false; - invisible_undead = false; - invisible_animals = false; - hidden = false; - improved_hidden = false; - } -} - -static uint64 CoinTypeCoppers(uint32 type) { - switch(type) { - case COINTYPE_PP: - return(1000); - case COINTYPE_GP: - return(100); - case COINTYPE_SP: - return(10); - case COINTYPE_CP: - default: - break; - } - return(1); -} - -void Client::OPMoveCoin(const EQApplicationPacket* app) -{ - MoveCoin_Struct* mc = (MoveCoin_Struct*)app->pBuffer; - uint64 value = 0, amount_to_take = 0, amount_to_add = 0; - int32 *from_bucket = 0, *to_bucket = 0; - Mob* trader = trade->With(); - - // could just do a range, but this is clearer and explicit - if - ( - ( - mc->cointype1 != COINTYPE_PP && - mc->cointype1 != COINTYPE_GP && - mc->cointype1 != COINTYPE_SP && - mc->cointype1 != COINTYPE_CP - ) || - ( - mc->cointype2 != COINTYPE_PP && - mc->cointype2 != COINTYPE_GP && - mc->cointype2 != COINTYPE_SP && - mc->cointype2 != COINTYPE_CP - ) - ) - { - return; - } - - switch(mc->from_slot) - { - case -1: // destroy - { - // I don't think you can move coin from the void, - // but need to check this - break; - } - case 0: // cursor - { - switch(mc->cointype1) - { - case COINTYPE_PP: - from_bucket = (int32 *) &m_pp.platinum_cursor; break; - case COINTYPE_GP: - from_bucket = (int32 *) &m_pp.gold_cursor; break; - case COINTYPE_SP: - from_bucket = (int32 *) &m_pp.silver_cursor; break; - case COINTYPE_CP: - from_bucket = (int32 *) &m_pp.copper_cursor; break; - } - break; - } - case 1: // inventory - { - switch(mc->cointype1) - { - case COINTYPE_PP: - from_bucket = (int32 *) &m_pp.platinum; break; - case COINTYPE_GP: - from_bucket = (int32 *) &m_pp.gold; break; - case COINTYPE_SP: - from_bucket = (int32 *) &m_pp.silver; break; - case COINTYPE_CP: - from_bucket = (int32 *) &m_pp.copper; break; - } - break; - } - case 2: // bank - { - uint32 distance = 0; - NPC *banker = entity_list.GetClosestBanker(this, distance); - if(!banker || distance > USE_NPC_RANGE2) - { - char *hacked_string = nullptr; - MakeAnyLenString(&hacked_string, "Player tried to make use of a banker(coin move) but %s is non-existant or too far away (%u units).", - banker ? banker->GetName() : "UNKNOWN NPC", distance); - database.SetMQDetectionFlag(AccountName(), GetName(), hacked_string, zone->GetShortName()); - safe_delete_array(hacked_string); - return; - } - - switch(mc->cointype1) - { - case COINTYPE_PP: - from_bucket = (int32 *) &m_pp.platinum_bank; break; - case COINTYPE_GP: - from_bucket = (int32 *) &m_pp.gold_bank; break; - case COINTYPE_SP: - from_bucket = (int32 *) &m_pp.silver_bank; break; - case COINTYPE_CP: - from_bucket = (int32 *) &m_pp.copper_bank; break; - } - break; - } - case 3: // trade - { - // can't move coin from trade - break; - } - case 4: // shared bank - { - uint32 distance = 0; - NPC *banker = entity_list.GetClosestBanker(this, distance); - if(!banker || distance > USE_NPC_RANGE2) - { - char *hacked_string = nullptr; - MakeAnyLenString(&hacked_string, "Player tried to make use of a banker(shared coin move) but %s is non-existant or too far away (%u units).", - banker ? banker->GetName() : "UNKNOWN NPC", distance); - database.SetMQDetectionFlag(AccountName(), GetName(), hacked_string, zone->GetShortName()); - safe_delete_array(hacked_string); - return; - } - if(mc->cointype1 == COINTYPE_PP) // there's only platinum here - from_bucket = (int32 *) &m_pp.platinum_shared; - break; - } - } - - switch(mc->to_slot) - { - case -1: // destroy - { - // no action required - break; - } - case 0: // cursor - { - switch(mc->cointype2) - { - case COINTYPE_PP: - to_bucket = (int32 *) &m_pp.platinum_cursor; break; - case COINTYPE_GP: - to_bucket = (int32 *) &m_pp.gold_cursor; break; - case COINTYPE_SP: - to_bucket = (int32 *) &m_pp.silver_cursor; break; - case COINTYPE_CP: - to_bucket = (int32 *) &m_pp.copper_cursor; break; - } - break; - } - case 1: // inventory - { - switch(mc->cointype2) - { - case COINTYPE_PP: - to_bucket = (int32 *) &m_pp.platinum; break; - case COINTYPE_GP: - to_bucket = (int32 *) &m_pp.gold; break; - case COINTYPE_SP: - to_bucket = (int32 *) &m_pp.silver; break; - case COINTYPE_CP: - to_bucket = (int32 *) &m_pp.copper; break; - } - break; - } - case 2: // bank - { - uint32 distance = 0; - NPC *banker = entity_list.GetClosestBanker(this, distance); - if(!banker || distance > USE_NPC_RANGE2) - { - char *hacked_string = nullptr; - MakeAnyLenString(&hacked_string, "Player tried to make use of a banker(coin move) but %s is non-existant or too far away (%u units).", - banker ? banker->GetName() : "UNKNOWN NPC", distance); - database.SetMQDetectionFlag(AccountName(), GetName(), hacked_string, zone->GetShortName()); - safe_delete_array(hacked_string); - return; - } - switch(mc->cointype2) - { - case COINTYPE_PP: - to_bucket = (int32 *) &m_pp.platinum_bank; break; - case COINTYPE_GP: - to_bucket = (int32 *) &m_pp.gold_bank; break; - case COINTYPE_SP: - to_bucket = (int32 *) &m_pp.silver_bank; break; - case COINTYPE_CP: - to_bucket = (int32 *) &m_pp.copper_bank; break; - } - break; - } - case 3: // trade - { - if(trader) - { - switch(mc->cointype2) - { - case COINTYPE_PP: - to_bucket = (int32 *) &trade->pp; break; - case COINTYPE_GP: - to_bucket = (int32 *) &trade->gp; break; - case COINTYPE_SP: - to_bucket = (int32 *) &trade->sp; break; - case COINTYPE_CP: - to_bucket = (int32 *) &trade->cp; break; - } - } - break; - } - case 4: // shared bank - { - uint32 distance = 0; - NPC *banker = entity_list.GetClosestBanker(this, distance); - if(!banker || distance > USE_NPC_RANGE2) - { - char *hacked_string = nullptr; - MakeAnyLenString(&hacked_string, "Player tried to make use of a banker(shared coin move) but %s is non-existant or too far away (%u units).", - banker ? banker->GetName() : "UNKNOWN NPC", distance); - database.SetMQDetectionFlag(AccountName(), GetName(), hacked_string, zone->GetShortName()); - safe_delete_array(hacked_string); - return; - } - if(mc->cointype2 == COINTYPE_PP) // there's only platinum here - to_bucket = (int32 *) &m_pp.platinum_shared; - break; - } - } - - if(!from_bucket) - { - return; - } - - // don't allow them to go into negatives (from our point of view) - amount_to_take = *from_bucket < mc->amount ? *from_bucket : mc->amount; - - // if you move 11 gold into a bank platinum location, the packet - // will say 11, but the client will have 1 left on their cursor, so we have - // to figure out the conversion ourselves - - amount_to_add = amount_to_take * ((float)CoinTypeCoppers(mc->cointype1) / (float)CoinTypeCoppers(mc->cointype2)); - - // the amount we're adding could be different than what was requested, so - // we have to adjust the amount we take as well - amount_to_take = amount_to_add * ((float)CoinTypeCoppers(mc->cointype2) / (float)CoinTypeCoppers(mc->cointype1)); - - // now we should have a from_bucket, a to_bucket, an amount_to_take - // and an amount_to_add - - // now we actually take it from the from bucket. if there's an error - // with the destination slot, they lose their money - *from_bucket -= amount_to_take; - // why are intentionally inducing a crash here rather than letting the code attempt to stumble on? - // assert(*from_bucket >= 0); - - if(to_bucket) - { - if(*to_bucket + amount_to_add > *to_bucket) // overflow check - *to_bucket += amount_to_add; - - //shared bank plat - if (RuleB(Character, SharedBankPlat)) - { - if (to_bucket == &m_pp.platinum_shared || from_bucket == &m_pp.platinum_shared) - { - if (from_bucket == &m_pp.platinum_shared) - amount_to_add = 0 - amount_to_take; - - database.SetSharedPlatinum(AccountID(),amount_to_add); - } - } - else{ - if (to_bucket == &m_pp.platinum_shared || from_bucket == &m_pp.platinum_shared){ - this->Message(13, "::: WARNING! ::: SHARED BANK IS DISABLED AND YOUR PLATINUM WILL BE DESTROYED IF YOU PUT IT HERE"); - } - } - } - - // if this is a trade move, inform the person being traded with - if(mc->to_slot == 3 && trader && trader->IsClient()) - { - - // If one party accepted the trade then some coin was added, their state needs to be reset - trade->state = Trading; - Mob* with = trade->With(); - if (with) - with->trade->state = Trading; - - Client* recipient = trader->CastToClient(); - recipient->Message(15, "%s adds some coins to the trade.", GetName()); - recipient->Message(15, "The total trade is: %i PP, %i GP, %i SP, %i CP", - trade->pp, trade->gp, - trade->sp, trade->cp - ); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeCoins,sizeof(TradeCoin_Struct)); - TradeCoin_Struct* tcs = (TradeCoin_Struct*)outapp->pBuffer; - tcs->trader = trader->GetID(); - tcs->slot = mc->cointype2; - tcs->unknown5 = 0x4fD2; - tcs->unknown7 = 0; - tcs->amount = amount_to_add; - recipient->QueuePacket(outapp); - safe_delete(outapp); - } - - SaveCurrency(); -} - -void Client::OPGMTraining(const EQApplicationPacket *app) -{ - - EQApplicationPacket* outapp = app->Copy(); - GMTrainee_Struct* gmtrain = (GMTrainee_Struct*) outapp->pBuffer; - - Mob* pTrainer = entity_list.GetMob(gmtrain->npcid); - - if(!pTrainer || !pTrainer->IsNPC() || pTrainer->GetClass() < WARRIORGM || pTrainer->GetClass() > BERSERKERGM) - return; - - //you can only use your own trainer, client enforces this, but why trust it - int trains_class = pTrainer->GetClass() - (WARRIORGM - WARRIOR); - if(GetClass() != trains_class) - return; - - //you have to be somewhat close to a trainer to be properly using them - if(DistNoRoot(*pTrainer) > USE_NPC_RANGE2) - return; - - // if this for-loop acts up again (crashes linux), try enabling the before and after #pragmas -//#pragma GCC push_options -//#pragma GCC optimize ("O0") - for (int sk = Skill1HBlunt; sk <= HIGHEST_SKILL; ++sk) { - if(sk == SkillTinkering && GetRace() != GNOME) { - gmtrain->skills[sk] = 0; //Non gnomes can't tinker! - } else { - gmtrain->skills[sk] = GetMaxSkillAfterSpecializationRules((SkillUseTypes)sk, MaxSkill((SkillUseTypes)sk, GetClass(), RuleI(Character, MaxLevel))); - //this is the highest level that the trainer can train you to, this is enforced clientside so we can't just - //Set it to 1 with CanHaveSkill or you wont be able to train past 1. - } - } -//#pragma GCC pop_options - - uchar ending[]={0x34,0x87,0x8a,0x3F,0x01 - ,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9 - ,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9 - ,0x76,0x75,0x3f}; - memcpy(&outapp->pBuffer[outapp->size-40],ending,sizeof(ending)); - FastQueuePacket(&outapp); - - // welcome message - if (pTrainer && pTrainer->IsNPC()) - { - pTrainer->Say_StringID(zone->random.Int(1204, 1207), GetCleanName()); - } -} - -void Client::OPGMEndTraining(const EQApplicationPacket *app) -{ - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GMEndTrainingResponse, 0); - GMTrainEnd_Struct *p = (GMTrainEnd_Struct *)app->pBuffer; - - FastQueuePacket(&outapp); - - Mob* pTrainer = entity_list.GetMob(p->npcid); - if(!pTrainer || !pTrainer->IsNPC() || pTrainer->GetClass() < WARRIORGM || pTrainer->GetClass() > BERSERKERGM) - return; - - //you can only use your own trainer, client enforces this, but why trust it - int trains_class = pTrainer->GetClass() - (WARRIORGM - WARRIOR); - if(GetClass() != trains_class) - return; - - //you have to be somewhat close to a trainer to be properly using them - if(DistNoRoot(*pTrainer) > USE_NPC_RANGE2) - return; - - // goodbye message - if (pTrainer->IsNPC()) - { - pTrainer->Say_StringID(zone->random.Int(1208, 1211), GetCleanName()); - } -} - -void Client::OPGMTrainSkill(const EQApplicationPacket *app) -{ - if(!m_pp.points) - return; - - int Cost = 0; - - GMSkillChange_Struct* gmskill = (GMSkillChange_Struct*) app->pBuffer; - - Mob* pTrainer = entity_list.GetMob(gmskill->npcid); - if(!pTrainer || !pTrainer->IsNPC() || pTrainer->GetClass() < WARRIORGM || pTrainer->GetClass() > BERSERKERGM) - return; - - //you can only use your own trainer, client enforces this, but why trust it - int trains_class = pTrainer->GetClass() - (WARRIORGM - WARRIOR); - if(GetClass() != trains_class) - return; - - //you have to be somewhat close to a trainer to be properly using them - if(DistNoRoot(*pTrainer) > USE_NPC_RANGE2) - return; - - if (gmskill->skillbank == 0x01) - { - // languages go here - if (gmskill->skill_id > 25) - { - std::cout << "Wrong Training Skill (languages)" << std::endl; - DumpPacket(app); - return; - } - int AdjustedSkillLevel = GetLanguageSkill(gmskill->skill_id) - 10; - if(AdjustedSkillLevel > 0) - Cost = AdjustedSkillLevel * AdjustedSkillLevel * AdjustedSkillLevel / 100; - - IncreaseLanguageSkill(gmskill->skill_id); - } - else if (gmskill->skillbank == 0x00) - { - // normal skills go here - if (gmskill->skill_id > HIGHEST_SKILL) - { - std::cout << "Wrong Training Skill (abilities)" << std::endl; - DumpPacket(app); - return; - } - - SkillUseTypes skill = (SkillUseTypes) gmskill->skill_id; - - if(!CanHaveSkill(skill)) { - mlog(CLIENT__ERROR, "Tried to train skill %d, which is not allowed.", skill); - return; - } - - if(MaxSkill(skill) == 0) { - mlog(CLIENT__ERROR, "Tried to train skill %d, but training is not allowed at this level.", skill); - return; - } - - uint16 skilllevel = GetRawSkill(skill); - - if(skilllevel == 0) { - //this is a new skill.. - uint16 t_level = SkillTrainLevel(skill, GetClass()); - if (t_level == 0) - { - return; - } - - SetSkill(skill, t_level); - } else { - switch(skill) { - case SkillBrewing: - case SkillMakePoison: - case SkillTinkering: - case SkillResearch: - case SkillAlchemy: - case SkillBaking: - case SkillTailoring: - case SkillBlacksmithing: - case SkillFletching: - case SkillJewelryMaking: - case SkillPottery: - if(skilllevel >= RuleI(Skills, MaxTrainTradeskills)) { - Message_StringID(13, MORE_SKILLED_THAN_I, pTrainer->GetCleanName()); - return; - } - break; - case SkillSpecializeAbjure: - case SkillSpecializeAlteration: - case SkillSpecializeConjuration: - case SkillSpecializeDivination: - case SkillSpecializeEvocation: - if(skilllevel >= RuleI(Skills, MaxTrainSpecializations)) { - Message_StringID(13, MORE_SKILLED_THAN_I, pTrainer->GetCleanName()); - return; - } - default: - break; - } - - int MaxSkillValue = MaxSkill(skill); - if (skilllevel >= MaxSkillValue) - { - // Don't allow training over max skill level - Message_StringID(13, MORE_SKILLED_THAN_I, pTrainer->GetCleanName()); - return; - } - - if(gmskill->skill_id >= SkillSpecializeAbjure && gmskill->skill_id <= SkillSpecializeEvocation) - { - int MaxSpecSkill = GetMaxSkillAfterSpecializationRules(skill, MaxSkillValue); - if (skilllevel >= MaxSpecSkill) - { - // Restrict specialization training to follow the rules - Message_StringID(13, MORE_SKILLED_THAN_I, pTrainer->GetCleanName()); - return; - } - } - - // Client train a valid skill - // - int AdjustedSkillLevel = skilllevel - 10; - - if(AdjustedSkillLevel > 0) - Cost = AdjustedSkillLevel * AdjustedSkillLevel * AdjustedSkillLevel / 100; - - SetSkill(skill, skilllevel + 1); - - - } - } - - if(GetClientVersion() >= EQClientSoF) { - // The following packet decreases the skill points left in the Training Window and - // produces the 'You have increased your skill / learned the basics of' message. - // - EQApplicationPacket* outapp = new EQApplicationPacket(OP_GMTrainSkillConfirm, sizeof(GMTrainSkillConfirm_Struct)); - - GMTrainSkillConfirm_Struct *gmtsc = (GMTrainSkillConfirm_Struct *)outapp->pBuffer; - gmtsc->SkillID = gmskill->skill_id; - - if(gmskill->skillbank == 1) { - gmtsc->NewSkill = (GetLanguageSkill(gmtsc->SkillID) == 1); - gmtsc->SkillID += 100; - } - else - gmtsc->NewSkill = (GetRawSkill((SkillUseTypes)gmtsc->SkillID) == 1); - - gmtsc->Cost = Cost; - - strcpy(gmtsc->TrainerName, pTrainer->GetCleanName()); - QueuePacket(outapp); - safe_delete(outapp); - } - - if(Cost) - TakeMoneyFromPP(Cost); - - m_pp.points--; -} - -// this is used for /summon and /corpse -void Client::OPGMSummon(const EQApplicationPacket *app) -{ - GMSummon_Struct* gms = (GMSummon_Struct*) app->pBuffer; - Mob* st = entity_list.GetMob(gms->charname); - - if(st && st->IsCorpse()) - { - st->CastToCorpse()->Summon(this, false, true); - } - else - { - if(admin < 80) - { - return; - } - if(st) - { - Message(0, "Local: Summoning %s to %f, %f, %f", gms->charname, gms->x, gms->y, gms->z); - if (st->IsClient() && (st->CastToClient()->GetAnon() != 1 || this->Admin() >= st->CastToClient()->Admin())) - st->CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), (float)gms->x, (float)gms->y, (float)gms->z, this->GetHeading(), true); - else - st->GMMove(this->GetX(), this->GetY(), this->GetZ(),this->GetHeading()); - } - else - { - uint8 tmp = gms->charname[strlen(gms->charname)-1]; - if (!worldserver.Connected()) - { - Message(0, "Error: World server disconnected"); - } - else if (tmp < '0' || tmp > '9') // dont send to world if it's not a player's name - { - ServerPacket* pack = new ServerPacket(ServerOP_ZonePlayer, sizeof(ServerZonePlayer_Struct)); - ServerZonePlayer_Struct* szp = (ServerZonePlayer_Struct*) pack->pBuffer; - strcpy(szp->adminname, this->GetName()); - szp->adminrank = this->Admin(); - strcpy(szp->name, gms->charname); - strcpy(szp->zone, zone->GetShortName()); - szp->x_pos = (float)gms->x; - szp->y_pos = (float)gms->y; - szp->z_pos = (float)gms->z; - szp->ignorerestrictions = 2; - worldserver.SendPacket(pack); - safe_delete(pack); - } - else { - //all options have been exhausted - //summon our target... - if(GetTarget() && GetTarget()->IsCorpse()){ - GetTarget()->CastToCorpse()->Summon(this, false, true); - } - } - } - } -} - -void Client::DoHPRegen() { - SetHP(GetHP() + CalcHPRegen() + RestRegenHP); - SendHPUpdate(); -} - -void Client::DoManaRegen() { - if (GetMana() >= max_mana) - return; - - SetMana(GetMana() + CalcManaRegen() + RestRegenMana); - SendManaUpdatePacket(); -} - - -void Client::DoStaminaUpdate() { - if(!stamina_timer.Check()) - return; - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); - Stamina_Struct* sta = (Stamina_Struct*)outapp->pBuffer; - - if(zone->GetZoneID() != 151) { - int loss = RuleI(Character, FoodLossPerUpdate); - if (m_pp.hunger_level > 0) - m_pp.hunger_level-=loss; - if (m_pp.thirst_level > 0) - m_pp.thirst_level-=loss; - sta->food = m_pp.hunger_level > 6000 ? 6000 : m_pp.hunger_level; - sta->water = m_pp.thirst_level> 6000 ? 6000 : m_pp.thirst_level; - } - else { - // No auto food/drink consumption in the Bazaar - sta->food = 6000; - sta->water = 6000; - } - FastQueuePacket(&outapp); -} - -void Client::DoEnduranceRegen() -{ - if(GetEndurance() >= GetMaxEndurance()) - return; - - SetEndurance(GetEndurance() + CalcEnduranceRegen() + RestRegenEndurance); -} - -void Client::DoEnduranceUpkeep() { - - if (!HasEndurUpkeep()) - return; - - int upkeep_sum = 0; - int cost_redux = spellbonuses.EnduranceReduction + itembonuses.EnduranceReduction + aabonuses.EnduranceReduction; - - bool has_effect = false; - uint32 buffs_i; - uint32 buff_count = GetMaxTotalSlots(); - for (buffs_i = 0; buffs_i < buff_count; buffs_i++) { - if (buffs[buffs_i].spellid != SPELL_UNKNOWN) { - int upkeep = spells[buffs[buffs_i].spellid].EndurUpkeep; - if(upkeep > 0) { - has_effect = true; - if(cost_redux > 0) { - if(upkeep <= cost_redux) - continue; //reduced to 0 - upkeep -= cost_redux; - } - if((upkeep+upkeep_sum) > GetEndurance()) { - //they do not have enough to keep this one going. - BuffFadeBySlot(buffs_i); - } else { - upkeep_sum += upkeep; - } - } - } - } - - if(upkeep_sum != 0){ - SetEndurance(GetEndurance() - upkeep_sum); - TryTriggerOnValueAmount(false, false, true); - } - - if (!has_effect) - SetEndurUpkeep(false); -} - -void Client::CalcRestState() { - - // This method calculates rest state HP and mana regeneration. - // The client 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(AggroCount || !IsSitting()) - return; - - if(!rest_timer.Check(false)) - return; - - uint32 buff_count = GetMaxTotalSlots(); - for (unsigned int j = 0; j < buff_count; j++) { - if(buffs[j].spellid != SPELL_UNKNOWN) { - if(IsDetrimentalSpell(buffs[j].spellid) && (buffs[j].ticsremaining > 0)) - if(!DetrimentalSpellAllowsRest(buffs[j].spellid)) - return; - } - } - - RestRegenHP = (GetMaxHP() * RuleI(Character, RestRegenPercent) / 100); - - RestRegenMana = (GetMaxMana() * RuleI(Character, RestRegenPercent) / 100); - - if(RuleB(Character, RestRegenEndurance)) - RestRegenEndurance = (GetMaxEndurance() * RuleI(Character, RestRegenPercent) / 100); -} - -void Client::DoTracking() -{ - if(TrackingID == 0) - return; - - Mob *m = entity_list.GetMob(TrackingID); - - if(!m || m->IsCorpse()) - { - Message_StringID(MT_Skills, TRACK_LOST_TARGET); - - TrackingID = 0; - - return; - } - - float RelativeHeading = GetHeading() - CalculateHeadingToTarget(m->GetX(), m->GetY()); - - if(RelativeHeading < 0) - RelativeHeading += 256; - - if((RelativeHeading <= 16) || (RelativeHeading >= 240)) - { - Message_StringID(MT_Skills, TRACK_STRAIGHT_AHEAD, m->GetCleanName()); - } - else if((RelativeHeading > 16) && (RelativeHeading <= 48)) - { - Message_StringID(MT_Skills, TRACK_AHEAD_AND_TO, m->GetCleanName(), "right"); - } - else if((RelativeHeading > 48) && (RelativeHeading <= 80)) - { - Message_StringID(MT_Skills, TRACK_TO_THE, m->GetCleanName(), "right"); - } - else if((RelativeHeading > 80) && (RelativeHeading <= 112)) - { - Message_StringID(MT_Skills, TRACK_BEHIND_AND_TO, m->GetCleanName(), "right"); - } - else if((RelativeHeading > 112) && (RelativeHeading <= 144)) - { - Message_StringID(MT_Skills, TRACK_BEHIND_YOU, m->GetCleanName()); - } - else if((RelativeHeading > 144) && (RelativeHeading <= 176)) - { - Message_StringID(MT_Skills, TRACK_BEHIND_AND_TO, m->GetCleanName(), "left"); - } - else if((RelativeHeading > 176) && (RelativeHeading <= 208)) - { - Message_StringID(MT_Skills, TRACK_TO_THE, m->GetCleanName(), "left"); - } - else if((RelativeHeading > 208) && (RelativeHeading < 240)) - { - Message_StringID(MT_Skills, TRACK_AHEAD_AND_TO, m->GetCleanName(), "left"); - } -} - -void Client::HandleRespawnFromHover(uint32 Option) -{ - RespawnFromHoverTimer.Disable(); - - RespawnOption* chosen = nullptr; - bool is_rez = false; - - //Find the selected option - if (Option == 0) - { - chosen = &respawn_options.front(); - } - else if (Option == (respawn_options.size() - 1)) - { - chosen = &respawn_options.back(); - is_rez = true; //Rez must always be the last option - } - else - { - std::list::iterator itr; - uint32 pos = 0; - for (itr = respawn_options.begin(); itr != respawn_options.end(); ++itr) - { - if (pos++ == Option) - { - chosen = &(*itr); - break; - } - } - } - - //If they somehow chose an option they don't have, just send them to bind - RespawnOption* default_to_bind = nullptr; - if (!chosen) - { - /* put error logging here */ - BindStruct* b = &m_pp.binds[0]; - default_to_bind = new RespawnOption; - default_to_bind->name = "Bind Location"; - default_to_bind->zone_id = b->zoneId; - default_to_bind->instance_id = b->instance_id; - default_to_bind->x = b->x; - default_to_bind->y = b->y; - default_to_bind->z = b->z; - default_to_bind->heading = b->heading; - chosen = default_to_bind; - is_rez = false; - } - - if (chosen->zone_id == zone->GetZoneID() && chosen->instance_id == zone->GetInstanceID()) //If they should respawn in the current zone... - { - if (is_rez) - { - if (PendingRezzXP < 0 || PendingRezzSpellID == 0) - { - _log(SPELLS__REZ, "Unexpected Rezz from hover request."); - return; - } - SetHP(GetMaxHP() / 5); - - Corpse* corpse = entity_list.GetCorpseByName(PendingRezzCorpseName.c_str()); - - if (corpse) - { - m_Position.m_X = corpse->GetX(); - m_Position.m_Y = corpse->GetY(); - m_Position.m_Z = corpse->GetZ(); - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ZonePlayerToBind, sizeof(ZonePlayerToBind_Struct) + 10); - ZonePlayerToBind_Struct* gmg = (ZonePlayerToBind_Struct*) outapp->pBuffer; - - gmg->bind_zone_id = zone->GetZoneID(); - gmg->bind_instance_id = zone->GetInstanceID(); - gmg->x = GetX(); - gmg->y = GetY(); - gmg->z = GetZ(); - gmg->heading = GetHeading(); - strcpy(gmg->zone_name, "Resurrect"); - - FastQueuePacket(&outapp); - - ClearHover(); - SendHPUpdate(); - OPRezzAnswer(1, PendingRezzSpellID, zone->GetZoneID(), zone->GetInstanceID(), GetX(), GetY(), GetZ()); - - if (corpse && corpse->IsCorpse()) - { - _log(SPELLS__REZ, "Hover Rez in zone %s for corpse %s", - zone->GetShortName(), PendingRezzCorpseName.c_str()); - - _log(SPELLS__REZ, "Found corpse. Marking corpse as rezzed."); - - corpse->IsRezzed(true); - corpse->CompleteResurrection(); - } - } - else //Not rez - { - PendingRezzSpellID = 0; - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ZonePlayerToBind, sizeof(ZonePlayerToBind_Struct) + chosen->name.length() + 1); - ZonePlayerToBind_Struct* gmg = (ZonePlayerToBind_Struct*) outapp->pBuffer; - - gmg->bind_zone_id = zone->GetZoneID(); - gmg->bind_instance_id = chosen->instance_id; - gmg->x = chosen->x; - gmg->y = chosen->y; - gmg->z = chosen->z; - gmg->heading = chosen->heading; - strcpy(gmg->zone_name, chosen->name.c_str()); - - FastQueuePacket(&outapp); - - CalcBonuses(); - SetHP(GetMaxHP()); - SetMana(GetMaxMana()); - SetEndurance(GetMaxEndurance()); - - m_Position.m_X = chosen->x; - m_Position.m_Y = chosen->y; - m_Position.m_Z = chosen->z; - m_Position.m_Heading = chosen->heading; - - ClearHover(); - entity_list.RefreshClientXTargets(this); - SendHPUpdate(); - } - - //After they've respawned into the same zone, trigger EVENT_RESPAWN - parse->EventPlayer(EVENT_RESPAWN, this, static_cast(itoa(Option)), is_rez ? 1 : 0); - - //Pop Rez option from the respawn options list; - //easiest way to make sure it stays at the end and - //doesn't disrupt adding/removing scripted options - respawn_options.pop_back(); - } - else - { - //Heading to a different zone - if(isgrouped) - { - Group *g = GetGroup(); - if(g) - g->MemberZoned(this); - } - - Raid* r = entity_list.GetRaidByClient(this); - if(r) - r->MemberZoned(this); - - m_pp.zone_id = chosen->zone_id; - m_pp.zoneInstance = chosen->instance_id; - database.MoveCharacterToZone(CharacterID(), database.GetZoneName(chosen->zone_id)); - - Save(); - - MovePC(chosen->zone_id, chosen->instance_id, chosen->x, chosen->y, chosen->z, chosen->heading, 1); - } - - safe_delete(default_to_bind); -} - -void Client::ClearHover() -{ - // Our Entity ID is currently zero, set in Client::Death - SetID(entity_list.GetFreeID()); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_ZoneEntry, sizeof(ServerZoneEntry_Struct)); - ServerZoneEntry_Struct* sze = (ServerZoneEntry_Struct*)outapp->pBuffer; - - FillSpawnStruct(&sze->player,CastToMob()); - - sze->player.spawn.NPC = 0; - sze->player.spawn.z += 6; //arbitrary lift, seems to help spawning under zone. - - entity_list.QueueClients(this, outapp, false); - safe_delete(outapp); - - if(IsClient() && CastToClient()->GetClientVersionBit() & BIT_UnderfootAndLater) - { - EQApplicationPacket *outapp = MakeBuffsPacket(false); - CastToClient()->FastQueuePacket(&outapp); - } - - dead = false; -} - -void Client::HandleLFGuildResponse(ServerPacket *pack) -{ - pack->SetReadPosition(8); - - char Tmp[257]; - - pack->ReadString(Tmp); - - pack->ReadSkipBytes(4); - uint32 SubType, NumberOfMatches; - - SubType = pack->ReadUInt32(); - - switch(SubType) - { - case QSG_LFGuild_PlayerMatches: - { - NumberOfMatches = pack->ReadUInt32(); - uint32 StartOfMatches = pack->GetReadPosition(); - uint32 i = NumberOfMatches; - uint32 PacketSize = 12; - while(i > 0) - { - pack->ReadString(Tmp); - PacketSize += strlen(Tmp) + 1; - pack->ReadString(Tmp); - PacketSize += strlen(Tmp) + 1; - PacketSize += 16; - pack->ReadSkipBytes(16); - --i; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_LFGuild, PacketSize); - outapp->WriteUInt32(3); - outapp->WriteUInt32(0xeb63); // Don't know the significance of this value. - outapp->WriteUInt32(NumberOfMatches); - pack->SetReadPosition(StartOfMatches); - - while(NumberOfMatches > 0) - { - pack->ReadString(Tmp); - outapp->WriteString(Tmp); - pack->ReadString(Tmp); - uint32 Level = pack->ReadUInt32(); - uint32 Class = pack->ReadUInt32(); - uint32 AACount = pack->ReadUInt32(); - uint32 TimeZone = pack->ReadUInt32(); - outapp->WriteUInt32(Level); - outapp->WriteUInt32(Class); - outapp->WriteUInt32(AACount); - outapp->WriteUInt32(TimeZone); - outapp->WriteString(Tmp); - --NumberOfMatches; - } - - FastQueuePacket(&outapp); - break; - } - case QSG_LFGuild_RequestPlayerInfo: - { - EQApplicationPacket *outapp = new EQApplicationPacket(OP_LFGuild, sizeof(LFGuild_PlayerToggle_Struct)); - LFGuild_PlayerToggle_Struct *pts = (LFGuild_PlayerToggle_Struct *)outapp->pBuffer; - - pts->Command = 0; - pack->ReadString(pts->Comment); - pts->TimeZone = pack->ReadUInt32(); - pts->TimePosted = pack->ReadUInt32(); - pts->Toggle = pack->ReadUInt32(); - - FastQueuePacket(&outapp); - - break; - } - case QSG_LFGuild_GuildMatches: - { - NumberOfMatches = pack->ReadUInt32(); - uint32 StartOfMatches = pack->GetReadPosition(); - uint32 i = NumberOfMatches; - uint32 PacketSize = 12; - while(i > 0) - { - pack->ReadString(Tmp); - PacketSize += strlen(Tmp) + 1; - pack->ReadSkipBytes(4); - pack->ReadString(Tmp); - PacketSize += strlen(Tmp) + 1; - PacketSize += 4; - --i; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_LFGuild, PacketSize); - outapp->WriteUInt32(4); - outapp->WriteUInt32(0xeb63); - outapp->WriteUInt32(NumberOfMatches); - pack->SetReadPosition(StartOfMatches); - - while(NumberOfMatches > 0) - { - pack->ReadString(Tmp); - uint32 TimeZone = pack->ReadUInt32(); - outapp->WriteString(Tmp); - outapp->WriteUInt32(TimeZone); - pack->ReadString(Tmp); - outapp->WriteString(Tmp); - --NumberOfMatches; - } - FastQueuePacket(&outapp); - - break; - } - case QSG_LFGuild_RequestGuildInfo: - { - - char Comments[257]; - uint32 FromLevel, ToLevel, Classes, AACount, TimeZone, TimePosted; - - pack->ReadString(Comments); - FromLevel = pack->ReadUInt32(); - ToLevel = pack->ReadUInt32(); - Classes = pack->ReadUInt32(); - AACount = pack->ReadUInt32(); - TimeZone = pack->ReadUInt32(); - TimePosted = pack->ReadUInt32(); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_LFGuild, sizeof(LFGuild_GuildToggle_Struct)); - - LFGuild_GuildToggle_Struct *gts = (LFGuild_GuildToggle_Struct *)outapp->pBuffer; - gts->Command = 1; - strcpy(gts->Comment, Comments); - gts->FromLevel = FromLevel; - gts->ToLevel = ToLevel; - gts->Classes = Classes; - gts->AACount = AACount; - gts->TimeZone = TimeZone; - gts->Toggle = 1; - gts->TimePosted = TimePosted; - gts->Name[0] = 0; - - FastQueuePacket(&outapp); - - break; - } - - default: - break; - } - -} - -void Client::SendLFGuildStatus() -{ - ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + 17); - - pack->WriteUInt32(zone->GetZoneID()); - pack->WriteUInt32(zone->GetInstanceID()); - pack->WriteString(GetName()); - pack->WriteUInt32(QSG_LFGuild); - pack->WriteUInt32(QSG_LFGuild_RequestPlayerInfo); - - worldserver.SendPacket(pack); - safe_delete(pack); - -} - -void Client::SendGuildLFGuildStatus() -{ - ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + +strlen(guild_mgr.GetGuildName(GuildID())) + 18); - - pack->WriteUInt32(zone->GetZoneID()); - pack->WriteUInt32(zone->GetInstanceID()); - pack->WriteString(GetName()); - pack->WriteUInt32(QSG_LFGuild); - pack->WriteUInt32(QSG_LFGuild_RequestGuildInfo); - pack->WriteString(guild_mgr.GetGuildName(GuildID())); - - worldserver.SendPacket(pack); - safe_delete(pack); -} diff --git a/zone/corpse.cpp.orig b/zone/corpse.cpp.orig deleted file mode 100644 index e29ca4303..000000000 --- a/zone/corpse.cpp.orig +++ /dev/null @@ -1,1676 +0,0 @@ -/* 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 -*/ -/* -New class for handeling corpses and everything associated with them. -Child of the Mob class. --Quagmire -*/ -#include "../common/debug.h" -#include -#include -#include -#include -#include -#ifdef _WINDOWS - #define snprintf _snprintf - #define vsnprintf _vsnprintf - #define strncasecmp _strnicmp - #define strcasecmp _stricmp -#endif - -#include "masterentity.h" -#include "../common/packet_functions.h" -#include "../common/string_util.h" -#include "../common/crc32.h" -#include "string_ids.h" -#include "worldserver.h" -#include "../common/rulesys.h" -#include "quest_parser_collection.h" - -extern EntityList entity_list; -extern Zone* zone; -extern WorldServer worldserver; -extern npcDecayTimes_Struct npcCorpseDecayTimes[100]; - -void Corpse::SendEndLootErrorPacket(Client* client) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_LootComplete, 0); - client->QueuePacket(outapp); - safe_delete(outapp); -} - -void Corpse::SendLootReqErrorPacket(Client* client, uint8 response) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_MoneyOnCorpse, sizeof(moneyOnCorpseStruct)); - moneyOnCorpseStruct* d = (moneyOnCorpseStruct*) outapp->pBuffer; - d->response = response; - d->unknown1 = 0x5a; - d->unknown2 = 0x40; - client->QueuePacket(outapp); - safe_delete(outapp); -} - -<<<<<<< HEAD -Corpse* Corpse::LoadFromDBData(uint32 in_dbid, uint32 in_charid, std::string in_charname, const xyz_heading& position, std::string time_of_death, bool rezzed, bool was_at_graveyard) -{ -======= -Corpse* Corpse::LoadCharacterCorpseEntity(uint32 in_dbid, uint32 in_charid, std::string in_charname, float in_x, float in_y, float in_z, float in_heading, std::string time_of_death, bool rezzed, bool was_at_graveyard){ ->>>>>>> master - uint32 item_count = database.GetCharacterCorpseItemCount(in_dbid); - char *buffer = new char[sizeof(PlayerCorpse_Struct) + (item_count * sizeof(player_lootitem::ServerLootItem_Struct))]; - PlayerCorpse_Struct *pcs = (PlayerCorpse_Struct*)buffer; - database.LoadCharacterCorpseData(in_dbid, pcs); - - /* Load Items */ - ItemList itemlist; - ServerLootItem_Struct* tmp = 0; - for (unsigned int i = 0; i < pcs->itemcount; i++) { - tmp = new ServerLootItem_Struct; - memcpy(tmp, &pcs->items[i], sizeof(player_lootitem::ServerLootItem_Struct)); - itemlist.push_back(tmp); - } - - /* Create Corpse Entity */ - Corpse* pc = new Corpse( - in_dbid, // uint32 in_dbid - in_charid, // uint32 in_charid - in_charname.c_str(), // char* in_charname - &itemlist, // ItemList* in_itemlist - pcs->copper, // uint32 in_copper - pcs->silver, // uint32 in_silver - pcs->gold, // uint32 in_gold - pcs->plat, // uint32 in_plat - position, - pcs->size, // float in_size - pcs->gender, // uint8 in_gender - pcs->race, // uint16 in_race - pcs->class_, // uint8 in_class - pcs->deity, // uint8 in_deity - pcs->level, // uint8 in_level - pcs->texture, // uint8 in_texture - pcs->helmtexture, // uint8 in_helmtexture - pcs->exp, // uint32 in_rezexp - was_at_graveyard // bool wasAtGraveyard - ); - if (pcs->locked){ - pc->Lock(); - } - - /* Load Item Tints */ - pc->item_tint[0].color = pcs->item_tint[0].color; - pc->item_tint[1].color = pcs->item_tint[1].color; - pc->item_tint[2].color = pcs->item_tint[2].color; - pc->item_tint[3].color = pcs->item_tint[3].color; - pc->item_tint[4].color = pcs->item_tint[4].color; - pc->item_tint[5].color = pcs->item_tint[5].color; - pc->item_tint[6].color = pcs->item_tint[6].color; - pc->item_tint[7].color = pcs->item_tint[7].color; - pc->item_tint[8].color = pcs->item_tint[8].color; - - /* Load Physical Appearance */ - pc->haircolor = pcs->haircolor; - pc->beardcolor = pcs->beardcolor; - pc->eyecolor1 = pcs->eyecolor1; - pc->eyecolor2 = pcs->eyecolor2; - pc->hairstyle = pcs->hairstyle; - pc->luclinface = pcs->face; - pc->beard = pcs->beard; - pc->drakkin_heritage = pcs->drakkin_heritage; - pc->drakkin_tattoo = pcs->drakkin_tattoo; - pc->drakkin_details = pcs->drakkin_details; - pc->IsRezzed(rezzed); - pc->become_npc = false; - - safe_delete_array(pcs); - - return pc; -} - -Corpse::Corpse(NPC* in_npc, ItemList* in_itemlist, uint32 in_npctypeid, const NPCType** in_npctypedata, uint32 in_decaytime) -<<<<<<< HEAD -// vesuvias - appearence fix -: Mob("Unnamed_Corpse","",0,0,in_npc->GetGender(),in_npc->GetRace(),in_npc->GetClass(),BT_Humanoid,//bodytype added - in_npc->GetDeity(),in_npc->GetLevel(),in_npc->GetNPCTypeID(),in_npc->GetSize(),0, - in_npc->GetPosition(), 0, in_npc->GetTexture(),in_npc->GetHelmTexture(), - 0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0xff,0,0,0,0,0,0,0,0,0), -======= - : Mob("Unnamed_Corpse", // const char* in_name, - "", // const char* in_lastname, - 0, // int32 in_cur_hp, - 0, // int32 in_max_hp, - in_npc->GetGender(), // uint8 in_gender, - in_npc->GetRace(), // uint16 in_race, - in_npc->GetClass(), // uint8 in_class, - BT_Humanoid, // bodyType in_bodytype, - in_npc->GetDeity(), // uint8 in_deity, - in_npc->GetLevel(), // uint8 in_level, - in_npc->GetNPCTypeID(), // uint32 in_npctype_id, - in_npc->GetSize(), // float in_size, - 0, // float in_runspeed, - in_npc->GetHeading(), // float in_heading, - in_npc->GetX(), // float in_x_pos, - in_npc->GetY(), // float in_y_pos, - in_npc->GetZ(), // float in_z_pos, - 0, // uint8 in_light, - in_npc->GetTexture(), // uint8 in_texture, - in_npc->GetHelmTexture(), // uint8 in_helmtexture, - 0, // uint16 in_ac, - 0, // uint16 in_atk, - 0, // uint16 in_str, - 0, // uint16 in_sta, - 0, // uint16 in_dex, - 0, // uint16 in_agi, - 0, // uint16 in_int, - 0, // uint16 in_wis, - 0, // uint16 in_cha, - 0, // uint8 in_haircolor, - 0, // uint8 in_beardcolor, - 0, // uint8 in_eyecolor1, // the eyecolors always seem to be the same, maybe left and right eye? - 0, // uint8 in_eyecolor2, - 0, // uint8 in_hairstyle, - 0, // uint8 in_luclinface, - 0, // uint8 in_beard, - 0, // uint32 in_drakkin_heritage, - 0, // uint32 in_drakkin_tattoo, - 0, // uint32 in_drakkin_details, - 0, // uint32 in_armor_tint[_MaterialCount], - 0xff, // uint8 in_aa_title, - 0, // uint8 in_see_invis, // see through invis/ivu - 0, // uint8 in_see_invis_undead, - 0, // uint8 in_see_hide, - 0, // uint8 in_see_improved_hide, - 0, // int32 in_hp_regen, - 0, // int32 in_mana_regen, - 0, // uint8 in_qglobal, - 0, // uint8 in_maxlevel, - 0 // uint32 in_scalerate -), ->>>>>>> master - corpse_decay_timer(in_decaytime), - corpse_rez_timer(0), - corpse_delay_timer(RuleI(NPC, CorpseUnlockTimer)), - corpse_graveyard_timer(0), - loot_cooldown_timer(10) -{ - corpse_graveyard_timer.Disable(); - - memset(item_tint, 0, sizeof(item_tint)); - - is_corpse_changed = false; - is_player_corpse = false; - is_locked = false; - being_looted_by = 0xFFFFFFFF; - if (in_itemlist) { - itemlist = *in_itemlist; - in_itemlist->clear(); - } - - SetCash(in_npc->GetCopper(), in_npc->GetSilver(), in_npc->GetGold(), in_npc->GetPlatinum()); - - npctype_id = in_npctypeid; - SetPlayerKillItemID(0); - char_id = 0; - corpse_db_id = 0; - player_corpse_depop = false; - strcpy(corpse_name, in_npc->GetName()); - strcpy(name, in_npc->GetName()); - - for(int count = 0; count < 100; count++) { - if ((level >= npcCorpseDecayTimes[count].minlvl) && (level <= npcCorpseDecayTimes[count].maxlvl)) { - corpse_decay_timer.SetTimer(npcCorpseDecayTimes[count].seconds*1000); - break; - } - } - if(IsEmpty()) { - corpse_decay_timer.SetTimer(RuleI(NPC,EmptyNPCCorpseDecayTimeMS)+1000); - } - - - if(in_npc->HasPrivateCorpse()) { - corpse_delay_timer.SetTimer(corpse_decay_timer.GetRemainingTime() + 1000); - } - - for (int i = 0; i < MAX_LOOTERS; i++){ - allowed_looters[i] = 0; - } - this->rez_experience = 0; -} - -Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( - "Unnamed_Corpse", // const char* in_name, - "", // const char* in_lastname, - 0, // int32 in_cur_hp, - 0, // int32 in_max_hp, - client->GetGender(), // uint8 in_gender, - client->GetRace(), // uint16 in_race, - client->GetClass(), // uint8 in_class, - BT_Humanoid, // bodyType in_bodytype, - client->GetDeity(), // uint8 in_deity, - client->GetLevel(), // uint8 in_level, - 0, // uint32 in_npctype_id, - client->GetSize(), // float in_size, - 0, // float in_runspeed, - client->GetPosition(), - 0, // uint8 in_light, - client->GetTexture(), // uint8 in_texture, - client->GetHelmTexture(), // uint8 in_helmtexture, - 0, // uint16 in_ac, - 0, // uint16 in_atk, - 0, // uint16 in_str, - 0, // uint16 in_sta, - 0, // uint16 in_dex, - 0, // uint16 in_agi, - 0, // uint16 in_int, - 0, // uint16 in_wis, - 0, // uint16 in_cha, - client->GetPP().haircolor, // uint8 in_haircolor, - client->GetPP().beardcolor, // uint8 in_beardcolor, - client->GetPP().eyecolor1, // uint8 in_eyecolor1, // the eyecolors always seem to be the same, maybe left and right eye? - client->GetPP().eyecolor2, // uint8 in_eyecolor2, - client->GetPP().hairstyle, // uint8 in_hairstyle, - client->GetPP().face, // uint8 in_luclinface, - client->GetPP().beard, // uint8 in_beard, - client->GetPP().drakkin_heritage, // uint32 in_drakkin_heritage, - client->GetPP().drakkin_tattoo, // uint32 in_drakkin_tattoo, - client->GetPP().drakkin_details, // uint32 in_drakkin_details, - 0, // uint32 in_armor_tint[_MaterialCount], - 0xff, // uint8 in_aa_title, - 0, // uint8 in_see_invis, // see through invis - 0, // uint8 in_see_invis_undead, // see through invis vs. undead - 0, // uint8 in_see_hide, - 0, // uint8 in_see_improved_hide, - 0, // int32 in_hp_regen, - 0, // int32 in_mana_regen, - 0, // uint8 in_qglobal, - 0, // uint8 in_maxlevel, - 0 // uint32 in_scalerate - ), - corpse_decay_timer(RuleI(Character, CorpseDecayTimeMS)), - corpse_rez_timer(RuleI(Character, CorpseResTimeMS)), - corpse_delay_timer(RuleI(NPC, CorpseUnlockTimer)), - corpse_graveyard_timer(RuleI(Zone, GraveyardTimeMS)), - loot_cooldown_timer(10) -{ - int i; - - PlayerProfile_Struct *pp = &client->GetPP(); - ItemInst *item; - - /* Check if Zone has Graveyard First */ - if(!zone->HasGraveyard()) { - corpse_graveyard_timer.Disable(); - } - - memset(item_tint, 0, sizeof(item_tint)); - - for (i = 0; i < MAX_LOOTERS; i++){ - allowed_looters[i] = 0; - } - - is_corpse_changed = true; - rez_experience = in_rezexp; - can_corpse_be_rezzed = true; - is_player_corpse = true; - is_locked = false; - being_looted_by = 0xFFFFFFFF; - char_id = client->CharacterID(); - corpse_db_id = 0; - player_corpse_depop = false; - copper = 0; - silver = 0; - gold = 0; - platinum = 0; - -<<<<<<< HEAD - strcpy(orgname, pp->name); - strcpy(name, pp->name); -======= - strcpy(corpse_name, pp->name); - strcpy(name, pp->name); ->>>>>>> master - - /* become_npc was not being initialized which led to some pretty funky things with newly created corpses */ - become_npc = false; - - SetPlayerKillItemID(0); - - /* Check Rule to see if we can leave corpses */ - if(!RuleB(Character, LeaveNakedCorpses) || - RuleB(Character, LeaveCorpses) && - GetLevel() >= RuleI(Character, DeathItemLossLevel)) { - // cash - // Let's not move the cash when 'RespawnFromHover = true' && 'client->GetClientVersion() < EQClientSoF' since the client doesn't. - // (change to first client that supports 'death hover' mode, if not SoF.) - if (!RuleB(Character, RespawnFromHover) || client->GetClientVersion() < EQClientSoF) { - SetCash(pp->copper, pp->silver, pp->gold, pp->platinum); - pp->copper = 0; - pp->silver = 0; - pp->gold = 0; - pp->platinum = 0; - } - - // get their tints - memcpy(item_tint, &client->GetPP().item_tint, sizeof(item_tint)); - - // solar: TODO soulbound items need not be added to corpse, but they need - // to go into the regular slots on the player, out of bags - - // worn + inventory + cursor - std::list removed_list; - bool cursor = false; - for(i = MAIN_BEGIN; i < EmuConstants::MAP_POSSESSIONS_SIZE; i++) { - if(i == MainAmmo && client->GetClientVersion() >= EQClientSoF) { - item = client->GetInv().GetItem(MainPowerSource); - if((item && (!client->IsBecomeNPC())) || (item && client->IsBecomeNPC() && !item->GetItem()->NoRent)) { - std::list slot_list = MoveItemToCorpse(client, item, MainPowerSource); - removed_list.merge(slot_list); - } - - } - - item = client->GetInv().GetItem(i); - if((item && (!client->IsBecomeNPC())) || (item && client->IsBecomeNPC() && !item->GetItem()->NoRent)) { - std::list slot_list = MoveItemToCorpse(client, item, i); - removed_list.merge(slot_list); - } - } - - // cursor queue // (change to first client that supports 'death hover' mode, if not SoF.) - if (!RuleB(Character, RespawnFromHover) || client->GetClientVersion() < EQClientSoF) { - - // bumped starting assignment to 8001 because any in-memory 'slot 8000' item was moved above as 'slot 30' - // this was mainly for client profile state reflection..should match db player inventory entries now. - - iter_queue it; - for (it = client->GetInv().cursor_begin(), i = 8001; it != client->GetInv().cursor_end(); ++it, i++) { - item = *it; - if ((item && (!client->IsBecomeNPC())) || (item && client->IsBecomeNPC() && !item->GetItem()->NoRent)) { - std::list slot_list = MoveItemToCorpse(client, item, i); - removed_list.merge(slot_list); - cursor = true; - } - } - } - - database.TransactionBegin(); - if (removed_list.size() != 0) { - std::stringstream ss(""); - ss << "DELETE FROM inventory WHERE charid=" << client->CharacterID(); - ss << " AND ("; - std::list::const_iterator iter = removed_list.begin(); - bool first = true; - while (iter != removed_list.end()) { - if (first) { - first = false; - } - else { - ss << " OR "; - } - ss << "slotid=" << (*iter); - ++iter; - } - ss << ")"; - database.QueryDatabase(ss.str().c_str()); - } - - if (cursor) { // all cursor items should be on corpse (client < SoF or RespawnFromHover = false) - while (!client->GetInv().CursorEmpty()) - client->DeleteItemInInventory(MainCursor, 0, false, false); - } - else { // only visible cursor made it to corpse (client >= Sof and RespawnFromHover = true) - std::list::const_iterator start = client->GetInv().cursor_begin(); - std::list::const_iterator finish = client->GetInv().cursor_end(); - database.SaveCursor(client->CharacterID(), start, finish); - } - - client->CalcBonuses(); // will only affect offline profile viewing of dead characters..unneeded overhead - client->Save(); - - IsRezzed(false); - Save(); - database.TransactionCommit(); - - return; - } //end "not leaving naked corpses" - - IsRezzed(false); - Save(); -} - -std::list Corpse::MoveItemToCorpse(Client *client, ItemInst *item, int16 equipslot) -{ - int bagindex; - int16 interior_slot; - ItemInst *interior_item; - std::list returnlist; - - AddItem(item->GetItem()->ID, item->GetCharges(), equipslot, item->GetAugmentItemID(0), item->GetAugmentItemID(1), item->GetAugmentItemID(2), item->GetAugmentItemID(3), item->GetAugmentItemID(4)); - returnlist.push_back(equipslot); - - // Qualified bag slot iterations. processing bag slots that don't exist is probably not a good idea. - if (item->IsType(ItemClassContainer) && ((equipslot >= EmuConstants::GENERAL_BEGIN && equipslot <= MainCursor))) { - for (bagindex = SUB_BEGIN; bagindex <= EmuConstants::ITEM_CONTAINER_SIZE; bagindex++) { - // For empty bags in cursor queue, slot was previously being resolved as SLOT_INVALID (-1) - interior_slot = Inventory::CalcSlotId(equipslot, bagindex); - interior_item = client->GetInv().GetItem(interior_slot); - - if (interior_item) { - AddItem(interior_item->GetItem()->ID, interior_item->GetCharges(), interior_slot, interior_item->GetAugmentItemID(0), interior_item->GetAugmentItemID(1), interior_item->GetAugmentItemID(2), interior_item->GetAugmentItemID(3), interior_item->GetAugmentItemID(4)); - returnlist.push_back(Inventory::CalcSlotId(equipslot, bagindex)); - client->DeleteItemInInventory(interior_slot, 0, true, false); - } - } - } - client->DeleteItemInInventory(equipslot, 0, true, false); - return returnlist; -} - -<<<<<<< HEAD -// To be called from LoadFromDBData -// Mongrel: added see_invis and see_invis_undead -Corpse::Corpse(uint32 in_dbid, uint32 in_charid, const char* in_charname, ItemList* in_itemlist, uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 in_plat, const xyz_heading& position, float in_size, uint8 in_gender, uint16 in_race, uint8 in_class, uint8 in_deity, uint8 in_level, uint8 in_texture, uint8 in_helmtexture,uint32 in_rezexp, bool wasAtGraveyard) -: Mob("Unnamed_Corpse", -"", -0, -0, -in_gender, -in_race, -in_class, -BT_Humanoid, -in_deity, -in_level, -0, -in_size, -0, -position, -0, -in_texture, -in_helmtexture, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0xff, -0, -0, -0, -0, -0, -0, -0, -0, -0), -======= -/* Called from Database Load */ - -Corpse::Corpse(uint32 in_dbid, uint32 in_charid, const char* in_charname, ItemList* in_itemlist, uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 in_plat, float in_x, float in_y, float in_z, float in_heading, float in_size, uint8 in_gender, uint16 in_race, uint8 in_class, uint8 in_deity, uint8 in_level, uint8 in_texture, uint8 in_helmtexture,uint32 in_rezexp, bool wasAtGraveyard) - : Mob("Unnamed_Corpse", // const char* in_name, - "", // const char* in_lastname, - 0, // int32 in_cur_hp, - 0, // int32 in_max_hp, - in_gender, // uint8 in_gender, - in_race, // uint16 in_race, - in_class, // uint8 in_class, - BT_Humanoid, // bodyType in_bodytype, - in_deity, // uint8 in_deity, - in_level, // uint8 in_level, - 0, // uint32 in_npctype_id, - in_size, // float in_size, - 0, // float in_runspeed, - in_heading, // float in_heading, - in_x, // float in_x_pos, - in_y, // float in_y_pos, - in_z, // float in_z_pos, - 0, // uint8 in_light, - in_texture, // uint8 in_texture, - in_helmtexture, // uint8 in_helmtexture, - 0, // uint16 in_ac, - 0, // uint16 in_atk, - 0, // uint16 in_str, - 0, // uint16 in_sta, - 0, // uint16 in_dex, - 0, // uint16 in_agi, - 0, // uint16 in_int, - 0, // uint16 in_wis, - 0, // uint16 in_cha, - 0, // uint8 in_haircolor, - 0, // uint8 in_beardcolor, - 0, // uint8 in_eyecolor1, // the eyecolors always seem to be the same, maybe left and right eye? - 0, // uint8 in_eyecolor2, - 0, // uint8 in_hairstyle, - 0, // uint8 in_luclinface, - 0, // uint8 in_beard, - 0, // uint32 in_drakkin_heritage, - 0, // uint32 in_drakkin_tattoo, - 0, // uint32 in_drakkin_details, - 0, // uint32 in_armor_tint[_MaterialCount], - 0xff, // uint8 in_aa_title, - 0, // uint8 in_see_invis, // see through invis/ivu - 0, // uint8 in_see_invis_undead, - 0, // uint8 in_see_hide, - 0, // uint8 in_see_improved_hide, - 0, // int32 in_hp_regen, - 0, // int32 in_mana_regen, - 0, // uint8 in_qglobal, - 0, // uint8 in_maxlevel, - 0), // uint32 in_scalerate ->>>>>>> master - corpse_decay_timer(RuleI(Character, CorpseDecayTimeMS)), - corpse_rez_timer(RuleI(Character, CorpseResTimeMS)), - corpse_delay_timer(RuleI(NPC, CorpseUnlockTimer)), - corpse_graveyard_timer(RuleI(Zone, GraveyardTimeMS)), - loot_cooldown_timer(10) -{ - - LoadPlayerCorpseDecayTime(in_dbid); - - if (!zone->HasGraveyard() || wasAtGraveyard){ - corpse_graveyard_timer.Disable(); - } - - memset(item_tint, 0, sizeof(item_tint)); - - is_corpse_changed = false; - is_player_corpse = true; - is_locked = false; - being_looted_by = 0xFFFFFFFF; - corpse_db_id = in_dbid; - player_corpse_depop = false; - char_id = in_charid; - itemlist = *in_itemlist; - in_itemlist->clear(); - - strcpy(corpse_name, in_charname); - strcpy(name, in_charname); - - this->copper = in_copper; - this->silver = in_silver; - this->gold = in_gold; - this->platinum = in_plat; - - rez_experience = in_rezexp; - - for (int i = 0; i < MAX_LOOTERS; i++) - allowed_looters[i] = 0; -<<<<<<< HEAD - - SetPKItem(0); -======= - } - SetPlayerKillItemID(0); ->>>>>>> master -} - -Corpse::~Corpse() { - if (is_player_corpse && !(player_corpse_depop && corpse_db_id == 0)) { - Save(); - } - ItemList::iterator cur,end; - cur = itemlist.begin(); - end = itemlist.end(); - for(; cur != end; ++cur) { - ServerLootItem_Struct* item = *cur; - safe_delete(item); - } - itemlist.clear(); -} - -/* -this needs to be called AFTER the entity_id is set -the client does this too, so it's unchangable -*/ -void Corpse::CalcCorpseName() { - EntityList::RemoveNumbers(name); - char tmp[64]; - if (is_player_corpse){ - snprintf(tmp, sizeof(tmp), "'s corpse%d", GetID()); - } - else{ - snprintf(tmp, sizeof(tmp), "`s_corpse%d", GetID()); - } - name[(sizeof(name) - 1) - strlen(tmp)] = 0; - strcat(name, tmp); -} - -bool Corpse::Save() { - if (!is_player_corpse) - return true; - if (!is_corpse_changed) - return true; - - uint32 tmp = this->CountItems(); - uint32 tmpsize = sizeof(PlayerCorpse_Struct) + (tmp * sizeof(player_lootitem::ServerLootItem_Struct)); - - PlayerCorpse_Struct* dbpc = (PlayerCorpse_Struct*) new uchar[tmpsize]; - memset(dbpc, 0, tmpsize); - dbpc->itemcount = tmp; - dbpc->size = this->size; - dbpc->locked = is_locked; - dbpc->copper = this->copper; - dbpc->silver = this->silver; - dbpc->gold = this->gold; - dbpc->plat = this->platinum; - dbpc->race = this->race; - dbpc->class_ = class_; - dbpc->gender = gender; - dbpc->deity = deity; - dbpc->level = level; - dbpc->texture = this->texture; - dbpc->helmtexture = this->helmtexture; - dbpc->exp = rez_experience; - - memcpy(dbpc->item_tint, item_tint, sizeof(dbpc->item_tint)); - dbpc->haircolor = haircolor; - dbpc->beardcolor = beardcolor; - dbpc->eyecolor2 = eyecolor1; - dbpc->hairstyle = hairstyle; - dbpc->face = luclinface; - dbpc->beard = beard; - dbpc->drakkin_heritage = drakkin_heritage; - dbpc->drakkin_tattoo = drakkin_tattoo; - dbpc->drakkin_details = drakkin_details; - - uint32 x = 0; - ItemList::iterator cur, end; - cur = itemlist.begin(); - end = itemlist.end(); - for (; cur != end; ++cur) { - ServerLootItem_Struct* item = *cur; - memcpy((char*)&dbpc->items[x++], (char*)item, sizeof(ServerLootItem_Struct)); - } - - /* Create New Corpse*/ - if (corpse_db_id == 0) { -<<<<<<< HEAD - corpse_db_id = database.SaveCharacterCorpse(char_id, orgname, zone->GetZoneID(), zone->GetInstanceID(), dbpc, m_Position); - } - /* Update Corpse Data */ - else{ - corpse_db_id = database.UpdateCharacterCorpse(corpse_db_id, char_id, orgname, zone->GetZoneID(), zone->GetInstanceID(), dbpc, m_Position, IsRezzed()); -======= - corpse_db_id = database.SaveCharacterCorpse(char_id, corpse_name, zone->GetZoneID(), zone->GetInstanceID(), dbpc, x_pos, y_pos, z_pos, heading); - } - /* Update Corpse Data */ - else{ - corpse_db_id = database.UpdateCharacterCorpse(corpse_db_id, char_id, corpse_name, zone->GetZoneID(), zone->GetInstanceID(), dbpc, x_pos, y_pos, z_pos, heading, IsRezzed()); ->>>>>>> master - } - - safe_delete_array(dbpc); - - return true; -} - -void Corpse::Delete() { - if (IsPlayerCorpse() && corpse_db_id != 0) - database.DeleteCharacterCorpse(corpse_db_id); - - corpse_db_id = 0; - player_corpse_depop = true; -} - -void Corpse::Bury() { - if (IsPlayerCorpse() && corpse_db_id != 0){ - database.BuryCharacterCorpse(corpse_db_id); - } - corpse_db_id = 0; - player_corpse_depop = true; -} - -void Corpse::DepopNPCCorpse() { - if (IsNPCCorpse()) - player_corpse_depop = true; -} - -void Corpse::DepopPlayerCorpse() { - player_corpse_depop = true; -} - -uint32 Corpse::CountItems() { - return itemlist.size(); -} - -void Corpse::AddItem(uint32 itemnum, uint16 charges, int16 slot, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5) { - if (!database.GetItem(itemnum)) - return; - - is_corpse_changed = true; - - ServerLootItem_Struct* item = new ServerLootItem_Struct; - - memset(item, 0, sizeof(ServerLootItem_Struct)); - item->item_id = itemnum; - item->charges = charges; - item->equip_slot = slot; - item->aug_1=aug1; - item->aug_2=aug2; - item->aug_3=aug3; - item->aug_4=aug4; - item->aug_5=aug5; - itemlist.push_back(item); -} - -ServerLootItem_Struct* Corpse::GetItem(uint16 lootslot, ServerLootItem_Struct** bag_item_data) { - ServerLootItem_Struct *sitem = 0, *sitem2; - - ItemList::iterator cur,end; - cur = itemlist.begin(); - end = itemlist.end(); - for(; cur != end; ++cur) { - if((*cur)->lootslot == lootslot) { - sitem = *cur; - break; - } - } - - if (sitem && bag_item_data && Inventory::SupportsContainers(sitem->equip_slot)) { - int16 bagstart = Inventory::CalcSlotId(sitem->equip_slot, SUB_BEGIN); - - cur = itemlist.begin(); - end = itemlist.end(); - for (; cur != end; ++cur) { - sitem2 = *cur; - if (sitem2->equip_slot >= bagstart && sitem2->equip_slot < bagstart + 10) { - bag_item_data[sitem2->equip_slot - bagstart] = sitem2; - } - } - } - - return sitem; -} - -uint32 Corpse::GetWornItem(int16 equipSlot) const { - ItemList::const_iterator cur,end; - cur = itemlist.begin(); - end = itemlist.end(); - for(; cur != end; ++cur) { - ServerLootItem_Struct* item = *cur; - if (item->equip_slot == equipSlot) { - return item->item_id; - } - } - - return 0; -} - -void Corpse::RemoveItem(uint16 lootslot) { - if (lootslot == 0xFFFF) - return; - - ItemList::iterator cur,end; - cur = itemlist.begin(); - end = itemlist.end(); - for (; cur != end; ++cur) { - ServerLootItem_Struct* sitem = *cur; - if (sitem->lootslot == lootslot) { - RemoveItem(sitem); - return; - } - } -} - -void Corpse::RemoveItem(ServerLootItem_Struct* item_data){ - uint8 material; - ItemList::iterator cur,end; - cur = itemlist.begin(); - end = itemlist.end(); - for(; cur != end; ++cur) { - ServerLootItem_Struct* sitem = *cur; - if (sitem == item_data) { - is_corpse_changed = true; - itemlist.erase(cur); - - material = Inventory::CalcMaterialFromSlot(sitem->equip_slot); - if(material != _MaterialInvalid) - SendWearChange(material); - - safe_delete(sitem); - - return; - } - } -} - -void Corpse::SetCash(uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 in_platinum) { - this->copper = in_copper; - this->silver = in_silver; - this->gold = in_gold; - this->platinum = in_platinum; - is_corpse_changed = true; -} - -void Corpse::RemoveCash() { - this->copper = 0; - this->silver = 0; - this->gold = 0; - this->platinum = 0; - is_corpse_changed = true; -} - -bool Corpse::IsEmpty() const { - if (copper != 0 || silver != 0 || gold != 0 || platinum != 0) - return false; - - return(itemlist.size() == 0); -} - -bool Corpse::Process() { - if (player_corpse_depop){ - return false; - } - - if (corpse_delay_timer.Check()) { - for (int i = 0; i < MAX_LOOTERS; i++){ - allowed_looters[i] = 0; - } - corpse_delay_timer.Disable(); - return true; - } - - if (corpse_graveyard_timer.Check()) { - if (zone->HasGraveyard()) { - Save(); - player_corpse_depop = true; - database.SendCharacterCorpseToGraveyard(corpse_db_id, zone->graveyard_zoneid(), - (zone->GetZoneID() == zone->graveyard_zoneid()) ? zone->GetInstanceID() : 0, zone->GetGraveyardPoint()); - corpse_graveyard_timer.Disable(); - ServerPacket* pack = new ServerPacket(ServerOP_SpawnPlayerCorpse, sizeof(SpawnPlayerCorpse_Struct)); - SpawnPlayerCorpse_Struct* spc = (SpawnPlayerCorpse_Struct*)pack->pBuffer; - spc->player_corpse_id = corpse_db_id; - spc->zone_id = zone->graveyard_zoneid(); - worldserver.SendPacket(pack); - safe_delete(pack); - LogFile->write(EQEMuLog::Debug, "Moved %s player corpse to the designated graveyard in zone %s.", this->GetName(), database.GetZoneName(zone->graveyard_zoneid())); - corpse_db_id = 0; - } - - corpse_graveyard_timer.Disable(); - return false; - } - /* - if(corpse_res_timer.Check()) { - can_rez = false; - corpse_res_timer.Disable(); - } - */ - - /* This is when a corpse hits decay timer and does checks*/ - if (corpse_decay_timer.Check()) { - /* NPC */ - if (IsNPCCorpse()){ - corpse_decay_timer.Disable(); - return false; - } - /* Client */ - if (!RuleB(Zone, EnableShadowrest)){ - Delete(); - } - else { - if (database.BuryCharacterCorpse(corpse_db_id)) { - Save(); - player_corpse_depop = true; - corpse_db_id = 0; - LogFile->write(EQEMuLog::Debug, "Tagged %s player corpse has burried.", this->GetName()); - } - else { - LogFile->write(EQEMuLog::Error, "Unable to bury %s player corpse.", this->GetName()); - return true; - } - } - corpse_decay_timer.Disable(); - return false; - } - - return true; -} - -void Corpse::SetDecayTimer(uint32 decaytime) { - if (decaytime == 0) - corpse_decay_timer.Trigger(); - else - corpse_decay_timer.Start(decaytime); -} - -bool Corpse::CanPlayerLoot(int charid) { - uint8 looters = 0; - for (int i = 0; i < MAX_LOOTERS; i++) { - if (allowed_looters[i] != 0){ - looters++; - } - - if (allowed_looters[i] == charid){ - return true; - } - } - /* If we have no looters, obviously client can loot */ - if (looters == 0){ - return true; - } - return false; -} - -void Corpse::AllowPlayerLoot(Mob *them, uint8 slot) { - if(slot >= MAX_LOOTERS) - return; - if(them == nullptr || !them->IsClient()) - return; - - allowed_looters[slot] = them->CastToClient()->CharacterID(); -} - -void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* app) { - // Added 12/08. Started compressing loot struct on live. - char tmp[10]; - if(player_corpse_depop) { - SendLootReqErrorPacket(client, 0); - return; - } - - if(IsPlayerCorpse() && corpse_db_id == 0) { - // SendLootReqErrorPacket(client, 0); - client->Message(13, "Warning: Corpse's dbid = 0! Corpse will not survive zone shutdown!"); - std::cout << "Error: PlayerCorpse::MakeLootRequestPackets: dbid = 0!" << std::endl; - // return; - } - - if(is_locked && client->Admin() < 100) { - SendLootReqErrorPacket(client, 0); - client->Message(13, "Error: Corpse locked by GM."); - return; - } - - if(being_looted_by == 0) { - being_looted_by = 0xFFFFFFFF; - } - - if(this->being_looted_by != 0xFFFFFFFF) { - // lets double check.... - Entity* looter = entity_list.GetID(this->being_looted_by); - if(looter == 0) { - this->being_looted_by = 0xFFFFFFFF; - } - } - - uint8 Loot_Request_Type = 1; - bool loot_coin = false; - if(database.GetVariable("LootCoin", tmp, 9)) { loot_coin = (atoi(tmp) == 1); } - - if (this->being_looted_by != 0xFFFFFFFF && this->being_looted_by != client->GetID()) { - SendLootReqErrorPacket(client, 0); - Loot_Request_Type = 0; - } - else if (IsPlayerCorpse() && char_id == client->CharacterID()) { - Loot_Request_Type = 2; - } - else if ((IsNPCCorpse() || become_npc) && CanPlayerLoot(client->CharacterID())) { - Loot_Request_Type = 2; - } - else if (GetPlayerKillItem() == -1 && CanPlayerLoot(client->CharacterID())) { /* PVP loot all items, variable cash */ - Loot_Request_Type = 3; - } - else if (GetPlayerKillItem() == 1 && CanPlayerLoot(client->CharacterID())) { /* PVP loot 1 item, variable cash */ - Loot_Request_Type = 4; - } - else if (GetPlayerKillItem() > 1 && CanPlayerLoot(client->CharacterID())) { /* PVP loot 1 set item, variable cash */ - Loot_Request_Type = 5; - } - - if (Loot_Request_Type == 1) { - if (client->Admin() < 100 || !client->GetGM()) { - SendLootReqErrorPacket(client, 2); - } - } - - if(Loot_Request_Type >= 2 || (Loot_Request_Type == 1 && client->Admin() >= 100 && client->GetGM())) { - this->being_looted_by = client->GetID(); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_MoneyOnCorpse, sizeof(moneyOnCorpseStruct)); - moneyOnCorpseStruct* d = (moneyOnCorpseStruct*) outapp->pBuffer; - - d->response = 1; - d->unknown1 = 0x42; - d->unknown2 = 0xef; - - /* Dont take the coin off if it's a gm peeking at the corpse */ - if(Loot_Request_Type == 2 || (Loot_Request_Type >= 3 && loot_coin)) { - if(!IsPlayerCorpse() && client->IsGrouped() && client->AutoSplitEnabled() && client->GetGroup()) { - d->copper = 0; - d->silver = 0; - d->gold = 0; - d->platinum = 0; - Group *cgroup = client->GetGroup(); - cgroup->SplitMoney(GetCopper(), GetSilver(), GetGold(), GetPlatinum(), client); - } - else { - d->copper = this->GetCopper(); - d->silver = this->GetSilver(); - d->gold = this->GetGold(); - d->platinum = this->GetPlatinum(); - client->AddMoneyToPP(GetCopper(), GetSilver(), GetGold(), GetPlatinum(), false); - } - - RemoveCash(); - Save(); - } - - outapp->priority = 6; - client->QueuePacket(outapp); - safe_delete(outapp); - if(Loot_Request_Type == 5) { - int pkitem = GetPlayerKillItem(); - const Item_Struct* item = database.GetItem(pkitem); - ItemInst* inst = database.CreateItem(item, item->MaxCharges); - if(inst) { - client->SendItemPacket(EmuConstants::CORPSE_BEGIN, inst, ItemPacketLoot); - safe_delete(inst); - } - else { client->Message(13, "Could not find item number %i to send!!", GetPlayerKillItem()); } - - client->QueuePacket(app); - return; - } - - int i = 0; - const Item_Struct* item = 0; - ItemList::iterator cur,end; - cur = itemlist.begin(); - end = itemlist.end(); - - int corpselootlimit = EQLimits::InventoryMapSize(MapCorpse, client->GetClientVersion()); - - for(; cur != end; ++cur) { - ServerLootItem_Struct* item_data = *cur; - item_data->lootslot = 0xFFFF; - - // Dont display the item if it's in a bag - - // Added cursor queue slots to corpse item visibility list. Nothing else should be making it to corpse. - if(!IsPlayerCorpse() || item_data->equip_slot <= MainCursor || item_data->equip_slot == MainPowerSource || Loot_Request_Type>=3 || - (item_data->equip_slot >= 8000 && item_data->equip_slot <= 8999)) { - if(i < corpselootlimit) { - item = database.GetItem(item_data->item_id); - if(client && item) { - ItemInst* inst = database.CreateItem(item, item_data->charges, item_data->aug_1, item_data->aug_2, item_data->aug_3, item_data->aug_4, item_data->aug_5); - if(inst) { - // MainGeneral1 is the corpse inventory start offset for Ti(EMu) - CORPSE_END = MainGeneral1 + MainCursor - client->SendItemPacket(i + EmuConstants::CORPSE_BEGIN, inst, ItemPacketLoot); - safe_delete(inst); - } - - item_data->lootslot = i; - } - } - - i++; - } - } - - if(IsPlayerCorpse() && (char_id == client->CharacterID() || client->GetGM())) { - if(i > corpselootlimit) { - client->Message(15, "*** This corpse contains more items than can be displayed! ***"); - client->Message(0, "Remove items and re-loot corpse to access remaining inventory."); - client->Message(0, "(%s contains %i additional %s.)", GetName(), (i - corpselootlimit), (i - corpselootlimit) == 1 ? "item" : "items"); - } - - if(IsPlayerCorpse() && i == 0 && itemlist.size() > 0) { // somehow, player corpse contains items, but client doesn't see them... - client->Message(13, "This corpse contains items that are inaccessable!"); - client->Message(15, "Contact a GM for item replacement, if necessary."); - client->Message(15, "BUGGED CORPSE [DBID: %i, Name: %s, Item Count: %i]", GetCorpseDBID(), GetName(), itemlist.size()); - - cur = itemlist.begin(); - end = itemlist.end(); - for(; cur != end; ++cur) { - ServerLootItem_Struct* item_data = *cur; - item = database.GetItem(item_data->item_id); - LogFile->write(EQEMuLog::Debug, "Corpse Looting: %s was not sent to client loot window (corpse_dbid: %i, charname: %s(%s))", item->Name, GetCorpseDBID(), client->GetName(), client->GetGM() ? "GM" : "Owner"); - client->Message(0, "Inaccessable Corpse Item: %s", item->Name); - } - } - } - } - - // Disgrace: Client seems to require that we send the packet back... - client->QueuePacket(app); - - // This is required for the 'Loot All' feature to work for SoD clients. I expect it is to tell the client that the - // server has now sent all the items on the corpse. - if(client->GetClientVersion() >= EQClientSoD) { SendLootReqErrorPacket(client, 6); } -} - -void Corpse::LootItem(Client* client, const EQApplicationPacket* app) { - /* This gets sent no matter what as a sort of ACK */ - client->QueuePacket(app); - - if (!loot_cooldown_timer.Check()) { - SendEndLootErrorPacket(client); - //unlock corpse for others - if (this->being_looted_by = client->GetID()) { - being_looted_by = 0xFFFFFFFF; - } - return; - } - - /* To prevent item loss for a player using 'Loot All' who doesn't have inventory space for all their items. */ - if (RuleB(Character, CheckCursorEmptyWhenLooting) && !client->GetInv().CursorEmpty()) { - client->Message(13, "You may not loot an item while you have an item on your cursor."); - SendEndLootErrorPacket(client); - /* Unlock corpse for others */ - if (this->being_looted_by = client->GetID()) { - being_looted_by = 0xFFFFFFFF; - } - return; - } - - LootingItem_Struct* lootitem = (LootingItem_Struct*)app->pBuffer; - - if (this->being_looted_by != client->GetID()) { - client->Message(13, "Error: Corpse::LootItem: BeingLootedBy != client"); - SendEndLootErrorPacket(client); - return; - } - if (IsPlayerCorpse() && !CanPlayerLoot(client->CharacterID()) && !become_npc && (char_id != client->CharacterID() && client->Admin() < 150)) { - client->Message(13, "Error: This is a player corpse and you dont own it."); - SendEndLootErrorPacket(client); - return; - } - if (is_locked && client->Admin() < 100) { - SendLootReqErrorPacket(client, 0); - client->Message(13, "Error: Corpse locked by GM."); - return; - } - if (IsPlayerCorpse() && (char_id != client->CharacterID()) && CanPlayerLoot(client->CharacterID()) && GetPlayerKillItem() == 0){ - client->Message(13, "Error: You cannot loot any more items from this corpse."); - SendEndLootErrorPacket(client); - being_looted_by = 0xFFFFFFFF; - return; - } - const Item_Struct* item = 0; - ItemInst *inst = 0; - ServerLootItem_Struct* item_data = nullptr, *bag_item_data[10]; - - memset(bag_item_data, 0, sizeof(bag_item_data)); - if (GetPlayerKillItem() > 1){ - item = database.GetItem(GetPlayerKillItem()); - } - else if (GetPlayerKillItem() == -1 || GetPlayerKillItem() == 1){ - item_data = GetItem(lootitem->slot_id - EmuConstants::CORPSE_BEGIN); //dont allow them to loot entire bags of items as pvp reward - } - else{ - item_data = GetItem(lootitem->slot_id - EmuConstants::CORPSE_BEGIN, bag_item_data); - } - - if (GetPlayerKillItem()<=1 && item_data != 0) { - item = database.GetItem(item_data->item_id); - } - - if (item != 0) { - if (item_data){ - inst = database.CreateItem(item, item_data ? item_data->charges : 0, item_data->aug_1, item_data->aug_2, item_data->aug_3, item_data->aug_4, item_data->aug_5); - } - else { - inst = database.CreateItem(item); - } - } - - if (client && inst) { - if (client->CheckLoreConflict(item)) { - client->Message_StringID(0, LOOT_LORE_ERROR); - SendEndLootErrorPacket(client); - being_looted_by = 0; - delete inst; - return; - } - - if (inst->IsAugmented()) { - for (int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) { - ItemInst *itm = inst->GetAugment(i); - if (itm) { - if (client->CheckLoreConflict(itm->GetItem())) { - client->Message_StringID(0, LOOT_LORE_ERROR); - SendEndLootErrorPacket(client); - being_looted_by = 0; - delete inst; - return; - } - } - } - } - - char buf[88]; - char corpse_name[64]; - strcpy(corpse_name, corpse_name); - snprintf(buf, 87, "%d %d %s", inst->GetItem()->ID, inst->GetCharges(), EntityList::RemoveNumbers(corpse_name)); - buf[87] = '\0'; - std::vector args; - args.push_back(inst); - args.push_back(this); - parse->EventPlayer(EVENT_LOOT, client, buf, 0, &args); - parse->EventItem(EVENT_LOOT, client, inst, this, buf, 0); - - if ((RuleB(Character, EnableDiscoveredItems))) { - if (client && !client->GetGM() && !client->IsDiscovered(inst->GetItem()->ID)) - client->DiscoverItem(inst->GetItem()->ID); - } - - if (zone->adv_data) { - ServerZoneAdventureDataReply_Struct *ad = (ServerZoneAdventureDataReply_Struct*)zone->adv_data; - if (ad->type == Adventure_Collect && !IsPlayerCorpse()) { - if (ad->data_id == inst->GetItem()->ID) { - zone->DoAdventureCountIncrease(); - } - } - } - - /* First add it to the looter - this will do the bag contents too */ - if (lootitem->auto_loot) { - if (!client->AutoPutLootInInventory(*inst, true, true, bag_item_data)) - client->PutLootInInventory(MainCursor, *inst, bag_item_data); - } - else { - client->PutLootInInventory(MainCursor, *inst, bag_item_data); - } - - /* Update any tasks that have an activity to loot this item */ - if (RuleB(TaskSystem, EnableTaskSystem)) - client->UpdateTasksForItem(ActivityLoot, item->ID); - - /* Remove it from Corpse */ - if (item_data){ - /* Delete needs to be before RemoveItem because its deletes the pointer for item_data/bag_item_data */ - database.DeleteItemOffCharacterCorpse(this->corpse_db_id, item_data->equip_slot, item_data->item_id); - /* Delete Item Instance */ - RemoveItem(item_data->lootslot); - } - - /* Remove Bag Contents */ - if (item->ItemClass == ItemClassContainer && (GetPlayerKillItem() != -1 || GetPlayerKillItem() != 1)) { - for (int i = SUB_BEGIN; i < EmuConstants::ITEM_CONTAINER_SIZE; i++) { - if (bag_item_data[i]) { - /* Delete needs to be before RemoveItem because its deletes the pointer for item_data/bag_item_data */ - database.DeleteItemOffCharacterCorpse(this->corpse_db_id, bag_item_data[i]->equip_slot, bag_item_data[i]->item_id); - /* Delete Item Instance */ - RemoveItem(bag_item_data[i]); - } - } - } - - if (GetPlayerKillItem() != -1){ - SetPlayerKillItemID(0); - } - - /* Send message with item link to groups and such */ - char *link = 0, *link2 = 0; //just like a db query :-) - client->MakeItemLink(link2, inst); - MakeAnyLenString(&link, "%c" "%s" "%s" "%c", - 0x12, - link2, - item->Name, - 0x12); - safe_delete_array(link2); - - client->Message_StringID(MT_LootMessages, LOOTED_MESSAGE, link); - if(!IsPlayerCorpse()) { - Group *g = client->GetGroup(); - if(g != nullptr) { - g->GroupMessage_StringID(client, MT_LootMessages, OTHER_LOOTED_MESSAGE, client->GetName(), link); - } else { - Raid *r = client->GetRaid(); - if(r != nullptr) { - r->RaidMessage_StringID(client, MT_LootMessages, OTHER_LOOTED_MESSAGE, client->GetName(), link); - } - } - } - safe_delete_array(link); - } - else { - SendEndLootErrorPacket(client); - safe_delete(inst); - return; - } - - if (IsPlayerCorpse()){ - client->SendItemLink(inst); - } - else{ - client->SendItemLink(inst, true); - } - - safe_delete(inst); -} - -void Corpse::EndLoot(Client* client, const EQApplicationPacket* app) { - EQApplicationPacket* outapp = new EQApplicationPacket; - outapp->SetOpcode(OP_LootComplete); - outapp->size = 0; - client->QueuePacket(outapp); - safe_delete(outapp); - - this->being_looted_by = 0xFFFFFFFF; - if (this->IsEmpty()) - Delete(); - else - Save(); -} - -void Corpse::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { - Mob::FillSpawnStruct(ns, ForWho); - - ns->spawn.max_hp = 120; - - if (IsPlayerCorpse()) - ns->spawn.NPC = 3; - else - ns->spawn.NPC = 2; -} - -void Corpse::QueryLoot(Client* to) { - int x = 0, y = 0; // x = visible items, y = total items - to->Message(0, "Coin: %ip, %ig, %is, %ic", platinum, gold, silver, copper); - - ItemList::iterator cur,end; - cur = itemlist.begin(); - end = itemlist.end(); - - int corpselootlimit = EQLimits::InventoryMapSize(MapCorpse, to->GetClientVersion()); - - for(; cur != end; ++cur) { - ServerLootItem_Struct* sitem = *cur; - - if (IsPlayerCorpse()) { - if (sitem->equip_slot >= EmuConstants::GENERAL_BAGS_BEGIN && sitem->equip_slot <= EmuConstants::CURSOR_BAG_END) - sitem->lootslot = 0xFFFF; - else - x < corpselootlimit ? sitem->lootslot = x : sitem->lootslot = 0xFFFF; - - const Item_Struct* item = database.GetItem(sitem->item_id); - - if (item) - to->Message((sitem->lootslot == 0xFFFF), "LootSlot: %i (EquipSlot: %i) Item: %s (%d), Count: %i", static_cast(sitem->lootslot), sitem->equip_slot, item->Name, item->ID, sitem->charges); - else - to->Message((sitem->lootslot == 0xFFFF), "Error: 0x%04x", sitem->item_id); - - if (sitem->lootslot != 0xFFFF) - x++; - - y++; - } - else { - sitem->lootslot=y; - const Item_Struct* item = database.GetItem(sitem->item_id); - - if (item) - to->Message(0, "LootSlot: %i Item: %s (%d), Count: %i", sitem->lootslot, item->Name, item->ID, sitem->charges); - else - to->Message(0, "Error: 0x%04x", sitem->item_id); - - y++; - } - } - - if (IsPlayerCorpse()) { - to->Message(0, "%i visible %s (%i total) on %s (DBID: %i).", x, x==1?"item":"items", y, this->GetName(), this->GetCorpseDBID()); - } - else { - to->Message(0, "%i %s on %s.", y, y==1?"item":"items", this->GetName()); - } -} - -bool Corpse::Summon(Client* client, bool spell, bool CheckDistance) { - uint32 dist2 = 10000; // pow(100, 2); - if (!spell) { - if (this->GetCharID() == client->CharacterID()) { - if (IsLocked() && client->Admin() < 100) { - client->Message(13, "That corpse is locked by a GM."); - return false; - } - if (!CheckDistance || (DistNoRootNoZ(*client) <= dist2)) { - GMMove(client->GetX(), client->GetY(), client->GetZ()); - is_corpse_changed = true; - } - else { - client->Message(0, "Corpse is too far away."); - return false; - } - } - else - { - bool consented = false; - std::list::iterator itr; - for(itr = client->consent_list.begin(); itr != client->consent_list.end(); ++itr) { - if(strcmp(this->GetOwnerName(), itr->c_str()) == 0) { - if (!CheckDistance || (DistNoRootNoZ(*client) <= dist2)) { - GMMove(client->GetX(), client->GetY(), client->GetZ()); - is_corpse_changed = true; - } - else { - client->Message(0, "Corpse is too far away."); - return false; - } - consented = true; - } - } - if(!consented) { - client->Message(0, "You do not have permission to move this corpse."); - return false; - } - } - } - else { - GMMove(client->GetX(), client->GetY(), client->GetZ()); - is_corpse_changed = true; - } - Save(); - return true; -} - -void Corpse::CompleteResurrection(){ - rez_experience = 0; - is_corpse_changed = true; - this->Save(); -} - -void Corpse::Spawn() { - EQApplicationPacket* app = new EQApplicationPacket; - this->CreateSpawnPacket(app, this); - entity_list.QueueClients(this, app); - safe_delete(app); -} - -uint32 Corpse::GetEquipment(uint8 material_slot) const { - int invslot; - - if(material_slot > EmuConstants::MATERIAL_END) { - return NO_ITEM; - } - - invslot = Inventory::CalcSlotFromMaterial(material_slot); - if(invslot == INVALID_INDEX) // GetWornItem() should be returning a NO_ITEM for any invalid index... - return NO_ITEM; - - return GetWornItem(invslot); -} - -uint32 Corpse::GetEquipmentColor(uint8 material_slot) const { - const Item_Struct *item; - - if(material_slot > EmuConstants::MATERIAL_END) { - return 0; - } - - item = database.GetItem(GetEquipment(material_slot)); - if(item != NO_ITEM) { - return item_tint[material_slot].rgb.use_tint ? - item_tint[material_slot].color : - item->Color; - } - - return 0; -} - -void Corpse::AddLooter(Mob* who) { - for (int i = 0; i < MAX_LOOTERS; i++) { - if (allowed_looters[i] == 0) { - allowed_looters[i] = who->CastToClient()->CharacterID(); - break; - } - } -} - -void Corpse::LoadPlayerCorpseDecayTime(uint32 corpse_db_id){ - if(!corpse_db_id) - return; - - uint32 active_corpse_decay_timer = database.GetCharacterCorpseDecayTimer(corpse_db_id); - if (active_corpse_decay_timer > 0 && RuleI(Character, CorpseDecayTimeMS) > (active_corpse_decay_timer * 1000)) { - corpse_decay_timer.SetTimer(RuleI(Character, CorpseDecayTimeMS) - (active_corpse_decay_timer * 1000)); - } - else { - corpse_decay_timer.SetTimer(2000); - } - if (active_corpse_decay_timer > 0 && RuleI(Zone, GraveyardTimeMS) > (active_corpse_decay_timer * 1000)) { - corpse_graveyard_timer.SetTimer(RuleI(Zone, GraveyardTimeMS) - (active_corpse_decay_timer * 1000)); - } - else { - corpse_graveyard_timer.SetTimer(3000); - } -<<<<<<< HEAD -} - -/* -** Corpse slot translations are needed until corpse database blobs are converted -** -** To account for the addition of MainPowerSource, MainGeneral9 and MainGeneral10 into -** the contiguous possessions slot enumeration, the following designations will be used: -** -** Designatiom Server Corpse Offset -** -------------------------------------------------- -** MainCharm 0 0 0 -** ... ... ... 0 -** MainWaist 20 20 0 -** MainPowerSource 21 9999 +9978 -** MainAmmo 22 21 -1 -** -** MainGeneral1 23 22 -1 -** ... ... ... -1 -** MainGeneral8 30 29 -1 -** MainGeneral9 31 9997 +9966 -** MainGeneral10 32 9998 +9966 -** -** MainCursor 33 30 -3 -** -** MainGeneral1_1 251 251 0 -** ... ... ... 0 -** MainGeneral8_10 330 330 0 -** MainGeneral9_1 331 341 +10 -** ... ... ... +10 -** MainGeneral10_10 350 360 +10 -** -** MainCursor_1 351 331 -20 -** ... ... ... -20 -** MainCursor_10 360 340 -20 -** -** (Not all slot designations are valid to all clients..see ##_constants.h files for valid slot enumerations) -*/ -int16 Corpse::ServerToCorpseSlot(int16 server_slot) -{ - return server_slot; // temporary return - - /* - switch (server_slot) - { - case MainPowerSource: - return 9999; - case MainGeneral9: - return 9997; - case MainGeneral10: - return 9998; - case MainCursor: - return 30; - case MainAmmo: - case MainGeneral1: - case MainGeneral2: - case MainGeneral3: - case MainGeneral4: - case MainGeneral5: - case MainGeneral6: - case MainGeneral7: - case MainGeneral8: - return server_slot - 1; - default: - if (server_slot >= EmuConstants::CURSOR_BAG_BEGIN && server_slot <= EmuConstants::CURSOR_BAG_END) - return server_slot - 20; - else if (server_slot >= EmuConstants::GENERAL_BAGS_END - 19 && server_slot <= EmuConstants::GENERAL_BAGS_END) - return server_slot + 10; - else - return server_slot; - } - */ -} - -int16 Corpse::CorpseToServerSlot(int16 corpse_slot) -{ - return corpse_slot; // temporary return - - /* - switch (corpse_slot) - { - case 9999: - return MainPowerSource; - case 9997: - return MainGeneral9; - case 9998: - return MainGeneral10; - case 30: - return MainCursor; - case 21: // old SLOT_AMMO - case 22: // old PERSONAL_BEGIN - case 23: - case 24: - case 25: - case 26: - case 27: - case 28: - case 29: // old PERSONAL_END - return corpse_slot + 1; - default: - if (corpse_slot >= 331 && corpse_slot <= 340) - return corpse_slot + 20; - else if (corpse_slot >= 341 && corpse_slot <= 360) - return corpse_slot - 10; - else - return corpse_slot; - } - */ -} -======= -} ->>>>>>> master diff --git a/zone/corpse.h.orig b/zone/corpse.h.orig deleted file mode 100644 index d79d84e96..000000000 --- a/zone/corpse.h.orig +++ /dev/null @@ -1,190 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) - - 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 CORPSE_H -#define CORPSE_H - -#include "mob.h" - -class Client; -class NPC; - -#define MAX_LOOTERS 72 - -class Corpse : public Mob { - public: - - static void SendEndLootErrorPacket(Client* client); - static void SendLootReqErrorPacket(Client* client, uint8 response = 2); - - Corpse(NPC* in_npc, ItemList* in_itemlist, uint32 in_npctypeid, const NPCType** in_npctypedata, uint32 in_decaytime = 600000); - Corpse(Client* client, int32 in_rezexp); -<<<<<<< HEAD - Corpse(uint32 in_corpseid, uint32 in_charid, const char* in_charname, ItemList* in_itemlist, uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 in_plat, const xyz_heading& position, float in_size, uint8 in_gender, uint16 in_race, uint8 in_class, uint8 in_deity, uint8 in_level, uint8 in_texture, uint8 in_helmtexture, uint32 in_rezexp, bool wasAtGraveyard = false); - ~Corpse(); - static Corpse* LoadFromDBData(uint32 in_dbid, uint32 in_charid, std::string in_charname, const xyz_heading& position, std::string time_of_death, bool rezzed, bool was_at_graveyard); -======= - Corpse(uint32 in_corpseid, uint32 in_charid, const char* in_charname, ItemList* in_itemlist, uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 in_plat, float in_x, float in_y, float in_z, float in_heading, float in_size, uint8 in_gender, uint16 in_race, uint8 in_class, uint8 in_deity, uint8 in_level, uint8 in_texture, uint8 in_helmtexture, uint32 in_rezexp, bool wasAtGraveyard = false); - - ~Corpse(); - static Corpse* LoadCharacterCorpseEntity(uint32 in_dbid, uint32 in_charid, std::string in_charname, float in_x, float in_y, float in_z, float in_heading, std::string time_of_death, bool rezzed, bool was_at_graveyard); ->>>>>>> master - - /* Corpse: General */ - 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, -<<<<<<< HEAD - bool IsStrikethrough = true, 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; } - - void LoadPlayerCorpseDecayTime(uint32 dbid); - -======= - bool IsStrikethrough = true, 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; } - inline uint32 GetCorpseDBID() { return corpse_db_id; } - inline char* GetOwnerName() { return corpse_name; } - bool IsEmpty() const; ->>>>>>> master - bool IsCorpse() const { return true; } - bool IsPlayerCorpse() const { return is_player_corpse; } - bool IsNPCCorpse() const { return !is_player_corpse; } - bool IsBecomeNPCCorpse() const { return become_npc; } - virtual void DepopNPCCorpse(); - virtual void DepopPlayerCorpse(); - bool Process(); - bool Save(); - uint32 GetCharID() { return char_id; } - uint32 SetCharID(uint32 iCharID) { if (IsPlayerCorpse()) { return (char_id = iCharID); } return 0xFFFFFFFF; }; - uint32 GetDecayTime() { if (!corpse_decay_timer.Enabled()) return 0xFFFFFFFF; else return corpse_decay_timer.GetRemainingTime(); } - uint32 GetRezTime() { if (!corpse_rez_timer.Enabled()) return 0; else return corpse_rez_timer.GetRemainingTime(); } - void SetDecayTimer(uint32 decay_time); - - void Delete(); - void Bury(); - void CalcCorpseName(); - void LoadPlayerCorpseDecayTime(uint32 dbid); - - /* Corpse: Items */ - uint32 GetWornItem(int16 equipSlot) const; - ServerLootItem_Struct* GetItem(uint16 lootslot, ServerLootItem_Struct** bag_item_data = 0); - void SetPlayerKillItemID(int32 pk_item_id) { player_kill_item = pk_item_id; } - int32 GetPlayerKillItem() { return player_kill_item; } - void RemoveItem(uint16 lootslot); - void RemoveItem(ServerLootItem_Struct* item_data); - void AddItem(uint32 itemnum, uint16 charges, int16 slot = 0, uint32 aug1 = 0, uint32 aug2 = 0, uint32 aug3 = 0, uint32 aug4 = 0, uint32 aug5 = 0); - - /* Corpse: Coin */ - void SetCash(uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 in_platinum); - void RemoveCash(); - uint32 GetCopper() { return copper; } - uint32 GetSilver() { return silver; } - uint32 GetGold() { return gold; } - uint32 GetPlatinum() { return platinum; } - - /* Corpse: Resurrection */ - bool IsRezzed() { return rez; } - void IsRezzed(bool in_rez) { rez = in_rez; } - void CastRezz(uint16 spellid, Mob* Caster); - void CompleteResurrection(); - - /* Corpse: Loot */ - void QueryLoot(Client* to); - void LootItem(Client* client, const EQApplicationPacket* app); - void EndLoot(Client* client, const EQApplicationPacket* app); - void MakeLootRequestPackets(Client* client, const EQApplicationPacket* app); - void AllowPlayerLoot(Mob *them, uint8 slot); - void AddLooter(Mob *who); - uint32 CountItems(); - bool CanPlayerLoot(int charid); - - inline void Lock() { is_locked = true; } - inline void UnLock() { is_locked = false; } - inline bool IsLocked() { return is_locked; } - inline void ResetLooter() { being_looted_by = 0xFFFFFFFF; } - inline bool IsBeingLooted() { return (being_looted_by != 0xFFFFFFFF); } - - /* Mob */ - void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho); - bool Summon(Client* client, bool spell, bool CheckDistance); - void Spawn(); - - char corpse_name[64]; - uint32 GetEquipment(uint8 material_slot) const; - uint32 GetEquipmentColor(uint8 material_slot) const; - inline int GetRezExp() { return rez_experience; } - -protected: - std::list MoveItemToCorpse(Client *client, ItemInst *item, int16 equipslot); - -private: -<<<<<<< HEAD - bool is_player_corpse; - bool is_corpse_changed; - bool is_locked; - int32 player_kill_item; - uint32 corpse_db_id; - uint32 char_id; - ItemList itemlist; - uint32 copper; -======= - bool is_player_corpse; /* Determines if Player Corpse or not */ - bool is_corpse_changed; /* Determines if corpse has changed or not */ - bool is_locked; /* Determines if corpse is locked */ - int32 player_kill_item; /* Determines if Player Kill Item */ - uint32 corpse_db_id; /* Corpse Database ID (Player Corpse) */ - uint32 char_id; /* Character ID */ - ItemList itemlist; /* Internal Item list used for corpses */ - uint32 copper; ->>>>>>> master - uint32 silver; - uint32 gold; - uint32 platinum; - bool player_corpse_depop; /* Sets up Corpse::Process to depop the player corpse */ - uint32 being_looted_by; /* Determines what the corpse is being looted by internally for logic */ - uint32 rez_experience; /* Amount of experience that the corpse would rez for */ - bool rez; - bool can_corpse_be_rezzed; /* Bool declaring whether or not a corpse can be rezzed */ - bool become_npc; -<<<<<<< HEAD - int allowed_looters[MAX_LOOTERS]; // People allowed to loot the corpse, character id - Timer corpse_decay_timer; - Timer corpse_res_timer; - Timer corpse_delay_timer; -======= - int allowed_looters[MAX_LOOTERS]; /* People allowed to loot the corpse, character id */ - Timer corpse_decay_timer; /* The amount of time in millseconds in which a corpse will take to decay (Depop/Poof) */ - Timer corpse_rez_timer; /* The amount of time in millseconds in which a corpse can be rezzed */ - Timer corpse_delay_timer; ->>>>>>> master - Timer corpse_graveyard_timer; - Timer loot_cooldown_timer; /* Delay between loot actions on the corpse entity */ - Color_Struct item_tint[9]; - -}; - -#endif diff --git a/zone/merc.cpp.orig b/zone/merc.cpp.orig deleted file mode 100644 index 5d2d8ca81..000000000 --- a/zone/merc.cpp.orig +++ /dev/null @@ -1,6329 +0,0 @@ -#include "merc.h" -#include "masterentity.h" -#include "npc_ai.h" -#include "../common/packet_dump.h" -#include "../common/eq_packet_structs.h" -#include "../common/eq_constants.h" -#include "../common/skills.h" -#include "../common/spdat.h" -#include "zone.h" -#include "string_ids.h" -#include "../common/misc_functions.h" -#include "../common/string_util.h" -#include "../common/rulesys.h" -#include "quest_parser_collection.h" -#include "water_map.h" - -extern volatile bool ZoneLoaded; - -Merc::Merc(const NPCType* d, float x, float y, float z, float heading) -: NPC(d, nullptr, xyz_heading(x, y, z, heading), 0, false), endupkeep_timer(1000), rest_timer(1), confidence_timer(6000), check_target_timer(2000) -{ - base_hp = d->max_hp; - base_mana = d->Mana; - _baseAC = d->AC; - _baseSTR = d->STR; - _baseSTA = d->STA; - _baseDEX = d->DEX; - _baseAGI = d->AGI; - _baseINT = d->INT; - _baseWIS = d->WIS; - _baseCHA = d->CHA; - _baseATK = d->ATK; - _baseRace = d->race; - _baseGender = d->gender; - _baseMR = d->MR; - _baseCR = d->CR; - _baseDR = d->DR; - _baseFR = d->FR; - _basePR = d->PR; - _baseCorrup = d->Corrup; - _OwnerClientVersion = EQClientTitanium; - RestRegenHP = 0; - RestRegenMana = 0; - RestRegenEndurance = 0; - cur_end = 0; - - _medding = false; - _suspended = false; - p_depop = false; - _check_confidence = false; - _lost_confidence = false; - _hatedCount = 0; - - memset(equipment, 0, sizeof(equipment)); - - SetMercID(0); - SetStance(MercStanceBalanced); - rest_timer.Disable(); - - int r; - for(r = 0; r <= HIGHEST_SKILL; r++) { - skills[r] = database.GetSkillCap(GetClass(),(SkillUseTypes)r,GetLevel()); - } - - size = d->size; - CalcBonuses(); - - SetHP(GetMaxHP()); - SetMana(GetMaxMana()); - SetEndurance(GetMaxEndurance()); - - AI_Init(); - AI_Start(); -} - -Merc::~Merc() { - AI_Stop(); - //entity_list.RemoveMerc(this->GetID()); - UninitializeBuffSlots(); -} - -void Merc::CalcBonuses() -{ - memset(&itembonuses, 0, sizeof(StatBonuses)); - memset(&aabonuses, 0, sizeof(StatBonuses)); - CalcItemBonuses(&itembonuses); - - CalcSpellBonuses(&spellbonuses); - - CalcAC(); - CalcATK(); - - CalcSTR(); - CalcSTA(); - CalcDEX(); - CalcAGI(); - CalcINT(); - CalcWIS(); - CalcCHA(); - - CalcMR(); - CalcFR(); - CalcDR(); - CalcPR(); - CalcCR(); - CalcCorrup(); - - CalcMaxHP(); - CalcMaxMana(); - CalcMaxEndurance(); - - rooted = FindType(SE_Root); -} - -float Merc::GetDefaultSize() { - - float MercSize = GetSize(); - - switch(this->GetRace()) - { - case 1: // Humans - MercSize = 6.0; - break; - case 2: // Barbarian - MercSize = 7.0; - break; - case 3: // Erudite - MercSize = 6.0; - break; - case 4: // Wood Elf - MercSize = 5.0; - break; - case 5: // High Elf - MercSize = 6.0; - break; - case 6: // Dark Elf - MercSize = 5.0; - break; - case 7: // Half Elf - MercSize = 5.5; - break; - case 8: // Dwarf - MercSize = 4.0; - break; - case 9: // Troll - MercSize = 8.0; - break; - case 10: // Ogre - MercSize = 9.0; - break; - case 11: // Halfling - MercSize = 3.5; - break; - case 12: // Gnome - MercSize = 3.0; - break; - case 128: // Iksar - MercSize = 6.0; - break; - case 130: // Vah Shir - MercSize = 7.0; - break; - case 330: // Froglok - MercSize = 5.0; - break; - case 522: // Drakkin - MercSize = 5.0; - break; - default: - MercSize = 6.0; - break; - } - - return MercSize; -} - -int Merc::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; -} - -void Merc::CalcItemBonuses(StatBonuses* newbon) { - //memset assumed to be done by caller. - - - unsigned int i; - //should not include 21 (SLOT_AMMO) - for (i=0; i= EQClientSoF) - { - const ItemInst* inst = m_inv[MainPowerSource]; - if(inst) - AddItemBonuses(inst, newbon); - }*/ - - // Caps - if(newbon->HPRegen > CalcHPRegenCap()) - newbon->HPRegen = CalcHPRegenCap(); - - if(newbon->ManaRegen > CalcManaRegenCap()) - newbon->ManaRegen = CalcManaRegenCap(); - - if(newbon->EnduranceRegen > CalcEnduranceRegenCap()) - newbon->EnduranceRegen = CalcEnduranceRegenCap(); - - SetAttackTimer(); -} - -void Merc::AddItemBonuses(const Item_Struct *item, StatBonuses* newbon) { - - 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->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->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 < (int16)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->Attack > 0) { - - unsigned int cap = RuleI(Character, ItemATKCap); - cap += itembonuses.ItemATKCap + spellbonuses.ItemATKCap + aabonuses.ItemATKCap; - - if((newbon->ATK + item->Attack) > cap) - newbon->ATK = RuleI(Character, ItemATKCap); - else - newbon->ATK += item->Attack; - } - 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, true); - } - - if (item->Focus.Effect>0 && (item->Focus.Type == ET_Focus)) { // focus effects - ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, newbon, 0, true); - } - - 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 (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; - } - } - - // 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; - } -} - -int Merc::GroupLeadershipAAHealthEnhancement() -{ - Group *g = GetGroup(); - - if(!g || (g->GroupCount() < 3)) - return 0; - - switch(g->GetLeadershipAA(groupAAHealthEnhancement)) - { - case 0: - return 0; - case 1: - return 30; - case 2: - return 60; - case 3: - return 100; - } - - return 0; -} - -int Merc::GroupLeadershipAAManaEnhancement() -{ - Group *g = GetGroup(); - - if(!g || (g->GroupCount() < 3)) - return 0; - - switch(g->GetLeadershipAA(groupAAManaEnhancement)) - { - case 0: - return 0; - case 1: - return 30; - case 2: - return 60; - case 3: - return 100; - } - - return 0; -} - -int Merc::GroupLeadershipAAHealthRegeneration() -{ - Group *g = GetGroup(); - - if(!g || (g->GroupCount() < 3)) - return 0; - - switch(g->GetLeadershipAA(groupAAHealthRegeneration)) - { - case 0: - return 0; - case 1: - return 4; - case 2: - return 6; - case 3: - return 8; - } - - return 0; -} - -int Merc::GroupLeadershipAAOffenseEnhancement() -{ - Group *g = GetGroup(); - - if(!g || (g->GroupCount() < 3)) - return 0; - - switch(g->GetLeadershipAA(groupAAOffenseEnhancement)) - { - case 0: - return 0; - case 1: - return 10; - case 2: - return 19; - case 3: - return 28; - case 4: - return 34; - case 5: - return 40; - } - return 0; -} - -int32 Merc::CalcSTR() { - int32 val = _baseSTR + itembonuses.STR + spellbonuses.STR; - - int32 mod = aabonuses.STR; - - STR = val + mod; - - if(STR < 1) - STR = 1; - - return(STR); -} - -int32 Merc::CalcSTA() { - int32 val = _baseSTA + itembonuses.STA + spellbonuses.STA; - - int32 mod = aabonuses.STA; - - STA = val + mod; - - if(STA < 1) - STA = 1; - - return(STA); -} - -int32 Merc::CalcAGI() { - int32 val = _baseAGI + itembonuses.AGI + spellbonuses.AGI; - int32 mod = aabonuses.AGI; - - int32 str = GetSTR(); - - AGI = val + mod; - - if(AGI < 1) - AGI = 1; - - return(AGI); -} - -int32 Merc::CalcDEX() { - int32 val = _baseDEX + itembonuses.DEX + spellbonuses.DEX; - - int32 mod = aabonuses.DEX; - - DEX = val + mod; - - if(DEX < 1) - DEX = 1; - - return(DEX); -} - -int32 Merc::CalcINT() { - int32 val = _baseINT + itembonuses.INT + spellbonuses.INT; - - int32 mod = aabonuses.INT; - - INT = val + mod; - - if(INT < 1) - INT = 1; - - return(INT); -} - -int32 Merc::CalcWIS() { - int32 val = _baseWIS + itembonuses.WIS + spellbonuses.WIS; - - int32 mod = aabonuses.WIS; - - WIS = val + mod; - - if(WIS < 1) - WIS = 1; - - return(WIS); -} - -int32 Merc::CalcCHA() { - int32 val = _baseCHA + itembonuses.CHA + spellbonuses.CHA; - - int32 mod = aabonuses.CHA; - - CHA = val + mod; - - if(CHA < 1) - CHA = 1; - - 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 Merc::CalcMR() -{ - MR = _baseMR + itembonuses.MR + spellbonuses.MR + aabonuses.MR; - - if(MR < 1) - MR = 1; - - return(MR); -} - -int32 Merc::CalcFR() -{ - FR = _baseFR + itembonuses.FR + spellbonuses.FR + aabonuses.FR; - - if(FR < 1) - FR = 1; - - return(FR); -} - -int32 Merc::CalcDR() -{ - DR = _baseDR + itembonuses.DR + spellbonuses.DR + aabonuses.DR; - - if(DR < 1) - DR = 1; - - return(DR); -} - -int32 Merc::CalcPR() -{ - PR = _basePR + itembonuses.PR + spellbonuses.PR + aabonuses.PR; - - if(PR < 1) - PR = 1; - - return(PR); -} - -int32 Merc::CalcCR() -{ - CR = _baseCR + itembonuses.CR + spellbonuses.CR + aabonuses.CR; - - if(CR < 1) - CR = 1; - - return(CR); -} - -int32 Merc::CalcCorrup() -{ - Corrup = _baseCorrup + itembonuses.Corrup + spellbonuses.Corrup + aabonuses.Corrup; - - return(Corrup); -} - -int32 Merc::CalcATK() { - ATK = _baseATK + itembonuses.ATK + spellbonuses.ATK + aabonuses.ATK + GroupLeadershipAAOffenseEnhancement(); - return(ATK); -} - -int32 Merc::CalcAC() { - //spell AC bonuses are added directly to natural total - AC = _baseAC + spellbonuses.AC; - return(AC); -} - -int32 Merc::CalcHPRegen() { - int32 regen = hp_regen + itembonuses.HPRegen + spellbonuses.HPRegen; - - regen += aabonuses.HPRegen + GroupLeadershipAAHealthRegeneration(); - - return (regen * RuleI(Character, HPRegenMultiplier) / 100); -} - -int32 Merc::CalcHPRegenCap() -{ - int cap = RuleI(Character, ItemHealthRegenCap) + itembonuses.HeroicSTA/25; - - cap += aabonuses.ItemHPRegenCap + spellbonuses.ItemHPRegenCap + itembonuses.ItemHPRegenCap; - - return (cap * RuleI(Character, HPRegenMultiplier) / 100); -} - -int32 Merc::CalcMaxHP() { - float nd = 10000; - max_hp = (CalcBaseHP() + itembonuses.HP); - - //The AA desc clearly says it only applies to base hp.. - //but the actual effect sent on live causes the client - //to apply it to (basehp + itemhp).. I will oblige to the client's whims over - //the aa description - nd += aabonuses.MaxHP; //Natural Durability, Physical Enhancement, Planar Durability - - max_hp = (float)max_hp * (float)nd / (float)10000; //this is to fix the HP-above-495k issue - max_hp += spellbonuses.HP + aabonuses.HP; - - max_hp += GroupLeadershipAAHealthEnhancement(); - - max_hp += max_hp * ((spellbonuses.MaxHPChange + itembonuses.MaxHPChange) / 10000.0f); - - 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; - if (cur_hp > curHP_cap || (spellbonuses.HPPercCap[1] && cur_hp > spellbonuses.HPPercCap[1])) - cur_hp = curHP_cap; - } - - return max_hp; -} - -int32 Merc::CalcBaseHP() -{ - return base_hp; -} - -int32 Merc::CalcMaxMana() -{ - switch(GetCasterClass()) - { - case 'I': - case 'W': { - max_mana = (CalcBaseMana() + itembonuses.Mana + spellbonuses.Mana + GroupLeadershipAAManaEnhancement()); - break; - } - case 'N': { - max_mana = 0; - break; - } - default: { - LogFile->write(EQEMuLog::Debug, "Invalid Class '%c' in CalcMaxMana", GetCasterClass()); - max_mana = 0; - break; - } - } - if (max_mana < 0) { - max_mana = 0; - } - - if (cur_mana > max_mana) { - cur_mana = max_mana; - } - - int mana_perc_cap = spellbonuses.ManaPercCap[0]; - if(mana_perc_cap) { - int curMana_cap = (max_mana * mana_perc_cap) / 100; - if (cur_mana > curMana_cap || (spellbonuses.ManaPercCap[1] && cur_mana > spellbonuses.ManaPercCap[1])) - cur_mana = curMana_cap; - } - -#if EQDEBUG >= 11 - LogFile->write(EQEMuLog::Debug, "Merc::CalcMaxMana() called for %s - returning %d", GetName(), max_mana); -#endif - return max_mana; -} - -int32 Merc::CalcBaseMana() -{ - return base_mana; -} - -int32 Merc::CalcBaseManaRegen() -{ - uint8 clevel = GetLevel(); - int32 regen = 0; - if (IsSitting()) - { - if(HasSkill(SkillMeditate)) - regen = (((GetSkill(SkillMeditate) / 10) + (clevel - (clevel / 4))) / 4) + 4; - else - regen = 2; - } - else { - regen = 2; - } - return regen; -} - -int32 Merc::CalcManaRegen() -{ - int32 regen = 0; - if (IsSitting()) - { - BuffFadeBySitModifier(); - if(HasSkill(SkillMeditate)) { - this->_medding = true; - regen = ((GetSkill(SkillMeditate) / 10) + mana_regen); - regen += spellbonuses.ManaRegen + itembonuses.ManaRegen; - } - else - regen = mana_regen + spellbonuses.ManaRegen + itembonuses.ManaRegen; - } - else { - this->_medding = false; - regen = mana_regen + spellbonuses.ManaRegen + itembonuses.ManaRegen; - } - - if(GetCasterClass() == 'I') - regen += (itembonuses.HeroicINT / 25); - else if(GetCasterClass() == 'W') - regen += (itembonuses.HeroicWIS / 25); - else - regen = 0; - - //AAs - regen += aabonuses.ManaRegen; - - return (regen * RuleI(Character, ManaRegenMultiplier) / 100); -} - -int32 Merc::CalcManaRegenCap() -{ - int32 cap = RuleI(Character, ItemManaRegenCap) + aabonuses.ItemManaRegenCap; - switch(GetCasterClass()) - { - case 'I': - cap += (itembonuses.HeroicINT / 25); - break; - case 'W': - cap += (itembonuses.HeroicWIS / 25); - break; - } - - return (cap * RuleI(Character, ManaRegenMultiplier) / 100); -} - -void Merc::CalcMaxEndurance() -{ - max_end = CalcBaseEndurance() + spellbonuses.Endurance + itembonuses.Endurance + aabonuses.Endurance; - - if (max_end < 0) { - max_end = 0; - } - - 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; - if (cur_end > curEnd_cap || (spellbonuses.EndPercCap[1] && cur_end > spellbonuses.EndPercCap[1])) - cur_end = curEnd_cap; - } -} - -int32 Merc::CalcBaseEndurance() -{ - int32 base_end = 0; - int32 base_endurance = 0; - int32 ConvertedStats = 0; - int32 sta_end = 0; - int Stats = 0; - - if(GetClientVersion() >= EQClientSoD && 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) { - ConvertedStats -= ((Stats - 201) * 5 / 4); - } - } - else { - ConvertedStats = Stats; - } - - if (GetLevel() < 41) { - sta_end = (GetLevel() * 75 * ConvertedStats / 1000); - base_endurance = (GetLevel() * 15); - } - else if (GetLevel() < 81) { - sta_end = ((3 * ConvertedStats) + ((GetLevel() - 40) * 15 * ConvertedStats / 100)); - base_endurance = (600 + ((GetLevel() - 40) * 30)); - } - 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; - - int at_most_800 = Stats; - if(at_most_800 > 800) - at_most_800 = 800; - - int Bonus400to800 = 0; - int HalfBonus400to800 = 0; - int Bonus800plus = 0; - int HalfBonus800plus = 0; - - 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 ); - - if(Stats > 800) { - Bonus800plus = int( (Stats - 800) / 8 ) * 2; - HalfBonus800plus = int( (Stats - 800) / 16 ); - } - } - 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; - } - return base_end; -} - -int32 Merc::CalcEnduranceRegen() { - int32 regen = int32(GetLevel() * 4 / 10) + 2; - regen += aabonuses.EnduranceRegen + spellbonuses.EnduranceRegen + itembonuses.EnduranceRegen; - - return (regen * RuleI(Character, EnduranceRegenMultiplier) / 100); -} - -int32 Merc::CalcEnduranceRegenCap() { - int cap = (RuleI(Character, ItemEnduranceRegenCap) + itembonuses.HeroicSTR/25 + itembonuses.HeroicDEX/25 + itembonuses.HeroicAGI/25 + itembonuses.HeroicSTA/25); - - return (cap * RuleI(Character, EnduranceRegenMultiplier) / 100); -} - -void Merc::SetEndurance(int32 newEnd) -{ - /*Endurance can't be less than 0 or greater than max*/ - if(newEnd < 0) - newEnd = 0; - else if(newEnd > GetMaxEndurance()){ - newEnd = GetMaxEndurance(); - } - - cur_end = newEnd; -} - -void Merc::DoEnduranceUpkeep() { - - if (!HasEndurUpkeep()) - return; - - int upkeep_sum = 0; - int cost_redux = spellbonuses.EnduranceReduction + itembonuses.EnduranceReduction; - - bool has_effect = false; - uint32 buffs_i; - uint32 buff_count = GetMaxTotalSlots(); - for (buffs_i = 0; buffs_i < buff_count; buffs_i++) { - if (buffs[buffs_i].spellid != SPELL_UNKNOWN) { - int upkeep = spells[buffs[buffs_i].spellid].EndurUpkeep; - if(upkeep > 0) { - has_effect = true; - if(cost_redux > 0) { - if(upkeep <= cost_redux) - continue; //reduced to 0 - upkeep -= cost_redux; - } - if((upkeep+upkeep_sum) > GetEndurance()) { - //they do not have enough to keep this one going. - BuffFadeBySlot(buffs_i); - } else { - upkeep_sum += upkeep; - } - } - } - } - - if(upkeep_sum != 0) - SetEndurance(GetEndurance() - upkeep_sum); - - if (!has_effect) - SetEndurUpkeep(false); -} - -void Merc::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)) - return; - - uint32 buff_count = GetMaxTotalSlots(); - for (unsigned int j = 0; j < buff_count; j++) { - if(buffs[j].spellid != SPELL_UNKNOWN) { - if(IsDetrimentalSpell(buffs[j].spellid) && (buffs[j].ticsremaining > 0)) - if(!DetrimentalSpellAllowsRest(buffs[j].spellid)) - return; - } - } - - RestRegenHP = (GetMaxHP() * RuleI(Character, RestRegenPercent) / 100); - - RestRegenMana = (GetMaxMana() * RuleI(Character, RestRegenPercent) / 100); - - if(RuleB(Character, RestRegenEndurance)) - RestRegenEndurance = (GetMaxEndurance() * RuleI(Character, RestRegenPercent) / 100); -} - -bool Merc::HasSkill(SkillUseTypes skill_id) const { - return((GetSkill(skill_id) > 0) && CanHaveSkill(skill_id)); -} - -bool Merc::CanHaveSkill(SkillUseTypes skill_id) const { - return(database.GetSkillCap(GetClass(), skill_id, RuleI(Character, MaxLevel)) > 0); - //if you don't have it by max level, then odds are you never will? -} - -uint16 Merc::MaxSkill(SkillUseTypes skillid, uint16 class_, uint16 level) const { - return(database.GetSkillCap(class_, skillid, level)); -} - -void Merc::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { - if(ns) { - Mob::FillSpawnStruct(ns, ForWho); - - ns->spawn.afk = 0; - ns->spawn.lfg = 0; - ns->spawn.anon = 0; - ns->spawn.gm = 0; - ns->spawn.guildID = 0xFFFFFFFF; // 0xFFFFFFFF = NO GUILD, 0 = Unknown Guild - ns->spawn.is_npc = 1; // 0=no, 1=yes - ns->spawn.is_pet = 0; - ns->spawn.guildrank = 0; - ns->spawn.showhelm = 1; - ns->spawn.flymode = 0; - ns->spawn.NPC = 1; // 0=player,1=npc,2=pc corpse,3=npc corpse - ns->spawn.IsMercenary = 1; - - unsigned int i; - //should not include 21 (SLOT_AMMO) - for (i = 0; i < MainAmmo; i++) { - if(equipment[i] == 0) - continue; - const Item_Struct* item = database.GetItem(equipment[i]); - if(item) - { - ns->spawn.equipment[i] = item->Material; - ns->spawn.colors[i].color = item->Color; - } - } - } -} - -bool Merc::Process() -{ - if(IsStunned() && stunned_timer.Check()) - { - this->stunned = false; - this->stunned_timer.Disable(); - } - - if (GetDepop()) - { - SetMercCharacterID(0); - SetOwnerID(0); - return false; - } - - if(!GetMercOwner()) { - //p_depop = true; //this was causing a crash - removed merc from entity list, but not group - //return false; //merc can live after client dies, not sure how long - } - - if(IsSuspended()) - { - return false; - } - - if (HasGroup() && GetMercOwner() && GetFollowID() == 0) { - SetFollowID(GetMercOwner()->GetID()); - } - - SpellProcess(); - - 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()) - { - SendPosition(); - if(IsSitting()) { - if(!rest_timer.Enabled()) { - rest_timer.Start(RuleI(Character, RestRegenTimeToActivate) * 1000); - } - } - } - - BuffProcess(); - - CalcRestState(); - - if(GetHP() < GetMaxHP()) - SetHP(GetHP() + CalcHPRegen() + RestRegenHP); - - if(GetMana() < GetMaxMana()) - SetMana(GetMana() + CalcManaRegen() + RestRegenMana); - - if(GetEndurance() < GetMaxEndurance()) - SetEndurance(GetEndurance() + CalcEnduranceRegen() + RestRegenEndurance); - } - - if(confidence_timer.Check()) { - _check_confidence = true; - } - - if (sendhpupdate_timer.Check()) { - SendHPUpdate(); - } - - if (endupkeep_timer.Check() && GetHP() > 0){ - DoEnduranceUpkeep(); - } - - if (IsStunned() || IsMezzed()) - return true; - - // Merc AI - AI_Process(); - - return true; -} - -bool Merc::IsMercCasterCombatRange(Mob *target) { - bool result = false; - - if(target) { - float range = MercAISpellRange; - - range *= range; - - // half the max so the merc doesn't always stop at max range to allow combat movement - range *= .5; - - float targetDistance = DistNoRootNoZ(*target); - - if(targetDistance > range) - result = false; - else - result = true; - } - - return result; -} - -void Merc::AI_Process() { - if(!IsAIControlled()) - return; - - if(IsCasting()) - return; - - // A bot wont start its AI if not grouped - if(!HasGroup()) { - return; - } - - Mob* MercOwner = GetOwner(); - - if(GetAppearance() == eaDead) - { - if(!MercOwner) - { - Depop(); - } - return; - } - - // The merc needs an owner - if(!MercOwner) { - //SetTarget(0); - //SetOwnerID(0); - // TODO: Need to wait and try casting rez if merc is a healer with a dead owner - return; - } - - /* - try { - if(MercOwner->CastToClient()->IsDead()) { - SetTarget(0); - SetOwnerID(0); - return; - } - } - catch(...) { - SetTarget(0); - SetOwnerID(0); - return; - } - */ - - if(check_target_timer.Check()) { - CheckHateList(); - } - - if(IsEngaged()) - { - if(rest_timer.Enabled()) - rest_timer.Disable(); - - if(IsRooted()) - SetTarget(hate_list.GetClosest(this)); - else - FindTarget(); - - if(!GetTarget()) - return; - - if(HasPet()) - GetPet()->SetTarget(GetTarget()); - - if(!IsSitting()) - FaceTarget(GetTarget()); - - if(DivineAura()) - return; - - int hateCount = entity_list.GetHatedCount(this, nullptr); - if(GetHatedCount() < hateCount) { - SetHatedCount(hateCount); - - if(!CheckConfidence()) { - if(!confidence_timer.Enabled()) { - confidence_timer.Start(10000); - } - } - } - - //Check specific conditions for merc to lose confidence and flee (or regain confidence once fleeing) - if(_check_confidence) { - //not already running - if(!_lost_confidence) { - //and fail confidence check - if(!CheckConfidence()) { - _lost_confidence = true; - - //move to bottom of hate lists? - //Iterate though hatelist - // SetHate(other, hate, damage) - - if(RuleB(Combat, EnableFearPathing)) { - CalculateNewFearpoint(); - if(curfp) { - return; - } - } - else { - Stun(12000 - (6000 - tic_timer.GetRemainingTime())); - } - } - } - else { //are fleeing due to lost confidence - if(CheckConfidence()) { //passed test - regain confidence - _lost_confidence = false; - } - } - - //they are in flee mode - if(_lost_confidence) - return; - } - - // Let's check if we have a los with our target. - // If we don't, our hate_list is wiped. - // Else, it was causing the merc to aggro behind wall etc... causing massive trains. - if(!CheckLosFN(GetTarget()) || GetTarget()->IsMezzed() || !IsAttackAllowed(GetTarget())) { - WipeHateList(); - - if(IsMoving()) { - SetHeading(0); - SetRunAnimSpeed(0); - - if(moved) { - moved = false; - SendPosition(); - SetMoving(false); - } - } - - return; - } - - bool atCombatRange = false; - - float meleeDistance = GetMaxMeleeRangeToTarget(GetTarget()); - - if(GetClass() == SHADOWKNIGHT || GetClass() == PALADIN || GetClass() == WARRIOR) { - meleeDistance = meleeDistance * .30; - } - else { - meleeDistance *= (float)zone->random.Real(.50, .85); - } - if(IsMercCaster() && GetLevel() > 12) { - if(IsMercCasterCombatRange(GetTarget())) - atCombatRange = true; - } - else if(DistNoRoot(*GetTarget()) <= meleeDistance) { - atCombatRange = true; - } - - if(atCombatRange) - { - if(IsMoving()) - { - SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); - SetRunAnimSpeed(0); - - if(moved) { - moved = false; - SendPosition(); - SetMoving(false); - } - } - - if(AImovement_timer->Check()) - { - if(!IsMoving() && GetClass() == ROGUE && !BehindMob(GetTarget(), GetX(), GetY())) - { - // Move the rogue to behind the mob - float newX = 0; - float newY = 0; - float newZ = 0; - - if(PlotPositionAroundTarget(GetTarget(), newX, newY, newZ)) - { - CalculateNewPosition2(newX, newY, newZ, GetRunspeed()); - return; - } - } - else if(!IsMoving() && GetClass() != ROGUE && (DistNoRootNoZ(*GetTarget()) < GetTarget()->GetSize())) - { - // If we are not a rogue trying to backstab, let's try to adjust our melee range so we don't appear to be bunched up - 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; - } - } - - if(IsMoving()) - SendPosUpdate(); - else - SendPosition(); - } - - if(!IsMercCaster() && 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; - //TODO: Implement Stances. - /*if(GetBotStance() == BotStancePassive) - return;*/ - - // First, special attack per class (kick, backstab etc..) - DoClassAttacks(GetTarget()); - - //try main hand first - if(attack_timer.Check()) - { - Attack(GetTarget(), MainPrimary); - - bool tripleSuccess = false; - - if(GetOwner() && GetTarget() && CanThisClassDoubleAttack()) - { - if(GetOwner()) { - Attack(GetTarget(), MainPrimary, true); - } - - if(GetOwner() && GetTarget() && GetSpecialAbility(SPECATK_TRIPLE)) { - tripleSuccess = true; - Attack(GetTarget(), MainPrimary, true); - } - - //quad attack, does this belong here?? - if(GetOwner() && GetTarget() && GetSpecialAbility(SPECATK_QUAD)) { - Attack(GetTarget(), MainPrimary, true); - } - } - - //Live AA - Flurry, Rapid Strikes ect (Flurry does not require Triple Attack). - int16 flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance; - - if (GetTarget() && flurrychance) - { - if(zone->random.Roll(flurrychance)) - { - Message_StringID(MT_NPCFlurry, YOU_FLURRY); - Attack(GetTarget(), MainPrimary, false); - Attack(GetTarget(), MainPrimary, false); - } - } - - int16 ExtraAttackChanceBonus = spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance + aabonuses.ExtraAttackChance; - - if (GetTarget() && ExtraAttackChanceBonus) { - if(zone->random.Roll(ExtraAttackChanceBonus)) - { - Attack(GetTarget(), MainPrimary, false); - } - } - } - - // TODO: Do mercs berserk? Find this out on live... - //if (GetClass() == WARRIOR || GetClass() == BERSERKER) { - // if(GetHP() > 0 && !berserk && this->GetHPRatio() < 30) { - // 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; - // } - //} - - //now off hand - if(GetTarget() && attack_dw_timer.Check() && CanThisClassDualWield()) - { - int weapontype = 0; // No weapon type - bool bIsFist = true; - - if(bIsFist || ((weapontype != ItemType2HSlash) && (weapontype != ItemType2HPiercing) && (weapontype != ItemType2HBlunt))) - { - float DualWieldProbability = 0.0f; - - int16 Ambidexterity = aabonuses.Ambidexterity + spellbonuses.Ambidexterity + itembonuses.Ambidexterity; - DualWieldProbability = (GetSkill(SkillDualWield) + GetLevel() + Ambidexterity) / 400.0f; // 78.0 max - int16 DWBonus = spellbonuses.DualWieldChance + itembonuses.DualWieldChance; - DualWieldProbability += DualWieldProbability*float(DWBonus)/ 100.0f; - - // Max 78% of DW - if (zone->random.Roll(DualWieldProbability)) - { - Attack(GetTarget(), MainSecondary); // Single attack with offhand - - if(CanThisClassDoubleAttack()) { - if(GetTarget() && GetTarget()->GetHP() > -10) - Attack(GetTarget(), MainSecondary); // Single attack with offhand - } - } - } - } - } - } - 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 - //TODO: Implement Stances. - //if(GetStance() != MercStancePassive) - AI_PursueCastCheck(); - } - - if (AImovement_timer->Check()) - { - if(!IsRooted()) { - mlog(AI__WAYPOINTS, "Pursuing %s while engaged.", GetTarget()->GetCleanName()); - CalculateNewPosition2(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), GetRunspeed()); - return; - } - - if(IsMoving()) - SendPosUpdate(); - else - SendPosition(); - } - } // end not in combat range - - if(!IsMoving() && !spellend_timer.Enabled()) - { - //TODO: Implement Stances. - //if(GetStance() == MercStancePassive) - // return; - - if(AI_EngagedCastCheck()) { - MercMeditate(false); - } - else if(GetArchetype() == ARCHETYPE_CASTER) - MercMeditate(true); - } - } - else - { - // Not engaged in combat - SetTarget(0); - SetHatedCount(0); - confidence_timer.Disable(); - _check_confidence = false; - - if(!check_target_timer.Enabled()) - check_target_timer.Start(2000, false); - - if(!IsMoving() && AIthink_timer->Check() && !spellend_timer.Enabled()) - { - //TODO: Implement passive stances. - //if(GetStance() != MercStancePassive) { - if(!AI_IdleCastCheck() && !IsCasting()) { - if(GetArchetype() == ARCHETYPE_CASTER) { - MercMeditate(true); - } - } - } - - if(AImovement_timer->Check()) - { - if(GetFollowID()) - { - Mob* follow = entity_list.GetMob(GetFollowID()); - - if(follow) - { - float dist = DistNoRoot(*follow); - float speed = GetRunspeed(); - - if(dist < GetFollowDistance() + 1000) - speed = GetWalkspeed(); - - SetRunAnimSpeed(0); - - if(dist > GetFollowDistance()) { - CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), speed); - if(rest_timer.Enabled()) - rest_timer.Disable(); - return; - } - else - { - if(moved) - { - moved=false; - SendPosition(); - SetMoving(false); - } - } - } - } - } - } -} - -void Merc::AI_Start(int32 iMoveDelay) { - NPC::AI_Start(iMoveDelay); - if (!pAIControlled) - return; - - if (merc_spells.size() == 0) { - AIautocastspell_timer->SetTimer(1000); - AIautocastspell_timer->Disable(); - } else { - AIautocastspell_timer->SetTimer(750); - AIautocastspell_timer->Start(RandomTimer(0, 2000), false); - } - - if (NPCTypedata_ours) { - ProcessSpecialAbilities(NPCTypedata_ours->special_abilities); - } - - SendTo(GetX(), GetY(), GetZ()); - SetChanged(); - SaveGuardSpot(); -} - -void Merc::AI_Stop() { - NPC::AI_Stop(); - Mob::AI_Stop(); -} - -bool Merc::AI_EngagedCastCheck() { - bool result = false; - bool failedToCast = false; - - if (GetTarget() && AIautocastspell_timer->Check(false)) - { - AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. - - mlog(AI__SPELLS, "Engaged autocast check triggered (MERCS)."); - - int8 mercClass = GetClass(); - - switch(mercClass) - { - case TANK: - if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - if (!AICastSpell(GetChanceToCastBySpellType(SpellType_InCombatBuff), SpellType_InCombatBuff)) { - failedToCast = true; - } - } - break; - case HEALER: - if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), MercAISpellRange, SpellType_Heal)) { - if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Buff), MercAISpellRange, SpellType_Buff)) { - failedToCast = true; - } - } - break; - case MELEEDPS: - if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - if (!AICastSpell(GetChanceToCastBySpellType(SpellType_InCombatBuff), SpellType_InCombatBuff)) { - failedToCast = true; - } - } - } - break; - case CASTERDPS: - if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - failedToCast = true; - } - } - break; - } - - if(!AIautocastspell_timer->Enabled()) { - AIautocastspell_timer->Start(RandomTimer(100, 250), false); - } - - if(!failedToCast) - result = true; - } - - return result; -} - -bool Merc::AI_IdleCastCheck() { - bool result = false; - bool failedToCast = false; - - if (AIautocastspell_timer->Check(false)) { -#if MobAI_DEBUG_Spells >= 25 - std::cout << "Non-Engaged autocast check triggered: " << this->GetCleanName() << std::endl; -#endif - AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. - - //Ok, IdleCastCheck depends of class. - int8 mercClass = GetClass(); - - switch(mercClass) - { - case TANK: - failedToCast = true; - break; - case HEALER: - if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Cure)) { - if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Heal)) { - if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Resurrect)) { - if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Buff)) { - failedToCast = true; - } - } - } - } - result = true; - break; - case MELEEDPS: - if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Buff)) { - failedToCast = true; - } - break; - case CASTERDPS: - failedToCast = true; - break; - } - - if(!AIautocastspell_timer->Enabled()) - AIautocastspell_timer->Start(RandomTimer(500, 1000), false); - - if(!failedToCast) - result = true; - } - - return result; -} - -bool EntityList::Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, float iRange, uint32 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(AI__ERROR, "Error: detrimental spells requested from AICheckCloseBeneficialSpells!!"); - return(false); - } - - if(!caster) - return false; - - if(!caster->AI_HasSpells()) - return false; - - if (iChance < 100) { - int8 tmp = zone->random.Int(1, 100); - if (tmp > iChance) - return false; - } - - int8 mercCasterClass = caster->GetClass(); - - if(caster->HasGroup()) { - if( mercCasterClass == HEALER) { - if( iSpellTypes == SpellType_Heal ) { - if(caster->AICastSpell(100, SpellType_Heal)) - return true; - } - - if( iSpellTypes == SpellType_Cure ) { - if(caster->AICastSpell(100, SpellType_Cure)) - return true; - } - - if( iSpellTypes == SpellType_Resurrect ) { - if(caster->AICastSpell(100, SpellType_Resurrect)) - return true; - } - } - - //Ok for the buffs.. - if( iSpellTypes == SpellType_Buff) { - if(caster->AICastSpell(100, SpellType_Buff)) - return true; - } - } - - return false; -} - -bool Merc::AIDoSpellCast(uint16 spellid, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore) { - bool result = false; - MercSpell mercSpell = GetMercSpellBySpellID(this, spellid); - - // manacost has special values, -1 is no mana cost, -2 is instant cast (no mana) - int32 manaCost = mana_cost; - - if (manaCost == -1) - manaCost = spells[spellid].mana; - else if (manaCost == -2) - manaCost = 0; - - int32 extraMana = 0; - int32 hasMana = GetMana(); - - float dist2 = 0; - - if (mercSpell.type & SpellType_Escape) { - dist2 = 0; - } else - dist2 = DistNoRoot(*tar); - - if (((((spells[spellid].targettype==ST_GroupTeleport && mercSpell.type==SpellType_Heal) - || spells[spellid].targettype==ST_AECaster - || spells[spellid].targettype==ST_Group - || spells[spellid].targettype==ST_AEBard) - && dist2 <= spells[spellid].aoerange*spells[spellid].aoerange) - || dist2 <= GetActSpellRange(spellid, spells[spellid].range)*GetActSpellRange(spellid, spells[spellid].range)) && (mana_cost <= GetMana() || GetMana() == GetMaxMana())) - { - SetRunAnimSpeed(0); - SendPosition(); - SetMoving(false); - - result = CastSpell(spellid, tar->GetID(), 1, -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, 0); - - if(IsCasting() && IsSitting()) - Stand(); - } - - // if the spell wasn't casted, then take back any extra mana that was given to the bot to cast that spell - if(!result) { - SetMana(hasMana); - extraMana = false; - } - else { //handle spell recast and recast timers - SetSpellTimeCanCast(mercSpell.spellid, spells[spellid].recast_time); - - if(spells[spellid].EndurTimerIndex > 0) { - SetSpellRecastTimer(spells[spellid].EndurTimerIndex, spellid, spells[spellid].recast_time); - } - } - - return result; -} - -bool Merc::AICastSpell(int8 iChance, int32 iSpellTypes) { - - if(!AI_HasSpells()) - return false; - - if (iChance < 100) { - if (zone->random.Int(0, 100) > iChance){ - return false; - } - } - - int8 mercClass = GetClass(); - uint8 mercLevel = GetLevel(); - - bool checked_los = false; //we do not check LOS until we are absolutely sure we need to, and we only do it once. - bool castedSpell = false; - bool isDiscipline = false; - - if(HasGroup()) { - Group *g = GetGroup(); - - if(g) { - MercSpell selectedMercSpell; - selectedMercSpell.spellid = 0; - selectedMercSpell.stance = 0; - selectedMercSpell.type = 0; - selectedMercSpell.slot = 0; - selectedMercSpell.proc_chance = 0; - selectedMercSpell.time_cancast = 0; - - switch(mercClass) - { - case TANK: - case MELEEDPS: - isDiscipline = true; - break; - default: - isDiscipline = false; - break; - } - - switch (iSpellTypes) { - case SpellType_Heal: { - Mob* tar = nullptr; - int8 numToHeal = g->GetNumberNeedingHealedInGroup(IsEngaged() ? 75 : 95, true); - int8 checkHPR = IsEngaged() ? 95 : 99; - int8 checkPetHPR = IsEngaged() ? 95 : 99; - - //todo: check stance to determine healing spell selection - - for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if(g->members[i] && !g->members[i]->qglobal) { - int8 hpr = (int8)g->members[i]->GetHPRatio(); - - if(g->members[i]->HasPet() && g->members[i]->GetPet()->GetHPRatio() < checkHPR) { - if(!tar || ((g->members[i]->GetPet()->GetHPRatio() + 25) < tar->GetHPRatio())) { - tar = g->members[i]->GetPet(); - checkPetHPR = g->members[i]->GetPet()->GetHPRatio() + 25; - } - } - - if(hpr > checkHPR) { - continue; - } - - if(IsEngaged() && (g->members[i]->GetClass() == NECROMANCER && hpr >= 50) - || (g->members[i]->GetClass() == SHAMAN && hpr >= 80)) { - //allow necros to lifetap & shaman to canni without wasting mana - continue; - } - - if(hpr < checkHPR && g->members[i] == GetMercOwner()) { - if(!tar || (hpr < tar->GetHPRatio() || (tar->IsPet() && hpr < checkPetHPR))) - tar = g->members[i]; //check owner first - } - else if(hpr < checkHPR && g->HasRole(g->members[i], RoleTank)){ - if(!tar || (hpr < tar->GetHPRatio() || (tar->IsPet() && hpr < checkPetHPR))) - tar = g->members[i]; - } - else if( hpr < checkHPR && (!tar || (hpr < tar->GetHPRatio() || (tar->IsPet() && hpr < checkPetHPR)))) { - tar = g->members[i]; - } - } - } - - if(numToHeal > 2) { - selectedMercSpell = GetBestMercSpellForGroupHeal(this); - } - - if(tar && selectedMercSpell.spellid == 0) { - if(tar->GetHPRatio() < 15) { - //check for very fast heals first (casting time < 1 s) - selectedMercSpell = GetBestMercSpellForVeryFastHeal(this); - - //check for fast heals next (casting time < 2 s) - if(selectedMercSpell.spellid == 0) { - selectedMercSpell = GetBestMercSpellForFastHeal(this); - } - - //get regular heal - if(selectedMercSpell.spellid == 0) { - selectedMercSpell = GetBestMercSpellForRegularSingleTargetHeal(this); - } - } - else if (tar->GetHPRatio() < 35) { - //check for fast heals next (casting time < 2 s) - selectedMercSpell = GetBestMercSpellForFastHeal(this); - - //get regular heal - if(selectedMercSpell.spellid == 0) { - selectedMercSpell = GetBestMercSpellForRegularSingleTargetHeal(this); - } - } - else if (tar->GetHPRatio() < 80) { - selectedMercSpell = GetBestMercSpellForPercentageHeal(this); - - //get regular heal - if(selectedMercSpell.spellid == 0) { - selectedMercSpell = GetBestMercSpellForRegularSingleTargetHeal(this); - } - } - else { - //check for heal over time. if not present, try it first - if(!tar->FindType(SE_HealOverTime)) { - selectedMercSpell = GetBestMercSpellForHealOverTime(this); - - //get regular heal - if(selectedMercSpell.spellid == 0) { - selectedMercSpell = GetBestMercSpellForRegularSingleTargetHeal(this); - } - } - } - } - - if(selectedMercSpell.spellid > 0) { - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar, -1); - } - - if(castedSpell) { - char* gmsg = 0; - - if(tar != this) { - //we don't need spam of bots healing themselves - MakeAnyLenString(&gmsg, "Casting %s on %s.", spells[selectedMercSpell.spellid].name, tar->GetCleanName()); - if(gmsg) - { - MercGroupSay(this, gmsg); - safe_delete_array(gmsg); - } - } - } - - break; - } - case SpellType_Root: { - break; - } - case SpellType_Buff: { - - if(GetClass() == HEALER && GetManaRatio() < 50) { - return false; //mercs buff when Mana > 50% - } - - std::list buffSpellList = GetMercSpellsBySpellType(this, SpellType_Buff); - - for(std::list::iterator itr = buffSpellList.begin(); itr != buffSpellList.end(); ++itr) { - MercSpell selectedMercSpell = *itr; - - if(!((spells[selectedMercSpell.spellid].targettype == ST_Target || spells[selectedMercSpell.spellid].targettype == ST_Pet || - spells[selectedMercSpell.spellid].targettype == ST_Group || spells[selectedMercSpell.spellid].targettype == ST_GroupTeleport || - spells[selectedMercSpell.spellid].targettype == ST_Self))) { - continue; - } - - if(spells[selectedMercSpell.spellid].targettype == ST_Self) { - if( !this->IsImmuneToSpell(selectedMercSpell.spellid, this) - && (this->CanBuffStack(selectedMercSpell.spellid, mercLevel, true) >= 0)) { - - if( this->GetArchetype() == ARCHETYPE_MELEE && IsEffectInSpell(selectedMercSpell.spellid, SE_IncreaseSpellHaste)) { - continue; - } - - uint32 TempDontBuffMeBeforeTime = this->DontBuffMeBefore(); - - if(selectedMercSpell.spellid > 0) { - if(isDiscipline) { - castedSpell = UseDiscipline(selectedMercSpell.spellid, GetID()); - } - else { - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, this, -1, &TempDontBuffMeBeforeTime); - - if(TempDontBuffMeBeforeTime != this->DontBuffMeBefore()) - this->SetDontBuffMeBefore(TempDontBuffMeBeforeTime); - } - } - } - } - else { - for( int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if(g->members[i]) { - Mob* tar = g->members[i]; - - if( !tar->IsImmuneToSpell(selectedMercSpell.spellid, this) - && (tar->CanBuffStack(selectedMercSpell.spellid, mercLevel, true) >= 0)) { - - if( tar->GetArchetype() == ARCHETYPE_MELEE && IsEffectInSpell(selectedMercSpell.spellid, SE_IncreaseSpellHaste)) { - continue; - } - - uint32 TempDontBuffMeBeforeTime = tar->DontBuffMeBefore(); - - if(selectedMercSpell.spellid > 0) { - if(isDiscipline) { - castedSpell = UseDiscipline(selectedMercSpell.spellid, tar->GetID()); - } - else { - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar, -1, &TempDontBuffMeBeforeTime); - - if(TempDontBuffMeBeforeTime != tar->DontBuffMeBefore()) - tar->SetDontBuffMeBefore(TempDontBuffMeBeforeTime); - } - } - } - - if(!castedSpell && tar->GetPet()) { - - //don't cast group spells on pets - if(IsGroupSpell(selectedMercSpell.spellid) - || spells[selectedMercSpell.spellid].targettype == ST_Group - || spells[selectedMercSpell.spellid].targettype == ST_GroupTeleport ) { - continue; - } - - if(!tar->GetPet()->IsImmuneToSpell(selectedMercSpell.spellid, this) - && (tar->GetPet()->CanBuffStack(selectedMercSpell.spellid, mercLevel, true) >= 0)) { - - uint32 TempDontBuffMeBeforeTime = tar->DontBuffMeBefore(); - - if(selectedMercSpell.spellid > 0) { - if(isDiscipline) { - castedSpell = UseDiscipline(selectedMercSpell.spellid, tar->GetPet()->GetID()); - } - else { - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar->GetPet(), -1, &TempDontBuffMeBeforeTime); - - if(TempDontBuffMeBeforeTime != tar->GetPet()->DontBuffMeBefore()) - tar->GetPet()->SetDontBuffMeBefore(TempDontBuffMeBeforeTime); - } - } - } - } - } - } - } - } - break; - } - case SpellType_Nuke: { - switch(mercClass) - { - case TANK: - //check for taunt - if(CheckAETaunt()) { - if(MERC_DEBUG > 0) - GetOwner()->Message(7, "AE Taunting"); - //get AE taunt - selectedMercSpell = GetBestMercSpellForAETaunt(this); - } - - if(selectedMercSpell.spellid == 0 && CheckTaunt()) { - //get taunt - selectedMercSpell = GetBestMercSpellForTaunt(this); - } - - //get hate disc - if(selectedMercSpell.spellid == 0) { - selectedMercSpell = GetBestMercSpellForHate(this); - } - - break; - case HEALER: - break; - case MELEEDPS: - break; - case CASTERDPS: - Mob* tar = GetTarget(); - - selectedMercSpell = GetBestMercSpellForAENuke(this, tar); - - if(selectedMercSpell.spellid == 0 && !tar->GetSpecialAbility(UNSTUNABLE) && !tar->IsStunned()) { - uint8 stunChance = 15; - if(zone->random.Roll(stunChance)) { - selectedMercSpell = GetBestMercSpellForStun(this); - } - } - - if(selectedMercSpell.spellid == 0) { - uint8 lureChance = 25; - if(zone->random.Roll(lureChance)) { - selectedMercSpell = GetBestMercSpellForNukeByTargetResists(this, tar); - } - } - - if(selectedMercSpell.spellid == 0) { - selectedMercSpell = GetBestMercSpellForNuke(this); - } - - break; - } - - if(selectedMercSpell.spellid > 0) { - if(isDiscipline) { - castedSpell = UseDiscipline(selectedMercSpell.spellid, GetTarget()->GetID()); - } - else { - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, GetTarget(), -1); - } - } - - break; - } - case SpellType_InCombatBuff: { - std::list buffSpellList = GetMercSpellsBySpellType(this, SpellType_InCombatBuff); - Mob* tar = this; - - for(std::list::iterator itr = buffSpellList.begin(); itr != buffSpellList.end(); ++itr) { - MercSpell selectedMercSpell = *itr; - - if(!(spells[selectedMercSpell.spellid].targettype == ST_Self)) { - continue; - } - - if(spells[selectedMercSpell.spellid].skill == SkillBackstab && spells[selectedMercSpell.spellid].targettype == ST_Self) { - if(!hidden) { - continue; - } - } - - if( !tar->IsImmuneToSpell(selectedMercSpell.spellid, this) - && (tar->CanBuffStack(selectedMercSpell.spellid, mercLevel, true) >= 0)) { - - uint32 TempDontBuffMeBeforeTime = tar->DontBuffMeBefore(); - - if(selectedMercSpell.spellid > 0) { - if(isDiscipline) { - castedSpell = UseDiscipline(selectedMercSpell.spellid, GetID()); - } - else { - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, this, -1); - } - } - } - } - break; - } - case SpellType_Cure: { - Mob* tar = nullptr; - for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if(g->members[i] && !g->members[i]->qglobal) { - if(GetNeedsCured(g->members[i]) && (g->members[i]->DontCureMeBefore() < Timer::GetCurrentTime())) { - tar = g->members[i]; - } - } - } - - if(tar && !(g->GetNumberNeedingHealedInGroup(IsEngaged() ? 25 : 40, false) > 0) && !(g->GetNumberNeedingHealedInGroup(IsEngaged() ? 40 : 60, false) > 2)) - { - selectedMercSpell = GetBestMercSpellForCure(this, tar); - - if(selectedMercSpell.spellid == 0) - break; - - uint32 TempDontCureMeBeforeTime = tar->DontCureMeBefore(); - - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar, spells[selectedMercSpell.spellid].mana, &TempDontCureMeBeforeTime); - - if(castedSpell) { - if(IsGroupSpell(selectedMercSpell.spellid)){ - - if(this->HasGroup()) { - Group *g = this->GetGroup(); - - if(g) { - for( int i = 0; imembers[i] && !g->members[i]->qglobal) { - if(TempDontCureMeBeforeTime != tar->DontCureMeBefore()) - g->members[i]->SetDontCureMeBefore(Timer::GetCurrentTime() + 4000); - } - } - } - } - } - else { - if(TempDontCureMeBeforeTime != tar->DontCureMeBefore()) - tar->SetDontCureMeBefore(Timer::GetCurrentTime() + 4000); - } - } - } - break; - } - case SpellType_Resurrect: { - Corpse *corpse = GetGroupMemberCorpse(); - - if(corpse) { - selectedMercSpell = GetFirstMercSpellBySpellType(this, SpellType_Resurrect); - - if(selectedMercSpell.spellid == 0) - break; - - uint32 TempDontRootMeBeforeTime = corpse->DontRootMeBefore(); - - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, corpse, spells[selectedMercSpell.spellid].mana, &TempDontRootMeBeforeTime); - - //CastSpell(selectedMercSpell.spellid, corpse->GetID(), 1, -1, -1, &TempDontRootMeBeforeTime); - corpse->SetDontRootMeBefore(TempDontRootMeBeforeTime); - } - - break; - } - case SpellType_Escape: { - Mob* tar = GetTarget(); - uint8 hpr = (uint8)GetHPRatio(); - bool mayGetAggro = false; - - if(tar && (mercClass == CASTERDPS) || (mercClass == MELEEDPS)) { - mayGetAggro = HasOrMayGetAggro(); //classes have hate reducing spells - - if (mayGetAggro) { - selectedMercSpell = GetFirstMercSpellBySpellType(this, SpellType_Escape); - - if(selectedMercSpell.spellid == 0) - break; - - if(isDiscipline) { - castedSpell = UseDiscipline(selectedMercSpell.spellid, tar->GetID()); - } - else { - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar, -1); - } - } - } - break; - } - } - } - } - - return castedSpell; -} - -void Merc::CheckHateList() { - if(check_target_timer.Enabled()) - check_target_timer.Disable(); - - if(!IsEngaged()) { - if(GetFollowID()) { - Group* g = GetGroup(); - if(g) { - Mob* MercOwner = GetOwner(); - if(MercOwner && MercOwner->GetTarget() && MercOwner->GetTarget()->IsNPC() && (MercOwner->GetTarget()->GetHateAmount(MercOwner) || MercOwner->CastToClient()->AutoAttackEnabled()) && IsAttackAllowed(MercOwner->GetTarget())) { - float range = g->HasRole(MercOwner, RolePuller) ? RuleI(Mercs, AggroRadiusPuller) : RuleI(Mercs, AggroRadius); - range = range * range; - if(MercOwner->GetTarget()->DistNoRootNoZ(*this) < range) { - AddToHateList(MercOwner->GetTarget(), 1); - } - } - else { - std::list npc_list; - entity_list.GetNPCList(npc_list); - - for(std::list::iterator itr = npc_list.begin(); itr != npc_list.end(); ++itr) { - NPC* npc = *itr; - float dist = npc->DistNoRootNoZ(*this); - int radius = RuleI(Mercs, AggroRadius); - radius *= radius; - if(dist <= radius) { - - for(int counter = 0; counter < g->GroupCount(); counter++) { - Mob* groupMember = g->members[counter]; - if(groupMember) { - if(npc->IsOnHatelist(groupMember)) { - if(!hate_list.IsOnHateList(npc)) { - float range = g->HasRole(groupMember, RolePuller) ? RuleI(Mercs, AggroRadiusPuller) : RuleI(Mercs, AggroRadius); - range *= range; - if(npc->DistNoRootNoZ(*this) < range) { - hate_list.Add(npc, 1); - } - } - } - } - } - } - } - } - } - } - } -} - -bool Merc::HasOrMayGetAggro() { - bool mayGetAggro = false; - - if(GetTarget() && GetTarget()->GetHateTop()) { - Mob *topHate = GetTarget()->GetHateTop(); - - if(topHate == this) - mayGetAggro = true; //I currently have aggro - 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 - mayGetAggro = true; - } - } - - return mayGetAggro; -} - -bool Merc::CheckAENuke(Merc* caster, Mob* tar, uint16 spell_id, uint8 &numTargets) { - std::list npc_list; - entity_list.GetNPCList(npc_list); - - for(std::list::iterator itr = npc_list.begin(); itr != npc_list.end(); ++itr) { - NPC* npc = *itr; - - if(npc->DistNoRootNoZ(*tar) <= spells[spell_id].aoerange * spells[spell_id].aoerange) { - if(!npc->IsMezzed()) { - numTargets++; - } - else { - numTargets = 0; - return false; - } - } - } - - if(numTargets > 1) - return true; - - return false; -} - -int16 Merc::GetFocusEffect(focusType type, uint16 spell_id) { - - int16 realTotal = 0; - int16 realTotal2 = 0; - int16 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((type == focusManaCost || type == focusImprovedHeal || type == focusImprovedDamage) - && RuleB(Spells, LiveLikeFocusEffects)) - { - rand_effectiveness = true; - } - - //Check if item focus effect exists for the client. - if (itembonuses.FocusEffects[type]){ - - const Item_Struct* TempItem = 0; - const Item_Struct* UsedItem = 0; - uint16 UsedFocusID = 0; - int16 Total = 0; - int16 focus_max = 0; - int16 focus_max_real = 0; - - //item focus - for (int x = 0; x < EmuConstants::EQUIPMENT_SIZE; ++x) - { - TempItem = nullptr; - if (equipment[x] == 0) - continue; - TempItem = database.GetItem(equipment[x]); - if (TempItem && TempItem->Focus.Effect > 0 && TempItem->Focus.Effect != SPELL_UNKNOWN) { - if(rand_effectiveness) { - focus_max = CalcFocusEffect(type, 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) { - focus_max_real = focus_max; - UsedItem = TempItem; - UsedFocusID = TempItem->Focus.Effect; - } - } - else { - Total = CalcFocusEffect(type, 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) { - realTotal = Total; - UsedItem = TempItem; - UsedFocusID = TempItem->Focus.Effect; - } - } - } - } - - 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); - } - - //Check if spell focus effect exists for the client. - if (spellbonuses.FocusEffects[type]){ - - //Spell Focus - int16 Total2 = 0; - int16 focus_max2 = 0; - int16 focus_max_real2 = 0; - - int buff_tracker = -1; - int buff_slot = 0; - uint16 focusspellid = 0; - uint16 focusspell_tracker = 0; - uint32 buff_max = GetMaxTotalSlots(); - for (buff_slot = 0; buff_slot < buff_max; buff_slot++) { - focusspellid = buffs[buff_slot].spellid; - if (focusspellid == 0 || focusspellid >= SPDAT_RECORDS) - continue; - - if(rand_effectiveness) { - focus_max2 = CalcFocusEffect(type, 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) { - focus_max_real2 = focus_max2; - buff_tracker = buff_slot; - focusspell_tracker = focusspellid; - } - } - else { - Total2 = CalcFocusEffect(type, 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) { - realTotal2 = Total2; - buff_tracker = buff_slot; - focusspell_tracker = focusspellid; - } - } - } - - if(focusspell_tracker && rand_effectiveness && focus_max_real2 != 0) - realTotal2 = CalcFocusEffect(type, 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) { - m_spellHitsLeft[buff_tracker] = focusspell_tracker; - } - } - - - // AA Focus - /*if (aabonuses.FocusEffects[type]){ - - int16 Total3 = 0; - uint32 slots = 0; - uint32 aa_AA = 0; - uint32 aa_value = 0; - - for (int i = 0; i < MAX_PP_AA_ARRAY; i++) - { - aa_AA = this->aa[i]->AA; - aa_value = this->aa[i]->value; - if (aa_AA < 1 || aa_value < 1) - continue; - - Total3 = CalcAAFocus(type, aa_AA, spell_id); - if (Total3 > 0 && realTotal3 >= 0 && Total3 > realTotal3) { - realTotal3 = Total3; - } - else if (Total3 < 0 && Total3 < realTotal3) { - realTotal3 = Total3; - } - } - }*/ - - if(type == focusReagentCost && IsSummonPetSpell(spell_id) && GetAA(aaElementalPact)) - return 100; - - if(type == focusReagentCost && (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 + realTotal3; -} - - -int32 Merc::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*GetFocusEffect(focusFcBaseEffects, spell_id)/100); - - int chance = RuleI(Spells, BaseCritChance); - chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance; - - if (chance > 0){ - - int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals. - - if (zone->random.Roll(chance)) { - Critical = true; - ratio += itembonuses.SpellCritDmgIncrease + spellbonuses.SpellCritDmgIncrease + aabonuses.SpellCritDmgIncrease; - ratio += itembonuses.SpellCritDmgIncNoStack + spellbonuses.SpellCritDmgIncNoStack + aabonuses.SpellCritDmgIncNoStack; - } - - else if (GetClass() == CASTERDPS && (GetLevel() >= RuleI(Spells, WizCritLevel)) && (zone->random.Roll(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. - Critical = true; - } - - ratio += RuleI(Spells, WizCritRatio); //Default is zero - - if (Critical){ - - value = value_BaseEffect*ratio/100; - - value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100; - - value += int(value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100; - - if (target) { - value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100; - value -= target->GetFcDamageAmtIncoming(this, spell_id); - } - - value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100; - - value -= GetFocusEffect(focusFcDamageAmt, spell_id); - - if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) - value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio/100; - - value = (value * GetSpellScale() / 100); - - entity_list.MessageClose_StringID(this, false, 100, MT_SpellCrits, - OTHER_CRIT_BLAST, GetName(), itoa(-value)); - - return value; - } - } - - value = value_BaseEffect; - - value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100; - - value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100; - - if (target) { - value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100; - value -= target->GetFcDamageAmtIncoming(this, spell_id); - } - - value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id); - - value -= GetFocusEffect(focusFcDamageAmt, spell_id); - - if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) - value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); - - value = (value * GetSpellScale() / 100); - - return value; -} - -int32 Merc::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { - - if (target == nullptr) - target = this; - - int32 value_BaseEffect = 0; - int16 chance = 0; - int8 modifier = 1; - bool Critical = false; - - value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100); - - value = value_BaseEffect; - - value += int(value_BaseEffect*GetFocusEffect(focusImprovedHeal, spell_id)/100); - - // Instant Heals - if(spells[spell_id].buffduration < 1) { - - 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.Roll(chance)) { - Critical = true; - modifier = 2; //At present time no critical heal amount modifier SPA exists. - } - - value *= modifier; - value += GetFocusEffect(focusFcHealAmtCrit, spell_id) * modifier; - 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) - 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; - - chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); - - if (spellbonuses.CriticalRegenDecay) - chance += GetDecayEffectValue(spell_id, SE_CriticalRegenDecay); - - if(chance && zone->random.Roll(chance)) - return (value * 2); - } - - return value; -} - -int32 Merc::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) - { - int16 mana_back = this->itembonuses.Clairvoyance * zone->random.Int(1, 100) / 100; - // Doesnt generate mana, so best case is a free spell - 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; - - int16 focus_redux = GetFocusEffect(focusManaCost, 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++) { - if (buffs[buffSlot].spellid == 0 || buffs[buffSlot].spellid >= SPDAT_RECORDS) - continue; - - if(IsEffectInSpell(buffs[buffSlot].spellid, SE_ReduceManaCost)) { - if(CalcFocusEffect(focusManaCost, buffs[buffSlot].spellid, spell_id) == 100) - cost = 1; - } - } - } - - if(cost < 0) - cost = 0; - - return cost; -} - -int32 Merc::GetActSpellCasttime(uint16 spell_id, int32 casttime) -{ - int32 cast_reducer = 0; - cast_reducer += GetFocusEffect(focusSpellHaste, spell_id); - - if (cast_reducer > RuleI(Spells, MaxCastTimeReduction)) - cast_reducer = RuleI(Spells, MaxCastTimeReduction); - - casttime = (casttime*(100 - cast_reducer)/100); - - return casttime; -} - -int8 Merc::GetChanceToCastBySpellType(int16 spellType) { - int mercStance = (int)GetStance(); - int8 mercClass = GetClass(); - int8 chance = 0; - - switch (spellType) { - case SpellType_Nuke: { - switch(mercClass) - { - case TANK: { - chance = 100; - break; - } - case HEALER:{ - break; - } - case MELEEDPS:{ - chance = 100; - break; - } - case CASTERDPS:{ - chance = 100; - break; - } - } - break; - } - case SpellType_Heal: { - switch(mercClass) - { - case TANK: { - break; - } - case HEALER:{ - chance = 100; - break; - } - case MELEEDPS:{ - break; - } - case CASTERDPS:{ - break; - } - } - break; - } - case SpellType_Root: { - switch(mercClass) - { - case TANK: { - break; - } - case HEALER:{ - break; - } - case MELEEDPS:{ - break; - } - case CASTERDPS:{ - break; - } - } - break; - } - case SpellType_Buff: { - switch(mercClass) - { - case TANK: { - break; - } - case HEALER:{ - chance = IsEngaged() ? 0 : 100; - break; - } - case MELEEDPS:{ - break; - } - case CASTERDPS:{ - break; - } - } - break; - } - case SpellType_InCombatBuff: { - switch(mercClass) - { - case TANK: { - chance = 50; - break; - } - case HEALER:{ - break; - } - case MELEEDPS:{ - chance = 50; - break; - } - case CASTERDPS:{ - break; - } - } - break; - } - case SpellType_Escape: { - switch(mercClass) - { - case TANK: { - break; - } - case HEALER:{ - break; - } - case MELEEDPS:{ - chance = 100; - break; - } - case CASTERDPS:{ - chance = 100; - break; - } - } - break; - } - default: - chance = 0; - break; - } - - return chance; -} - -bool Merc::CheckStance(int16 stance) { - - //checks of current stance matches stances listed as valid for spell in database - //stance = 0 for all stances, stance # for only that stance & -stance# for all but that stance - if(stance == 0 - || (stance > 0 && stance == GetStance()) - || (stance < 0 && abs(stance) != GetStance())) { - return true; - } - - return false; -} - -std::list Merc::GetMercSpellsBySpellType(Merc* caster, int spellType) { - std::list result; - - if(caster && caster->AI_HasSpells()) { - std::vector mercSpellList = caster->GetMercSpells(); - - for (int i = mercSpellList.size() - 1; i >= 0; i--) { - if (mercSpellList[i].spellid <= 0 || mercSpellList[i].spellid >= SPDAT_RECORDS) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here - continue; - } - - if((mercSpellList[i].type & spellType) && caster->CheckStance(mercSpellList[i].stance)) { - MercSpell mercSpell; - mercSpell.spellid = mercSpellList[i].spellid; - mercSpell.stance = mercSpellList[i].stance; - mercSpell.type = mercSpellList[i].type; - mercSpell.slot = mercSpellList[i].slot; - mercSpell.proc_chance = mercSpellList[i].proc_chance; - mercSpell.time_cancast = mercSpellList[i].time_cancast; - - result.push_back(mercSpell); - } - } - } - - return result; -} - -MercSpell Merc::GetFirstMercSpellBySpellType(Merc* caster, int spellType) { - MercSpell result; - - result.spellid = 0; - result.stance = 0; - result.type = 0; - result.slot = 0; - result.proc_chance = 0; - result.time_cancast = 0; - - if(caster && caster->AI_HasSpells()) { - std::vector mercSpellList = caster->GetMercSpells(); - - for (int i = mercSpellList.size() - 1; i >= 0; i--) { - if (mercSpellList[i].spellid <= 0 || mercSpellList[i].spellid >= SPDAT_RECORDS) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here - continue; - } - - if((mercSpellList[i].type & spellType) - && caster->CheckStance(mercSpellList[i].stance) - && CheckSpellRecastTimers(caster, mercSpellList[i].spellid)) { - result.spellid = mercSpellList[i].spellid; - result.stance = mercSpellList[i].stance; - result.type = mercSpellList[i].type; - result.slot = mercSpellList[i].slot; - result.proc_chance = mercSpellList[i].proc_chance; - result.time_cancast = mercSpellList[i].time_cancast; - - break; - } - } - } - - return result; -} - -MercSpell Merc::GetMercSpellBySpellID(Merc* caster, uint16 spellid) { - MercSpell result; - - result.spellid = 0; - result.stance = 0; - result.type = 0; - result.slot = 0; - result.proc_chance = 0; - result.time_cancast = 0; - - if(caster && caster->AI_HasSpells()) { - std::vector mercSpellList = caster->GetMercSpells(); - - for (int i = mercSpellList.size() - 1; i >= 0; i--) { - if (mercSpellList[i].spellid <= 0 || mercSpellList[i].spellid >= SPDAT_RECORDS) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here - continue; - } - - if((mercSpellList[i].spellid == spellid) - && caster->CheckStance(mercSpellList[i].stance)) { - result.spellid = mercSpellList[i].spellid; - result.stance = mercSpellList[i].stance; - result.type = mercSpellList[i].type; - result.slot = mercSpellList[i].slot; - result.proc_chance = mercSpellList[i].proc_chance; - result.time_cancast = mercSpellList[i].time_cancast; - - break; - } - } - } - - return result; -} - -std::list Merc::GetMercSpellsForSpellEffect(Merc* caster, int spellEffect) { - std::list result; - - if(caster && caster->AI_HasSpells()) { - std::vector mercSpellList = caster->GetMercSpells(); - - for (int i = mercSpellList.size() - 1; i >= 0; i--) { - if (mercSpellList[i].spellid <= 0 || mercSpellList[i].spellid >= SPDAT_RECORDS) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here - continue; - } - - if(IsEffectInSpell(mercSpellList[i].spellid, spellEffect) && caster->CheckStance(mercSpellList[i].stance)) { - MercSpell MercSpell; - MercSpell.spellid = mercSpellList[i].spellid; - MercSpell.stance = mercSpellList[i].stance; - MercSpell.type = mercSpellList[i].type; - MercSpell.slot = mercSpellList[i].slot; - MercSpell.proc_chance = mercSpellList[i].proc_chance; - MercSpell.time_cancast = mercSpellList[i].time_cancast; - - result.push_back(MercSpell); - } - } - } - - return result; -} - -std::list Merc::GetMercSpellsForSpellEffectAndTargetType(Merc* caster, int spellEffect, SpellTargetType targetType) { - std::list result; - - if(caster && caster->AI_HasSpells()) { - std::vector mercSpellList = caster->GetMercSpells(); - - for (int i = mercSpellList.size() - 1; i >= 0; i--) { - if (mercSpellList[i].spellid <= 0 || mercSpellList[i].spellid >= SPDAT_RECORDS) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here - continue; - } - - if(IsEffectInSpell(mercSpellList[i].spellid, spellEffect) && caster->CheckStance(mercSpellList[i].stance)) { - if(spells[mercSpellList[i].spellid].targettype == targetType) { - MercSpell MercSpell; - MercSpell.spellid = mercSpellList[i].spellid; - MercSpell.stance = mercSpellList[i].stance; - MercSpell.type = mercSpellList[i].type; - MercSpell.slot = mercSpellList[i].slot; - MercSpell.proc_chance = mercSpellList[i].proc_chance; - MercSpell.time_cancast = mercSpellList[i].time_cancast; - - result.push_back(MercSpell); - } - } - } - } - - return result; -} - -MercSpell Merc::GetBestMercSpellForVeryFastHeal(Merc* caster) { - MercSpell result; - - result.spellid = 0; - result.stance = 0; - result.type = 0; - result.slot = 0; - result.proc_chance = 0; - result.time_cancast = 0; - - if(caster) { - std::list mercSpellList = GetMercSpellsForSpellEffect(caster, SE_CurrentHP); - - for(std::list::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); ++mercSpellListItr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if(IsVeryFastHealSpell(mercSpellListItr->spellid) - && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; - - break; - } - } - } - - return result; -} - -MercSpell Merc::GetBestMercSpellForFastHeal(Merc* caster) { - MercSpell result; - - result.spellid = 0; - result.stance = 0; - result.type = 0; - result.slot = 0; - result.proc_chance = 0; - result.time_cancast = 0; - - if(caster) { - std::list mercSpellList = GetMercSpellsForSpellEffect(caster, SE_CurrentHP); - - for(std::list::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); ++mercSpellListItr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if(IsFastHealSpell(mercSpellListItr->spellid) - && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; - - break; - } - } - } - - return result; -} - -MercSpell Merc::GetBestMercSpellForHealOverTime(Merc* caster) { - MercSpell result; - - result.spellid = 0; - result.stance = 0; - result.type = 0; - result.slot = 0; - result.proc_chance = 0; - result.time_cancast = 0; - - if(caster) { - std::list mercHoTSpellList = GetMercSpellsForSpellEffect(caster, SE_HealOverTime); - - for(std::list::iterator mercSpellListItr = mercHoTSpellList.begin(); mercSpellListItr != mercHoTSpellList.end(); ++mercSpellListItr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if(IsHealOverTimeSpell(mercSpellListItr->spellid)) { - - if (mercSpellListItr->spellid <= 0 || mercSpellListItr->spellid >= SPDAT_RECORDS) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here - continue; - } - - if(CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; - } - - break; - } - } - } - - return result; -} - -MercSpell Merc::GetBestMercSpellForPercentageHeal(Merc* caster) { - MercSpell result; - - result.spellid = 0; - result.stance = 0; - result.type = 0; - result.slot = 0; - result.proc_chance = 0; - result.time_cancast = 0; - - if(caster && caster->AI_HasSpells()) { - std::list mercSpellList = GetMercSpellsForSpellEffect(caster, SE_CurrentHP); - - for(std::list::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); ++mercSpellListItr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if(IsCompleteHealSpell(mercSpellListItr->spellid) - && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; - - break; - } - } - } - - return result; -} - -MercSpell Merc::GetBestMercSpellForRegularSingleTargetHeal(Merc* caster) { - MercSpell result; - - result.spellid = 0; - result.stance = 0; - result.type = 0; - result.slot = 0; - result.proc_chance = 0; - result.time_cancast = 0; - - if(caster) { - std::list mercSpellList = GetMercSpellsForSpellEffect(caster, SE_CurrentHP); - - for(std::list::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); ++mercSpellListItr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if(IsRegularSingleTargetHealSpell(mercSpellListItr->spellid) - && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; - - break; - } - } - } - - return result; -} - -MercSpell Merc::GetFirstMercSpellForSingleTargetHeal(Merc* caster) { - MercSpell result; - - result.spellid = 0; - result.stance = 0; - result.type = 0; - result.slot = 0; - result.proc_chance = 0; - result.time_cancast = 0; - - if(caster) { - std::list mercSpellList = GetMercSpellsForSpellEffect(caster, SE_CurrentHP); - - for(std::list::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); ++mercSpellListItr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if((IsRegularSingleTargetHealSpell(mercSpellListItr->spellid) - || IsFastHealSpell(mercSpellListItr->spellid)) - && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; - - break; - } - } - } - - return result; -} - -MercSpell Merc::GetBestMercSpellForGroupHeal(Merc* caster) { - MercSpell result; - - result.spellid = 0; - result.stance = 0; - result.type = 0; - result.slot = 0; - result.proc_chance = 0; - result.time_cancast = 0; - - if(caster) { - std::list mercSpellList = GetMercSpellsForSpellEffect(caster, SE_CurrentHP); - - for(std::list::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); ++mercSpellListItr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if(IsRegularGroupHealSpell(mercSpellListItr->spellid) - && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; - - break; - } - } - } - - return result; -} - -MercSpell Merc::GetBestMercSpellForGroupHealOverTime(Merc* caster) { - MercSpell result; - - result.spellid = 0; - result.stance = 0; - result.type = 0; - result.slot = 0; - result.proc_chance = 0; - result.time_cancast = 0; - - if(caster) { - std::list mercHoTSpellList = GetMercSpellsForSpellEffect(caster, SE_HealOverTime); - - for(std::list::iterator mercSpellListItr = mercHoTSpellList.begin(); mercSpellListItr != mercHoTSpellList.end(); ++mercSpellListItr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if(IsGroupHealOverTimeSpell(mercSpellListItr->spellid)) { - - if (mercSpellListItr->spellid <= 0 || mercSpellListItr->spellid >= SPDAT_RECORDS) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here - continue; - } - - if(CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; - } - - break; - } - } - } - - return result; -} - -MercSpell Merc::GetBestMercSpellForGroupCompleteHeal(Merc* caster) { - MercSpell result; - - result.spellid = 0; - result.stance = 0; - result.type = 0; - result.slot = 0; - result.proc_chance = 0; - result.time_cancast = 0; - - if(caster) { - std::list mercSpellList = GetMercSpellsForSpellEffect(caster, SE_CompleteHeal); - - for(std::list::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); ++mercSpellListItr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if(IsGroupCompleteHealSpell(mercSpellListItr->spellid) - && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; - - break; - } - } - } - - return result; -} - -MercSpell Merc::GetBestMercSpellForAETaunt(Merc* caster) { - MercSpell result; - - result.spellid = 0; - result.stance = 0; - result.type = 0; - result.slot = 0; - result.proc_chance = 0; - result.time_cancast = 0; - - if(caster) { - std::list mercSpellList = GetMercSpellsForSpellEffect(caster, SE_Taunt); - - for(std::list::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); ++mercSpellListItr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if((spells[mercSpellListItr->spellid].targettype == ST_AECaster - || spells[mercSpellListItr->spellid].targettype == ST_AETarget - || spells[mercSpellListItr->spellid].targettype == ST_UndeadAE) - && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; - - break; - } - } - } - - return result; -} - -MercSpell Merc::GetBestMercSpellForTaunt(Merc* caster) { - MercSpell result; - - result.spellid = 0; - result.stance = 0; - result.type = 0; - result.slot = 0; - result.proc_chance = 0; - result.time_cancast = 0; - - if(caster) { - std::list mercSpellList = GetMercSpellsForSpellEffect(caster, SE_Taunt); - - for(std::list::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); ++mercSpellListItr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if((spells[mercSpellListItr->spellid].targettype == ST_Target) - && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; - - break; - } - } - } - - return result; -} - -MercSpell Merc::GetBestMercSpellForHate(Merc* caster) { - MercSpell result; - - result.spellid = 0; - result.stance = 0; - result.type = 0; - result.slot = 0; - result.proc_chance = 0; - result.time_cancast = 0; - - if(caster) { - std::list mercSpellList = GetMercSpellsForSpellEffect(caster, SE_InstantHate); - - for(std::list::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); ++mercSpellListItr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if(CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; - - break; - } - } - } - - return result; -} - -MercSpell Merc::GetBestMercSpellForCure(Merc* caster, Mob *tar) { - MercSpell result; - bool spellSelected = false; - - result.spellid = 0; - result.stance = 0; - result.type = 0; - result.slot = 0; - result.proc_chance = 0; - result.time_cancast = 0; - - if(!tar) - return result; - - int countNeedsCured = 0; - bool isPoisoned = tar->FindType(SE_PoisonCounter); - bool isDiseased = tar->FindType(SE_DiseaseCounter); - bool isCursed = tar->FindType(SE_CurseCounter); - bool isCorrupted = tar->FindType(SE_CorruptionCounter); - - if(caster && caster->AI_HasSpells()) { - std::list cureList = GetMercSpellsBySpellType(caster, SpellType_Cure); - - if(tar->HasGroup()) { - Group *g = tar->GetGroup(); - - if(g) { - for( int i = 0; imembers[i] && !g->members[i]->qglobal) { - if(caster->GetNeedsCured(g->members[i])) - countNeedsCured++; - } - } - } - } - - //Check for group cure first - if(countNeedsCured > 2) { - for(std::list::iterator itr = cureList.begin(); itr != cureList.end(); ++itr) { - MercSpell selectedMercSpell = *itr; - - if(IsGroupSpell(itr->spellid) && CheckSpellRecastTimers(caster, itr->spellid)) { - if(selectedMercSpell.spellid == 0) - continue; - - if(isPoisoned && IsEffectInSpell(itr->spellid, SE_PoisonCounter)) { - spellSelected = true; - } - else if(isDiseased && IsEffectInSpell(itr->spellid, SE_DiseaseCounter)) { - spellSelected = true; - } - else if(isCursed && IsEffectInSpell(itr->spellid, SE_CurseCounter)) { - spellSelected = true; - } - else if(isCorrupted && IsEffectInSpell(itr->spellid, SE_CorruptionCounter)) { - spellSelected = true; - } - else if(IsEffectInSpell(itr->spellid, SE_DispelDetrimental)) { - spellSelected = true; - } - - if(spellSelected) - { - result.spellid = itr->spellid; - result.stance = itr->stance; - result.type = itr->type; - result.slot = itr->slot; - result.proc_chance = itr->proc_chance; - result.time_cancast = itr->time_cancast; - - break; - } - } - } - } - - //no group cure for target- try to find single target spell - if(!spellSelected) { - for(std::list::iterator itr = cureList.begin(); itr != cureList.end(); ++itr) { - MercSpell selectedMercSpell = *itr; - - if(CheckSpellRecastTimers(caster, itr->spellid)) { - if(selectedMercSpell.spellid == 0) - continue; - - if(isPoisoned && IsEffectInSpell(itr->spellid, SE_PoisonCounter)) { - spellSelected = true; - } - else if(isDiseased && IsEffectInSpell(itr->spellid, SE_DiseaseCounter)) { - spellSelected = true; - } - else if(isCursed && IsEffectInSpell(itr->spellid, SE_CurseCounter)) { - spellSelected = true; - } - else if(isCorrupted && IsEffectInSpell(itr->spellid, SE_CorruptionCounter)) { - spellSelected = true; - } - else if(IsEffectInSpell(itr->spellid, SE_DispelDetrimental)) { - spellSelected = true; - } - - if(spellSelected) - { - result.spellid = itr->spellid; - result.stance = itr->stance; - result.type = itr->type; - result.slot = itr->slot; - result.proc_chance = itr->proc_chance; - result.time_cancast = itr->time_cancast; - - break; - } - } - } - } - } - - return result; -} - -MercSpell Merc::GetBestMercSpellForStun(Merc* caster) { - MercSpell result; - bool spellSelected = false; - - result.spellid = 0; - result.stance = 0; - result.type = 0; - result.slot = 0; - result.proc_chance = 0; - result.time_cancast = 0; - - if(caster) { - std::list mercSpellList = GetMercSpellsForSpellEffect(caster, SE_Stun); - - for(std::list::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); ++mercSpellListItr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if(CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; - - break; - } - } - } - - return result; -} - -MercSpell Merc::GetBestMercSpellForAENuke(Merc* caster, Mob* tar) { - MercSpell result; - bool spellSelected = false; - - result.spellid = 0; - result.stance = 0; - result.type = 0; - result.slot = 0; - result.proc_chance = 0; - result.time_cancast = 0; - - if(caster) { - uint8 initialCastChance = 0; - uint8 castChanceFalloff = 75; - - switch(caster->GetStance()) - { - case MercStanceBurnAE: - initialCastChance = 50; - break; - case MercStanceBalanced: - initialCastChance = 25; - break; - case MercStanceBurn: - initialCastChance = 0; - break; - } - - //check of we even want to cast an AE nuke - if(zone->random.Roll(initialCastChance)) { - - result = GetBestMercSpellForAERainNuke(caster, tar); - - //check if we have a spell & allow for other AE nuke types - if(result.spellid == 0 && zone->random.Roll(castChanceFalloff)) { - - result = GetBestMercSpellForPBAENuke(caster, tar); - - //check if we have a spell & allow for other AE nuke types - if(result.spellid == 0 && zone->random.Roll(castChanceFalloff)) { - - result = GetBestMercSpellForTargetedAENuke(caster, tar); - } - } - } - } - - return result; -} - -MercSpell Merc::GetBestMercSpellForTargetedAENuke(Merc* caster, Mob* tar) { - MercSpell result; - int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.) - int numTargetsCheck = 1; //used to check for min number of targets to use AE - bool spellSelected = false; - - result.spellid = 0; - result.stance = 0; - result.type = 0; - result.slot = 0; - result.proc_chance = 0; - result.time_cancast = 0; - - switch(caster->GetStance()) - { - case MercStanceBurnAE: - numTargetsCheck = 1; - break; - case MercStanceBalanced: - case MercStanceBurn: - numTargetsCheck = 2; - break; - } - - if(caster) { - std::list mercSpellList = GetMercSpellsBySpellType(caster, SpellType_Nuke); - - for(std::list::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); ++mercSpellListItr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if(IsAENukeSpell(mercSpellListItr->spellid) && !IsAERainNukeSpell(mercSpellListItr->spellid) - && !IsPBAENukeSpell(mercSpellListItr->spellid) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - uint8 numTargets = 0; - if(CheckAENuke(caster, tar, mercSpellListItr->spellid, numTargets)) { - if(numTargets >= numTargetsCheck && zone->random.Roll(castChance)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; - } - } - - break; - } - } - } - - return result; -} - -MercSpell Merc::GetBestMercSpellForPBAENuke(Merc* caster, Mob* tar) { - MercSpell result; - int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.) - int numTargetsCheck = 1; //used to check for min number of targets to use AE - bool spellSelected = false; - - result.spellid = 0; - result.stance = 0; - result.type = 0; - result.slot = 0; - result.proc_chance = 0; - result.time_cancast = 0; - - switch(caster->GetStance()) - { - case MercStanceBurnAE: - numTargetsCheck = 2; - break; - case MercStanceBalanced: - case MercStanceBurn: - numTargetsCheck = 3; - break; - } - - if(caster) { - std::list mercSpellList = GetMercSpellsBySpellType(caster, SpellType_Nuke); - - for(std::list::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); ++mercSpellListItr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if(IsPBAENukeSpell(mercSpellListItr->spellid) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - uint8 numTargets = 0; - if(CheckAENuke(caster, caster, mercSpellListItr->spellid, numTargets)) { - if(numTargets >= numTargetsCheck && zone->random.Roll(castChance)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; - } - } - - break; - } - } - } - - return result; -} - -MercSpell Merc::GetBestMercSpellForAERainNuke(Merc* caster, Mob* tar) { - MercSpell result; - int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.) - int numTargetsCheck = 1; //used to check for min number of targets to use AE - bool spellSelected = false; - - result.spellid = 0; - result.stance = 0; - result.type = 0; - result.slot = 0; - result.proc_chance = 0; - result.time_cancast = 0; - - switch(caster->GetStance()) - { - case MercStanceBurnAE: - numTargetsCheck = 1; - break; - case MercStanceBalanced: - case MercStanceBurn: - numTargetsCheck = 2; - break; - } - - if(caster) { - std::list mercSpellList = GetMercSpellsBySpellType(caster, SpellType_Nuke); - - for(std::list::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); ++mercSpellListItr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if(IsAERainNukeSpell(mercSpellListItr->spellid) && zone->random.Roll(castChance) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - uint8 numTargets = 0; - if(CheckAENuke(caster, tar, mercSpellListItr->spellid, numTargets)) { - if(numTargets >= numTargetsCheck) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; - } - } - - break; - } - } - } - - return result; -} - -MercSpell Merc::GetBestMercSpellForNuke(Merc* caster) { - MercSpell result; - int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.) - bool spellSelected = false; - - result.spellid = 0; - result.stance = 0; - result.type = 0; - result.slot = 0; - result.proc_chance = 0; - result.time_cancast = 0; - - if(caster) { - std::list mercSpellList = GetMercSpellsBySpellType(caster, SpellType_Nuke); - - for(std::list::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); ++mercSpellListItr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if(IsPureNukeSpell(mercSpellListItr->spellid) && !IsAENukeSpell(mercSpellListItr->spellid) - && zone->random.Roll(castChance) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; - - break; - } - } - } - - return result; -} - -MercSpell Merc::GetBestMercSpellForNukeByTargetResists(Merc* caster, Mob* target) { - MercSpell result; - bool spellSelected = false; - - result.spellid = 0; - result.stance = 0; - result.type = 0; - result.slot = 0; - result.proc_chance = 0; - result.time_cancast = 0; - - if(!target) - return result; - - if(caster) { - const int lureResisValue = -100; - const int maxTargetResistValue = 300; - bool selectLureNuke = false; - - if((target->GetMR() > maxTargetResistValue) && (target->GetCR() > maxTargetResistValue) && (target->GetFR() > maxTargetResistValue)) - selectLureNuke = true; - - std::list mercSpellList = GetMercSpellsBySpellType(caster, SpellType_Nuke); - - for(std::list::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); ++mercSpellListItr) { - // Assuming all the spells have been loaded into this list by level and in descending order - - if(IsPureNukeSpell(mercSpellListItr->spellid) && !IsAENukeSpell(mercSpellListItr->spellid) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - if(selectLureNuke && (spells[mercSpellListItr->spellid].ResistDiff < lureResisValue)) { - spellSelected = true; - } - else { - if(((target->GetMR() < target->GetCR()) || (target->GetMR() < target->GetFR())) && (GetSpellResistType(mercSpellListItr->spellid) == RESIST_MAGIC) - && (spells[mercSpellListItr->spellid].ResistDiff > lureResisValue)) - { - spellSelected = true; - } - else if(((target->GetCR() < target->GetMR()) || (target->GetCR() < target->GetFR())) && (GetSpellResistType(mercSpellListItr->spellid) == RESIST_COLD) - && (spells[mercSpellListItr->spellid].ResistDiff > lureResisValue)) - { - spellSelected = true; - } - else if(((target->GetFR() < target->GetCR()) || (target->GetFR() < target->GetMR())) && (GetSpellResistType(mercSpellListItr->spellid) == RESIST_FIRE) - && (spells[mercSpellListItr->spellid].ResistDiff > lureResisValue)) - { - spellSelected = true; - } - } - } - - if(spellSelected) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; - - break; - } - } - } - - return result; -} - -bool Merc::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 = tar->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; - } - } - } - } - } - } - - return needCured; -} - -void Merc::MercGroupSay(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); - } -} - -bool Merc::UseDiscipline(int32 spell_id, int32 target) { - // Dont let client waste a reuse timer if they can't use the disc - if (IsStunned() || IsFeared() || IsMezzed() || IsAmnesiad()) - { - return(false); - } - - //make sure we can use it.. - if(!IsValidSpell(spell_id)) { - return(false); - } - - const SPDat_Spell_Struct &spell = spells[spell_id]; - - if(spell.recast_time > 0) - { - if(CheckDisciplineRecastTimers(this, spell_id, spells[spell_id].EndurTimerIndex)) { - if(spells[spell_id].EndurTimerIndex > 0) { - SetDisciplineRecastTimer(spells[spell_id].EndurTimerIndex, spell_id, spell.recast_time); - } - - SetSpellTimeCanCast(spell_id, spells[spell_id].recast_time); - } - else { - return(false); - } - } - - if(GetEndurance() > spell.EndurCost) { - SetEndurance(GetEndurance() - spell.EndurCost); - } else { - //too fatigued to use this skill right now. - return(false); - } - - if(IsCasting()) - InterruptSpell(); - - CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT); - - return(true); -} - -void Merc::SetSpellRecastTimer(uint16 timer_id, uint16 spellid, uint32 recast_delay) { - if(timer_id > 0) { - MercTimer timer; - timer.timerid = timer_id; - timer.timertype = 1; - timer.spellid = spellid; - timer.time_cancast = Timer::GetCurrentTime() + recast_delay; - timers[timer_id] = timer; - } -} - -int32 Merc::GetSpellRecastTimer(Merc *caster, uint16 timer_id) { - int32 result = 0; - if(caster && timer_id > 0) { - if(caster->timers.find(timer_id) != caster->timers.end()) { - result = caster->timers[timer_id].time_cancast; - } - } - return result; -} - -bool Merc::CheckSpellRecastTimers(Merc *caster, uint16 spell_id) { - if(caster) { - MercSpell mercSpell = GetMercSpellBySpellID(caster, spell_id); - if(mercSpell.spellid > 0 && mercSpell.time_cancast < Timer::GetCurrentTime()) { //checks spell recast - if(GetSpellRecastTimer(caster, spells[spell_id].EndurTimerIndex) < Timer::GetCurrentTime()) { //checks for spells on the same timer - return true; //can cast spell - } - } - } - return false; -} - -void Merc::SetDisciplineRecastTimer(uint16 timer_id, uint16 spellid, uint32 recast_delay) { - if(timer_id > 0) { - MercTimer timer; - timer.timerid = timer_id; - timer.timertype = 2; - timer.spellid = spellid; - timer.time_cancast = Timer::GetCurrentTime() + recast_delay; - timers[timer_id] = timer; - } -} - -int32 Merc::GetDisciplineRecastTimer(Merc *caster, uint16 timer_id) { - int32 result = 0; - if(caster && timer_id > 0) { - if(caster->timers.find(timer_id) != caster->timers.end()) { - result = caster->timers[timer_id].time_cancast; - } - } - return result; -} - -int32 Merc::GetDisciplineRemainingTime(Merc *caster, uint16 timer_id) { - int32 result = 0; - if(caster && timer_id > 0) { - int32 time_cancast = GetDisciplineRecastTimer(caster, timer_id); - if(time_cancast > Timer::GetCurrentTime()) - result = time_cancast - Timer::GetCurrentTime(); - } - return result; -} - -bool Merc::CheckDisciplineRecastTimers(Merc *caster, uint16 spell_id, uint16 timer_id) { - if(caster) { - MercSpell mercSpell = GetMercSpellBySpellID(caster, spell_id); - if(mercSpell.spellid > 0 && mercSpell.time_cancast < Timer::GetCurrentTime()) { //checks spell recast - if(timer_id > 0 && !(GetDisciplineRecastTimer(caster, timer_id) < Timer::GetCurrentTime())) { //checks for spells on the same timer - return false; //can't cast spell - } - return true; - } - } - return false; -} - -void Merc::SetSpellTimeCanCast(uint16 spellid, uint32 recast_delay) { - for (int i = 0; i < merc_spells.size(); i++) { - if(merc_spells[i].spellid == spellid) { - merc_spells[i].time_cancast = Timer::GetCurrentTime() + recast_delay; - } - } -} - -bool Merc::CheckTaunt() { - Mob* tar = GetTarget(); - //Only taunt if we are not top on target's hate list - //This ensures we have taunt available to regain aggro if needed - if(tar && tar->GetHateTop() && tar->GetHateTop() != this) { - return true; - } - return false; -} - -bool Merc::CheckAETaunt() { - //need to check area for mobs needing taunted - MercSpell mercSpell = GetBestMercSpellForAETaunt(this); - uint8 result = 0; - - if(mercSpell.spellid != 0) { - - std::list npc_list; - entity_list.GetNPCList(npc_list); - - for(std::list::iterator itr = npc_list.begin(); itr != npc_list.end(); ++itr) { - NPC* npc = *itr; - float dist = npc->DistNoRootNoZ(*this); - int range = GetActSpellRange(mercSpell.spellid, spells[mercSpell.spellid].range); - range *= range; - - if(dist <= range) { - if(!npc->IsMezzed()) { - if(HasGroup()) { - Group* g = GetGroup(); - - if(g) { - for(int i = 0; i < g->GroupCount(); i++) { - //if(npc->IsOnHatelist(g->members[i]) && g->members[i]->GetTarget() != npc && g->members[i]->IsEngaged()) { - if(GetTarget() != npc && g->members[i]->GetTarget() != npc && npc->IsOnHatelist(g->members[i])) { - result++; - } - } - } - } - } - } - } - - if(result >= 1) { - if(MERC_DEBUG > 0) - Message(7, "%s: Attempting AE Taunt", GetCleanName()); - return true; - } - } - return false; -} - -Corpse* Merc::GetGroupMemberCorpse() { - Corpse* corpse = nullptr; - - if(HasGroup()) { - Group* g = GetGroup(); - - if(g) { - for(int i = 0; i < g->GroupCount(); i++) { - if(g->members[i] && g->members[i]->IsClient()) { - corpse = entity_list.GetCorpseByOwnerWithinRange(g->members[i]->CastToClient(), this, RuleI(Mercs, ResurrectRadius)); - - if(corpse && !corpse->IsRezzed()) { - return corpse; - } - } - } - } - } - return 0; -} - -bool Merc::TryHide() { - if(GetClass() != MELEEDPS) { - return false; - } - - //don't hide if already hidden - if(hidden == true) { - return false; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x03; - sa_out->parameter = 1; - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - hidden = true; - - return true; -} - -//Checks if Merc still has confidence. Can be checked to begin fleeing, or to regain confidence after confidence loss - true = confident, false = confidence loss -bool Merc::CheckConfidence() { - bool result = true; - int ConfidenceLossChance = 0; - float ConfidenceCheck = 0; - int ConfidenceRating = 2 * GetProficiencyID(); - - std::list npc_list; - entity_list.GetNPCList(npc_list); - - for(std::list::iterator itr = npc_list.begin(); itr != npc_list.end(); ++itr) { - NPC* mob = *itr; - float ConRating = 1.0; - int CurrentCon = 0; - - if(!mob) continue; - - if(!mob->IsEngaged()) continue; - - if(mob->IsFeared() || mob->IsMezzed() || mob->IsStunned() || mob->IsRooted() || mob->IsCharmed()) continue; - - if(!mob->CheckAggro(this)) continue; - - float AggroRange = mob->GetAggroRange(); - - // Square it because we will be using DistNoRoot - - AggroRange = AggroRange * AggroRange; - - if(mob->DistNoRoot(*this) > AggroRange) continue; - - CurrentCon = this->GetLevelCon(mob->GetLevel()); - switch(CurrentCon) { - - case CON_GREEN: { - ConRating = 0; - break; - } - - case CON_LIGHTBLUE: { - ConRating = 0.2; - break; - } - - case CON_BLUE: { - ConRating = 0.6; - break; - } - - case CON_WHITE: { - ConRating = 1.0; - break; - } - - case CON_YELLOW: { - ConRating = 1.2; - break; - } - - case CON_RED: { - ConRating = 1.5; - break; - } - - default: { - ConRating = 0; - break; - } - } - - ConfidenceCheck += ConRating; - } - - if(ConfidenceRating < ConfidenceCheck) { - ConfidenceLossChance = 25 - ( 5 * (GetTierID() - 1)); - } - - if(zone->random.Roll(ConfidenceLossChance)) { - result = false; - } - - return result; -} - -void Merc::MercMeditate(bool isSitting) { - // Don't try to meditate if engaged or dead - if (IsEngaged() || GetAppearance() == eaDead) - { - return; - } - if(isSitting) { - // If the merc is a caster and has less than 99% mana while its not engaged, he needs to sit to meditate - if(GetManaRatio() < 99.0f) - { - if(!IsSitting()) - Sit(); - } - else - { - if(IsSitting()) - Stand(); - } - } - else - { - if(IsSitting()) - Stand(); - } - - if(IsSitting()) { - if(!rest_timer.Enabled()) { - rest_timer.Start(RuleI(Character, RestRegenTimeToActivate) * 1000); - } - } - else { - rest_timer.Disable(); - } -} - - -void Merc::Sit() { - if(IsMoving()) { - moved = false; - // SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); - SendPosition(); - SetMoving(false); - tar_ndx = 0; - } - - SetAppearance(eaSitting); -} - -void Merc::Stand() { - SetAppearance(eaStanding); -} - -bool Merc::IsSitting() { - bool result = false; - - if(GetAppearance() == eaSitting && !IsMoving()) - result = true; - - return result; -} - -bool Merc::IsStanding() { - bool result = false; - - if(GetAppearance() == eaStanding) - result = true; - - return result; -} - -float Merc::GetMaxMeleeRangeToTarget(Mob* target) { - float result = 0; - - if(target) { - float size_mod = GetSize(); - float other_size_mod = target->GetSize(); - - if(GetRace() == 49 || GetRace() == 158 || GetRace() == 196) //For races with a fixed size - size_mod = 60.0f; - else if (size_mod < 6.0) - size_mod = 8.0f; - - if(target->GetRace() == 49 || target->GetRace() == 158 || target->GetRace() == 196) //For races with a fixed size - other_size_mod = 60.0f; - else if (other_size_mod < 6.0) - other_size_mod = 8.0f; - - 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; - else - size_mod *= size_mod * 4; - - // prevention of ridiculously sized hit boxes - if (size_mod > 10000) - size_mod = size_mod / 7; - - result = size_mod; - } - - return result; -} - -void Merc::DoClassAttacks(Mob *target) { - if(target == nullptr) - return; //gotta have a target for all these - - bool ca_time = classattack_timer.Check(false); - - //only check attack allowed if we are going to do something - if(ca_time && !IsAttackAllowed(target)) - return; - - if(!ca_time) - return; - - float HasteModifier = GetHaste() * 0.01f; - - int level = GetLevel(); - int reuse = TauntReuseTime * 1000; //make this very long since if they dont use it once, they prolly never will - bool did_attack = false; - //class specific stuff... - switch(GetClass()) { - case MELEEDPS: - if(level >= 10) { - reuse = BackstabReuseTime * 1000; - TryBackstab(target, reuse); - did_attack = true; - } - break; - case TANK:{ - if(level >= RuleI(Combat, NPCBashKickLevel)){ - if(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. - { - DoAnim(animKick); - int32 dmg = 0; - - if(GetWeaponDamage(target, (const Item_Struct*)nullptr) <= 0){ - dmg = -5; - } - else{ - if(target->CheckHitChance(this, SkillKick, 0)) { - if(RuleB(Combat, UseIntervalAC)) - dmg = GetKickDamage(); - else - dmg = zone->random.Int(1, GetKickDamage()); - - } - } - - reuse = KickReuseTime * 1000; - DoSpecialAttackDamage(target, SkillKick, dmg, 1, -1, reuse); - did_attack = true; - } - else - { - DoAnim(animTailRake); - int32 dmg = 0; - - if(GetWeaponDamage(target, (const Item_Struct*)nullptr) <= 0){ - dmg = -5; - } - else{ - if(target->CheckHitChance(this, SkillBash, 0)) { - if(RuleB(Combat, UseIntervalAC)) - dmg = GetBashDamage(); - else - dmg = zone->random.Int(1, GetBashDamage()); - } - } - - reuse = BashReuseTime * 1000; - DoSpecialAttackDamage(target, SkillBash, dmg, 1, -1, reuse); - did_attack = true; - } - } - break; - } - } - - classattack_timer.Start(reuse / HasteModifier); -} - -bool Merc::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts) -{ - if (!other) { - SetTarget(nullptr); - LogFile->write(EQEMuLog::Error, "A null Mob object was passed to Merc::Attack() for evaluation!"); - return false; - } - - return NPC::Attack(other, Hand, bRiposte, IsStrikethrough, IsFromSpell, opts); -} - -void Merc::Damage(Mob* other, int32 damage, uint16 spell_id, SkillUseTypes attack_skill, bool avoidable, int8 buffslot, bool iBuffTic) -{ - if(IsDead() || IsCorpse()) - return; - - if(spell_id==0) - spell_id = SPELL_UNKNOWN; - - NPC::Damage(other, damage, spell_id, attack_skill, avoidable, buffslot, iBuffTic); - - //Not needed since we're using NPC damage. - //CommonDamage(other, damage, spell_id, attack_skill, avoidable, buffslot, iBuffTic); -} - -bool Merc::FindTarget() { - bool found = false; - Mob* target = GetHateTop(); - - if(target) { - found = true; - SetTarget(target); - } - - return found; -} - -void Merc::SetTarget(Mob* mob) { - NPC::SetTarget(mob); -} - -Mob* Merc::GetOwnerOrSelf() { - Mob* Result = 0; - - if(this->GetMercOwner()) - Result = GetMercOwner(); - else - Result = this; - - return Result; -} - -bool Merc::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack_skill) -{ - if(!NPC::Death(killerMob, damage, spell, attack_skill)) - { - return false; - } - - Save(); - - //no corpse, no exp if we're a merc. - //We'll suspend instead, since that's what live does. - //Not actually sure live supports 'depopping' merc corpses. - //if(entity_list.GetCorpseByID(GetID())) - // entity_list.GetCorpseByID(GetID())->Depop(); - - // If client is in zone, suspend merc, else depop it. - if (!Suspend()) - { - Depop(); - } - - return true; -} - -Client* Merc::GetMercOwner() { - Client* mercOwner = 0; - - if(GetOwner()) - { - if(GetOwner()->IsClient()) - { - mercOwner = GetOwner()->CastToClient(); - } - } - - return mercOwner; -} - -Mob* Merc::GetOwner() { - Mob* Result = 0; - - Result = entity_list.GetMob(GetOwnerID()); - - if(!Result) { - this->SetOwnerID(0); - } - - return Result->CastToMob(); -} - -const char* Merc::GetRandomName(){ - // creates up to a 10 char name - static char name[17]; - char vowels[18]="aeiouyaeiouaeioe"; - char cons[48]="bcdfghjklmnpqrstvwxzybcdgklmnprstvwbcdgkpstrkd"; - char rndname[17]="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; - char paircons[33]="ngrkndstshthphsktrdrbrgrfrclcr"; - bool valid = false; - - while(!valid) { - int rndnum=zone->random.Int(0, 75),n=1; - bool dlc=false; - bool vwl=false; - bool dbl=false; - if (rndnum>63) - { // rndnum is 0 - 75 where 64-75 is cons pair, 17-63 is cons, 0-16 is vowel - rndnum=(rndnum-61)*2; // name can't start with "ng" "nd" or "rk" - rndname[0]=paircons[rndnum]; - rndname[1]=paircons[rndnum+1]; - n=2; - } - else if (rndnum>16) - { - rndnum-=17; - rndname[0]=cons[rndnum]; - } - else - { - rndname[0]=vowels[rndnum]; - vwl=true; - } - int namlen=zone->random.Int(5, 10); - for (int i=n;irandom.Int(0, 62); - if (rndnum>46) - { // pick a cons pair - if (i>namlen-3) // last 2 chars in name? - { // name can only end in cons pair "rk" "st" "sh" "th" "ph" "sk" "nd" or "ng" - rndnum=zone->random.Int(0, 7)*2; - } - else - { // pick any from the set - rndnum=(rndnum-47)*2; - } - rndname[i]=paircons[rndnum]; - rndname[i+1]=paircons[rndnum+1]; - dlc=true; // flag keeps second letter from being doubled below - i+=1; - } - else - { // select a single cons - rndname[i]=cons[rndnum]; - } - } - else - { // select a vowel - rndname[i]=vowels[zone->random.Int(0, 16)]; - } - vwl=!vwl; - if (!dbl && !dlc) - { // one chance at double letters in name - if (!zone->random.Int(0, i+9)) // chances decrease towards end of name - { - rndname[i+1]=rndname[i]; - dbl=true; - i+=1; - } - } - } - - rndname[0]=toupper(rndname[0]); - - if(!database.CheckNameFilter(rndname)) { - valid = false; - } - else if(rndname[0] < 'A' && rndname[0] > 'Z') { - //name must begin with an upper-case letter. - valid = false; - } - else if (database.CheckUsedName(rndname)) { - valid = true; - } - else { - valid = false; - } - } - - memset(name, 0, 17); - strcpy(name, rndname); - return name; -} - -bool Compare_Merc_Spells(MercSpell i, MercSpell j); - -bool Compare_Merc_Spells(MercSpell i, MercSpell j) -{ - return(i.slot > j.slot); -} - -bool Merc::LoadMercSpells() { - // loads mercs spells into list - merc_spells.clear(); - - std::list spellList = zone->merc_spells_list[GetClass()]; - - if (spellList.size() == 0) { - AIautocastspell_timer->Disable(); - return false; - } - - uint8 proficiency_id = GetProficiencyID(); - int16 attack_proc_spell = -1; - int8 proc_chance = 0; - - for (std::list::iterator mercSpellEntryItr = spellList.begin(); mercSpellEntryItr != spellList.end(); ++mercSpellEntryItr) { - if (proficiency_id == mercSpellEntryItr->proficiencyid && GetLevel() >= mercSpellEntryItr->minlevel && GetLevel() <= mercSpellEntryItr->maxlevel && mercSpellEntryItr->spellid > 0) { - MercSpell mercSpell; - - mercSpell.spellid = mercSpellEntryItr->spellid; - mercSpell.type = mercSpellEntryItr->type; - mercSpell.stance = mercSpellEntryItr->stance; - mercSpell.slot = mercSpellEntryItr->slot; - mercSpell.proc_chance = mercSpellEntryItr->proc_chance; - mercSpell.time_cancast = 0; - - merc_spells.push_back(mercSpell); - - if(mercSpellEntryItr->proc_chance > 0) - AddProcToWeapon(mercSpellEntryItr->spellid, true, mercSpellEntryItr->proc_chance); - } - } - std::sort(merc_spells.begin(), merc_spells.end(), Compare_Merc_Spells); - - if (merc_spells.size() == 0) - AIautocastspell_timer->Disable(); - else { - HasAISpell = true; - AIautocastspell_timer->Trigger(); - } - - if(MERC_DEBUG > 0) { - /*GetMercOwner()->Message(7, "Mercenary Debug: Loaded %i spells for merc.", merc_spells.size()); - - GetMercOwner()->Message(7, "Mercenary Debug: Spell list for merc."); - for (int i = merc_spells.size() - 1; i >= 0; i--) { - GetMercOwner()->Message(7, "%i] Slot: %i, SpellID: %i, Type: %i, Stance: %i, Proc Chance: %i", i, merc_spells[i].slot, merc_spells[i].spellid, merc_spells[i].type, merc_spells[i].stance, merc_spells[i].proc_chance); - }*/ - } - - return true; -} - -bool Merc::Save() { - - if(database.SaveMerc(this)){ - return true; - } - - return false; -} - -Merc* Merc::LoadMerc(Client *c, MercTemplate* merc_template, uint32 merchant_id, bool updateFromDB) { - - if(c) - { - if(c->GetMercID()) - { - merc_template = zone->GetMercTemplate(c->GetMercInfo().MercTemplateID); - } - } - - //get mercenary data - if(merc_template) - { - //TODO: Maybe add a way of updating client merc stats in a seperate function? like, for example, on leveling up. - const NPCType* npc_type_to_copy = database.GetMercType(merc_template->MercNPCID, merc_template->RaceID, c->GetLevel()); - if(npc_type_to_copy != nullptr) - { - //This is actually a very terrible method of assigning stats, and should be changed at some point. See the comment in merc's deconstructor. - NPCType* npc_type = new NPCType; - memset(npc_type, 0, sizeof(NPCType)); - memcpy(npc_type, npc_type_to_copy, sizeof(NPCType)); - if(c && !updateFromDB) - { - if(c->GetMercInfo().merc_name[0] == 0) - { - snprintf(c->GetMercInfo().merc_name, 64, "%s", GetRandomName()); //sanity check. - } - snprintf(npc_type->name, 64, "%s", c->GetMercInfo().merc_name); - } - - npc_type->race = merc_template->RaceID; - - // Use the Gender and Size of the Merchant if possible - uint8 tmpgender = 0; - float tmpsize = 6.0f; - if(merchant_id > 0) - { - NPC* tar = entity_list.GetNPCByID(merchant_id); - if(tar) - { - tmpgender = tar->GetGender(); - tmpsize = tar->GetSize(); - } - else - { - tmpgender = Mob::GetDefaultGender(npc_type->race, c->GetMercInfo().Gender); - } - - } - else - { - tmpgender = c->GetMercInfo().Gender; - tmpsize = c->GetMercInfo().MercSize; - } - - sprintf(npc_type->lastname, "%s's Mercenary", c->GetName()); - npc_type->gender = tmpgender; - npc_type->size = tmpsize; - npc_type->loottable_id = 0; // Loottable has to be 0, otherwise we'll be leavin' some corpses! - npc_type->npc_id = 0; //NPC ID has to be 0, otherwise db gets all confuzzled. - npc_type->class_ = merc_template->ClassID; - npc_type->maxlevel = 0; //We should hard-set this to override scalerate's functionality in the NPC class when it is constructed. - - Merc* merc = new Merc(npc_type, c->GetX(), c->GetY(), c->GetZ(), 0); - - if(merc) - { - merc->SetMercData( merc_template->MercTemplateID ); - database.LoadMercEquipment(merc); - merc->UpdateMercStats(c); - - if(updateFromDB) - { - database.LoadCurrentMerc(c); - - merc->SetMercID(c->GetMercInfo().mercid); - snprintf(merc->name, 64, "%s", c->GetMercInfo().merc_name); - merc->SetSuspended(c->GetMercInfo().IsSuspended); - merc->gender = c->GetMercInfo().Gender; - merc->size = c->GetMercInfo().MercSize; - merc->SetHP(c->GetMercInfo().hp <= 0 ? merc->GetMaxHP() : c->GetMercInfo().hp); - merc->SetMana(c->GetMercInfo().hp <= 0 ? merc->GetMaxMana() : c->GetMercInfo().mana); - merc->SetEndurance(c->GetMercInfo().endurance); - merc->luclinface = c->GetMercInfo().face; - merc->hairstyle = c->GetMercInfo().luclinHairStyle; - merc->haircolor = c->GetMercInfo().luclinHairColor; - merc->eyecolor1 = c->GetMercInfo().luclinEyeColor; - merc->eyecolor2 = c->GetMercInfo().luclinEyeColor2; - merc->beardcolor = c->GetMercInfo().luclinBeardColor; - merc->beard = c->GetMercInfo().luclinBeard; - merc->drakkin_heritage = c->GetMercInfo().drakkinHeritage; - merc->drakkin_tattoo = c->GetMercInfo().drakkinTattoo; - merc->drakkin_details = c->GetMercInfo().drakkinDetails; - } - else - { - // Give Random Features to newly hired Mercs - merc->RandomizeFeatures(false, true); - } - - if(merc->GetMercID()) { - database.LoadMercBuffs(merc); - } - - merc->LoadMercSpells(); - } - - return merc; - } - } - - return 0; -} - -void Merc::UpdateMercInfo(Client *c) { - snprintf(c->GetMercInfo().merc_name, 64, "%s", name); - c->GetMercInfo().mercid = GetMercID(); - c->GetMercInfo().IsSuspended = IsSuspended(); - c->GetMercInfo().Gender = GetGender(); - c->GetMercInfo().MercSize = GetSize(); - c->GetMercInfo().hp = GetHP(); - c->GetMercInfo().mana = GetMana(); - c->GetMercInfo().endurance = GetEndurance(); - c->GetMercInfo().face = luclinface; - c->GetMercInfo().luclinHairStyle = hairstyle; - c->GetMercInfo().luclinHairColor = haircolor; - c->GetMercInfo().luclinEyeColor = eyecolor1; - c->GetMercInfo().luclinEyeColor2 = eyecolor2; - c->GetMercInfo().luclinBeardColor = beardcolor; - c->GetMercInfo().luclinBeard = beard; - c->GetMercInfo().drakkinHeritage = drakkin_heritage; - c->GetMercInfo().drakkinTattoo = drakkin_tattoo; - c->GetMercInfo().drakkinDetails = drakkin_details; -} - -void Merc::UpdateMercStats(Client *c) { - if(c->GetMercInfo().MercTemplateID >0) - { - const NPCType* npc_type = database.GetMercType( zone->GetMercTemplate(c->GetMercInfo().MercTemplateID)->MercNPCID, GetRace(), c->GetLevel()); - if (npc_type) - { - max_hp = (npc_type->max_hp * npc_type->scalerate) / 100; - base_hp = (npc_type->max_hp * npc_type->scalerate) / 100; - max_mana = (npc_type->Mana * npc_type->scalerate) / 100; - base_mana = (npc_type->Mana * npc_type->scalerate) / 100; - hp_regen = (npc_type->hp_regen * npc_type->scalerate) / 100; - mana_regen = (npc_type->mana_regen * npc_type->scalerate) / 100; - level = npc_type->level; - max_dmg = (npc_type->max_dmg * npc_type->scalerate) / 100; - min_dmg = (npc_type->min_dmg * npc_type->scalerate) / 100; - _baseSTR = (npc_type->STR * npc_type->scalerate) / 100; - _baseSTA = (npc_type->STA * npc_type->scalerate) / 100; - _baseDEX = (npc_type->DEX * npc_type->scalerate) / 100; - _baseAGI = (npc_type->AGI * npc_type->scalerate) / 100; - _baseWIS = (npc_type->WIS * npc_type->scalerate) / 100; - _baseINT = (npc_type->INT * npc_type->scalerate) / 100; - _baseCHA = (npc_type->CHA * npc_type->scalerate) / 100; - _baseATK = (npc_type->ATK * npc_type->scalerate) / 100; - _baseMR = (npc_type->MR * npc_type->scalerate) / 100; - _baseFR = (npc_type->FR * npc_type->scalerate) / 100; - _baseDR = (npc_type->DR * npc_type->scalerate) / 100; - _basePR = (npc_type->PR * npc_type->scalerate) / 100; - _baseCR = (npc_type->CR * npc_type->scalerate) / 100; - _baseCorrup = (npc_type->Corrup * npc_type->scalerate) / 100; - _baseAC = (npc_type->AC * npc_type->scalerate) / 100; - attack_speed = npc_type->attack_speed; - attack_count = npc_type->attack_count; - spellscale = npc_type->spellscale; - healscale = npc_type->healscale; - - CalcBonuses(); - - CalcMaxEndurance(); - CalcMaxHP(); - CalcMaxMana(); - } - } -} - -void Merc::UpdateMercAppearance() { - // Copied from Bot Code: - uint32 itemID = NO_ITEM; - uint8 materialFromSlot = _MaterialInvalid; - for(int i = EmuConstants::EQUIPMENT_BEGIN; i <= EmuConstants::EQUIPMENT_END; ++i) { - itemID = equipment[i]; - if(itemID != NO_ITEM) { - materialFromSlot = Inventory::CalcMaterialFromSlot(i); - if(materialFromSlot != _MaterialInvalid) - this->SendWearChange(materialFromSlot); - } - } -} - -void Merc::AddItem(uint8 slot, uint32 item_id) { - equipment[slot] = item_id; -} - -bool Merc::Spawn(Client *owner) { - - if(!owner) - return false; - - MercTemplate* merc_template = zone->GetMercTemplate(GetMercTemplateID()); - - if(!merc_template) - return false; - - entity_list.AddMerc(this, true, true); - - SendPosition(); - - if (MERC_DEBUG > 0) - owner->Message(7, "Mercenary Debug: Spawn."); - - //UpdateMercAppearance(); - - //printf("Spawned Merc with ID %i\n", npc->GetID()); fflush(stdout); - - return true; -} - -void Client::SendMercResponsePackets(uint32 ResponseType) -{ - switch (ResponseType) - { - case 0: // Mercenary Spawned Successfully? - SendMercMerchantResponsePacket(0); - break; - case 1: //You do not have enough funds to make that purchase! - SendMercMerchantResponsePacket(1); - break; - case 2: //Mercenary does not exist! - SendMercMerchantResponsePacket(2); - break; - case 3: //Mercenary failed to spawn! - SendMercMerchantResponsePacket(3); - break; - case 4: //Mercenaries are not allowed in raids! - SendMercMerchantResponsePacket(4); - break; - case 5: //You already have a pending mercenary purchase! - SendMercMerchantResponsePacket(5); - break; - case 6: //You have the maximum number of mercenaries. You must dismiss one before purchasing a new one! - SendMercMerchantResponsePacket(6); - break; - case 7: //You must dismiss your suspended mercenary before purchasing a new one! - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(7); - else - //You have the maximum number of mercenaries. You must dismiss one before purchasing a new one! - SendMercMerchantResponsePacket(6); - break; - case 8: //You can not purchase a mercenary because your group is full! - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(8); - else - SendMercMerchantResponsePacket(7); - break; - case 9: //You can not purchase a mercenary because you are in combat! - if (GetClientVersion() < EQClientRoF) - //Mercenary failed to spawn! - SendMercMerchantResponsePacket(3); - else - SendMercMerchantResponsePacket(8); - break; - case 10: //You have recently dismissed a mercenary and must wait a few more seconds before you can purchase a new one! - if (GetClientVersion() < EQClientRoF) - //Mercenary failed to spawn! - SendMercMerchantResponsePacket(3); - else - SendMercMerchantResponsePacket(9); - break; - case 11: //An error occurred created your mercenary! - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(9); - else - SendMercMerchantResponsePacket(10); - break; - case 12: //Upkeep Charge Message - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(10); - else - SendMercMerchantResponsePacket(11); - break; - case 13: // ??? - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(11); - else - SendMercMerchantResponsePacket(12); - break; - case 14: //You ran out of funds to pay for your mercenary! - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(12); - else - SendMercMerchantResponsePacket(13); - break; - case 15: // ??? - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(13); - else - SendMercMerchantResponsePacket(14); - break; - case 16: //Your mercenary is about to be suspended due to insufficient funds! - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(14); - else - SendMercMerchantResponsePacket(15); - break; - case 17: //There is no mercenary liaison nearby! - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(15); - else - SendMercMerchantResponsePacket(16); - break; - case 18: //You are too far from the liaison! - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(16); - else - SendMercMerchantResponsePacket(17); - break; - case 19: //You do not meet the requirements for that mercenary! - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(17); - else - SendMercMerchantResponsePacket(18); - break; - case 20: //You are unable to interact with the liaison! - if (GetClientVersion() < EQClientRoF) - //You are too far from the liaison! - SendMercMerchantResponsePacket(16); - else - SendMercMerchantResponsePacket(19); - break; - case 21: //You do not have a high enough membership level to purchase this mercenary! - if (GetClientVersion() < EQClientRoF) - //You do not meet the requirements for that mercenary! - SendMercMerchantResponsePacket(17); - else - SendMercMerchantResponsePacket(20); - break; - case 22: //Your purchase has failed because this mercenary requires a Gold membership! - if (GetClientVersion() < EQClientRoF) - //You do not meet the requirements for that mercenary! - SendMercMerchantResponsePacket(17); - else - SendMercMerchantResponsePacket(21); - break; - case 23: //Your purchase has failed because this mercenary requires at least a Silver membership! - if (GetClientVersion() < EQClientRoF) - //You do not meet the requirements for that mercenary! - SendMercMerchantResponsePacket(17); - else - SendMercMerchantResponsePacket(22); - break; - default: //Mercenary failed to spawn! - SendMercMerchantResponsePacket(3); - break; - } - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: SendMercResponsePackets %i.", ResponseType); -} - -void Client::UpdateMercTimer() -{ - Merc *merc = GetMerc(); - - if(merc && !merc->IsSuspended()) - { - if(GetMercTimer()->Check()) - { - uint32 upkeep = merc->CalcUpkeepCost(merc->GetMercTemplateID(), GetLevel()); - - if(CheckCanRetainMerc(upkeep)) - { - if(RuleB(Mercs, ChargeMercUpkeepCost)) - { - TakeMoneyFromPP((upkeep * 100), true); - } - } - else - { - merc->Suspend(); - return; - } - - // Reset the upkeep timer - GetMercInfo().MercTimerRemaining = RuleI(Mercs, UpkeepIntervalMS); - SendMercTimer(merc); - GetMercTimer()->Start(RuleI(Mercs, UpkeepIntervalMS)); - GetMercTimer()->SetTimer(GetMercInfo().MercTimerRemaining); - - // Send upkeep charge message - SendMercResponsePackets(12); - - // Warn that mercenary is about to be suspended due to insufficient funds (on next upkeep) - if (RuleB(Mercs, ChargeMercUpkeepCost) && upkeep > 0 && !HasMoney(upkeep * 100)) - { - SendMercResponsePackets(16); - } - - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: UpdateMercTimer Complete."); - - // Normal upkeep charge message - //Message(7, "You have been charged a mercenary upkeep cost of %i plat, and %i gold and your mercenary upkeep cost timer has been reset to 15 minutes.", upkeep_plat, upkeep_gold, (int)(RuleI(Mercs, UpkeepIntervalMS) / 1000 / 60)); - - // Message below given when too low level to be charged - //Message(7, "Your mercenary waived an upkeep cost of %i plat, and %i gold or %i %s and your mercenary upkeep cost timer has been reset to %i minutes", upkeep_plat, upkeep_gold, 1, "Bayle Marks", (int)(RuleI(Mercs, UpkeepIntervalMS) / 1000 / 60)); - } - } -} - -bool Client::CheckCanHireMerc(Mob* merchant, uint32 template_id) { - - if (!CheckCanSpawnMerc(template_id)) - { - return false; - } - - MercTemplate* mercTemplate = zone->GetMercTemplate(template_id); - - //check for suspended merc - if(GetMercInfo().mercid != 0 && GetMercInfo().IsSuspended) { - SendMercResponsePackets(6); - return false; - } - - // Check if max number of mercs is already reached - if(GetNumMercs() >= MAXMERCS) { - SendMercResponsePackets(6); - return false; - } - - //check for valid merchant - if(!merchant) { - SendMercResponsePackets(17); - return false; - } - - //check for merchant too far away - if(DistNoRoot(*merchant) > USE_NPC_RANGE2) { - SendMercResponsePackets(18); - return false; - } - - //check for sufficient funds and remove them last - if(RuleB(Mercs, ChargeMercPurchaseCost)) { - uint32 cost = Merc::CalcPurchaseCost(template_id, GetLevel()) * 100; // Cost is in gold - if(cost > 0 && !HasMoney(cost)) { - SendMercResponsePackets(1); - return false; - } - } - - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: CheckCanHireMerc True."); - - return true; -} - -bool Client::CheckCanRetainMerc(uint32 upkeep) { - Merc* merc = GetMerc(); - - //check for sufficient funds - if(RuleB(Mercs, ChargeMercPurchaseCost)) { - if(merc) { - if(upkeep > 0 && !HasMoney(upkeep * 100)) { - SendMercResponsePackets(14); - return false; - } - } - } - - return true; -} - -bool Client::CheckCanSpawnMerc(uint32 template_id) { - - // Check if mercs are enabled globally - if(!RuleB(Mercs, AllowMercs)) - { - return false; - } - - // Check if zone allows mercs - if(!zone->AllowMercs()) - { - SendMercResponsePackets(3); - return false; - } - - MercTemplate* mercTemplate = zone->GetMercTemplate(template_id); - - // Invalid merc data - if(!mercTemplate) - { - SendMercResponsePackets(11); - return false; - } - - // Check client version - if(GetClientVersion() < mercTemplate->ClientVersion) - { - SendMercResponsePackets(3); - return false; - } - - // Check for raid - if(HasRaid()) - { - SendMercResponsePackets(4); - return false; - } - - // Check group size - if(GetGroup() && GetGroup()->GroupCount() >= MAX_GROUP_MEMBERS) // database.GroupCount(GetGroup()->GetID()) - { - SendMercResponsePackets(8); - return false; - } - - // Check in combat - if(GetAggroCount() > 0) - { - SendMercResponsePackets(9); - return false; - } - - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: CheckCanSpawnMerc True."); - - return true; -} - -bool Client::CheckCanUnsuspendMerc() { - - if (!CheckCanSpawnMerc(GetMercInfo().MercTemplateID)) - { - return false; - } - - MercTemplate* mercTemplate = zone->GetMercTemplate(GetMercInfo().MercTemplateID); - - if(!GetPTimers().Expired(&database, pTimerMercSuspend, false)) - { - SendMercResponsePackets(10); - //TODO: find this packet response and tell them properly. - Message(0, "You must wait %i seconds before unsuspending your mercenary.", GetPTimers().GetRemainingTime(pTimerMercSuspend)); - return false; - } - - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: CheckCanUnsuspendMerc True."); - - return true; -} - -void Client::CheckMercSuspendTimer() { - - if(GetMercInfo().SuspendedTime != 0) - { - if(time(nullptr) >= GetMercInfo().SuspendedTime) - { - GetMercInfo().SuspendedTime = 0; - SendMercResponsePackets(0); - SendMercSuspendResponsePacket(GetMercInfo().SuspendedTime); - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: CheckMercSuspendTimer Ready."); - } - } -} - -void Client::SuspendMercCommand() { - - if(GetMercInfo().MercTemplateID != 0) - { - if(GetMercInfo().IsSuspended) - { - if(!CheckCanUnsuspendMerc()) - { - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: SuspendMercCommand Unable to Unsuspend."); - - return; - } - - // Get merc, assign it to client & spawn - Merc* merc = Merc::LoadMerc(this, &zone->merc_templates[GetMercInfo().MercTemplateID], 0, true); - if(merc) - { - SpawnMerc(merc, true); - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: SuspendMercCommand Successful Unsuspend."); - } - else - { - //merc failed to spawn - SendMercResponsePackets(3); - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: SuspendMercCommand Failed to Spawn Merc."); - } - } - else - { - Merc* CurrentMerc = GetMerc(); - - if(CurrentMerc && GetMercID()) - { - CurrentMerc->Suspend(); - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: SuspendMercCommand Successful Suspend."); - } - } - } -} - - -// Handles all client zone change event -void Merc::ProcessClientZoneChange(Client* mercOwner) { - - if(mercOwner) - { - Zone(); - } -} - -void Client::SpawnMercOnZone() { - - if(!RuleB(Mercs, AllowMercs)) - return; - - if (GetMerc()) - return; - - if(database.LoadMercInfo(this)) - { - if(!GetMercInfo().IsSuspended) - { - GetMercInfo().SuspendedTime = 0; - // Get merc, assign it to client & spawn - Merc* merc = Merc::LoadMerc(this, &zone->merc_templates[GetMercInfo().MercTemplateID], 0, true); - if(merc) - { - SpawnMerc(merc, false); - } - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: SpawnMercOnZone Normal Merc."); - } - else - { - int32 TimeDiff = GetMercInfo().SuspendedTime - time(nullptr); - if (TimeDiff > 0) - { - if (!GetPTimers().Enabled(pTimerMercSuspend)) - { - // Start the timer to send the packet that refreshes the Unsuspend Button - GetPTimers().Start(pTimerMercSuspend, TimeDiff); - } - } - // Send Mercenary Status/Timer packet - SendMercTimer(GetMerc()); - //SendMercPersonalInfo(); - CheckMercSuspendTimer(); - - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: SpawnMercOnZone Suspended Merc."); - } - } - else - { - // No Merc Hired - SendClearMercInfo(); - } -} - -void Client::SendMercTimer(Merc* merc) { - - if (GetMercInfo().mercid == 0) - { - return; - } - - if (!merc) - { - SendMercTimerPacket(NO_MERC_ID, MERC_STATE_SUSPENDED, GetMercInfo().SuspendedTime, GetMercInfo().MercTimerRemaining, RuleI(Mercs, SuspendIntervalMS)); - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: SendMercTimer No Merc."); - } - else if (merc->IsSuspended()) - { - SendMercTimerPacket(NO_MERC_ID, MERC_STATE_SUSPENDED, GetMercInfo().SuspendedTime, GetMercInfo().MercTimerRemaining, RuleI(Mercs, SuspendIntervalMS)); - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: SendMercTimer Suspended Merc."); - } - else - { - SendMercTimerPacket(merc->GetID(), MERC_STATE_NORMAL, NOT_SUSPENDED_TIME, GetMercInfo().MercTimerRemaining, RuleI(Mercs, SuspendIntervalMS)); - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: SendMercTimer Normal Merc."); - } - -} - -void Client::SpawnMerc(Merc* merc, bool setMaxStats) { - - if (!merc || !CheckCanSpawnMerc(merc->GetMercTemplateID())) - { - if (merc) - { - merc->Suspend(); - } - return; - } - - merc->Spawn(this); - merc->SetSuspended(false); - SetMerc(merc); - merc->Unsuspend(setMaxStats); - merc->SetStance(GetMercInfo().Stance); - -<<<<<<< HEAD - //SendMercTimer(merc); - -======= ->>>>>>> master - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: SpawnMerc Success."); - - return; - -} - -bool Merc::Suspend() { - - Client* mercOwner = GetMercOwner(); - - if(!mercOwner) - return false; - - SetSuspended(true); - - mercOwner->GetMercInfo().IsSuspended = true; - mercOwner->GetMercInfo().SuspendedTime = time(nullptr) + RuleI(Mercs, SuspendIntervalS); - mercOwner->GetMercInfo().MercTimerRemaining = mercOwner->GetMercTimer()->GetRemainingTime(); - mercOwner->GetMercInfo().Stance = GetStance(); - Save(); - mercOwner->GetMercTimer()->Disable(); - mercOwner->SendMercSuspendResponsePacket(mercOwner->GetMercInfo().SuspendedTime); - mercOwner->SendMercTimer(this); - - Depop(); - - // Start the timer to send the packet that refreshes the Unsuspend Button - mercOwner->GetPTimers().Start(pTimerMercSuspend, RuleI(Mercs, SuspendIntervalS)); - - if (MERC_DEBUG > 0) - mercOwner->Message(7, "Mercenary Debug: Suspend Complete."); - - return true; -} - -bool Client::MercOnlyOrNoGroup() { - - if (!GetGroup()) - { - return true; - } - if (GetMerc()) - { - if (GetMerc()->HasGroup() && GetMerc()->GetGroup() == GetGroup()) - { - if (GetGroup()->GroupCount() < 3) - { - return true; - } - } - } - return false; -} - -bool Merc::Unsuspend(bool setMaxStats) { - - Client* mercOwner = nullptr; - - if(GetMercOwner()) { - mercOwner = GetMercOwner(); - } - - if(!mercOwner) - return false; - - if(GetID()) - { - // Set time remaining to max on unsuspend - there is a charge for unsuspending as well - SetSuspended(false); - - mercOwner->GetMercInfo().mercid = GetMercID(); - mercOwner->GetMercInfo().IsSuspended = false; - - mercOwner->SendMercenaryUnsuspendPacket(0); - mercOwner->SendMercenaryUnknownPacket(1); - mercOwner->GetMercInfo().SuspendedTime = 0; - // Reset the upkeep timer - mercOwner->GetMercInfo().MercTimerRemaining = RuleI(Mercs, UpkeepIntervalMS); - mercOwner->GetMercTimer()->Start(RuleI(Mercs, UpkeepIntervalMS)); - //mercOwner->GetMercTimer()->SetTimer(mercOwner->GetMercInfo().MercTimerRemaining); - mercOwner->SendMercTimer(this); - if(!mercOwner->GetPTimers().Expired(&database, pTimerMercSuspend, false)) - mercOwner->GetPTimers().Clear(&database, pTimerMercSuspend); - - if (MercJoinClientGroup()) - { - if(setMaxStats) - { - SetHP(GetMaxHP()); - SetMana(GetMaxMana()); - SetEndurance(GetMaxEndurance()); - } - - //check for sufficient funds and remove them last - if(RuleB(Mercs, ChargeMercUpkeepCost)) - { - uint32 cost = CalcUpkeepCost(GetMercTemplateID(), GetLevel()) * 100; // Cost is in gold - if(cost > 0 && !mercOwner->HasMoney(cost)) - { - mercOwner->SendMercResponsePackets(1); - Suspend(); - return false; - } - } - Save(); - } - } - - return true; -} - -bool Client::DismissMerc(uint32 MercID) { - - bool Dismissed = true; - if (!database.DeleteMerc(MercID)) - { - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Dismiss Failed for MercID %i", MercID); - Dismissed = false; - } - else - { - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Dismiss Successful."); - } - - if (GetMerc()) - { - GetMerc()->Depop(); - } - - SendClearMercInfo(); - SetMerc(nullptr); - - return Dismissed; -} - -void Merc::Zone() { - Save(); - Depop(); -} - -void Merc::Depop() { - - WipeHateList(); - - if(IsCasting()) - { - InterruptSpell(); - } - - entity_list.RemoveFromHateLists(this); - - if(GetGroup()) - { - RemoveMercFromGroup(this, GetGroup()); - } - - entity_list.RemoveMerc(this->GetID()); - - if(HasPet()) - { - GetPet()->Depop(); - } - - p_depop = true; - - NPC::Depop(false); -} - -bool Merc::RemoveMercFromGroup(Merc* merc, Group* group) { - - bool Result = false; - - if(merc && group) - { - if(merc->HasGroup()) - { - if(!group->IsLeader(merc)) - { - merc->SetFollowID(0); - - if(group->DelMember(merc, true)) - { - if(merc->GetMercCharacterID() != 0) - { - database.SetGroupID(merc->GetName(), 0, merc->GetMercCharacterID(), true); - } - } - - if(group->GroupCount() <= 1 && ZoneLoaded) - { - group->DisbandGroup(); - } - } - else - { - // A merc is group leader - Disband and re-group each member with their mercs - for(int i = 0; i < MAX_GROUP_MEMBERS; i++) - { - if(!group->members[i]) - continue; - - if(!group->members[i]->IsClient()) - continue; - - group->members[i]->CastToClient()->LeaveGroup(); - } - for(int i = 0; i < MAX_GROUP_MEMBERS; i++) - { - if(!group->members[i]) - continue; - - if(!group->members[i]->IsMerc()) - continue; - - group->members[i]->CastToMerc()->MercJoinClientGroup(); - } - // Group should be removed by now, but just in case: - group->DisbandGroup(); - } - - Result = true; - } - } - - return Result; -} - -bool Merc::MercJoinClientGroup() { - - Client* mercOwner = nullptr; - - if(GetMercOwner()) - { - mercOwner = GetMercOwner(); - } - - if(!mercOwner) - { - Suspend(); - return false; - } - - if(GetID()) - { - if (HasGroup()) - { - RemoveMercFromGroup(this, GetGroup()); - } - - Group* g = entity_list.GetGroupByClient(mercOwner); - - //nobody from our group is here... start a new group - if(!g) - { - g = new Group(mercOwner); - - if(!g) - { - delete g; - g = nullptr; - return false; - } - - entity_list.AddGroup(g); - - if(g->GetID() == 0) - { - delete g; - g = nullptr; - return false; - } - - if(AddMercToGroup(this, g)) - { - entity_list.AddGroup(g, g->GetID()); - database.SetGroupLeaderName(g->GetID(), mercOwner->GetName()); - database.SetGroupID(mercOwner->GetName(), g->GetID(), mercOwner->CharacterID(), false); - database.SetGroupID(this->GetName(), g->GetID(), mercOwner->CharacterID(), true); - database.RefreshGroupFromDB(mercOwner); - g->SaveGroupLeaderAA(); - if(MERC_DEBUG > 0) - mercOwner->Message(7, "Mercenary Debug: Mercenary joined new group."); - } - else - { - g->DisbandGroup(); - Suspend(); - - if(MERC_DEBUG > 0) - mercOwner->Message(7, "Mercenary Debug: Mercenary disbanded new group."); - } - - } - else if (AddMercToGroup(this, mercOwner->GetGroup())) - { - // Group already exists - database.SetGroupID(GetName(), mercOwner->GetGroup()->GetID(), mercOwner->CharacterID(), true); - database.RefreshGroupFromDB(mercOwner); - // Update members that are out of zone - GetGroup()->SendGroupJoinOOZ(this); - - if(MERC_DEBUG > 0) - mercOwner->Message(7, "Mercenary Debug: Mercenary joined existing group."); - } - else - { - Suspend(); - - if(MERC_DEBUG > 0) - mercOwner->Message(7, "Mercenary Debug: Mercenary failed to join the group - Suspending"); - } - } - - return true; -} - -bool Merc::AddMercToGroup(Merc* merc, Group* group) { - bool Result = false; - - if(merc && group) { - // Remove merc from current group if it's not the destination group - if(merc->HasGroup()) - { - if(merc->GetGroup() == group && merc->GetMercOwner()) - { - // Merc is already in the destination group - merc->SetFollowID(merc->GetMercOwner()->GetID()); - return true; - } - merc->RemoveMercFromGroup(merc, merc->GetGroup()); - } - //Try and add the member, followed by checking if the merc owner exists. - if(group->AddMember(merc) && merc->GetMercOwner()) - { - merc->SetFollowID(merc->GetMercOwner()->GetID()); - Result = true; - } - else - { - //Suspend it if the member is not added or the merc's owner is not valid. - merc->Suspend(); - } - } - - return Result; -} - -void Client::InitializeMercInfo() { - - for(int i=0; i 0) - Message(7, "Mercenary Debug: GetMerc 0."); - return (nullptr); - } - - Merc* tmp = entity_list.GetMercByID(GetMercID()); - if(tmp == nullptr) - { - SetMercID(0); - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: GetMerc No Merc."); - return (nullptr); - } - - if(tmp->GetOwnerID() != GetID()) - { - SetMercID(0); - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: GetMerc Owner Mismatch."); - return (nullptr); - } -<<<<<<< HEAD - - if (MERC_DEBUG > 0) - //Message(7, "Mercenary Debug: GetMerc Success."); -======= ->>>>>>> master - - return (tmp); -} - -uint8 Client::GetNumMercs() { - - uint8 numMercs = 0; - - for(int i=0; i 0) - Message(7, "Mercenary Debug: GetNumMercs %i.", numMercs); - - return numMercs; -} - -void Merc::SetMercData( uint32 template_id ) { - - MercTemplate* merc_template = zone->GetMercTemplate(template_id); - SetMercTemplateID( merc_template->MercTemplateID ); - SetMercType( merc_template->MercType ); - SetMercSubType( merc_template->MercSubType ); - SetProficiencyID( merc_template->ProficiencyID ); - SetTierID( merc_template->TierID ); - SetCostFormula( merc_template->CostFormula ); - SetMercNameType( merc_template->MercNameType ); - -} - -MercTemplate* Zone::GetMercTemplate( uint32 template_id ) { - return &merc_templates[template_id]; -} - -void Client::SetMerc(Merc* newmerc) { - - Merc* oldmerc = GetMerc(); - if (oldmerc) - { - oldmerc->SetOwnerID(0); - } - - if (!newmerc) - { - SetMercID(0); - GetMercInfo().mercid = 0; - GetMercInfo().MercTemplateID = 0; - GetMercInfo().myTemplate = nullptr; - GetMercInfo().IsSuspended = false; - GetMercInfo().SuspendedTime = 0; - GetMercInfo().Gender = 0; - GetMercInfo().State = 0; - memset(GetMercInfo().merc_name, 0, 64); - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: SetMerc No Merc."); - } - else - { - SetMercID(newmerc->GetID()); - //Client* oldowner = entity_list.GetClientByID(newmerc->GetOwnerID()); - newmerc->SetOwnerID(this->GetID()); - newmerc->SetMercCharacterID(this->CharacterID()); - newmerc->SetClientVersion((uint8)this->GetClientVersion()); - GetMercInfo().mercid = newmerc->GetMercID(); - GetMercInfo().MercTemplateID = newmerc->GetMercTemplateID(); - GetMercInfo().myTemplate = zone->GetMercTemplate(GetMercInfo().MercTemplateID); - GetMercInfo().IsSuspended = newmerc->IsSuspended(); - GetMercInfo().SuspendedTime = 0; - GetMercInfo().Gender = newmerc->GetGender(); - GetMercInfo().State = newmerc->IsSuspended() ? MERC_STATE_SUSPENDED : MERC_STATE_NORMAL; - snprintf(GetMercInfo().merc_name, 64, "%s", newmerc->GetName()); - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: SetMerc New Merc."); - } -} - -void Client::UpdateMercLevel() { - Merc* merc = GetMerc(); - if (merc) - { - merc->UpdateMercStats(this); - } -} - -void Client::SendMercMerchantResponsePacket(int32 response_type) { - // This response packet brings up the Mercenary Manager window - if(GetClientVersion() >= EQClientSoD) - { - EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryHire, sizeof(MercenaryMerchantResponse_Struct)); - MercenaryMerchantResponse_Struct* mmr = (MercenaryMerchantResponse_Struct*)outapp->pBuffer; - mmr->ResponseType = response_type; // send specified response type - FastQueuePacket(&outapp); - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Sent SendMercMerchantResponsePacket %i.", response_type); - } -} - -void Client::SendMercenaryUnknownPacket(uint8 type) { - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryUnknown1, 1); - outapp->WriteUInt8(type); - FastQueuePacket(&outapp); - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Sent SendMercenaryUnknownPacket %i.", type); - -} - -void Client::SendMercenaryUnsuspendPacket(uint8 type) { - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryUnsuspendResponse, 1); - outapp->WriteUInt8(type); - FastQueuePacket(&outapp); - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Sent SendMercenaryUnsuspendPacket %i.", type); - -} - -void Client::SendMercSuspendResponsePacket(uint32 suspended_time) { - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenarySuspendResponse, sizeof(SuspendMercenaryResponse_Struct)); - SuspendMercenaryResponse_Struct* smr = (SuspendMercenaryResponse_Struct*)outapp->pBuffer; - smr->SuspendTime = suspended_time; // Seen 0 (not suspended) or c9 c2 64 4f (suspended on Sat Mar 17 11:58:49 2012) - Unix Timestamp - FastQueuePacket(&outapp); - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Sent SendMercSuspendResponsePacket %i.", suspended_time); - -} - -void Client::SendMercTimerPacket(int32 entity_id, int32 merc_state, int32 suspended_time, int32 update_interval, int32 unk01) { - - // Send Mercenary Status/Timer packet - EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryTimer, sizeof(MercenaryStatus_Struct)); - MercenaryStatus_Struct* mss = (MercenaryStatus_Struct*)outapp->pBuffer; - mss->MercEntityID = entity_id; // Seen 0 (no merc spawned) or unknown value when merc is spawned - mss->MercState = merc_state; // Seen 5 (normal) or 1 (suspended) - mss->SuspendedTime = suspended_time; // Seen 0 for not suspended or Unix Timestamp for suspended merc - mss->UpdateInterval = update_interval; // Seen 900000 - 15 minutes in ms - mss->MercUnk01 = unk01; // Seen 180000 - 3 minutes in ms - Used for the unsuspend button refresh timer - FastQueuePacket(&outapp); - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Sent SendMercTimerPacket %i, %i, %i, %i, %i.", entity_id, merc_state, suspended_time, update_interval, unk01); - -} - -void Client::SendMercAssignPacket(uint32 entityID, uint32 unk01, uint32 unk02) { - EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryAssign, sizeof(MercenaryAssign_Struct)); - MercenaryAssign_Struct* mas = (MercenaryAssign_Struct*)outapp->pBuffer; - mas->MercEntityID = entityID; - mas->MercUnk01 = unk01; - mas->MercUnk02 = unk02; - FastQueuePacket(&outapp); - if (MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Sent SendMercAssignPacket %i, %i, %i.", entityID, unk01, unk02); -} - -void NPC::LoadMercTypes() { - - std::string query = StringFormat("SELECT DISTINCT MTyp.dbstring, MTyp.clientversion " - "FROM merc_merchant_entries MME, merc_merchant_template_entries MMTE, " - "merc_types MTyp, merc_templates MTem " - "WHERE MME.merchant_id = %i " - "AND MME.merc_merchant_template_id = MMTE.merc_merchant_template_id " - "AND MMTE.merc_template_id = MTem.merc_template_id " - "AND MTem.merc_type_id = MTyp.merc_type_id;", GetNPCTypeID()); - auto results = database.QueryDatabase(query); - if (!results.Success()) - { - LogFile->write(EQEMuLog::Error, "Error in NPC::LoadMercTypes()"); - return; - } - - for (auto row = results.begin(); row != results.end(); ++row) - { - MercType tempMercType; - - tempMercType.Type = atoi(row[0]); - tempMercType.ClientVersion = atoi(row[1]); - - mercTypeList.push_back(tempMercType); - } - -} - -void NPC::LoadMercs() { - - std::string query = StringFormat("SELECT DISTINCT MTem.merc_template_id, MTyp.dbstring AS merc_type_id, " - "MTem.dbstring AS merc_subtype_id, 0 AS CostFormula, " - "CASE WHEN MTem.clientversion > MTyp.clientversion " - "THEN MTem.clientversion " - "ELSE MTyp.clientversion END AS clientversion, MTem.merc_npc_type_id " - "FROM merc_merchant_entries MME, merc_merchant_template_entries MMTE, " - "merc_types MTyp, merc_templates MTem " - "WHERE MME.merchant_id = %i AND " - "MME.merc_merchant_template_id = MMTE.merc_merchant_template_id " - "AND MMTE.merc_template_id = MTem.merc_template_id " - "AND MTem.merc_type_id = MTyp.merc_type_id;", GetNPCTypeID()); - auto results = database.QueryDatabase(query); - - if (!results.Success()) - { - LogFile->write(EQEMuLog::Error, "Error in NPC::LoadMercTypes()"); - return; - } - - for (auto row = results.begin(); row != results.end(); ++row) - { - MercData tempMerc; - - tempMerc.MercTemplateID = atoi(row[0]); - tempMerc.MercType = atoi(row[1]); - tempMerc.MercSubType = atoi(row[2]); - tempMerc.CostFormula = atoi(row[3]); - tempMerc.ClientVersion = atoi(row[4]); - tempMerc.NPCID = atoi(row[5]); - - mercDataList.push_back(tempMerc); - } - -} - -int NPC::GetNumMercTypes(uint32 clientVersion) { - - int count = 0; - std::list mercTypeList = GetMercTypesList(); - - for(std::list::iterator mercTypeListItr = mercTypeList.begin(); mercTypeListItr != mercTypeList.end(); ++mercTypeListItr) { - if(mercTypeListItr->ClientVersion <= clientVersion) - count++; - } - - return count; -} - -int NPC::GetNumMercs(uint32 clientVersion) { - - int count = 0; - std::list mercDataList = GetMercsList(); - - for(std::list::iterator mercListItr = mercDataList.begin(); mercListItr != mercDataList.end(); ++mercListItr) { - if(mercListItr->ClientVersion <= clientVersion) - count++; - } - - return count; -} - -std::list NPC::GetMercTypesList(uint32 clientVersion) { - - std::list result; - - if(GetNumMercTypes() > 0) - { - for(std::list::iterator mercTypeListItr = mercTypeList.begin(); mercTypeListItr != mercTypeList.end(); ++mercTypeListItr) - { - if(mercTypeListItr->ClientVersion <= clientVersion) - { - MercType mercType; - mercType.Type = mercTypeListItr->Type; - mercType.ClientVersion = mercTypeListItr->ClientVersion; - result.push_back(mercType); - } - } - } - - return result; -} - -std::list NPC::GetMercsList(uint32 clientVersion) { - - std::list result; - - if(GetNumMercs() > 0) - { - for(std::list::iterator mercListItr = mercDataList.begin(); mercListItr != mercDataList.end(); ++mercListItr) - { - if(mercListItr->ClientVersion <= clientVersion) - { - MercTemplate *merc_template = zone->GetMercTemplate(mercListItr->MercTemplateID); - - if(merc_template) - { - MercData mercData; - mercData.MercTemplateID = mercListItr->MercTemplateID; - mercData.MercType = merc_template->MercType; - mercData.MercSubType = merc_template->MercSubType; - mercData.CostFormula = merc_template->CostFormula; - mercData.ClientVersion = merc_template->ClientVersion; - mercData.NPCID = merc_template->MercNPCID; - result.push_back(mercData); - } - } - } - } - - return result; -} - -uint32 Merc::CalcPurchaseCost(uint32 templateID , uint8 level, uint8 currency_type) { - - uint32 cost = 0; - - MercTemplate *mercData = zone->GetMercTemplate(templateID); - - if(mercData) - { - //calculate cost in coin - cost in gold - if(currency_type == 0) - { - int levels_above_cutoff; - switch (mercData->CostFormula) - { - case 0: - levels_above_cutoff = level > 10 ? (level - 10) : 0; - cost = levels_above_cutoff * 300; - cost += level >= 10 ? 100 : 0; - cost /= 100; - break; - default: - break; - } - } - else if(currency_type == 19) - { - // cost in Bayle Marks - cost = 1; - } - } - - return cost; -} - -uint32 Merc::CalcUpkeepCost(uint32 templateID , uint8 level, uint8 currency_type) { - - uint32 cost = 0; - - MercTemplate *mercData = zone->GetMercTemplate(templateID); - - if(mercData) - { - //calculate cost in coin - cost in gold - if(currency_type == 0) - { - int levels_above_cutoff; - switch (mercData->CostFormula) - { - case 0: - levels_above_cutoff = level > 10 ? (level - 10) : 0; - cost = levels_above_cutoff * 300; - cost += level >= 10 ? 100 : 0; - cost /= 100; - break; - default: - break; - } - } - else if(currency_type == 19) - { - // cost in Bayle Marks - cost = 1; - } - } - - return cost; -} diff --git a/zone/npc.cpp.orig b/zone/npc.cpp.orig deleted file mode 100644 index d519810f6..000000000 --- a/zone/npc.cpp.orig +++ /dev/null @@ -1,2446 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) - - 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 -*/ - -#include "../common/bodytypes.h" -#include "../common/classes.h" -#include "../common/debug.h" -#include "../common/misc_functions.h" -#include "../common/rulesys.h" -#include "../common/seperator.h" -#include "../common/spdat.h" -#include "../common/string_util.h" -#include "../common/clientversions.h" -#include "../common/features.h" -#include "../common/item.h" -#include "../common/item_struct.h" -#include "../common/linked_list.h" -#include "../common/servertalk.h" - -#include "aa.h" -#include "client.h" -#include "entity.h" -#include "npc.h" -#include "string_ids.h" -#include "spawn2.h" -#include "zone.h" - -#include -#include -#include - -#ifdef _WINDOWS -#define snprintf _snprintf -#define strncasecmp _strnicmp -#define strcasecmp _stricmp -#else -#include -#include -#endif - -extern Zone* zone; -extern volatile bool ZoneLoaded; -extern EntityList entity_list; - -<<<<<<< HEAD -#include "quest_parser_collection.h" - -NPC::NPC(const NPCType* d, Spawn2* in_respawn, const xyz_heading& position, int iflymode, bool IsCorpse) -======= -NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float heading, int iflymode, bool IsCorpse) ->>>>>>> master -: Mob(d->name, - d->lastname, - d->max_hp, - d->max_hp, - d->gender, - d->race, - d->class_, - (bodyType)d->bodytype, - d->deity, - d->level, - d->npc_id, - d->size, - d->runspeed, - position, - d->light, - d->texture, - d->helmtexture, - d->AC, - d->ATK, - d->STR, - d->STA, - d->DEX, - d->AGI, - d->INT, - d->WIS, - d->CHA, - d->haircolor, - d->beardcolor, - d->eyecolor1, - d->eyecolor2, - d->hairstyle, - d->luclinface, - d->beard, - d->drakkin_heritage, - d->drakkin_tattoo, - d->drakkin_details, - (uint32*)d->armor_tint, - 0, - d->see_invis, // pass see_invis/see_ivu flags to mob constructor - d->see_invis_undead, - d->see_hide, - d->see_improved_hide, - d->hp_regen, - d->mana_regen, - d->qglobal, - d->maxlevel, - d->scalerate ), - attacked_timer(CombatEventTimer_expire), - swarm_timer(100), - classattack_timer(1000), - knightattack_timer(1000), - assist_timer(AIassistcheck_delay), - qglobal_purge_timer(30000), - sendhpupdate_timer(1000), - enraged_timer(1000), - taunt_timer(TauntReuseTime * 1000), - m_SpawnPoint(position), - m_GuardPoint(-1,-1,-1,0), - m_GuardPointSaved(0,0,0,0) -{ - //What is the point of this, since the names get mangled.. - Mob* mob = entity_list.GetMob(name); - if(mob != 0) - entity_list.RemoveEntity(mob->GetID()); - - int moblevel=GetLevel(); - - NPCTypedata = d; - NPCTypedata_ours = nullptr; - respawn2 = in_respawn; - swarm_timer.Disable(); - - taunting = false; - proximity = nullptr; - copper = 0; - silver = 0; - gold = 0; - platinum = 0; - max_dmg = d->max_dmg; - min_dmg = d->min_dmg; - attack_count = d->attack_count; - grid = 0; - wp_m = 0; - max_wp=0; - save_wp = 0; - spawn_group = 0; - swarmInfoPtr = nullptr; - spellscale = d->spellscale; - healscale = d->healscale; - - logging_enabled = NPC_DEFAULT_LOGGING_ENABLED; - - pAggroRange = d->aggroradius; - pAssistRange = d->assistradius; - findable = d->findable; - trackable = d->trackable; - - MR = d->MR; - CR = d->CR; - DR = d->DR; - FR = d->FR; - PR = d->PR; - Corrup = d->Corrup; - PhR = d->PhR; - - STR = d->STR; - STA = d->STA; - AGI = d->AGI; - DEX = d->DEX; - INT = d->INT; - WIS = d->WIS; - CHA = d->CHA; - npc_mana = d->Mana; - - //quick fix of ordering if they screwed it up in the DB - if(max_dmg < min_dmg) { - int tmp = min_dmg; - min_dmg = max_dmg; - max_dmg = tmp; - } - - // Max Level and Stat Scaling if maxlevel is set - if(maxlevel > level) - { - LevelScale(); - } - - // Set Resists if they are 0 in the DB - CalcNPCResists(); - - // Set Mana and HP Regen Rates if they are 0 in the DB - CalcNPCRegen(); - - // Set Min and Max Damage if they are 0 in the DB - if(max_dmg == 0){ - CalcNPCDamage(); - } - - accuracy_rating = d->accuracy_rating; - avoidance_rating = d->avoidance_rating; - ATK = d->ATK; - - CalcMaxMana(); - SetMana(GetMaxMana()); - - MerchantType = d->merchanttype; - merchant_open = GetClass() == MERCHANT; - adventure_template_id = d->adventure_template; - flymode = iflymode; - guard_anim = eaStanding; - roambox_distance = 0; - roambox_max_x = -2; - roambox_max_y = -2; - roambox_min_x = -2; - roambox_min_y = -2; - roambox_movingto_x = -2; - roambox_movingto_y = -2; - roambox_min_delay = 1000; - roambox_delay = 1000; - p_depop = false; - loottable_id = d->loottable_id; - - no_target_hotkey = d->no_target_hotkey; - - primary_faction = 0; - SetNPCFactionID(d->npc_faction_id); - - npc_spells_id = 0; - HasAISpell = false; - HasAISpellEffects = false; - - if(GetClass() == MERCERNARY_MASTER && RuleB(Mercs, AllowMercs)) - { - LoadMercTypes(); - LoadMercs(); - } - - SpellFocusDMG = 0; - SpellFocusHeal = 0; - - pet_spell_id = 0; - - delaytimer = false; - combat_event = false; - attack_speed = d->attack_speed; - attack_delay = d->attack_delay; - slow_mitigation = d->slow_mitigation; - - EntityList::RemoveNumbers(name); - entity_list.MakeNameUnique(name); - - npc_aggro = d->npc_aggro; - - if(!IsMerc()) - AI_Start(); - - d_meele_texture1 = d->d_meele_texture1; - d_meele_texture2 = d->d_meele_texture2; - ammo_idfile = d->ammo_idfile; - memset(equipment, 0, sizeof(equipment)); - prim_melee_type = d->prim_melee_type; - sec_melee_type = d->sec_melee_type; - ranged_type = d->ranged_type; - - // If Melee Textures are not set, set attack type to Hand to Hand as default - if(!d_meele_texture1) - prim_melee_type = 28; - if(!d_meele_texture2) - sec_melee_type = 28; - - //give NPCs skill values... - int r; - for(r = 0; r <= HIGHEST_SKILL; r++) { - skills[r] = database.GetSkillCap(GetClass(),(SkillUseTypes)r,moblevel); - } - - if(d->trap_template > 0) - { - std::map >::iterator trap_ent_iter; - std::list trap_list; - - trap_ent_iter = zone->ldon_trap_entry_list.find(d->trap_template); - if(trap_ent_iter != zone->ldon_trap_entry_list.end()) - { - trap_list = trap_ent_iter->second; - if(trap_list.size() > 0) - { - std::list::iterator trap_list_iter = trap_list.begin(); - std::advance(trap_list_iter, zone->random.Int(0, trap_list.size() - 1)); - LDoNTrapTemplate* tt = (*trap_list_iter); - if(tt) - { - if((uint8)tt->spell_id > 0) - { - ldon_trapped = true; - ldon_spell_id = tt->spell_id; - } - else - { - ldon_trapped = false; - ldon_spell_id = 0; - } - - ldon_trap_type = (uint8)tt->type; - if(tt->locked > 0) - { - ldon_locked = true; - ldon_locked_skill = tt->skill; - } - else - { - ldon_locked = false; - ldon_locked_skill = 0; - } - ldon_trap_detected = 0; - } - } - else - { - ldon_trapped = false; - ldon_trap_type = 0; - ldon_spell_id = 0; - ldon_locked = false; - ldon_locked_skill = 0; - ldon_trap_detected = 0; - } - } - else - { - ldon_trapped = false; - ldon_trap_type = 0; - ldon_spell_id = 0; - ldon_locked = false; - ldon_locked_skill = 0; - ldon_trap_detected = 0; - } - } - else - { - ldon_trapped = false; - ldon_trap_type = 0; - ldon_spell_id = 0; - ldon_locked = false; - ldon_locked_skill = 0; - ldon_trap_detected = 0; - } - reface_timer = new Timer(15000); - reface_timer->Disable(); - qGlobals = nullptr; - SetEmoteID(d->emoteid); - InitializeBuffSlots(); - CalcBonuses(); - raid_target = d->raid_target; -} - -NPC::~NPC() -{ - AI_Stop(); - - if(proximity != nullptr) { - entity_list.RemoveProximity(GetID()); - safe_delete(proximity); - } - - safe_delete(NPCTypedata_ours); - - { - ItemList::iterator cur,end; - cur = itemlist.begin(); - end = itemlist.end(); - for(; cur != end; ++cur) { - ServerLootItem_Struct* item = *cur; - safe_delete(item); - } - itemlist.clear(); - } - - { - std::list::iterator cur,end; - cur = faction_list.begin(); - end = faction_list.end(); - for(; cur != end; ++cur) { - struct NPCFaction* fac = *cur; - safe_delete(fac); - } - faction_list.clear(); - } - - safe_delete(reface_timer); - safe_delete(swarmInfoPtr); - safe_delete(qGlobals); - UninitializeBuffSlots(); -} - -void NPC::SetTarget(Mob* mob) { - if(mob == GetTarget()) //dont bother if they are allready our target - return; - - //This is not the default behavior for swarm pets, must be specified from quest functions or rules value. - if(GetSwarmInfo() && GetSwarmInfo()->target && GetTarget() && (GetTarget()->GetHP() > 0)) { - Mob *targ = entity_list.GetMob(GetSwarmInfo()->target); - if(targ != mob){ - return; - } - } - - if (mob) { - SetAttackTimer(); - } else { - ranged_timer.Disable(); - //attack_timer.Disable(); - attack_dw_timer.Disable(); - } - Mob::SetTarget(mob); -} - -ServerLootItem_Struct* NPC::GetItem(int slot_id) { - ItemList::iterator cur,end; - cur = itemlist.begin(); - end = itemlist.end(); - for(; cur != end; ++cur) { - ServerLootItem_Struct* item = *cur; - if (item->equip_slot == slot_id) { - return item; - } - } - return(nullptr); -} - -void NPC::RemoveItem(uint32 item_id, uint16 quantity, uint16 slot) { - ItemList::iterator cur,end; - cur = itemlist.begin(); - end = itemlist.end(); - for(; cur != end; ++cur) { - ServerLootItem_Struct* item = *cur; - if (item->item_id == item_id && slot <= 0 && quantity <= 0) { - itemlist.erase(cur); - return; - } - else if (item->item_id == item_id && item->equip_slot == slot && quantity >= 1) { - //std::cout<<"NPC::RemoveItem"<<" equipSlot:"<equipSlot<<" quantity:"<< quantity<charges <= quantity) - itemlist.erase(cur); - else - item->charges -= quantity; - return; - } - } -} - -void NPC::CheckMinMaxLevel(Mob *them) -{ - if(them == nullptr || !them->IsClient()) - return; - - uint16 themlevel = them->GetLevel(); - uint8 material; - - std::list::iterator cur = itemlist.begin(); - while(cur != itemlist.end()) - { - if(!(*cur)) - return; - - if(themlevel < (*cur)->min_level || themlevel > (*cur)->max_level) - { - material = Inventory::CalcMaterialFromSlot((*cur)->equip_slot); - if(material != 0xFF) - SendWearChange(material); - - cur = itemlist.erase(cur); - continue; - } - ++cur; - } - -} - -void NPC::ClearItemList() { - ItemList::iterator cur,end; - cur = itemlist.begin(); - end = itemlist.end(); - for(; cur != end; ++cur) { - ServerLootItem_Struct* item = *cur; - safe_delete(item); - } - itemlist.clear(); -} - -void NPC::QueryLoot(Client* to) { - int x = 0; - to->Message(0, "Coin: %ip %ig %is %ic", platinum, gold, silver, copper); - - ItemList::iterator cur,end; - cur = itemlist.begin(); - end = itemlist.end(); - for(; cur != end; ++cur) { - const Item_Struct* item = database.GetItem((*cur)->item_id); - if (item) - if (to->GetClientVersion() >= EQClientRoF) - { - to->Message(0, "minlvl: %i maxlvl: %i %i: %c%06X0000000000000000000000000000000000000000000000000%s%c",(*cur)->min_level, (*cur)->max_level, (int) item->ID,0x12, item->ID, item->Name, 0x12); - } - else if (to->GetClientVersion() >= EQClientSoF) - { - to->Message(0, "minlvl: %i maxlvl: %i %i: %c%06X00000000000000000000000000000000000000000000%s%c",(*cur)->min_level, (*cur)->max_level, (int) item->ID,0x12, item->ID, item->Name, 0x12); - } - else - { - to->Message(0, "minlvl: %i maxlvl: %i %i: %c%06X000000000000000000000000000000000000000%s%c",(*cur)->min_level, (*cur)->max_level, (int) item->ID,0x12, item->ID, item->Name, 0x12); - } - else - LogFile->write(EQEMuLog::Error, "Database error, invalid item"); - x++; - } - to->Message(0, "%i items on %s.", x, GetName()); -} - -void NPC::AddCash(uint16 in_copper, uint16 in_silver, uint16 in_gold, uint16 in_platinum) { - if(in_copper >= 0) - copper = in_copper; - else - copper = 0; - - if(in_silver >= 0) - silver = in_silver; - else - silver = 0; - - if(in_gold >= 0) - gold = in_gold; - else - gold = 0; - - if(in_platinum >= 0) - platinum = in_platinum; - else - platinum = 0; -} - -void NPC::AddCash() { - copper = zone->random.Int(1, 100); - silver = zone->random.Int(1, 50); - gold = zone->random.Int(1, 10); - platinum = zone->random.Int(1, 5); -} - -void NPC::RemoveCash() { - copper = 0; - silver = 0; - gold = 0; - platinum = 0; -} - -bool NPC::Process() -{ - if (IsStunned() && stunned_timer.Check()) - { - this->stunned = false; - this->stunned_timer.Disable(); - this->spun_timer.Disable(); - } - - if (p_depop) - { - Mob* owner = entity_list.GetMob(this->ownerid); - if (owner != 0) - { - //if(GetBodyType() != BT_SwarmPet) - // owner->SetPetID(0); - this->ownerid = 0; - this->petid = 0; - } - return false; - } - - SpellProcess(); - - if(tic_timer.Check()) - { - BuffProcess(); - - if(curfp) - ProcessFlee(); - - uint32 bonus = 0; - - if(GetAppearance() == eaSitting) - bonus+=3; - - int32 OOCRegen = 0; - if(oocregen > 0){ //should pull from Mob class - OOCRegen += GetMaxHP() * oocregen / 100; - } - //Lieka Edit:Fixing NPC regen.NPCs should regen to full during a set duration, not based on their HPs.Increase NPC's HPs by % of total HPs / tick. - if((GetHP() < GetMaxHP()) && !IsPet()) { - if(!IsEngaged()) {//NPC out of combat - if(GetNPCHPRegen() > OOCRegen) - SetHP(GetHP() + GetNPCHPRegen()); - else - SetHP(GetHP() + OOCRegen); - } else - SetHP(GetHP()+GetNPCHPRegen()); - } else if(GetHP() < GetMaxHP() && GetOwnerID() !=0) { - if(!IsEngaged()) //pet - SetHP(GetHP()+GetNPCHPRegen()+bonus+(GetLevel()/5)); - else - SetHP(GetHP()+GetNPCHPRegen()+bonus); - } else - SetHP(GetHP()+GetNPCHPRegen()); - - if(GetMana() < GetMaxMana()) { - SetMana(GetMana()+mana_regen+bonus); - } - - - if(zone->adv_data && !p_depop) - { - ServerZoneAdventureDataReply_Struct* ds = (ServerZoneAdventureDataReply_Struct*)zone->adv_data; - if(ds->type == Adventure_Rescue && ds->data_id == GetNPCTypeID()) - { - Mob *o = GetOwner(); - if(o && o->IsClient()) - { - float x_diff = ds->dest_x - GetX(); - float y_diff = ds->dest_y - GetY(); - float z_diff = ds->dest_z - GetZ(); - float dist = ((x_diff * x_diff) + (y_diff * y_diff) + (z_diff * z_diff)); - if(dist < RuleR(Adventure, DistanceForRescueComplete)) - { - zone->DoAdventureCountIncrease(); - Say("You don't know what this means to me. Thank you so much for finding and saving me from" - " this wretched place. I'll find my way from here."); - Depop(); - } - } - } - } - } - - if (sendhpupdate_timer.Check() && (IsTargeted() || (IsPet() && GetOwner() && GetOwner()->IsClient()))) { - if(!IsFullHP || cur_hp 999) - viral_timer_counter = 0; - } - - ProjectileAttack(); - - if(spellbonuses.GravityEffect == 1) { - if(gravity_timer.Check()) - DoGravityEffect(); - } - - if(reface_timer->Check() && !IsEngaged() && (m_GuardPoint.m_X == GetX() && m_GuardPoint.m_Y == GetY() && m_GuardPoint.m_Z == GetZ())) { - SetHeading(m_GuardPoint.m_Heading); - SendPosition(); - reface_timer->Disable(); - } - - if (IsMezzed()) - return true; - - if(IsStunned()) { - if(spun_timer.Check()) - Spin(); - return true; - } - - if (enraged_timer.Check()){ - ProcessEnrage(); - } - - //Handle assists... - if(assist_timer.Check() && IsEngaged() && !Charmed()) { - entity_list.AIYellForHelp(this, GetTarget()); - } - - if(qGlobals) - { - if(qglobal_purge_timer.Check()) - { - qGlobals->PurgeExpiredGlobals(); - } - } - - AI_Process(); - - return true; -} - -uint32 NPC::CountLoot() { - return(itemlist.size()); -} - -void NPC::Depop(bool StartSpawnTimer) { - uint16 emoteid = this->GetEmoteID(); - if(emoteid != 0) - this->DoNPCEmote(ONDESPAWN,emoteid); - p_depop = true; - if (StartSpawnTimer) { - if (respawn2 != 0) { - respawn2->DeathReset(); - } - } -} - -bool NPC::DatabaseCastAccepted(int spell_id) { - for (int i=0; i < 12; i++) { - switch(spells[spell_id].effectid[i]) { - case SE_Stamina: { - if(IsEngaged() && GetHPRatio() < 100) - return true; - else - return false; - break; - } - case SE_CurrentHPOnce: - case SE_CurrentHP: { - if(this->GetHPRatio() < 100 && spells[spell_id].buffduration == 0) - return true; - else - return false; - break; - } - - case SE_HealOverTime: { - if(this->GetHPRatio() < 100) - return true; - else - return false; - break; - } - case SE_DamageShield: { - return true; - } - case SE_NecPet: - case SE_SummonPet: { - if(GetPet()){ -#ifdef SPELLQUEUE - printf("%s: Attempted to make a second pet, denied.\n",GetName()); -#endif - return false; - } - break; - } - case SE_LocateCorpse: - case SE_SummonCorpse: { - return false; //Pfft, npcs don't need to summon corpses/locate corpses! - break; - } - default: - if(spells[spell_id].goodEffect == 1 && !(spells[spell_id].buffduration == 0 && this->GetHPRatio() == 100) && !IsEngaged()) - return true; - return false; - } - } - return false; -} - -NPC* NPC::SpawnNPC(const char* spawncommand, const xyz_heading& position, Client* client) { - if(spawncommand == 0 || spawncommand[0] == 0) { - return 0; - } - else { - Seperator sep(spawncommand); - //Lets see if someone didn't fill out the whole #spawn function properly - if (!sep.IsNumber(1)) - sprintf(sep.arg[1],"1"); - if (!sep.IsNumber(2)) - sprintf(sep.arg[2],"1"); - if (!sep.IsNumber(3)) - sprintf(sep.arg[3],"0"); - if (atoi(sep.arg[4]) > 2100000000 || atoi(sep.arg[4]) <= 0) - sprintf(sep.arg[4]," "); - if (!strcmp(sep.arg[5],"-")) - sprintf(sep.arg[5]," "); - if (!sep.IsNumber(5)) - sprintf(sep.arg[5]," "); - if (!sep.IsNumber(6)) - sprintf(sep.arg[6],"1"); - if (!sep.IsNumber(8)) - sprintf(sep.arg[8],"0"); - if (!sep.IsNumber(9)) - sprintf(sep.arg[9], "0"); - if (!sep.IsNumber(7)) - sprintf(sep.arg[7],"0"); - if (!strcmp(sep.arg[4],"-")) - sprintf(sep.arg[4]," "); - if (!sep.IsNumber(10)) // bodytype - sprintf(sep.arg[10], "0"); - //Calc MaxHP if client neglected to enter it... - if (!sep.IsNumber(4)) { - //Stolen from Client::GetMaxHP... - uint8 multiplier = 0; - int tmplevel = atoi(sep.arg[2]); - switch(atoi(sep.arg[5])) - { - case WARRIOR: - if (tmplevel < 20) - multiplier = 22; - else if (tmplevel < 30) - multiplier = 23; - else if (tmplevel < 40) - multiplier = 25; - else if (tmplevel < 53) - multiplier = 27; - else if (tmplevel < 57) - multiplier = 28; - else - multiplier = 30; - break; - - case DRUID: - case CLERIC: - case SHAMAN: - multiplier = 15; - break; - - case PALADIN: - case SHADOWKNIGHT: - if (tmplevel < 35) - multiplier = 21; - else if (tmplevel < 45) - multiplier = 22; - else if (tmplevel < 51) - multiplier = 23; - else if (tmplevel < 56) - multiplier = 24; - else if (tmplevel < 60) - multiplier = 25; - else - multiplier = 26; - break; - - case MONK: - case BARD: - case ROGUE: - //case BEASTLORD: - if (tmplevel < 51) - multiplier = 18; - else if (tmplevel < 58) - multiplier = 19; - else - multiplier = 20; - break; - - case RANGER: - if (tmplevel < 58) - multiplier = 20; - else - multiplier = 21; - break; - - case MAGICIAN: - case WIZARD: - case NECROMANCER: - case ENCHANTER: - multiplier = 12; - break; - - default: - if (tmplevel < 35) - multiplier = 21; - else if (tmplevel < 45) - multiplier = 22; - else if (tmplevel < 51) - multiplier = 23; - else if (tmplevel < 56) - multiplier = 24; - else if (tmplevel < 60) - multiplier = 25; - else - multiplier = 26; - break; - } - sprintf(sep.arg[4],"%i",5+multiplier*atoi(sep.arg[2])+multiplier*atoi(sep.arg[2])*75/300); - } - - // Autoselect NPC Gender - if (sep.arg[5][0] == 0) { - sprintf(sep.arg[5], "%i", (int) Mob::GetDefaultGender(atoi(sep.arg[1]))); - } - - //Time to create the NPC!! - NPCType* npc_type = new NPCType; - memset(npc_type, 0, sizeof(NPCType)); - - strncpy(npc_type->name, sep.arg[0], 60); - npc_type->cur_hp = atoi(sep.arg[4]); - npc_type->max_hp = atoi(sep.arg[4]); - npc_type->race = atoi(sep.arg[1]); - npc_type->gender = atoi(sep.arg[5]); - npc_type->class_ = atoi(sep.arg[6]); - npc_type->deity = 1; - npc_type->level = atoi(sep.arg[2]); - npc_type->npc_id = 0; - npc_type->loottable_id = 0; - npc_type->texture = atoi(sep.arg[3]); - npc_type->light = 0; - npc_type->runspeed = 1.25; - npc_type->d_meele_texture1 = atoi(sep.arg[7]); - npc_type->d_meele_texture2 = atoi(sep.arg[8]); - npc_type->merchanttype = atoi(sep.arg[9]); - npc_type->bodytype = atoi(sep.arg[10]); - - npc_type->STR = 150; - npc_type->STA = 150; - npc_type->DEX = 150; - npc_type->AGI = 150; - npc_type->INT = 150; - npc_type->WIS = 150; - npc_type->CHA = 150; - - npc_type->attack_delay = 30; - - npc_type->prim_melee_type = 28; - npc_type->sec_melee_type = 28; - - NPC* npc = new NPC(npc_type, nullptr, position, FlyMode3); - npc->GiveNPCTypeData(npc_type); - - entity_list.AddNPC(npc); - - if (client) { - // Notify client of spawn data - client->Message(0, "New spawn:"); - client->Message(0, "Name: %s", npc->name); - client->Message(0, "Race: %u", npc->race); - client->Message(0, "Level: %u", npc->level); - client->Message(0, "Material: %u", npc->texture); - client->Message(0, "Current/Max HP: %i", npc->max_hp); - client->Message(0, "Gender: %u", npc->gender); - client->Message(0, "Class: %u", npc->class_); - client->Message(0, "Weapon Item Number: %u/%u", npc->d_meele_texture1, npc->d_meele_texture2); - client->Message(0, "MerchantID: %u", npc->MerchantType); - client->Message(0, "Bodytype: %u", npc->bodytype); - } - - return npc; - } -} - -uint32 ZoneDatabase::CreateNewNPCCommand(const char* zone, uint32 zone_version,Client *client, NPC* spawn, uint32 extra) { - - uint32 npc_type_id = 0; - - if (extra && client && client->GetZoneID()) - { - // Set an npc_type ID within the standard range for the current zone if possible (zone_id * 1000) - int starting_npc_id = client->GetZoneID() * 1000; - - std::string query = StringFormat("SELECT MAX(id) FROM npc_types WHERE id >= %i AND id < %i", - starting_npc_id, starting_npc_id + 1000); - auto results = QueryDatabase(query); - if (results.Success()) { - if (results.RowCount() != 0) - { - auto row = results.begin(); - npc_type_id = atoi(row[0]) + 1; - // Prevent the npc_type id from exceeding the range for this zone - if (npc_type_id >= (starting_npc_id + 1000)) - npc_type_id = 0; - } - else // No npc_type IDs set in this range yet - npc_type_id = starting_npc_id; - } - } - - char tmpstr[64]; - EntityList::RemoveNumbers(strn0cpy(tmpstr, spawn->GetName(), sizeof(tmpstr))); - std::string query; - if (npc_type_id) - { - query = StringFormat("INSERT INTO npc_types (id, name, level, race, class, hp, gender, " - "texture, helmtexture, size, loottable_id, merchant_id, face, " - "runspeed, prim_melee_type, sec_melee_type) " - "VALUES(%i, \"%s\" , %i, %i, %i, %i, %i, %i, %i, %f, %i, %i, %i, %f, %i, %i)", - npc_type_id, tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), - spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), - spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), - spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28); - auto results = QueryDatabase(query); - if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); - return false; - } - npc_type_id = results.LastInsertedID(); - } - else - { - query = StringFormat("INSERT INTO npc_types (name, level, race, class, hp, gender, " - "texture, helmtexture, size, loottable_id, merchant_id, face, " - "runspeed, prim_melee_type, sec_melee_type) " - "VALUES(\"%s\", %i, %i, %i, %i, %i, %i, %i, %f, %i, %i, %i, %f, %i, %i)", - tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), - spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), - spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), - spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28); - auto results = QueryDatabase(query); - if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); - return false; - } - npc_type_id = results.LastInsertedID(); - } - - if(client) - client->LogSQL(query.c_str()); - - query = StringFormat("INSERT INTO spawngroup (id, name) VALUES(%i, '%s-%s')", 0, zone, spawn->GetName()); - auto results = QueryDatabase(query); - if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); - return false; - } - uint32 spawngroupid = results.LastInsertedID(); - - if(client) - client->LogSQL(query.c_str()); - - query = StringFormat("INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) " - "VALUES('%s', %u, %f, %f, %f, %i, %f, %i)", - zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), 1200, - spawn->GetHeading(), spawngroupid); - results = QueryDatabase(query); - if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); - return false; - } - - if(client) - client->LogSQL(query.c_str()); - - query = StringFormat("INSERT INTO spawnentry (spawngroupID, npcID, chance) VALUES(%i, %i, %i)", - spawngroupid, npc_type_id, 100); - results = QueryDatabase(query); - if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); - return false; - } - - if(client) - client->LogSQL(query.c_str()); - - return true; -} - -uint32 ZoneDatabase::AddNewNPCSpawnGroupCommand(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 respawnTime) { - uint32 last_insert_id = 0; - - std::string query = StringFormat("INSERT INTO spawngroup (name) VALUES('%s%s%i')", - zone, spawn->GetName(), Timer::GetCurrentTime()); - auto results = QueryDatabase(query); - if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "CreateNewNPCSpawnGroupCommand Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); - return 0; - } - last_insert_id = results.LastInsertedID(); - - if(client) - client->LogSQL(query.c_str()); - - uint32 respawntime = 0; - uint32 spawnid = 0; - if (respawnTime) - respawntime = respawnTime; - else if(spawn->respawn2 && spawn->respawn2->RespawnTimer() != 0) - respawntime = spawn->respawn2->RespawnTimer(); - else - respawntime = 1200; - - query = StringFormat("INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) " - "VALUES('%s', %u, %f, %f, %f, %i, %f, %i)", - zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), respawntime, - spawn->GetHeading(), last_insert_id); - results = QueryDatabase(query); - if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "CreateNewNPCSpawnGroupCommand Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); - return 0; - } - spawnid = results.LastInsertedID(); - - if(client) - client->LogSQL(query.c_str()); - - query = StringFormat("INSERT INTO spawnentry (spawngroupID, npcID, chance) VALUES(%i, %i, %i)", - last_insert_id, spawn->GetNPCTypeID(), 100); - results = QueryDatabase(query); - if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "CreateNewNPCSpawnGroupCommand Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); - return 0; - } - - if(client) - client->LogSQL(query.c_str()); - - return spawnid; -} - -uint32 ZoneDatabase::UpdateNPCTypeAppearance(Client *client, NPC* spawn) { - - std::string query = StringFormat("UPDATE npc_types SET name = \"%s\", level = %i, race = %i, class = %i, " - "hp = %i, gender = %i, texture = %i, helmtexture = %i, size = %i, " - "loottable_id = %i, merchant_id = %i, face = %i, WHERE id = %i", - spawn->GetName(), spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), - spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), - spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), - spawn->MerchantType, spawn->GetNPCTypeID()); - auto results = QueryDatabase(query); - if (!results.Success() && client) - client->LogSQL(query.c_str()); - - return results.Success() == true? 1: 0; -} - -uint32 ZoneDatabase::DeleteSpawnLeaveInNPCTypeTable(const char* zone, Client *client, NPC* spawn) { - uint32 id = 0; - uint32 spawngroupID = 0; - - std::string query = StringFormat("SELECT id, spawngroupID FROM spawn2 WHERE " - "zone='%s' AND spawngroupID=%i", zone, spawn->GetSp2()); - auto results = QueryDatabase(query); - if (!results.Success()) - return 0; - - if (results.RowCount() == 0) - return 0; - - auto row = results.begin(); - if (row[0]) - id = atoi(row[0]); - - if (row[1]) - spawngroupID = atoi(row[1]); - - query = StringFormat("DELETE FROM spawn2 WHERE id = '%i'", id); - results = QueryDatabase(query); - if (!results.Success()) - return 0; - - if(client) - client->LogSQL(query.c_str()); - - query = StringFormat("DELETE FROM spawngroup WHERE id = '%i'", spawngroupID); - results = QueryDatabase(query); - if (!results.Success()) - return 0; - - if(client) - client->LogSQL(query.c_str()); - - query = StringFormat("DELETE FROM spawnentry WHERE spawngroupID = '%i'", spawngroupID); - results = QueryDatabase(query); - if (!results.Success()) - return 0; - - if(client) - client->LogSQL(query.c_str()); - - return 1; -} - -uint32 ZoneDatabase::DeleteSpawnRemoveFromNPCTypeTable(const char* zone, uint32 zone_version, Client *client, NPC* spawn) { - - uint32 id = 0; - uint32 spawngroupID = 0; - - std::string query = StringFormat("SELECT id, spawngroupID FROM spawn2 WHERE zone = '%s' " - "AND version = %u AND spawngroupID = %i", - zone, zone_version, spawn->GetSp2()); - auto results = QueryDatabase(query); - if (!results.Success()) - return 0; - - if (results.RowCount() == 0) - return 0; - - auto row = results.begin(); - - if (row[0]) - id = atoi(row[0]); - - if (row[1]) - spawngroupID = atoi(row[1]); - - query = StringFormat("DELETE FROM spawn2 WHERE id = '%i'", id); - results = QueryDatabase(query); - if (!results.Success()) - return 0; - - if(client) - client->LogSQL(query.c_str()); - - query = StringFormat("DELETE FROM spawngroup WHERE id = '%i'", spawngroupID); - results = QueryDatabase(query); - if (!results.Success()) - return 0; - - if(client) - client->LogSQL(query.c_str()); - - query = StringFormat("DELETE FROM spawnentry WHERE spawngroupID = '%i'", spawngroupID); - results = QueryDatabase(query); - if (!results.Success()) - return 0; - - if(client) - client->LogSQL(query.c_str()); - - query = StringFormat("DELETE FROM npc_types WHERE id = '%i'", spawn->GetNPCTypeID()); - results = QueryDatabase(query); - if (!results.Success()) - return 0; - - if(client) - client->LogSQL(query.c_str()); - - return 1; -} - -uint32 ZoneDatabase::AddSpawnFromSpawnGroup(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID) { - - uint32 last_insert_id = 0; - std::string query = StringFormat("INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) " - "VALUES('%s', %u, %f, %f, %f, %i, %f, %i)", - zone, zone_version, client->GetX(), client->GetY(), client->GetZ(), - 120, client->GetHeading(), spawnGroupID); - auto results = QueryDatabase(query); - if (!results.Success()) - return 0; - - if(client) - client->LogSQL(query.c_str()); - - return 1; -} - -uint32 ZoneDatabase::AddNPCTypes(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID) { - - uint32 npc_type_id; - char numberlessName[64]; - - EntityList::RemoveNumbers(strn0cpy(numberlessName, spawn->GetName(), sizeof(numberlessName))); - std::string query = StringFormat("INSERT INTO npc_types (name, level, race, class, hp, gender, " - "texture, helmtexture, size, loottable_id, merchant_id, face, " - "runspeed, prim_melee_type, sec_melee_type) " - "VALUES(\"%s\", %i, %i, %i, %i, %i, %i, %i, %f, %i, %i, %i, %f, %i, %i)", - numberlessName, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), - spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), - spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), - spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28); - auto results = QueryDatabase(query); - if (!results.Success()) - return 0; - npc_type_id = results.LastInsertedID(); - - if(client) - client->LogSQL(query.c_str()); - - if(client) - client->Message(0, "%s npc_type ID %i created successfully!", numberlessName, npc_type_id); - - return 1; -} - -uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra) { - - switch (command) { - case 0: { // Create a new NPC and add all spawn related data - return CreateNewNPCCommand(zone, zone_version, c, spawn, extra); - } - case 1:{ // Add new spawn group and spawn point for an existing NPC Type ID - return AddNewNPCSpawnGroupCommand(zone, zone_version, c, spawn, extra); - } - case 2: { // Update npc_type appearance and other data on targeted spawn - return UpdateNPCTypeAppearance(c, spawn); - } - case 3: { // delete spawn from spawning, but leave in npc_types table - return DeleteSpawnLeaveInNPCTypeTable(zone, c, spawn); - } - case 4: { //delete spawn from DB (including npc_type) - return DeleteSpawnRemoveFromNPCTypeTable(zone, zone_version, c, spawn); - } - case 5: { // add a spawn from spawngroup - return AddSpawnFromSpawnGroup(zone, zone_version, c, spawn, extra); - } - case 6: { // add npc_type - return AddNPCTypes(zone, zone_version, c, spawn, extra); - } - } - return false; -} - -int32 NPC::GetEquipmentMaterial(uint8 material_slot) const -{ - if (material_slot >= _MaterialCount) - return 0; - - int inv_slot = Inventory::CalcSlotFromMaterial(material_slot); - if (inv_slot == -1) - return 0; - if(equipment[inv_slot] == 0) { - switch(material_slot) { - case MaterialHead: - return helmtexture; - case MaterialChest: - return texture; - case MaterialPrimary: - return d_meele_texture1; - case MaterialSecondary: - return d_meele_texture2; - default: - //they have nothing in the slot, and its not a special slot... they get nothing. - return(0); - } - } - - //they have some loot item in this slot, pass it up to the default handler - return(Mob::GetEquipmentMaterial(material_slot)); -} - -uint32 NPC::GetMaxDamage(uint8 tlevel) -{ - uint32 dmg = 0; - if (tlevel < 40) - dmg = tlevel*2+2; - else if (tlevel < 50) - dmg = level*25/10+2; - else if (tlevel < 60) - dmg = (tlevel*3+2)+((tlevel-50)*30); - else - dmg = (tlevel*3+2)+((tlevel-50)*35); - return dmg; -} - -void NPC::PickPocket(Client* thief) { - - thief->CheckIncreaseSkill(SkillPickPockets, nullptr, 5); - - //make sure were allowed to targte them: - int olevel = GetLevel(); - if(olevel > (thief->GetLevel() + THIEF_PICKPOCKET_OVER)) { - thief->Message(13, "You are too inexperienced to pick pocket this target"); - thief->SendPickPocketResponse(this, 0, PickPocketFailed); - //should we check aggro - return; - } - - if(zone->random.Roll(5)) { - AddToHateList(thief, 50); - Say("Stop thief!"); - thief->Message(13, "You are noticed trying to steal!"); - thief->SendPickPocketResponse(this, 0, PickPocketFailed); - return; - } - - int steal_skill = thief->GetSkill(SkillPickPockets); - int stealchance = steal_skill*100/(5*olevel+5); - ItemInst* inst = 0; - int x = 0; - int slot[50]; - int steal_items[50]; - int charges[50]; - int money[4]; - money[0] = GetPlatinum(); - money[1] = GetGold(); - money[2] = GetSilver(); - money[3] = GetCopper(); - if (steal_skill < 125) - money[0] = 0; - if (steal_skill < 60) - money[1] = 0; - memset(slot,0,50); - memset(steal_items,0,50); - memset(charges,0,50); - //Determine wheter to steal money or an item. - bool no_coin = ((money[0] + money[1] + money[2] + money[3]) == 0); - bool steal_item = (zone->random.Roll(50) || no_coin); - if (steal_item) - { - ItemList::iterator cur,end; - cur = itemlist.begin(); - end = itemlist.end(); - for(; cur != end && x < 49; ++cur) { - ServerLootItem_Struct* citem = *cur; - const Item_Struct* item = database.GetItem(citem->item_id); - if (item) - { - inst = database.CreateItem(item, citem->charges); - bool is_arrow = (item->ItemType == ItemTypeArrow) ? true : false; - int slot_id = thief->GetInv().FindFreeSlot(false, true, inst->GetItem()->Size, is_arrow); - if (/*!Equipped(item->ID) &&*/ - !item->Magic && item->NoDrop != 0 && !inst->IsType(ItemClassContainer) && slot_id != INVALID_INDEX - /*&& steal_skill > item->StealSkill*/ ) - { - slot[x] = slot_id; - steal_items[x] = item->ID; - if (inst->IsStackable()) - charges[x] = 1; - else - charges[x] = citem->charges; - x++; - } - } - } - if (x > 0) - { - int random = zone->random.Int(0, x-1); - inst = database.CreateItem(steal_items[random], charges[random]); - if (inst) - { - const Item_Struct* item = inst->GetItem(); - if (item) - { - if (/*item->StealSkill || */steal_skill >= stealchance) - { - thief->PutItemInInventory(slot[random], *inst); - thief->SendItemPacket(slot[random], inst, ItemPacketTrade); - RemoveItem(item->ID); - thief->SendPickPocketResponse(this, 0, PickPocketItem, item); - } - else - steal_item = false; - } - else - steal_item = false; - } - else - steal_item = false; - } - else if (!no_coin) - { - steal_item = false; - } - else - { - thief->Message(0, "This target's pockets are empty"); - thief->SendPickPocketResponse(this, 0, PickPocketFailed); - } - } - if (!steal_item) //Steal money - { - uint32 amt = zone->random.Int(1, (steal_skill/25)+1); - int steal_type = 0; - if (!money[0]) - { - steal_type = 1; - if (!money[1]) - { - steal_type = 2; - if (!money[2]) - { - steal_type = 3; - } - } - } - - if (zone->random.Roll(stealchance)) - { - switch (steal_type) - { - case 0:{ - if (amt > GetPlatinum()) - amt = GetPlatinum(); - SetPlatinum(GetPlatinum()-amt); - thief->AddMoneyToPP(0,0,0,amt,false); - thief->SendPickPocketResponse(this, amt, PickPocketPlatinum); - break; - } - case 1:{ - if (amt > GetGold()) - amt = GetGold(); - SetGold(GetGold()-amt); - thief->AddMoneyToPP(0,0,amt,0,false); - thief->SendPickPocketResponse(this, amt, PickPocketGold); - break; - } - case 2:{ - if (amt > GetSilver()) - amt = GetSilver(); - SetSilver(GetSilver()-amt); - thief->AddMoneyToPP(0,amt,0,0,false); - thief->SendPickPocketResponse(this, amt, PickPocketSilver); - break; - } - case 3:{ - if (amt > GetCopper()) - amt = GetCopper(); - SetCopper(GetCopper()-amt); - thief->AddMoneyToPP(amt,0,0,0,false); - thief->SendPickPocketResponse(this, amt, PickPocketCopper); - break; - } - } - } - else - { - thief->SendPickPocketResponse(this, 0, PickPocketFailed); - } - } - safe_delete(inst); -} - -void Mob::NPCSpecialAttacks(const char* parse, int permtag, bool reset, bool remove) { - if(reset) - { - ClearSpecialAbilities(); - } - - const char* orig_parse = parse; - while (*parse) - { - switch(*parse) - { - case 'E': - SetSpecialAbility(SPECATK_ENRAGE, remove ? 0 : 1); - break; - case 'F': - SetSpecialAbility(SPECATK_FLURRY, remove ? 0 : 1); - break; - case 'R': - SetSpecialAbility(SPECATK_RAMPAGE, remove ? 0 : 1); - break; - case 'r': - SetSpecialAbility(SPECATK_AREA_RAMPAGE, remove ? 0 : 1); - break; - case 'S': - if(remove) { - SetSpecialAbility(SPECATK_SUMMON, 0); - StopSpecialAbilityTimer(SPECATK_SUMMON); - } else { - SetSpecialAbility(SPECATK_SUMMON, 1); - } - break; - case 'T': - SetSpecialAbility(SPECATK_TRIPLE, remove ? 0 : 1); - break; - case 'Q': - //quad requires triple to work properly - if(remove) { - SetSpecialAbility(SPECATK_QUAD, 0); - } else { - SetSpecialAbility(SPECATK_TRIPLE, 1); - SetSpecialAbility(SPECATK_QUAD, 1); - } - break; - case 'b': - SetSpecialAbility(SPECATK_BANE, remove ? 0 : 1); - break; - case 'm': - SetSpecialAbility(SPECATK_MAGICAL, remove ? 0 : 1); - break; - case 'U': - SetSpecialAbility(UNSLOWABLE, remove ? 0 : 1); - break; - case 'M': - SetSpecialAbility(UNMEZABLE, remove ? 0 : 1); - break; - case 'C': - SetSpecialAbility(UNCHARMABLE, remove ? 0 : 1); - break; - case 'N': - SetSpecialAbility(UNSTUNABLE, remove ? 0 : 1); - break; - case 'I': - SetSpecialAbility(UNSNAREABLE, remove ? 0 : 1); - break; - case 'D': - SetSpecialAbility(UNFEARABLE, remove ? 0 : 1); - break; - case 'K': - SetSpecialAbility(UNDISPELLABLE, remove ? 0 : 1); - break; - case 'A': - SetSpecialAbility(IMMUNE_MELEE, remove ? 0 : 1); - break; - case 'B': - SetSpecialAbility(IMMUNE_MAGIC, remove ? 0 : 1); - break; - case 'f': - SetSpecialAbility(IMMUNE_FLEEING, remove ? 0 : 1); - break; - case 'O': - SetSpecialAbility(IMMUNE_MELEE_EXCEPT_BANE, remove ? 0 : 1); - break; - case 'W': - SetSpecialAbility(IMMUNE_MELEE_NONMAGICAL, remove ? 0 : 1); - break; - case 'H': - SetSpecialAbility(IMMUNE_AGGRO, remove ? 0 : 1); - break; - case 'G': - SetSpecialAbility(IMMUNE_AGGRO_ON, remove ? 0 : 1); - break; - case 'g': - SetSpecialAbility(IMMUNE_CASTING_FROM_RANGE, remove ? 0 : 1); - break; - case 'd': - SetSpecialAbility(IMMUNE_FEIGN_DEATH, remove ? 0 : 1); - break; - case 'Y': - SetSpecialAbility(SPECATK_RANGED_ATK, remove ? 0 : 1); - break; - case 'L': - SetSpecialAbility(SPECATK_INNATE_DW, remove ? 0 : 1); - break; - case 't': - SetSpecialAbility(NPC_TUNNELVISION, remove ? 0 : 1); - break; - case 'n': - SetSpecialAbility(NPC_NO_BUFFHEAL_FRIENDS, remove ? 0 : 1); - break; - case 'p': - SetSpecialAbility(IMMUNE_PACIFY, remove ? 0 : 1); - break; - case 'J': - SetSpecialAbility(LEASH, remove ? 0 : 1); - break; - case 'j': - SetSpecialAbility(TETHER, remove ? 0 : 1); - break; - case 'o': - SetSpecialAbility(DESTRUCTIBLE_OBJECT, remove ? 0 : 1); - SetDestructibleObject(remove ? true : false); - break; - case 'Z': - SetSpecialAbility(NO_HARM_FROM_CLIENT, remove ? 0 : 1); - break; - case 'i': - SetSpecialAbility(IMMUNE_TAUNT, remove ? 0 : 1); - break; - case 'e': - SetSpecialAbility(ALWAYS_FLEE, remove ? 0 : 1); - break; - case 'h': - SetSpecialAbility(FLEE_PERCENT, remove ? 0 : 1); - break; - - default: - break; - } - parse++; - } - - if(permtag == 1 && this->GetNPCTypeID() > 0) - { - if(database.SetSpecialAttkFlag(this->GetNPCTypeID(), orig_parse)) - { - LogFile->write(EQEMuLog::Normal, "NPCTypeID: %i flagged to '%s' for Special Attacks.\n",this->GetNPCTypeID(),orig_parse); - } - } -} - -bool Mob::HasNPCSpecialAtk(const char* parse) { - - bool HasAllAttacks = true; - - while (*parse && HasAllAttacks == true) - { - switch(*parse) - { - case 'E': - if (!GetSpecialAbility(SPECATK_ENRAGE)) - HasAllAttacks = false; - break; - case 'F': - if (!GetSpecialAbility(SPECATK_FLURRY)) - HasAllAttacks = false; - break; - case 'R': - if (!GetSpecialAbility(SPECATK_RAMPAGE)) - HasAllAttacks = false; - break; - case 'r': - if (!GetSpecialAbility(SPECATK_AREA_RAMPAGE)) - HasAllAttacks = false; - break; - case 'S': - if (!GetSpecialAbility(SPECATK_SUMMON)) - HasAllAttacks = false; - break; - case 'T': - if (!GetSpecialAbility(SPECATK_TRIPLE)) - HasAllAttacks = false; - break; - case 'Q': - if (!GetSpecialAbility(SPECATK_QUAD)) - HasAllAttacks = false; - break; - case 'b': - if (!GetSpecialAbility(SPECATK_BANE)) - HasAllAttacks = false; - break; - case 'm': - if (!GetSpecialAbility(SPECATK_MAGICAL)) - HasAllAttacks = false; - break; - case 'U': - if (!GetSpecialAbility(UNSLOWABLE)) - HasAllAttacks = false; - break; - case 'M': - if (!GetSpecialAbility(UNMEZABLE)) - HasAllAttacks = false; - break; - case 'C': - if (!GetSpecialAbility(UNCHARMABLE)) - HasAllAttacks = false; - break; - case 'N': - if (!GetSpecialAbility(UNSTUNABLE)) - HasAllAttacks = false; - break; - case 'I': - if (!GetSpecialAbility(UNSNAREABLE)) - HasAllAttacks = false; - break; - case 'D': - if (!GetSpecialAbility(UNFEARABLE)) - HasAllAttacks = false; - break; - case 'A': - if (!GetSpecialAbility(IMMUNE_MELEE)) - HasAllAttacks = false; - break; - case 'B': - if (!GetSpecialAbility(IMMUNE_MAGIC)) - HasAllAttacks = false; - break; - case 'f': - if (!GetSpecialAbility(IMMUNE_FLEEING)) - HasAllAttacks = false; - break; - case 'O': - if (!GetSpecialAbility(IMMUNE_MELEE_EXCEPT_BANE)) - HasAllAttacks = false; - break; - case 'W': - if (!GetSpecialAbility(IMMUNE_MELEE_NONMAGICAL)) - HasAllAttacks = false; - break; - case 'H': - if (!GetSpecialAbility(IMMUNE_AGGRO)) - HasAllAttacks = false; - break; - case 'G': - if (!GetSpecialAbility(IMMUNE_AGGRO_ON)) - HasAllAttacks = false; - break; - case 'g': - if (!GetSpecialAbility(IMMUNE_CASTING_FROM_RANGE)) - HasAllAttacks = false; - break; - case 'd': - if (!GetSpecialAbility(IMMUNE_FEIGN_DEATH)) - HasAllAttacks = false; - break; - case 'Y': - if (!GetSpecialAbility(SPECATK_RANGED_ATK)) - HasAllAttacks = false; - break; - case 'L': - if (!GetSpecialAbility(SPECATK_INNATE_DW)) - HasAllAttacks = false; - break; - case 't': - if (!GetSpecialAbility(NPC_TUNNELVISION)) - HasAllAttacks = false; - break; - case 'n': - if (!GetSpecialAbility(NPC_NO_BUFFHEAL_FRIENDS)) - HasAllAttacks = false; - break; - case 'p': - if(!GetSpecialAbility(IMMUNE_PACIFY)) - HasAllAttacks = false; - break; - case 'J': - if(!GetSpecialAbility(LEASH)) - HasAllAttacks = false; - break; - case 'j': - if(!GetSpecialAbility(TETHER)) - HasAllAttacks = false; - break; - case 'o': - if(!GetSpecialAbility(DESTRUCTIBLE_OBJECT)) - { - HasAllAttacks = false; - SetDestructibleObject(false); - } - break; - case 'Z': - if(!GetSpecialAbility(NO_HARM_FROM_CLIENT)){ - HasAllAttacks = false; - } - break; - case 'e': - if(!GetSpecialAbility(ALWAYS_FLEE)) - HasAllAttacks = false; - break; - case 'h': - if(!GetSpecialAbility(FLEE_PERCENT)) - HasAllAttacks = false; - break; - default: - HasAllAttacks = false; - break; - } - parse++; - } - - return HasAllAttacks; -} - -void NPC::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) -{ - Mob::FillSpawnStruct(ns, ForWho); - PetOnSpawn(ns); - ns->spawn.is_npc = 1; -} - -void NPC::PetOnSpawn(NewSpawn_Struct* ns) -{ - //Basic settings to make sure swarm pets work properly. - Mob *swarmOwner = nullptr; - if (GetSwarmOwner()) - { - swarmOwner = entity_list.GetMobID(GetSwarmOwner()); - } - - if (swarmOwner != nullptr) - { - if(swarmOwner->IsClient()) - { - SetPetOwnerClient(true); //Simple flag to determine if pet belongs to a client - SetAllowBeneficial(1);//Allow temp pets to receive buffs and heals if owner is client. - //This is a hack to allow CLIENT swarm pets NOT to be targeted with F8. Warning: Will turn name 'Yellow'! - if (RuleB(Pets, SwarmPetNotTargetableWithHotKey)) - ns->spawn.IsMercenary = 1; - } - else - { - //NPC cast swarm pets should still be targetable with F8. - ns->spawn.IsMercenary = 0; - } - - SetTempPet(true); //Simple mob flag for checking if temp pet - swarmOwner->SetTempPetsActive(true); //Necessary fail safe flag set if mob ever had a swarm pet to ensure they are removed. - swarmOwner->SetTempPetCount(swarmOwner->GetTempPetCount() + 1); - - //Not recommended if using above (However, this will work better on older clients). - if (RuleB(Pets, UnTargetableSwarmPet)) - { - ns->spawn.bodytype = 11; - if(!IsCharmed() && swarmOwner->IsClient()) - sprintf(ns->spawn.lastName, "%s's Pet", swarmOwner->GetName()); - } - } - else if(GetOwnerID()) - { - ns->spawn.is_pet = 1; - if (!IsCharmed()) - { - Client *client = entity_list.GetClientByID(GetOwnerID()); - if(client) - { - SetPetOwnerClient(true); - sprintf(ns->spawn.lastName, "%s's Pet", client->GetName()); - } - } - } - else - { - ns->spawn.is_pet = 0; - } -} - -void NPC::SetLevel(uint8 in_level, bool command) -{ - if(in_level > level) - SendLevelAppearance(); - level = in_level; - SendAppearancePacket(AT_WhoLevel, in_level); -} - -void NPC::ModifyNPCStat(const char *identifier, const char *newValue) -{ - std::string id = identifier; - std::string val = newValue; - for(int i = 0; i < id.length(); ++i) { - id[i] = std::tolower(id[i]); - } - - if(id == "ac") { AC = atoi(val.c_str()); return; } - else if(id == "str") { STR = atoi(val.c_str()); return; } - else if(id == "sta") { STA = atoi(val.c_str()); return; } - else if(id == "agi") { AGI = atoi(val.c_str()); return; } - else if(id == "dex") { DEX = atoi(val.c_str()); return; } - else if(id == "wis") { WIS = atoi(val.c_str()); CalcMaxMana(); return; } - else if(id == "int" || id == "_int") { INT = atoi(val.c_str()); CalcMaxMana(); return; } - else if(id == "cha") { CHA = atoi(val.c_str()); return; } - else if(id == "max_hp") { base_hp = atoi(val.c_str()); CalcMaxHP(); if (cur_hp > max_hp) { cur_hp = max_hp; } return; } - else if(id == "max_mana") { npc_mana = atoi(val.c_str()); CalcMaxMana(); if (cur_mana > max_mana){ cur_mana = max_mana; } return; } - else if(id == "mr") { MR = atoi(val.c_str()); return; } - else if(id == "fr") { FR = atoi(val.c_str()); return; } - else if(id == "cr") { CR = atoi(val.c_str()); return; } - else if(id == "pr") { PR = atoi(val.c_str()); return; } - else if(id == "dr") { DR = atoi(val.c_str()); return; } - else if(id == "PhR") { PhR = atoi(val.c_str()); return; } - else if(id == "runspeed") { runspeed = (float)atof(val.c_str()); CalcBonuses(); return; } - 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 == "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; } - else if(id == "trackable") { trackable = atoi(val.c_str()); return; } - else if(id == "min_hit") { min_dmg = atoi(val.c_str()); return; } - else if(id == "max_hit") { max_dmg = atoi(val.c_str()); return; } - else if(id == "attack_count") { attack_count = atoi(val.c_str()); return; } - else if(id == "see_invis") { see_invis = atoi(val.c_str()); return; } - else if(id == "see_invis_undead") { see_invis_undead = atoi(val.c_str()); return; } - else if(id == "see_hide") { see_hide = atoi(val.c_str()); return; } - else if(id == "see_improved_hide") { see_improved_hide = atoi(val.c_str()); return; } - else if(id == "hp_regen") { hp_regen = atoi(val.c_str()); return; } - else if(id == "mana_regen") { mana_regen = atoi(val.c_str()); return; } - else if(id == "level") { SetLevel(atoi(val.c_str())); return; } - else if(id == "aggro") { pAggroRange = atof(val.c_str()); return; } - else if(id == "assist") { pAssistRange = atof(val.c_str()); return; } - else if(id == "slow_mitigation") { slow_mitigation = atoi(val.c_str()); return; } - else if(id == "loottable_id") { loottable_id = atof(val.c_str()); return; } - else if(id == "healscale") { healscale = atof(val.c_str()); return; } - else if(id == "spellscale") { spellscale = atof(val.c_str()); return; } -} - -void NPC::LevelScale() { - - uint8 random_level = (zone->random.Int(level, maxlevel)); - - float scaling = (((random_level / (float)level) - 1) * (scalerate / 100.0f)); - - // Compensate for scale rates at low levels so they don't add too much - uint8 scale_adjust = 1; - if(level > 0 && level <= 5) - scale_adjust = 10; - if(level > 5 && level <= 10) - scale_adjust = 5; - if(level > 10 && level <= 15) - scale_adjust = 3; - if(level > 15 && level <= 25) - scale_adjust = 2; - - base_hp += (int)(base_hp * scaling); - max_hp += (int)(max_hp * scaling); - cur_hp = max_hp; - STR += (int)(STR * scaling / scale_adjust); - STA += (int)(STA * scaling / scale_adjust); - AGI += (int)(AGI * scaling / scale_adjust); - DEX += (int)(DEX * scaling / scale_adjust); - INT += (int)(INT * scaling / scale_adjust); - WIS += (int)(WIS * scaling / scale_adjust); - CHA += (int)(CHA * scaling / scale_adjust); - if (MR) - MR += (int)(MR * scaling / scale_adjust); - if (CR) - CR += (int)(CR * scaling / scale_adjust); - if (DR) - DR += (int)(DR * scaling / scale_adjust); - if (FR) - FR += (int)(FR * scaling / scale_adjust); - if (PR) - PR += (int)(PR * scaling / scale_adjust); - - if (max_dmg) - { - max_dmg += (int)(max_dmg * scaling / scale_adjust); - min_dmg += (int)(min_dmg * scaling / scale_adjust); - } - - level = random_level; - - return; -} - -void NPC::CalcNPCResists() { - - if (!MR) - MR = (GetLevel() * 11)/10; - if (!CR) - CR = (GetLevel() * 11)/10; - if (!DR) - DR = (GetLevel() * 11)/10; - if (!FR) - FR = (GetLevel() * 11)/10; - if (!PR) - PR = (GetLevel() * 11)/10; - if (!Corrup) - Corrup = 15; - if (!PhR) - PhR = 10; - return; -} - -void NPC::CalcNPCRegen() { - - // Fix for lazy db-updaters (regen values left at 0) - if (GetCasterClass() != 'N' && mana_regen == 0) - mana_regen = (GetLevel() / 10) + 4; - else if(mana_regen < 0) - mana_regen = 0; - else - mana_regen = mana_regen; - - // Gives low end monsters no regen if set to 0 in database. Should make low end monsters killable - // Might want to lower this to /5 rather than 10. - if(hp_regen == 0) - { - if(GetLevel() <= 6) - hp_regen = 1; - else if(GetLevel() > 6 && GetLevel() <= 10) - hp_regen = 2; - else if(GetLevel() > 10 && GetLevel() <= 15) - hp_regen = 3; - else if(GetLevel() > 15 && GetLevel() <= 20) - hp_regen = 5; - else if(GetLevel() > 20 && GetLevel() <= 30) - hp_regen = 7; - else if(GetLevel() > 30 && GetLevel() <= 35) - hp_regen = 9; - else if(GetLevel() > 35 && GetLevel() <= 40) - hp_regen = 12; - else if(GetLevel() > 40 && GetLevel() <= 45) - hp_regen = 18; - else if(GetLevel() > 45 && GetLevel() <= 50) - hp_regen = 21; - else - hp_regen = 30; - } else if(hp_regen < 0) { - hp_regen = 0; - } else - hp_regen = hp_regen; - - return; -} - -void NPC::CalcNPCDamage() { - - int AC_adjust=12; - - if (GetLevel() >= 66) { - if (min_dmg==0) - min_dmg = 220; - if (max_dmg==0) - max_dmg = ((((99000)*(GetLevel()-64))/400)*AC_adjust/10); - } - else if (GetLevel() >= 60 && GetLevel() <= 65){ - if(min_dmg==0) - min_dmg = (GetLevel()+(GetLevel()/3)); - if(max_dmg==0) - max_dmg = (GetLevel()*3)*AC_adjust/10; - } - else if (GetLevel() >= 51 && GetLevel() <= 59){ - if(min_dmg==0) - min_dmg = (GetLevel()+(GetLevel()/3)); - if(max_dmg==0) - max_dmg = (GetLevel()*3)*AC_adjust/10; - } - else if (GetLevel() >= 40 && GetLevel() <= 50) { - if (min_dmg==0) - min_dmg = GetLevel(); - if(max_dmg==0) - max_dmg = (GetLevel()*3)*AC_adjust/10; - } - else if (GetLevel() >= 28 && GetLevel() <= 39) { - if (min_dmg==0) - min_dmg = GetLevel() / 2; - if (max_dmg==0) - max_dmg = ((GetLevel()*2)+2)*AC_adjust/10; - } - else if (GetLevel() <= 27) { - if (min_dmg==0) - min_dmg=1; - if (max_dmg==0) - max_dmg = (GetLevel()*2)*AC_adjust/10; - } - - int32 clfact = GetClassLevelFactor(); - min_dmg = (min_dmg * clfact) / 220; - max_dmg = (max_dmg * clfact) / 220; - - return; -} - - -uint32 NPC::GetSpawnPointID() const -{ - if(respawn2) - { - return respawn2->GetID(); - } - return 0; -} - -void NPC::NPCSlotTexture(uint8 slot, uint16 texture) -{ - if (slot == 7) { - d_meele_texture1 = texture; - } - else if (slot == 8) { - d_meele_texture2 = texture; - } - else if (slot < 6) { - // Reserved for texturing individual armor slots - } - return; -} - -uint32 NPC::GetSwarmOwner() -{ - if(GetSwarmInfo() != nullptr) - { - return GetSwarmInfo()->owner_id; - } - return 0; -} - -uint32 NPC::GetSwarmTarget() -{ - if(GetSwarmInfo() != nullptr) - { - return GetSwarmInfo()->target; - } - return 0; -} - -void NPC::SetSwarmTarget(int target_id) -{ - if(GetSwarmInfo() != nullptr) - { - GetSwarmInfo()->target = target_id; - } - return; -} - -int32 NPC::CalcMaxMana() { - if(npc_mana == 0) { - switch (GetCasterClass()) { - case 'I': - max_mana = (((GetINT()/2)+1) * GetLevel()) + spellbonuses.Mana + itembonuses.Mana; - break; - case 'W': - max_mana = (((GetWIS()/2)+1) * GetLevel()) + spellbonuses.Mana + itembonuses.Mana; - break; - case 'N': - default: - max_mana = 0; - break; - } - if (max_mana < 0) { - max_mana = 0; - } - - return max_mana; - } else { - switch (GetCasterClass()) { - case 'I': - max_mana = npc_mana + spellbonuses.Mana + itembonuses.Mana; - break; - case 'W': - max_mana = npc_mana + spellbonuses.Mana + itembonuses.Mana; - break; - case 'N': - default: - max_mana = 0; - break; - } - if (max_mana < 0) { - max_mana = 0; - } - - return max_mana; - } -} - -void NPC::SignalNPC(int _signal_id) -{ - signal_q.push_back(_signal_id); -} - -NPC_Emote_Struct* NPC::GetNPCEmote(uint16 emoteid, uint8 event_) { - LinkedListIterator iterator(zone->NPCEmoteList); - iterator.Reset(); - while(iterator.MoreElements()) - { - NPC_Emote_Struct* nes = iterator.GetData(); - if (emoteid == nes->emoteid && event_ == nes->event_) { - return (nes); - } - iterator.Advance(); - } - return (nullptr); -} - -void NPC::DoNPCEmote(uint8 event_, uint16 emoteid) -{ - if(this == nullptr || emoteid == 0) - { - return; - } - - NPC_Emote_Struct* nes = GetNPCEmote(emoteid,event_); - if(nes == nullptr) - { - return; - } - - if(emoteid == nes->emoteid) - { - if(nes->type == 1) - this->Emote("%s",nes->text); - else if(nes->type == 2) - this->Shout("%s",nes->text); - else if(nes->type == 3) - entity_list.MessageClose_StringID(this, true, 200, 10, GENERIC_STRING, nes->text); - else - this->Say("%s",nes->text); - } -} - -bool NPC::CanTalk() -{ - //Races that should be able to talk. (Races up to Titanium) - - uint16 TalkRace[473] = - {1,2,3,4,5,6,7,8,9,10,11,12,0,0,15,16,0,18,19,20,0,0,23,0,25,0,0,0,0,0,0, - 32,0,0,0,0,0,0,39,40,0,0,0,44,0,0,0,0,49,0,51,0,53,54,55,56,57,58,0,0,0, - 62,0,64,65,66,67,0,0,70,71,0,0,0,0,0,77,78,79,0,81,82,0,0,0,86,0,0,0,90, - 0,92,93,94,95,0,0,98,99,0,101,0,103,0,0,0,0,0,0,110,111,112,0,0,0,0,0,0, - 0,0,0,0,123,0,0,126,0,128,0,130,131,0,0,0,0,136,137,0,139,140,0,0,0,144, - 0,0,0,0,0,150,151,152,153,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,183,184,0,0,187,188,189,0,0,0,0,0,195,196,0,198,0,0,0,202,0, - 0,205,0,0,208,0,0,0,0,0,0,0,0,217,0,219,0,0,0,0,0,0,226,0,0,229,230,0,0, - 0,0,235,236,0,238,239,240,241,242,243,244,0,246,247,0,0,0,251,0,0,254,255, - 256,257,0,0,0,0,0,0,0,0,266,267,0,0,270,271,0,0,0,0,0,277,278,0,0,0,0,283, - 284,0,286,0,288,289,290,0,0,0,0,295,296,297,298,299,300,0,0,0,304,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,320,0,322,323,324,325,0,0,0,0,330,331,332,333,334,335, - 336,337,338,339,340,341,342,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,359,360,361,362, - 0,364,365,366,0,368,369,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,385,386,0,0,0,0,0,392, - 393,394,395,396,397,398,0,400,402,0,0,0,0,406,0,408,0,0,411,0,413,0,0,0,417, - 0,0,420,0,0,0,0,425,0,0,0,0,0,0,0,433,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,458,0,0,0,0,0,0,0,0,467,0,0,470,0,0,473}; - - int talk_check = TalkRace[GetRace() - 1]; - - if (TalkRace[GetRace() - 1] > 0) - return true; - - return false; -} - -//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 NPC::GetReverseFactionCon(Mob* iOther) { - iOther = iOther->GetOwnerOrSelf(); - int primaryFaction= iOther->GetPrimaryFaction(); - - //I am pretty sure that this special faction call is backwards - //and should be iOther->GetSpecialFactionCon(this) - if (primaryFaction < 0) - return GetSpecialFactionCon(iOther); - - if (primaryFaction == 0) - return FACTION_INDIFFERENT; - - //if we are a pet, use our owner's faction stuff - Mob *own = GetOwner(); - if (own != nullptr) - return own->GetReverseFactionCon(iOther); - - //make sure iOther is an npc - //also, if we dont have a faction, then they arnt gunna think anything of us either - if(!iOther->IsNPC() || GetPrimaryFaction() == 0) - return(FACTION_INDIFFERENT); - - //if we get here, iOther is an NPC too - - //otherwise, employ the npc faction stuff - //so we need to look at iOther's faction table to see - //what iOther thinks about our primary faction - return(iOther->CastToNPC()->CheckNPCFactionAlly(GetPrimaryFaction())); -} - -//Look through our faction list and return a faction con based -//on the npc_value for the other person's primary faction in our list. -FACTION_VALUE NPC::CheckNPCFactionAlly(int32 other_faction) { - std::list::iterator cur,end; - cur = faction_list.begin(); - end = faction_list.end(); - for(; cur != end; ++cur) { - struct NPCFaction* fac = *cur; - if ((int32)fac->factionID == other_faction) { - if (fac->npc_value > 0) - return FACTION_ALLY; - else if (fac->npc_value < 0) - return FACTION_SCOWLS; - else - return FACTION_INDIFFERENT; - } - } - return FACTION_INDIFFERENT; -} - -bool NPC::IsFactionListAlly(uint32 other_faction) { - return(CheckNPCFactionAlly(other_faction) == FACTION_ALLY); -} - -int NPC::GetScore() -{ - int lv = std::min(70, (int)GetLevel()); - int basedmg = (lv*2)*(1+(lv / 100)) - (lv / 2); - int minx = 0; - int basehp = 0; - int hpcontrib = 0; - int dmgcontrib = 0; - int spccontrib = 0; - int hp = GetMaxHP(); - int mindmg = min_dmg; - int maxdmg = max_dmg; - int final; - - if(lv < 46) - { - minx = static_cast (ceil( ((lv - (lv / 10.0)) - 1.0) )); - basehp = (lv * 10) + (lv * lv); - } - else - { - minx = static_cast (ceil( ((lv - (lv / 10.0)) - 1.0) - (( lv - 45.0 ) / 2.0) )); - basehp = (lv * 10) + ((lv * lv) * 4); - } - - if(hp > basehp) - { - hpcontrib = static_cast (((hp / static_cast (basehp)) * 1.5)); - if(hpcontrib > 5) { hpcontrib = 5; } - - if(maxdmg > basedmg) - { - dmgcontrib = static_cast (ceil( ((maxdmg / basedmg) * 1.5) )); - } - - if(HasNPCSpecialAtk("E")) { spccontrib++; } //Enrage - if(HasNPCSpecialAtk("F")) { spccontrib++; } //Flurry - if(HasNPCSpecialAtk("R")) { spccontrib++; } //Rampage - if(HasNPCSpecialAtk("r")) { spccontrib++; } //Area Rampage - if(HasNPCSpecialAtk("S")) { spccontrib++; } //Summon - if(HasNPCSpecialAtk("T")) { spccontrib += 2; } //Triple - if(HasNPCSpecialAtk("Q")) { spccontrib += 3; } //Quad - if(HasNPCSpecialAtk("U")) { spccontrib += 5; } //Unslowable - if(HasNPCSpecialAtk("L")) { spccontrib++; } //Innate Dual Wield - } - - if(npc_spells_id > 12) - { - if(lv < 16) - spccontrib++; - else - spccontrib += static_cast (floor(lv/15.0)); - } - - final = minx + hpcontrib + dmgcontrib + spccontrib; - final = std::max(1, final); - final = std::min(100, final); - return(final); -} - -uint32 NPC::GetSpawnKillCount() -{ - uint32 sid = GetSpawnPointID(); - - if(sid > 0) - { - return(zone->GetSpawnKillCount(sid)); - } - - return(0); -} - -void NPC::DoQuestPause(Mob *other) { - if(IsMoving() && !IsOnHatelist(other)) { - PauseWandering(RuleI(NPC, SayPauseTimeInSec)); - FaceTarget(other); - } else if(!IsMoving()) { - FaceTarget(other); - } - -} - -void NPC::DepopSwarmPets() -{ - if (GetSwarmInfo()) { - if (GetSwarmInfo()->duration->Check(false)){ - Mob* owner = entity_list.GetMobID(GetSwarmInfo()->owner_id); - if (owner) - owner->SetTempPetCount(owner->GetTempPetCount() - 1); - - Depop(); - return; - } - - //This is only used for optional quest or rule derived behavior now if you force a temp pet on a specific target. - if (GetSwarmInfo()->target) { - Mob *targMob = entity_list.GetMob(GetSwarmInfo()->target); - if(!targMob || (targMob && targMob->IsCorpse())){ - Mob* owner = entity_list.GetMobID(GetSwarmInfo()->owner_id); - if (owner) - owner->SetTempPetCount(owner->GetTempPetCount() - 1); - - Depop(); - return; - } - } - } -} diff --git a/zone/trap.cpp.orig b/zone/trap.cpp.orig deleted file mode 100644 index 23485bf08..000000000 --- a/zone/trap.cpp.orig +++ /dev/null @@ -1,332 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2004 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 -*/ -#include "../common/debug.h" -#include "../common/types.h" -#include "entity.h" -#include "masterentity.h" -#include "../common/spdat.h" -#include "../common/misc_functions.h" -#include "../common/string_util.h" - -/* - -Schema: -CREATE TABLE traps ( - id int(11) NOT NULL auto_increment, - zone varchar(16) NOT NULL default '', - x int(11) NOT NULL default '0', - y int(11) NOT NULL default '0', - z int(11) NOT NULL default '0', - chance tinyint NOT NULL default '0', - maxzdiff float NOT NULL default '0', - radius float NOT NULL default '0', - effect int(11) NOT NULL default '0', - effectvalue int(11) NOT NULL default '0', - effectvalue2 int(11) NOT NULL default '0', - message varcahr(200) NOT NULL; - skill int(11) NOT NULL default '0', - spawnchance int(11) NOT NULL default '0', - PRIMARY KEY (id) -) TYPE=MyISAM; - - -*/ - -Trap::Trap() : - Entity(), - respawn_timer(600000), - chkarea_timer(500), - m_Position(xyz_location::Origin()) -{ - trap_id = 0; - maxzdiff = 0; - radius = 0; - effect = 0; - effectvalue = 0; - effectvalue2 = 0; - skill = 0; - level = 0; - respawn_timer.Disable(); - detected = false; - disarmed = false; - respawn_time = 0; - respawn_var = 0; - hiddenTrigger = nullptr; - ownHiddenTrigger = false; -} - -Trap::~Trap() -{ - //don't need to clean up mob as traps are always cleaned up same time as NPCs - //cleaning up mob here can actually cause a crash via race condition -} - -bool Trap::Process() -{ - if (chkarea_timer.Enabled() && chkarea_timer.Check() - /*&& zone->GetClientCount() > 0*/ ) - { - Mob* trigger = entity_list.GetTrapTrigger(this); - if (trigger && !(trigger->IsClient() && trigger->CastToClient()->GetGM())) - { - Trigger(trigger); - } - } - if (respawn_timer.Enabled() && respawn_timer.Check()) - { - detected = false; - disarmed = false; - chkarea_timer.Enable(); - respawn_timer.Disable(); - } - return true; -} - -void Trap::Trigger(Mob* trigger) -{ - int i = 0; - const NPCType* tmp = 0; - switch (effect) - { - case trapTypeDebuff: - if(message.empty()) - { - entity_list.MessageClose(trigger,false,100,13,"%s triggers a trap!",trigger->GetName()); - } - else - { - entity_list.MessageClose(trigger,false,100,13,"%s",message.c_str()); - } - if(hiddenTrigger){ - hiddenTrigger->SpellFinished(effectvalue, trigger, 10, 0, -1, spells[effectvalue].ResistDiff); - } - break; - case trapTypeAlarm: - if (message.empty()) - { - entity_list.MessageClose(trigger,false,effectvalue,13,"A loud alarm rings out through the air..."); - } - else - { - entity_list.MessageClose(trigger,false,effectvalue,13,"%s",message.c_str()); - } - - entity_list.SendAlarm(this,trigger,effectvalue); - break; - case trapTypeMysticSpawn: - if (message.empty()) - { - entity_list.MessageClose(trigger,false,100,13,"The air shimmers..."); - } - else - { - entity_list.MessageClose(trigger,false,100,13,"%s",message.c_str()); - } - - for (i = 0; i < effectvalue2; i++) - { - if ((tmp = database.GetNPCType(effectvalue))) - { -<<<<<<< HEAD - auto randomOffset = xyz_heading(-5 + MakeRandomInt(0, 10),-5 + MakeRandomInt(0, 10),-5 + MakeRandomInt(0, 10), MakeRandomInt(0, 249)); - auto spawnPosition = randomOffset + m_Position; - NPC* new_npc = new NPC(tmp, nullptr, spawnPosition, FlyMode3); -======= - NPC* new_npc = new NPC(tmp, 0, x-5+zone->random.Int(0, 10), y-5+zone->random.Int(0, 10), z-5+zone->random.Int(0, 10), zone->random.Int(0, 249), FlyMode3); ->>>>>>> master - new_npc->AddLootTable(); - entity_list.AddNPC(new_npc); - new_npc->AddToHateList(trigger,1); - } - } - break; - case trapTypeBanditSpawn: - if (message.empty()) - { - entity_list.MessageClose(trigger,false,100,13,"A bandit leaps out from behind a tree!"); - } - else - { - entity_list.MessageClose(trigger,false,100,13,"%s",message.c_str()); - } - - for (i = 0; i < effectvalue2; i++) - { - if ((tmp = database.GetNPCType(effectvalue))) - { -<<<<<<< HEAD - auto randomOffset = xyz_heading(-2 + MakeRandomInt(0, 5), -2 + MakeRandomInt(0, 5), -2 + MakeRandomInt(0, 5), MakeRandomInt(0, 249)); - auto spawnPosition = randomOffset + m_Position; - NPC* new_npc = new NPC(tmp, nullptr, spawnPosition, FlyMode3); -======= - NPC* new_npc = new NPC(tmp, 0, x-2+zone->random.Int(0, 5), y-2+zone->random.Int(0, 5), z-2+zone->random.Int(0, 5), zone->random.Int(0, 249), FlyMode3); ->>>>>>> master - new_npc->AddLootTable(); - entity_list.AddNPC(new_npc); - new_npc->AddToHateList(trigger,1); - } - } - break; - case trapTypeDamage: - if (message.empty()) - { - entity_list.MessageClose(trigger,false,100,13,"%s triggers a trap!",trigger->GetName()); - } - else - { - entity_list.MessageClose(trigger,false,100,13,"%s",message.c_str()); - } - if(trigger->IsClient()) - { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct)); - CombatDamage_Struct* a = (CombatDamage_Struct*)outapp->pBuffer; - int dmg = zone->random.Int(effectvalue, effectvalue2); - trigger->SetHP(trigger->GetHP() - dmg); - a->damage = dmg; - a->sequence = zone->random.Int(0, 1234567); - a->source = GetHiddenTrigger()!=nullptr ? GetHiddenTrigger()->GetID() : trigger->GetID(); - a->spellid = 0; - a->target = trigger->GetID(); - a->type = 253; - trigger->CastToClient()->QueuePacket(outapp); - safe_delete(outapp); - } - } - respawn_timer.Start((respawn_time + zone->random.Int(0, respawn_var)) * 1000); - chkarea_timer.Disable(); - disarmed = true; -} - -Trap* EntityList::FindNearbyTrap(Mob* searcher, float max_dist) { - float dist = 999999; - Trap* current_trap = nullptr; - - float max_dist2 = max_dist*max_dist; - Trap *cur; - - for (auto it = trap_list.begin(); it != trap_list.end(); ++it) { - cur = it->second; - if(cur->disarmed) - continue; - - auto diff = searcher->GetPosition() - cur->m_Position; - float curdist = diff.m_X * diff.m_X + diff.m_Y * diff.m_Y + diff.m_Z * diff.m_Z; - - if (curdist < max_dist2 && curdist < dist) - { - dist = curdist; - current_trap = cur; - } - } - - return current_trap; -} - -Mob* EntityList::GetTrapTrigger(Trap* trap) { - Mob* savemob = 0; - - float maxdist = trap->radius * trap->radius; - - for (auto it = client_list.begin(); it != client_list.end(); ++it) { - Client* cur = it->second; - - auto diff = cur->GetPosition() - trap->m_Position; - diff.ABS_XYZ(); - - if ((diff.m_X*diff.m_X + diff.m_Y*diff.m_Y) <= maxdist - && diff.m_Z < trap->maxzdiff) - { -<<<<<<< HEAD - if (MakeRandomInt(0,100) < trap->chance) - return cur; -======= - if (zone->random.Roll(trap->chance)) - return(cur); ->>>>>>> master - else - savemob = cur; - } - - } - - return savemob; -} - -//todo: rewrite this to not need direct access to trap members. -bool ZoneDatabase::LoadTraps(const char* zonename, int16 version) { - - std::string query = StringFormat("SELECT id, x, y, z, effect, effectvalue, effectvalue2, skill, " - "maxzdiff, radius, chance, message, respawn_time, respawn_var, level " - "FROM traps WHERE zone='%s' AND version=%u", zonename, version); - auto results = QueryDatabase(query); - if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "Error in LoadTraps query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); - return false; - } - - for (auto row = results.begin(); row != results.end(); ++row) { - Trap* trap = new Trap(); - trap->trap_id = atoi(row[0]); - trap->m_Position = xyz_location(atof(row[1]), atof(row[2]), atof(row[3])); - trap->effect = atoi(row[4]); - trap->effectvalue = atoi(row[5]); - trap->effectvalue2 = atoi(row[6]); - trap->skill = atoi(row[7]); - trap->maxzdiff = atof(row[8]); - trap->radius = atof(row[9]); - trap->chance = atoi(row[10]); - trap->message = row[11]; - trap->respawn_time = atoi(row[12]); - trap->respawn_var = atoi(row[13]); - trap->level = atoi(row[14]); - entity_list.AddTrap(trap); - trap->CreateHiddenTrigger(); - } - - return true; -} - -void Trap::CreateHiddenTrigger() -{ - if(hiddenTrigger) - return; - - const NPCType *base_type = database.GetNPCType(500); - NPCType *make_npc = new NPCType; - memcpy(make_npc, base_type, sizeof(NPCType)); - make_npc->max_hp = 100000; - make_npc->cur_hp = 100000; - strcpy(make_npc->name, "a_trap"); - make_npc->runspeed = 0.0f; - make_npc->bodytype = BT_Special; - make_npc->race = 127; - make_npc->gender = 0; - make_npc->loottable_id = 0; - make_npc->npc_spells_id = 0; - make_npc->d_meele_texture1 = 0; - make_npc->d_meele_texture2 = 0; - make_npc->trackable = 0; - make_npc->level = level; - strcpy(make_npc->special_abilities, "19,1^20,1^24,1^25,1"); - NPC* npca = new NPC(make_npc, nullptr, xyz_heading(m_Position, 0.0f), FlyMode3); - npca->GiveNPCTypeData(make_npc); - entity_list.AddNPC(npca); - - hiddenTrigger = npca; - ownHiddenTrigger = true; -} diff --git a/zone/zonedb.cpp.orig b/zone/zonedb.cpp.orig deleted file mode 100644 index f7c701f1f..000000000 --- a/zone/zonedb.cpp.orig +++ /dev/null @@ -1,3877 +0,0 @@ - -#include "../common/extprofile.h" -#include "../common/item.h" -#include "../common/rulesys.h" -#include "../common/string_util.h" -#include "client.h" -#include "corpse.h" -#include "groups.h" -#include "merc.h" -#include "zone.h" -#include "zonedb.h" -#include -#include - -extern Zone* zone; - -ZoneDatabase database; - -ZoneDatabase::ZoneDatabase() -: SharedDatabase() -{ - ZDBInitVars(); -} - -ZoneDatabase::ZoneDatabase(const char* host, const char* user, const char* passwd, const char* database, uint32 port) -: SharedDatabase(host, user, passwd, database, port) -{ - ZDBInitVars(); -} - -void ZoneDatabase::ZDBInitVars() { - memset(door_isopen_array, 0, sizeof(door_isopen_array)); - npc_spells_maxid = 0; - npc_spellseffects_maxid = 0; - npc_spells_cache = 0; - npc_spellseffects_cache = 0; - npc_spells_loadtried = 0; - npc_spellseffects_loadtried = 0; - max_faction = 0; - faction_array = nullptr; -} - -ZoneDatabase::~ZoneDatabase() { - unsigned int x; - if (npc_spells_cache) { - for (x=0; x<=npc_spells_maxid; x++) { - safe_delete_array(npc_spells_cache[x]); - } - safe_delete_array(npc_spells_cache); - } - safe_delete_array(npc_spells_loadtried); - - if (npc_spellseffects_cache) { - for (x=0; x<=npc_spellseffects_maxid; x++) { - safe_delete_array(npc_spellseffects_cache[x]); - } - safe_delete_array(npc_spellseffects_cache); - } - safe_delete_array(npc_spellseffects_loadtried); - - if (faction_array != nullptr) { - for (x=0; x <= max_faction; x++) { - if (faction_array[x] != 0) - safe_delete(faction_array[x]); - } - safe_delete_array(faction_array); - } -} - -bool ZoneDatabase::SaveZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct* zd) { - - std::string query = StringFormat("UPDATE zone SET underworld = %f, minclip = %f, " - "maxclip = %f, fog_minclip = %f, fog_maxclip = %f, " - "fog_blue = %i, fog_red = %i, fog_green = %i, " - "sky = %i, ztype = %i, zone_exp_multiplier = %f, " - "safe_x = %f, safe_y = %f, safe_z = %f " - "WHERE zoneidnumber = %i AND version = %i", - zd->underworld, zd->minclip, - zd->maxclip, zd->fog_minclip[0], zd->fog_maxclip[0], - zd->fog_blue[0], zd->fog_red[0], zd->fog_green[0], - zd->sky, zd->ztype, zd->zone_exp_multiplier, - zd->safe_x, zd->safe_y, zd->safe_z, - zoneid, instance_id); - auto results = QueryDatabase(query); - if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "Error in SaveZoneCFG query %s: %s", query.c_str(), results.ErrorMessage().c_str()); - return false; - } - - return true; -} - -bool ZoneDatabase::GetZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct *zone_data, bool &can_bind, bool &can_combat, bool &can_levitate, bool &can_castoutdoor, bool &is_city, bool &is_hotzone, bool &allow_mercs, uint8 &zone_type, int &ruleset, char **map_filename) { - - *map_filename = new char[100]; - zone_data->zone_id = zoneid; - - std::string query = StringFormat("SELECT ztype, fog_red, fog_green, fog_blue, fog_minclip, fog_maxclip, " // 5 - "fog_red2, fog_green2, fog_blue2, fog_minclip2, fog_maxclip2, " // 5 - "fog_red3, fog_green3, fog_blue3, fog_minclip3, fog_maxclip3, " // 5 - "fog_red4, fog_green4, fog_blue4, fog_minclip4, fog_maxclip4, " // 5 - "fog_density, sky, zone_exp_multiplier, safe_x, safe_y, safe_z, underworld, " // 7 - "minclip, maxclip, time_type, canbind, cancombat, canlevitate, " // 6 - "castoutdoor, hotzone, ruleset, suspendbuffs, map_file_name, short_name, " // 6 - "rain_chance1, rain_chance2, rain_chance3, rain_chance4, " // 4 - "rain_duration1, rain_duration2, rain_duration3, rain_duration4, " // 4 - "snow_chance1, snow_chance2, snow_chance3, snow_chance4, " // 4 - "snow_duration1, snow_duration2, snow_duration3, snow_duration4 " // 4 - "FROM zone WHERE zoneidnumber = %i AND version = %i", zoneid, instance_id); - auto results = QueryDatabase(query); - if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "Error in GetZoneCFG query %s: %s", query.c_str(), results.ErrorMessage().c_str()); - strcpy(*map_filename, "default"); - return false; - } - - if (results.RowCount() == 0) { - strcpy(*map_filename, "default"); - return false; - } - - auto row = results.begin(); - - memset(zone_data, 0, sizeof(NewZone_Struct)); - zone_data->ztype = atoi(row[0]); - zone_type = zone_data->ztype; - - int index; - for(index = 0; index < 4; index++) { - zone_data->fog_red[index]=atoi(row[1 + index * 5]); - zone_data->fog_green[index]=atoi(row[2 + index * 5]); - zone_data->fog_blue[index]=atoi(row[3 + index * 5]); - zone_data->fog_minclip[index]=atof(row[4 + index * 5]); - zone_data->fog_maxclip[index]=atof(row[5 + index * 5]); - } - - zone_data->fog_density = atof(row[21]); - zone_data->sky=atoi(row[22]); - zone_data->zone_exp_multiplier=atof(row[23]); - zone_data->safe_x=atof(row[24]); - zone_data->safe_y=atof(row[25]); - zone_data->safe_z=atof(row[26]); - zone_data->underworld=atof(row[27]); - zone_data->minclip=atof(row[28]); - zone_data->maxclip=atof(row[29]); - zone_data->time_type=atoi(row[30]); - - //not in the DB yet: - zone_data->gravity = 0.4; - allow_mercs = true; - - int bindable = 0; - bindable = atoi(row[31]); - - can_bind = bindable == 0? false: true; - is_city = bindable == 2? true: false; - can_combat = atoi(row[32]) == 0? false: true; - can_levitate = atoi(row[33]) == 0? false: true; - can_castoutdoor = atoi(row[34]) == 0? false: true; - is_hotzone = atoi(row[35]) == 0? false: true; - - - ruleset = atoi(row[36]); - zone_data->SuspendBuffs = atoi(row[37]); - - char *file = row[38]; - if(file) - strcpy(*map_filename, file); - else - strcpy(*map_filename, row[39]); - - for(index = 0; index < 4; index++) - zone_data->rain_chance[index]=atoi(row[40 + index]); - - for(index = 0; index < 4; index++) - zone_data->rain_duration[index]=atoi(row[44 + index]); - - for(index = 0; index < 4; index++) - zone_data->snow_chance[index]=atoi(row[48 + index]); - - for(index = 0; index < 4; index++) - zone_data->snow_duration[index]=atof(row[52 + index]); - - return true; -} - -//updates or clears the respawn time in the database for the current spawn id -void ZoneDatabase::UpdateSpawn2Timeleft(uint32 id, uint16 instance_id, uint32 timeleft) -{ - timeval tv; - gettimeofday(&tv, nullptr); - uint32 cur = tv.tv_sec; - - //if we pass timeleft as 0 that means we clear from respawn time - //otherwise we update with a REPLACE INTO - if(timeleft == 0) { - std::string query = StringFormat("DELETE FROM respawn_times WHERE id=%lu " - "AND instance_id = %lu",(unsigned long)id, (unsigned long)instance_id); - auto results = QueryDatabase(query); - if (!results.Success()) - LogFile->write(EQEMuLog::Error, "Error in UpdateTimeLeft query %s: %s", query.c_str(), results.ErrorMessage().c_str()); - - return; - } - - std::string query = StringFormat("REPLACE INTO respawn_times (id, start, duration, instance_id) " - "VALUES (%lu, %lu, %lu, %lu)", - (unsigned long)id, (unsigned long)cur, - (unsigned long)timeleft, (unsigned long)instance_id); - auto results = QueryDatabase(query); - if (!results.Success()) - LogFile->write(EQEMuLog::Error, "Error in UpdateTimeLeft query %s: %s", query.c_str(), results.ErrorMessage().c_str()); - - return; -} - -//Gets the respawn time left in the database for the current spawn id -uint32 ZoneDatabase::GetSpawnTimeLeft(uint32 id, uint16 instance_id) -{ - std::string query = StringFormat("SELECT start, duration FROM respawn_times " - "WHERE id = %lu AND instance_id = %lu", - (unsigned long)id, (unsigned long)zone->GetInstanceID()); - auto results = QueryDatabase(query); - if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "Error in GetSpawnTimeLeft query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); - return 0; - } - - if (results.RowCount() != 1) - return 0; - - auto row = results.begin(); - - timeval tv; - gettimeofday(&tv, nullptr); - uint32 resStart = atoi(row[0]); - uint32 resDuration = atoi(row[1]); - - //compare our values to current time - if((resStart + resDuration) <= tv.tv_sec) { - //our current time was expired - return 0; - } - - //we still have time left on this timer - return ((resStart + resDuration) - tv.tv_sec); - -} - -void ZoneDatabase::UpdateSpawn2Status(uint32 id, uint8 new_status) -{ - std::string query = StringFormat("UPDATE spawn2 SET enabled = %i WHERE id = %lu", new_status, (unsigned long)id); - auto results = QueryDatabase(query); - if(!results.Success()) - LogFile->write(EQEMuLog::Error, "Error in UpdateSpawn2Status query %s: %s", query.c_str(), results.ErrorMessage().c_str()); - -} - -bool ZoneDatabase::logevents(const char* accountname,uint32 accountid,uint8 status,const char* charname, const char* target,const char* descriptiontype, const char* description,int event_nid){ - - uint32 len = strlen(description); - uint32 len2 = strlen(target); - char* descriptiontext = new char[2*len+1]; - char* targetarr = new char[2*len2+1]; - memset(descriptiontext, 0, 2*len+1); - memset(targetarr, 0, 2*len2+1); - DoEscapeString(descriptiontext, description, len); - DoEscapeString(targetarr, target, len2); - - std::string query = StringFormat("INSERT INTO eventlog (accountname, accountid, status, " - "charname, target, descriptiontype, description, event_nid) " - "VALUES('%s', %i, %i, '%s', '%s', '%s', '%s', '%i')", - accountname, accountid, status, charname, targetarr, - descriptiontype, descriptiontext, event_nid); - safe_delete_array(descriptiontext); - safe_delete_array(targetarr); - auto results = QueryDatabase(query); - if (!results.Success()) { - std::cerr << "Error in logevents" << query << "' " << results.ErrorMessage() << std::endl; - return false; - } - - return true; -} - - -void ZoneDatabase::UpdateBug(BugStruct* bug) { - - uint32 len = strlen(bug->bug); - char* bugtext = nullptr; - if(len > 0) - { - bugtext = new char[2*len+1]; - memset(bugtext, 0, 2*len+1); - DoEscapeString(bugtext, bug->bug, len); - } - - len = strlen(bug->ui); - char* uitext = nullptr; - if(len > 0) - { - uitext = new char[2*len+1]; - memset(uitext, 0, 2*len+1); - DoEscapeString(uitext, bug->ui, len); - } - - len = strlen(bug->target_name); - char* targettext = nullptr; - if(len > 0) - { - targettext = new char[2*len+1]; - memset(targettext, 0, 2*len+1); - DoEscapeString(targettext, bug->target_name, len); - } - - //x and y are intentionally swapped because eq is inversexy coords - std::string query = StringFormat("INSERT INTO bugs (zone, name, ui, x, y, z, type, flag, target, bug, date) " - "VALUES('%s', '%s', '%s', '%.2f', '%.2f', '%.2f', '%s', %d, '%s', '%s', CURDATE())", - zone->GetShortName(), bug->name, uitext == nullptr ? "": uitext, - bug->x, bug->y, bug->z, bug->chartype, bug->type, targettext == nullptr? "Unknown Target": targettext, - bugtext==nullptr?"":bugtext); - safe_delete_array(bugtext); - safe_delete_array(uitext); - safe_delete_array(targettext); - auto results = QueryDatabase(query); - if (!results.Success()) - std::cerr << "Error in UpdateBug '" << query << "' " << results.ErrorMessage() << std::endl; - -} - -void ZoneDatabase::UpdateBug(PetitionBug_Struct* bug){ - - uint32 len = strlen(bug->text); - char* bugtext = new char[2*len+1]; - memset(bugtext, 0, 2*len+1); - DoEscapeString(bugtext, bug->text, len); - - std::string query = StringFormat("INSERT INTO bugs (type, name, bugtext, flag) " - "VALUES('%s', '%s', '%s', %i)", - "Petition", bug->name, bugtext, 25); - safe_delete_array(bugtext); - auto results = QueryDatabase(query); - if (!results.Success()) - std::cerr << "Error in UpdateBug '" << query << "' " << results.ErrorMessage() << std::endl; - -} - -bool ZoneDatabase::SetSpecialAttkFlag(uint8 id, const char* flag) { - - std::string query = StringFormat("UPDATE npc_types SET npcspecialattks='%s' WHERE id = %i;", flag, id); - auto results = QueryDatabase(query); - if (!results.Success()) - return false; - - return results.RowsAffected() != 0; -} - -bool ZoneDatabase::DoorIsOpen(uint8 door_id,const char* zone_name) -{ - if(door_isopen_array[door_id] == 0) { - SetDoorPlace(1,door_id,zone_name); - return false; - } - else { - SetDoorPlace(0,door_id,zone_name); - return true; - } -} - -void ZoneDatabase::SetDoorPlace(uint8 value,uint8 door_id,const char* zone_name) -{ - door_isopen_array[door_id] = value; -} - -void ZoneDatabase::GetEventLogs(const char* name,char* target,uint32 account_id,uint8 eventid,char* detail,char* timestamp, CharacterEventLog_Struct* cel) -{ - char modifications[200]; - if(strlen(name) != 0) - sprintf(modifications,"charname=\'%s\'",name); - else if(account_id != 0) - sprintf(modifications,"accountid=%i",account_id); - - if(strlen(target) != 0) - sprintf(modifications,"%s AND target LIKE \'%%%s%%\'",modifications,target); - - if(strlen(detail) != 0) - sprintf(modifications,"%s AND description LIKE \'%%%s%%\'",modifications,detail); - - if(strlen(timestamp) != 0) - sprintf(modifications,"%s AND time LIKE \'%%%s%%\'",modifications,timestamp); - - if(eventid == 0) - eventid =1; - sprintf(modifications,"%s AND event_nid=%i",modifications,eventid); - - std::string query = StringFormat("SELECT id, accountname, accountid, status, charname, target, " - "time, descriptiontype, description FROM eventlog WHERE %s", modifications); - auto results = QueryDatabase(query); - if (!results.Success()) - return; - - int index = 0; - for (auto row = results.begin(); row != results.end(); ++row, ++index) { - if(index == 255) - break; - - cel->eld[index].id = atoi(row[0]); - strn0cpy(cel->eld[index].accountname,row[1],64); - cel->eld[index].account_id = atoi(row[2]); - cel->eld[index].status = atoi(row[3]); - strn0cpy(cel->eld[index].charactername,row[4],64); - strn0cpy(cel->eld[index].targetname,row[5],64); - sprintf(cel->eld[index].timestamp,"%s",row[6]); - strn0cpy(cel->eld[index].descriptiontype,row[7],64); - strn0cpy(cel->eld[index].details,row[8],128); - cel->eventid = eventid; - cel->count = index + 1; - } - -} - -// Load child objects for a world container (i.e., forge, bag dropped to ground, etc) -void ZoneDatabase::LoadWorldContainer(uint32 parentid, ItemInst* container) -{ - if (!container) { - LogFile->write(EQEMuLog::Error, "Programming error: LoadWorldContainer passed nullptr pointer"); - return; - } - - std::string query = StringFormat("SELECT bagidx, itemid, charges, augslot1, augslot2, augslot3, augslot4, augslot5 " - "FROM object_contents WHERE parentid = %i", parentid); - auto results = QueryDatabase(query); - if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "Error in DB::LoadWorldContainer: %s", results.ErrorMessage().c_str()); - return; - } - - for (auto row = results.begin(); row != results.end(); ++row) { - uint8 index = (uint8)atoi(row[0]); - uint32 item_id = (uint32)atoi(row[1]); - int8 charges = (int8)atoi(row[2]); - uint32 aug[EmuConstants::ITEM_COMMON_SIZE]; - aug[0] = (uint32)atoi(row[3]); - aug[1] = (uint32)atoi(row[4]); - aug[2] = (uint32)atoi(row[5]); - aug[3] = (uint32)atoi(row[6]); - aug[4] = (uint32)atoi(row[7]); - - ItemInst* inst = database.CreateItem(item_id, charges); - if (inst && inst->GetItem()->ItemClass == ItemClassCommon) { - for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) - if (aug[i]) - inst->PutAugment(&database, i, aug[i]); - // Put item inside world container - container->PutItem(index, *inst); - safe_delete(inst); - } - } - -} - -// Save child objects for a world container (i.e., forge, bag dropped to ground, etc) -void ZoneDatabase::SaveWorldContainer(uint32 zone_id, uint32 parent_id, const ItemInst* container) -{ - // Since state is not saved for each world container action, we'll just delete - // all and save from scratch .. we may come back later to optimize - if (!container) - return; - - //Delete all items from container - DeleteWorldContainer(parent_id,zone_id); - - // Save all 10 items, if they exist - for (uint8 index = SUB_BEGIN; index < EmuConstants::ITEM_CONTAINER_SIZE; index++) { - - ItemInst* inst = container->GetItem(index); - if (!inst) - continue; - - uint32 item_id = inst->GetItem()->ID; - uint32 augslot[EmuConstants::ITEM_COMMON_SIZE] = { NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM }; - - if (inst->IsType(ItemClassCommon)) { - for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) { - ItemInst *auginst=inst->GetAugment(i); - augslot[i]=(auginst && auginst->GetItem()) ? auginst->GetItem()->ID : 0; - } - } - - std::string query = StringFormat("REPLACE INTO object_contents " - "(zoneid, parentid, bagidx, itemid, charges, " - "augslot1, augslot2, augslot3, augslot4, augslot5, droptime) " - "VALUES (%i, %i, %i, %i, %i, %i, %i, %i, %i, %i, now())", - zone_id, parent_id, index, item_id, inst->GetCharges(), - augslot[0], augslot[1], augslot[2], augslot[3], augslot[4]); - auto results = QueryDatabase(query); - if (!results.Success()) - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::SaveWorldContainer: %s", results.ErrorMessage().c_str()); - - } - -} - -// Remove all child objects inside a world container (i.e., forge, bag dropped to ground, etc) -void ZoneDatabase::DeleteWorldContainer(uint32 parent_id, uint32 zone_id) -{ - std::string query = StringFormat("DELETE FROM object_contents WHERE parentid = %i AND zoneid = %i", parent_id, zone_id); - auto results = QueryDatabase(query); - if (!results.Success()) - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::DeleteWorldContainer: %s", results.ErrorMessage().c_str()); - -} - -Trader_Struct* ZoneDatabase::LoadTraderItem(uint32 char_id) -{ - Trader_Struct* loadti = new Trader_Struct; - memset(loadti,0,sizeof(Trader_Struct)); - - std::string query = StringFormat("SELECT * FROM trader WHERE char_id = %i ORDER BY slot_id LIMIT 80", char_id); - auto results = QueryDatabase(query); - if (!results.Success()) { - _log(TRADING__CLIENT, "Failed to load trader information!\n"); - return loadti; - } - - loadti->Code = BazaarTrader_ShowItems; - for (auto row = results.begin(); row != results.end(); ++row) { - if (atoi(row[5]) >= 80 || atoi(row[4]) < 0) { - _log(TRADING__CLIENT, "Bad Slot number when trying to load trader information!\n"); - continue; - } - - loadti->Items[atoi(row[5])] = atoi(row[1]); - loadti->ItemCost[atoi(row[5])] = atoi(row[4]); - } - return loadti; -} - -TraderCharges_Struct* ZoneDatabase::LoadTraderItemWithCharges(uint32 char_id) -{ - TraderCharges_Struct* loadti = new TraderCharges_Struct; - memset(loadti,0,sizeof(TraderCharges_Struct)); - - std::string query = StringFormat("SELECT * FROM trader WHERE char_id=%i ORDER BY slot_id LIMIT 80", char_id); - auto results = QueryDatabase(query); - if (!results.Success()) { - _log(TRADING__CLIENT, "Failed to load trader information!\n"); - return loadti; - } - - for (auto row = results.begin(); row != results.end(); ++row) { - if (atoi(row[5]) >= 80 || atoi(row[5]) < 0) { - _log(TRADING__CLIENT, "Bad Slot number when trying to load trader information!\n"); - continue; - } - - loadti->ItemID[atoi(row[5])] = atoi(row[1]); - loadti->SerialNumber[atoi(row[5])] = atoi(row[2]); - loadti->Charges[atoi(row[5])] = atoi(row[3]); - loadti->ItemCost[atoi(row[5])] = atoi(row[4]); - } - return loadti; -} - -ItemInst* ZoneDatabase::LoadSingleTraderItem(uint32 CharID, int SerialNumber) { - std::string query = StringFormat("SELECT * FROM trader WHERE char_id = %i AND serialnumber = %i " - "ORDER BY slot_id LIMIT 80", CharID, SerialNumber); - auto results = QueryDatabase(query); - if (!results.Success()) - return nullptr; - - if (results.RowCount() == 0) { - _log(TRADING__CLIENT, "Bad result from query\n"); fflush(stdout); - return nullptr; - } - - auto row = results.begin(); - - int ItemID = atoi(row[1]); - int Charges = atoi(row[3]); - int Cost = atoi(row[4]); - - const Item_Struct *item = database.GetItem(ItemID); - - if(!item) { - _log(TRADING__CLIENT, "Unable to create item\n"); - fflush(stdout); - return nullptr; - } - - if (item->NoDrop == 0) - return nullptr; - - ItemInst* inst = database.CreateItem(item); - if(!inst) { - _log(TRADING__CLIENT, "Unable to create item instance\n"); - fflush(stdout); - return nullptr; - } - - inst->SetCharges(Charges); - inst->SetSerialNumber(SerialNumber); - inst->SetMerchantSlot(SerialNumber); - inst->SetPrice(Cost); - - if(inst->IsStackable()) - inst->SetMerchantCount(Charges); - - return inst; -} - -void ZoneDatabase::SaveTraderItem(uint32 CharID, uint32 ItemID, uint32 SerialNumber, int32 Charges, uint32 ItemCost, uint8 Slot){ - - std::string query = StringFormat("REPLACE INTO trader VALUES(%i, %i, %i, %i, %i, %i)", - CharID, ItemID, SerialNumber, Charges, ItemCost, Slot); - auto results = QueryDatabase(query); - if (!results.Success()) - _log(TRADING__CLIENT, "Failed to save trader item: %i for char_id: %i, the error was: %s\n", ItemID, CharID, results.ErrorMessage().c_str()); - -} - -void ZoneDatabase::UpdateTraderItemCharges(int CharID, uint32 SerialNumber, int32 Charges) { - _log(TRADING__CLIENT, "ZoneDatabase::UpdateTraderItemCharges(%i, %i, %i)", CharID, SerialNumber, Charges); - - std::string query = StringFormat("UPDATE trader SET charges = %i WHERE char_id = %i AND serialnumber = %i", - Charges, CharID, SerialNumber); - auto results = QueryDatabase(query); - if (!results.Success()) - _log(TRADING__CLIENT, "Failed to update charges for trader item: %i for char_id: %i, the error was: %s\n", - SerialNumber, CharID, results.ErrorMessage().c_str()); - -} - -void ZoneDatabase::UpdateTraderItemPrice(int CharID, uint32 ItemID, uint32 Charges, uint32 NewPrice) { - - _log(TRADING__CLIENT, "ZoneDatabase::UpdateTraderPrice(%i, %i, %i, %i)", CharID, ItemID, Charges, NewPrice); - - const Item_Struct *item = database.GetItem(ItemID); - - if(!item) - return; - - if(NewPrice == 0) { - _log(TRADING__CLIENT, "Removing Trader items from the DB for CharID %i, ItemID %i", CharID, ItemID); - - std::string query = StringFormat("DELETE FROM trader WHERE char_id = %i AND item_id = %i",CharID, ItemID); - auto results = QueryDatabase(query); - if (!results.Success()) - _log(TRADING__CLIENT, "Failed to remove trader item(s): %i for char_id: %i, the error was: %s\n", ItemID, CharID, results.ErrorMessage().c_str()); - - return; - } - - if(!item->Stackable) { - std::string query = StringFormat("UPDATE trader SET item_cost = %i " - "WHERE char_id = %i AND item_id = %i AND charges=%i", - NewPrice, CharID, ItemID, Charges); - auto results = QueryDatabase(query); - if (!results.Success()) - _log(TRADING__CLIENT, "Failed to update price for trader item: %i for char_id: %i, the error was: %s\n", ItemID, CharID, results.ErrorMessage().c_str()); - - return; - } - - std::string query = StringFormat("UPDATE trader SET item_cost = %i " - "WHERE char_id = %i AND item_id = %i", - NewPrice, CharID, ItemID); - auto results = QueryDatabase(query); - if (!results.Success()) - _log(TRADING__CLIENT, "Failed to update price for trader item: %i for char_id: %i, the error was: %s\n", ItemID, CharID, results.ErrorMessage().c_str()); -} - -void ZoneDatabase::DeleteTraderItem(uint32 char_id){ - - if(char_id==0) { - const std::string query = "DELETE FROM trader"; - auto results = QueryDatabase(query); - if (!results.Success()) - _log(TRADING__CLIENT, "Failed to delete all trader items data, the error was: %s\n", results.ErrorMessage().c_str()); - - return; - } - - std::string query = StringFormat("DELETE FROM trader WHERE char_id = %i", char_id); - auto results = QueryDatabase(query); - if (!results.Success()) - _log(TRADING__CLIENT, "Failed to delete trader item data for char_id: %i, the error was: %s\n", char_id, results.ErrorMessage().c_str()); - -} -void ZoneDatabase::DeleteTraderItem(uint32 CharID,uint16 SlotID) { - - std::string query = StringFormat("DELETE FROM trader WHERE char_id = %i And slot_id = %i", CharID, SlotID); - auto results = QueryDatabase(query); - if (!results.Success()) - _log(TRADING__CLIENT, "Failed to delete trader item data for char_id: %i, the error was: %s\n",CharID, results.ErrorMessage().c_str()); -} - -void ZoneDatabase::DeleteBuyLines(uint32 CharID) { - - if(CharID==0) { - const std::string query = "DELETE FROM buyer"; - auto results = QueryDatabase(query); - if (!results.Success()) - _log(TRADING__CLIENT, "Failed to delete all buyer items data, the error was: %s\n",results.ErrorMessage().c_str()); - - return; - } - - std::string query = StringFormat("DELETE FROM buyer WHERE charid = %i", CharID); - auto results = QueryDatabase(query); - if (!results.Success()) - _log(TRADING__CLIENT, "Failed to delete buyer item data for charid: %i, the error was: %s\n",CharID,results.ErrorMessage().c_str()); - -} - -void ZoneDatabase::AddBuyLine(uint32 CharID, uint32 BuySlot, uint32 ItemID, const char* ItemName, uint32 Quantity, uint32 Price) { - std::string query = StringFormat("REPLACE INTO buyer VALUES(%i, %i, %i, \"%s\", %i, %i)", - CharID, BuySlot, ItemID, ItemName, Quantity, Price); - auto results = QueryDatabase(query); - if (!results.Success()) - _log(TRADING__CLIENT, "Failed to save buline item: %i for char_id: %i, the error was: %s\n", ItemID, CharID, results.ErrorMessage().c_str()); - -} - -void ZoneDatabase::RemoveBuyLine(uint32 CharID, uint32 BuySlot) { - std::string query = StringFormat("DELETE FROM buyer WHERE charid = %i AND buyslot = %i", CharID, BuySlot); - auto results = QueryDatabase(query); - if (!results.Success()) - _log(TRADING__CLIENT, "Failed to delete buyslot %i for charid: %i, the error was: %s\n", BuySlot, CharID, results.ErrorMessage().c_str()); - -} - -void ZoneDatabase::UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity) { - if(Quantity <= 0) { - RemoveBuyLine(CharID, BuySlot); - return; - } - - std::string query = StringFormat("UPDATE buyer SET quantity = %i WHERE charid = %i AND buyslot = %i", Quantity, CharID, BuySlot); - auto results = QueryDatabase(query); - if (!results.Success()) - _log(TRADING__CLIENT, "Failed to update quantity in buyslot %i for charid: %i, the error was: %s\n", BuySlot, CharID, results.ErrorMessage().c_str()); - -} - -#define StructDist(in, f1, f2) (uint32(&in->f2)-uint32(&in->f1)) - -bool ZoneDatabase::LoadCharacterData(uint32 character_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp){ - std::string query = StringFormat( - "SELECT " - "`name`, " - "last_name, " - "gender, " - "race, " - "class, " - "`level`, " - "deity, " - "birthday, " - "last_login, " - "time_played, " - "pvp_status, " - "level2, " - "anon, " - "gm, " - "intoxication, " - "hair_color, " - "beard_color, " - "eye_color_1, " - "eye_color_2, " - "hair_style, " - "beard, " - "ability_time_seconds, " - "ability_number, " - "ability_time_minutes, " - "ability_time_hours, " - "title, " - "suffix, " - "exp, " - "points, " - "mana, " - "cur_hp, " - "str, " - "sta, " - "cha, " - "dex, " - "`int`, " - "agi, " - "wis, " - "face, " - "y, " - "x, " - "z, " - "heading, " - "pvp2, " - "pvp_type, " - "autosplit_enabled, " - "zone_change_count, " - "drakkin_heritage, " - "drakkin_tattoo, " - "drakkin_details, " - "toxicity, " - "hunger_level, " - "thirst_level, " - "ability_up, " - "zone_id, " - "zone_instance, " - "leadership_exp_on, " - "ldon_points_guk, " - "ldon_points_mir, " - "ldon_points_mmc, " - "ldon_points_ruj, " - "ldon_points_tak, " - "ldon_points_available, " - "tribute_time_remaining, " - "show_helm, " - "career_tribute_points, " - "tribute_points, " - "tribute_active, " - "endurance, " - "group_leadership_exp, " - "raid_leadership_exp, " - "group_leadership_points, " - "raid_leadership_points, " - "air_remaining, " - "pvp_kills, " - "pvp_deaths, " - "pvp_current_points, " - "pvp_career_points, " - "pvp_best_kill_streak, " - "pvp_worst_death_streak, " - "pvp_current_kill_streak, " - "aa_points_spent, " - "aa_exp, " - "aa_points, " - "group_auto_consent, " - "raid_auto_consent, " - "guild_auto_consent, " - "RestTimer, " - "`e_aa_effects`, " - "`e_percent_to_aa`, " - "`e_expended_aa_spent` " - "FROM " - "character_data " - "WHERE `id` = %i ", character_id); - auto results = database.QueryDatabase(query); int r = 0; - for (auto row = results.begin(); row != results.end(); ++row) { - strcpy(pp->name, row[r]); r++; // "`name`, " - strcpy(pp->last_name, row[r]); r++; // "last_name, " - pp->gender = atoi(row[r]); r++; // "gender, " - pp->race = atoi(row[r]); r++; // "race, " - pp->class_ = atoi(row[r]); r++; // "class, " - pp->level = atoi(row[r]); r++; // "`level`, " - pp->deity = atoi(row[r]); r++; // "deity, " - pp->birthday = atoi(row[r]); r++; // "birthday, " - pp->lastlogin = atoi(row[r]); r++; // "last_login, " - pp->timePlayedMin = atoi(row[r]); r++; // "time_played, " - pp->pvp = atoi(row[r]); r++; // "pvp_status, " - pp->level2 = atoi(row[r]); r++; // "level2, " - pp->anon = atoi(row[r]); r++; // "anon, " - pp->gm = atoi(row[r]); r++; // "gm, " - pp->intoxication = atoi(row[r]); r++; // "intoxication, " - pp->haircolor = atoi(row[r]); r++; // "hair_color, " - pp->beardcolor = atoi(row[r]); r++; // "beard_color, " - pp->eyecolor1 = atoi(row[r]); r++; // "eye_color_1, " - pp->eyecolor2 = atoi(row[r]); r++; // "eye_color_2, " - pp->hairstyle = atoi(row[r]); r++; // "hair_style, " - pp->beard = atoi(row[r]); r++; // "beard, " - pp->ability_time_seconds = atoi(row[r]); r++; // "ability_time_seconds, " - pp->ability_number = atoi(row[r]); r++; // "ability_number, " - pp->ability_time_minutes = atoi(row[r]); r++; // "ability_time_minutes, " - pp->ability_time_hours = atoi(row[r]); r++; // "ability_time_hours, " - strcpy(pp->title, row[r]); r++; // "title, " - strcpy(pp->suffix, row[r]); r++; // "suffix, " - pp->exp = atoi(row[r]); r++; // "exp, " - pp->points = atoi(row[r]); r++; // "points, " - pp->mana = atoi(row[r]); r++; // "mana, " - pp->cur_hp = atoi(row[r]); r++; // "cur_hp, " - pp->STR = atoi(row[r]); r++; // "str, " - pp->STA = atoi(row[r]); r++; // "sta, " - pp->CHA = atoi(row[r]); r++; // "cha, " - pp->DEX = atoi(row[r]); r++; // "dex, " - pp->INT = atoi(row[r]); r++; // "`int`, " - pp->AGI = atoi(row[r]); r++; // "agi, " - pp->WIS = atoi(row[r]); r++; // "wis, " - pp->face = atoi(row[r]); r++; // "face, " - pp->y = atof(row[r]); r++; // "y, " - pp->x = atof(row[r]); r++; // "x, " - pp->z = atof(row[r]); r++; // "z, " - pp->heading = atof(row[r]); r++; // "heading, " - pp->pvp2 = atoi(row[r]); r++; // "pvp2, " - pp->pvptype = atoi(row[r]); r++; // "pvp_type, " - pp->autosplit = atoi(row[r]); r++; // "autosplit_enabled, " - pp->zone_change_count = atoi(row[r]); r++; // "zone_change_count, " - pp->drakkin_heritage = atoi(row[r]); r++; // "drakkin_heritage, " - pp->drakkin_tattoo = atoi(row[r]); r++; // "drakkin_tattoo, " - pp->drakkin_details = atoi(row[r]); r++; // "drakkin_details, " - pp->toxicity = atoi(row[r]); r++; // "toxicity, " - pp->hunger_level = atoi(row[r]); r++; // "hunger_level, " - pp->thirst_level = atoi(row[r]); r++; // "thirst_level, " - pp->ability_up = atoi(row[r]); r++; // "ability_up, " - pp->zone_id = atoi(row[r]); r++; // "zone_id, " - pp->zoneInstance = atoi(row[r]); r++; // "zone_instance, " - pp->leadAAActive = atoi(row[r]); r++; // "leadership_exp_on, " - pp->ldon_points_guk = atoi(row[r]); r++; // "ldon_points_guk, " - pp->ldon_points_mir = atoi(row[r]); r++; // "ldon_points_mir, " - pp->ldon_points_mmc = atoi(row[r]); r++; // "ldon_points_mmc, " - pp->ldon_points_ruj = atoi(row[r]); r++; // "ldon_points_ruj, " - pp->ldon_points_tak = atoi(row[r]); r++; // "ldon_points_tak, " - pp->ldon_points_available = atoi(row[r]); r++; // "ldon_points_available, " - pp->tribute_time_remaining = atoi(row[r]); r++; // "tribute_time_remaining, " - pp->showhelm = atoi(row[r]); r++; // "show_helm, " - pp->career_tribute_points = atoi(row[r]); r++; // "career_tribute_points, " - pp->tribute_points = atoi(row[r]); r++; // "tribute_points, " - pp->tribute_active = atoi(row[r]); r++; // "tribute_active, " - pp->endurance = atoi(row[r]); r++; // "endurance, " - pp->group_leadership_exp = atoi(row[r]); r++; // "group_leadership_exp, " - pp->raid_leadership_exp = atoi(row[r]); r++; // "raid_leadership_exp, " - pp->group_leadership_points = atoi(row[r]); r++; // "group_leadership_points, " - pp->raid_leadership_points = atoi(row[r]); r++; // "raid_leadership_points, " - pp->air_remaining = atoi(row[r]); r++; // "air_remaining, " - pp->PVPKills = atoi(row[r]); r++; // "pvp_kills, " - pp->PVPDeaths = atoi(row[r]); r++; // "pvp_deaths, " - pp->PVPCurrentPoints = atoi(row[r]); r++; // "pvp_current_points, " - pp->PVPCareerPoints = atoi(row[r]); r++; // "pvp_career_points, " - pp->PVPBestKillStreak = atoi(row[r]); r++; // "pvp_best_kill_streak, " - pp->PVPWorstDeathStreak = atoi(row[r]); r++; // "pvp_worst_death_streak, " - pp->PVPCurrentKillStreak = atoi(row[r]); r++; // "pvp_current_kill_streak, " - pp->aapoints_spent = atoi(row[r]); r++; // "aa_points_spent, " - pp->expAA = atoi(row[r]); r++; // "aa_exp, " - pp->aapoints = atoi(row[r]); r++; // "aa_points, " - pp->groupAutoconsent = atoi(row[r]); r++; // "group_auto_consent, " - pp->raidAutoconsent = atoi(row[r]); r++; // "raid_auto_consent, " - pp->guildAutoconsent = atoi(row[r]); r++; // "guild_auto_consent, " - pp->RestTimer = atoi(row[r]); r++; // "RestTimer, " - m_epp->aa_effects = atoi(row[r]); r++; // "`e_aa_effects`, " - m_epp->perAA = atoi(row[r]); r++; // "`e_percent_to_aa`, " - m_epp->expended_aa = atoi(row[r]); r++; // "`e_expended_aa_spent` " - } - return true; -} - -bool ZoneDatabase::LoadCharacterFactionValues(uint32 character_id, faction_map & val_list) { - std::string query = StringFormat("SELECT `faction_id`, `current_value` FROM `faction_values` WHERE `char_id` = %i", character_id); - auto results = database.QueryDatabase(query); - for (auto row = results.begin(); row != results.end(); ++row) { val_list[atoi(row[0])] = atoi(row[1]); } - return true; -} - -bool ZoneDatabase::LoadCharacterMemmedSpells(uint32 character_id, PlayerProfile_Struct* pp){ - std::string query = StringFormat( - "SELECT " - "slot_id, " - "`spell_id` " - "FROM " - "`character_memmed_spells` " - "WHERE `id` = %u ORDER BY `slot_id`", character_id); - auto results = database.QueryDatabase(query); - int i = 0; - /* Initialize Spells */ - for (i = 0; i < MAX_PP_MEMSPELL; i++){ - pp->mem_spells[i] = 0xFFFFFFFF; - } - for (auto row = results.begin(); row != results.end(); ++row) { - i = atoi(row[0]); - if (i < MAX_PP_MEMSPELL && atoi(row[1]) <= SPDAT_RECORDS){ - pp->mem_spells[i] = atoi(row[1]); - } - } - return true; -} - -bool ZoneDatabase::LoadCharacterSpellBook(uint32 character_id, PlayerProfile_Struct* pp){ - std::string query = StringFormat( - "SELECT " - "slot_id, " - "`spell_id` " - "FROM " - "`character_spells` " - "WHERE `id` = %u ORDER BY `slot_id`", character_id); - auto results = database.QueryDatabase(query); - int i = 0; - /* Initialize Spells */ - for (i = 0; i < MAX_PP_SPELLBOOK; i++){ - pp->spell_book[i] = 0xFFFFFFFF; - } - for (auto row = results.begin(); row != results.end(); ++row) { - i = atoi(row[0]); - if (i < MAX_PP_SPELLBOOK && atoi(row[1]) <= SPDAT_RECORDS){ - pp->spell_book[i] = atoi(row[1]); - } - } - return true; -} - -bool ZoneDatabase::LoadCharacterLanguages(uint32 character_id, PlayerProfile_Struct* pp){ - std::string query = StringFormat( - "SELECT " - "lang_id, " - "`value` " - "FROM " - "`character_languages` " - "WHERE `id` = %u ORDER BY `lang_id`", character_id); - auto results = database.QueryDatabase(query); int i = 0; - /* Initialize Languages */ - 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]); - if (i < MAX_PP_LANGUAGE){ - pp->languages[i] = atoi(row[1]); - } - } - return true; -} - -bool ZoneDatabase::LoadCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp){ - std::string query = StringFormat("SELECT slot, rank FROM character_leadership_abilities WHERE `id` = %u", character_id); - auto results = database.QueryDatabase(query); uint32 slot = 0; - for (auto row = results.begin(); row != results.end(); ++row) { - slot = atoi(row[0]); - pp->leader_abilities.ranks[slot] = atoi(row[1]); - } - return true; -} - -bool ZoneDatabase::LoadCharacterDisciplines(uint32 character_id, PlayerProfile_Struct* pp){ - std::string query = StringFormat( - "SELECT " - "disc_id " - "FROM " - "`character_disciplines`" - "WHERE `id` = %u ORDER BY `slot_id`", character_id); - auto results = database.QueryDatabase(query); - int i = 0; - /* Initialize Disciplines */ - memset(pp->disciplines.values, 0, (sizeof(pp->disciplines.values[0]) * MAX_PP_DISCIPLINES)); - for (auto row = results.begin(); row != results.end(); ++row) { - if (i < MAX_PP_DISCIPLINES){ - pp->disciplines.values[i] = atoi(row[0]); - } - i++; - } - return true; -} - -bool ZoneDatabase::LoadCharacterSkills(uint32 character_id, PlayerProfile_Struct* pp){ - std::string query = StringFormat( - "SELECT " - "skill_id, " - "`value` " - "FROM " - "`character_skills` " - "WHERE `id` = %u ORDER BY `skill_id`", character_id); - 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]); - if (i < MAX_PP_SKILL){ - pp->skills[i] = atoi(row[1]); - } - } - return true; -} - -bool ZoneDatabase::LoadCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp){ - std::string query = StringFormat( - "SELECT " - "platinum, " - "gold, " - "silver, " - "copper, " - "platinum_bank, " - "gold_bank, " - "silver_bank, " - "copper_bank, " - "platinum_cursor, " - "gold_cursor, " - "silver_cursor, " - "copper_cursor, " - "radiant_crystals, " - "career_radiant_crystals," - "ebon_crystals, " - "career_ebon_crystals " - "FROM " - "character_currency " - "WHERE `id` = %i ", character_id); - auto results = database.QueryDatabase(query); - for (auto row = results.begin(); row != results.end(); ++row) { - pp->platinum = atoi(row[0]); - pp->gold = atoi(row[1]); - pp->silver = atoi(row[2]); - pp->copper = atoi(row[3]); - pp->platinum_bank = atoi(row[4]); - pp->gold_bank = atoi(row[5]); - pp->silver_bank = atoi(row[6]); - pp->copper_bank = atoi(row[7]); - pp->platinum_cursor = atoi(row[8]); - pp->gold_cursor = atoi(row[9]); - pp->silver_cursor = atoi(row[10]); - pp->copper_cursor = atoi(row[11]); - pp->currentRadCrystals = atoi(row[12]); - pp->careerRadCrystals = atoi(row[13]); - pp->currentEbonCrystals = atoi(row[14]); - pp->careerEbonCrystals = atoi(row[15]); - } - return true; -} - -bool ZoneDatabase::LoadCharacterMaterialColor(uint32 character_id, PlayerProfile_Struct* pp){ - std::string query = StringFormat("SELECT slot, blue, green, red, use_tint, color FROM `character_material` WHERE `id` = %u LIMIT 9", character_id); - auto results = database.QueryDatabase(query); int i = 0; int r = 0; - for (auto row = results.begin(); row != results.end(); ++row) { - r = 0; - i = atoi(row[r]); /* Slot */ r++; - pp->item_tint[i].rgb.blue = atoi(row[r]); r++; - pp->item_tint[i].rgb.green = atoi(row[r]); r++; - pp->item_tint[i].rgb.red = atoi(row[r]); r++; - pp->item_tint[i].rgb.use_tint = atoi(row[r]); - } - return true; -} - -bool ZoneDatabase::LoadCharacterBandolier(uint32 character_id, PlayerProfile_Struct* pp){ - std::string query = StringFormat("SELECT `bandolier_id`, `bandolier_slot`, `item_id`, `icon`, `bandolier_name` FROM `character_bandolier` WHERE `id` = %u LIMIT 16", character_id); - auto results = database.QueryDatabase(query); int i = 0; int r = 0; int si = 0; - for (i = 0; i <= EmuConstants::BANDOLIERS_COUNT; i++){ - for (int si = 0; si < EmuConstants::BANDOLIER_SIZE; si++){ - pp->bandoliers[i].items[si].icon = 0; - } - } - - for (auto row = results.begin(); row != results.end(); ++row) { - r = 0; - i = atoi(row[r]); /* Bandolier ID */ r++; - si = atoi(row[r]); /* Bandolier Slot */ r++; - pp->bandoliers[i].items[si].item_id = atoi(row[r]); r++; - pp->bandoliers[i].items[si].icon = atoi(row[r]); r++; - strcpy(pp->bandoliers[i].name, row[r]); r++; - si++; - } - return true; -} - -bool ZoneDatabase::LoadCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp){ - std::string query = StringFormat("SELECT `tier`, `tribute` FROM `character_tribute` WHERE `id` = %u", character_id); - auto results = database.QueryDatabase(query); - int i = 0; - for (i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ - pp->tributes[i].tribute = 0xFFFFFFFF; - pp->tributes[i].tier = 0; - } - i = 0; - for (auto row = results.begin(); row != results.end(); ++row) { - if(atoi(row[1]) != TRIBUTE_NONE){ - pp->tributes[i].tier = atoi(row[0]); - pp->tributes[i].tribute = atoi(row[1]); - i++; - } - } - return true; -} - -bool ZoneDatabase::LoadCharacterPotions(uint32 character_id, PlayerProfile_Struct* pp){ - std::string query = StringFormat("SELECT `potion_id`, `item_id`, `icon` FROM `character_potionbelt` WHERE `id` = %u LIMIT 4", character_id); - auto results = database.QueryDatabase(query); int i = 0; - for (i = 0; i < EmuConstants::POTION_BELT_SIZE; i++){ - pp->potionbelt.items[i].icon = 0; - pp->potionbelt.items[i].item_id = 0; - strncpy(pp->potionbelt.items[i].item_name, "\0", 1); - } - for (auto row = results.begin(); row != results.end(); ++row) { - i = atoi(row[0]); /* Potion belt slot number */ - uint32 item_id = atoi(row[1]); - const Item_Struct *item = database.GetItem(item_id); - - if(item) { - pp->potionbelt.items[i].item_id = item_id; - pp->potionbelt.items[i].icon = atoi(row[2]); - strncpy(pp->potionbelt.items[i].item_name, item->Name, 64); - } - } - return true; -} - -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); int i = 0; - for (auto row = results.begin(); row != results.end(); ++row) { - i = 0; - /* Is home bind */ - if (atoi(row[6]) == 1){ - pp->binds[4].zoneId = atoi(row[i++]); - pp->binds[4].instance_id = atoi(row[i++]); - pp->binds[4].x = atoi(row[i++]); - pp->binds[4].y = atoi(row[i++]); - pp->binds[4].z = atoi(row[i++]); - pp->binds[4].heading = atoi(row[i++]); - } - /* Is regular bind point */ - else{ - pp->binds[0].zoneId = atoi(row[i++]); - pp->binds[0].instance_id = atoi(row[i++]); - pp->binds[0].x = atoi(row[i++]); - pp->binds[0].y = atoi(row[i++]); - pp->binds[0].z = atoi(row[i++]); - pp->binds[0].heading = atoi(row[i++]); - } - } - return true; -} - -bool ZoneDatabase::SaveCharacterLanguage(uint32 character_id, uint32 lang_id, uint32 value){ - std::string query = StringFormat("REPLACE INTO `character_languages` (id, lang_id, value) VALUES (%u, %u, %u)", character_id, lang_id, value); QueryDatabase(query); - LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterLanguage for character ID: %i, lang_id:%u value:%u done", character_id, lang_id, value); - return true; -} - -bool ZoneDatabase::SaveCharacterBindPoint(uint32 character_id, uint32 zone_id, uint32 instance_id, const xyz_heading& position, uint8 is_home){ - if (zone_id <= 0) { - return false; - } - - /* Save Home Bind Point */ - std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" - " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", character_id, zone_id, instance_id, position.m_X, position.m_Y, position.m_Z, position.m_Heading, is_home); - LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterBindPoint for character ID: %i zone_id: %u instance_id: %u position: %s ishome: %u", character_id, zone_id, instance_id, to_string(position).c_str(), is_home); - auto results = QueryDatabase(query); - if (!results.RowsAffected()) { - LogFile->write(EQEMuLog::Debug, "ERROR Bind Home Save: %s. %s", results.ErrorMessage().c_str(), query.c_str()); - } - return true; -} - -bool ZoneDatabase::SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color){ - uint8 red = (color & 0x00FF0000) >> 16; - uint8 green = (color & 0x0000FF00) >> 8; - uint8 blue = (color & 0x000000FF); - - std::string query = StringFormat("REPLACE INTO `character_material` (id, slot, red, green, blue, color, use_tint) VALUES (%u, %u, %u, %u, %u, %u, 255)", character_id, slot_id, red, green, blue, color); auto results = QueryDatabase(query); - LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterMaterialColor for character ID: %i, slot_id: %u color: %u done", character_id, slot_id, color); - return true; -} - -bool ZoneDatabase::SaveCharacterSkill(uint32 character_id, uint32 skill_id, uint32 value){ - std::string query = StringFormat("REPLACE INTO `character_skills` (id, skill_id, value) VALUES (%u, %u, %u)", character_id, skill_id, value); auto results = QueryDatabase(query); - LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterSkill for character ID: %i, skill_id:%u value:%u done", character_id, skill_id, value); - return true; -} - -bool ZoneDatabase::SaveCharacterDisc(uint32 character_id, uint32 slot_id, uint32 disc_id){ - std::string query = StringFormat("REPLACE INTO `character_disciplines` (id, slot_id, disc_id) VALUES (%u, %u, %u)", character_id, slot_id, disc_id); - auto results = QueryDatabase(query); - LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterDisc for character ID: %i, slot:%u disc_id:%u done", character_id, slot_id, disc_id); - return true; -} - -bool ZoneDatabase::SaveCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp){ - std::string query = StringFormat("DELETE FROM `character_tribute` WHERE `id` = %u", character_id); - QueryDatabase(query); - /* Save Tributes only if we have values... */ - for (int i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ - if (pp->tributes[i].tribute > 0 && pp->tributes[i].tribute != TRIBUTE_NONE){ - std::string query = StringFormat("REPLACE INTO `character_tribute` (id, tier, tribute) VALUES (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); - QueryDatabase(query); - LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterTribute for character ID: %i, tier:%u tribute:%u done", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); - } - } - return true; -} - -bool ZoneDatabase::SaveCharacterBandolier(uint32 character_id, uint8 bandolier_id, uint8 bandolier_slot, uint32 item_id, uint32 icon, const char* bandolier_name){ - char bandolier_name_esc[64]; - DoEscapeString(bandolier_name_esc, bandolier_name, strlen(bandolier_name)); - std::string query = StringFormat("REPLACE INTO `character_bandolier` (id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name) VALUES (%u, %u, %u, %u, %u,'%s')", character_id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name_esc); - auto results = QueryDatabase(query); - LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterBandolier for character ID: %i, bandolier_id: %u, bandolier_slot: %u item_id: %u, icon:%u band_name:%s done", character_id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name); - if (!results.RowsAffected()){ std::cout << "ERROR Bandolier Save: " << results.ErrorMessage() << "\n\n" << query << "\n" << std::endl; } - return true; -} - -bool ZoneDatabase::SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, uint32 item_id, uint32 icon) { - std::string query = StringFormat("REPLACE INTO `character_potionbelt` (id, potion_id, item_id, icon) VALUES (%u, %u, %u, %u)", character_id, potion_id, item_id, icon); - auto results = QueryDatabase(query); - if (!results.RowsAffected()){ std::cout << "ERROR Potionbelt Save: " << results.ErrorMessage() << "\n\n" << query << "\n" << std::endl; } - return true; -} - -bool ZoneDatabase::SaveCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp){ - uint8 first_entry = 0; std::string query = ""; - for (int i = 0; i < MAX_LEADERSHIP_AA_ARRAY; i++){ - if (pp->leader_abilities.ranks[i] > 0){ - if (first_entry != 1){ - query = StringFormat("REPLACE INTO `character_leadership_abilities` (id, slot, rank) VALUES (%i, %u, %u)", character_id, i, pp->leader_abilities.ranks[i]); - first_entry = 1; - } - query = query + StringFormat(", (%i, %u, %u)", character_id, i, pp->leader_abilities.ranks[i]); - } - } - auto results = QueryDatabase(query); - return true; -} - -bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp){ - clock_t t = std::clock(); /* Function timer start */ - std::string query = StringFormat( - "REPLACE INTO `character_data` (" - " id, " - " account_id, " - " `name`, " - " last_name, " - " gender, " - " race, " - " class, " - " `level`, " - " deity, " - " birthday, " - " last_login, " - " time_played, " - " pvp_status, " - " level2, " - " anon, " - " gm, " - " intoxication, " - " hair_color, " - " beard_color, " - " eye_color_1, " - " eye_color_2, " - " hair_style, " - " beard, " - " ability_time_seconds, " - " ability_number, " - " ability_time_minutes, " - " ability_time_hours, " - " title, " - " suffix, " - " exp, " - " points, " - " mana, " - " cur_hp, " - " str, " - " sta, " - " cha, " - " dex, " - " `int`, " - " agi, " - " wis, " - " face, " - " y, " - " x, " - " z, " - " heading, " - " pvp2, " - " pvp_type, " - " autosplit_enabled, " - " zone_change_count, " - " drakkin_heritage, " - " drakkin_tattoo, " - " drakkin_details, " - " toxicity, " - " hunger_level, " - " thirst_level, " - " ability_up, " - " zone_id, " - " zone_instance, " - " leadership_exp_on, " - " ldon_points_guk, " - " ldon_points_mir, " - " ldon_points_mmc, " - " ldon_points_ruj, " - " ldon_points_tak, " - " ldon_points_available, " - " tribute_time_remaining, " - " show_helm, " - " career_tribute_points, " - " tribute_points, " - " tribute_active, " - " endurance, " - " group_leadership_exp, " - " raid_leadership_exp, " - " group_leadership_points, " - " raid_leadership_points, " - " air_remaining, " - " pvp_kills, " - " pvp_deaths, " - " pvp_current_points, " - " pvp_career_points, " - " pvp_best_kill_streak, " - " pvp_worst_death_streak, " - " pvp_current_kill_streak, " - " aa_points_spent, " - " aa_exp, " - " aa_points, " - " group_auto_consent, " - " raid_auto_consent, " - " guild_auto_consent, " - " RestTimer, " - " e_aa_effects, " - " e_percent_to_aa, " - " e_expended_aa_spent " - ") " - "VALUES (" - "%u," // id " id, " - "%u," // account_id " account_id, " - "'%s'," // `name` pp->name, " `name`, " - "'%s'," // last_name pp->last_name, " last_name, " - "%u," // gender pp->gender, " gender, " - "%u," // race pp->race, " race, " - "%u," // class pp->class_, " class, " - "%u," // `level` pp->level, " `level`, " - "%u," // deity pp->deity, " deity, " - "%u," // birthday pp->birthday, " birthday, " - "%u," // last_login pp->lastlogin, " last_login, " - "%u," // time_played pp->timePlayedMin, " time_played, " - "%u," // pvp_status pp->pvp, " pvp_status, " - "%u," // level2 pp->level2, " level2, " - "%u," // anon pp->anon, " anon, " - "%u," // gm pp->gm, " gm, " - "%u," // intoxication pp->intoxication, " intoxication, " - "%u," // hair_color pp->haircolor, " hair_color, " - "%u," // beard_color pp->beardcolor, " beard_color, " - "%u," // eye_color_1 pp->eyecolor1, " eye_color_1, " - "%u," // eye_color_2 pp->eyecolor2, " eye_color_2, " - "%u," // hair_style pp->hairstyle, " hair_style, " - "%u," // beard pp->beard, " beard, " - "%u," // ability_time_seconds pp->ability_time_seconds, " ability_time_seconds, " - "%u," // ability_number pp->ability_number, " ability_number, " - "%u," // ability_time_minutes pp->ability_time_minutes, " ability_time_minutes, " - "%u," // ability_time_hours pp->ability_time_hours, " ability_time_hours, " - "'%s'," // title pp->title, " title, " " - "'%s'," // suffix pp->suffix, " suffix, " - "%u," // exp pp->exp, " exp, " - "%u," // points pp->points, " points, " - "%u," // mana pp->mana, " mana, " - "%u," // cur_hp pp->cur_hp, " cur_hp, " - "%u," // str pp->STR, " str, " - "%u," // sta pp->STA, " sta, " - "%u," // cha pp->CHA, " cha, " - "%u," // dex pp->DEX, " dex, " - "%u," // `int` pp->INT, " `int`, " - "%u," // agi pp->AGI, " agi, " - "%u," // wis pp->WIS, " wis, " - "%u," // face pp->face, " face, " - "%f," // y pp->y, " y, " - "%f," // x pp->x, " x, " - "%f," // z pp->z, " z, " - "%f," // heading pp->heading, " heading, " - "%u," // pvp2 pp->pvp2, " pvp2, " - "%u," // pvp_type pp->pvptype, " pvp_type, " - "%u," // autosplit_enabled pp->autosplit, " autosplit_enabled, " - "%u," // zone_change_count pp->zone_change_count, " zone_change_count, " - "%u," // drakkin_heritage pp->drakkin_heritage, " drakkin_heritage, " - "%u," // drakkin_tattoo pp->drakkin_tattoo, " drakkin_tattoo, " - "%u," // drakkin_details pp->drakkin_details, " drakkin_details, " - "%i," // toxicity pp->toxicity, " toxicity, " - "%i," // hunger_level pp->hunger_level, " hunger_level, " - "%i," // thirst_level pp->thirst_level, " thirst_level, " - "%u," // ability_up pp->ability_up, " ability_up, " - "%u," // zone_id pp->zone_id, " zone_id, " - "%u," // zone_instance pp->zoneInstance, " zone_instance, " - "%u," // leadership_exp_on pp->leadAAActive, " leadership_exp_on, " - "%u," // ldon_points_guk pp->ldon_points_guk, " ldon_points_guk, " - "%u," // ldon_points_mir pp->ldon_points_mir, " ldon_points_mir, " - "%u," // ldon_points_mmc pp->ldon_points_mmc, " ldon_points_mmc, " - "%u," // ldon_points_ruj pp->ldon_points_ruj, " ldon_points_ruj, " - "%u," // ldon_points_tak pp->ldon_points_tak, " ldon_points_tak, " - "%u," // ldon_points_available pp->ldon_points_available, " ldon_points_available, " - "%u," // tribute_time_remaining pp->tribute_time_remaining, " tribute_time_remaining, " - "%u," // show_helm pp->showhelm, " show_helm, " - "%u," // career_tribute_points pp->career_tribute_points, " career_tribute_points, " - "%u," // tribute_points pp->tribute_points, " tribute_points, " - "%u," // tribute_active pp->tribute_active, " tribute_active, " - "%u," // endurance pp->endurance, " endurance, " - "%u," // group_leadership_exp pp->group_leadership_exp, " group_leadership_exp, " - "%u," // raid_leadership_exp pp->raid_leadership_exp, " raid_leadership_exp, " - "%u," // group_leadership_points pp->group_leadership_points, " group_leadership_points, " - "%u," // raid_leadership_points pp->raid_leadership_points, " raid_leadership_points, " - "%u," // air_remaining pp->air_remaining, " air_remaining, " - "%u," // pvp_kills pp->PVPKills, " pvp_kills, " - "%u," // pvp_deaths pp->PVPDeaths, " pvp_deaths, " - "%u," // pvp_current_points pp->PVPCurrentPoints, " pvp_current_points, " - "%u," // pvp_career_points pp->PVPCareerPoints, " pvp_career_points, " - "%u," // pvp_best_kill_streak pp->PVPBestKillStreak, " pvp_best_kill_streak, " - "%u," // pvp_worst_death_streak pp->PVPWorstDeathStreak, " pvp_worst_death_streak, " - "%u," // pvp_current_kill_streak pp->PVPCurrentKillStreak, " pvp_current_kill_streak, " - "%u," // aa_points_spent pp->aapoints_spent, " aa_points_spent, " - "%u," // aa_exp pp->expAA, " aa_exp, " - "%u," // aa_points pp->aapoints, " aa_points, " - "%u," // group_auto_consent pp->groupAutoconsent, " group_auto_consent, " - "%u," // raid_auto_consent pp->raidAutoconsent, " raid_auto_consent, " - "%u," // guild_auto_consent pp->guildAutoconsent, " guild_auto_consent, " - "%u," // RestTimer pp->RestTimer, " RestTimer) " - "%u," // e_aa_effects - "%u," // e_percent_to_aa - "%u" // e_expended_aa_spent - ")", - character_id, // " id, " - account_id, // " account_id, " - EscapeString(pp->name).c_str(), // " `name`, " - EscapeString(pp->last_name).c_str(), // " last_name, " - pp->gender, // " gender, " - pp->race, // " race, " - pp->class_, // " class, " - pp->level, // " `level`, " - pp->deity, // " deity, " - pp->birthday, // " birthday, " - pp->lastlogin, // " last_login, " - pp->timePlayedMin, // " time_played, " - pp->pvp, // " pvp_status, " - pp->level2, // " level2, " - pp->anon, // " anon, " - pp->gm, // " gm, " - pp->intoxication, // " intoxication, " - pp->haircolor, // " hair_color, " - pp->beardcolor, // " beard_color, " - pp->eyecolor1, // " eye_color_1, " - pp->eyecolor2, // " eye_color_2, " - pp->hairstyle, // " hair_style, " - pp->beard, // " beard, " - pp->ability_time_seconds, // " ability_time_seconds, " - pp->ability_number, // " ability_number, " - pp->ability_time_minutes, // " ability_time_minutes, " - pp->ability_time_hours, // " ability_time_hours, " - EscapeString(pp->title).c_str(), // " title, " - EscapeString(pp->suffix).c_str(), // " suffix, " - pp->exp, // " exp, " - pp->points, // " points, " - pp->mana, // " mana, " - pp->cur_hp, // " cur_hp, " - pp->STR, // " str, " - pp->STA, // " sta, " - pp->CHA, // " cha, " - pp->DEX, // " dex, " - pp->INT, // " `int`, " - pp->AGI, // " agi, " - pp->WIS, // " wis, " - pp->face, // " face, " - pp->y, // " y, " - pp->x, // " x, " - pp->z, // " z, " - pp->heading, // " heading, " - pp->pvp2, // " pvp2, " - pp->pvptype, // " pvp_type, " - pp->autosplit, // " autosplit_enabled, " - pp->zone_change_count, // " zone_change_count, " - pp->drakkin_heritage, // " drakkin_heritage, " - pp->drakkin_tattoo, // " drakkin_tattoo, " - pp->drakkin_details, // " drakkin_details, " - pp->toxicity, // " toxicity, " - pp->hunger_level, // " hunger_level, " - pp->thirst_level, // " thirst_level, " - pp->ability_up, // " ability_up, " - pp->zone_id, // " zone_id, " - pp->zoneInstance, // " zone_instance, " - pp->leadAAActive, // " leadership_exp_on, " - pp->ldon_points_guk, // " ldon_points_guk, " - pp->ldon_points_mir, // " ldon_points_mir, " - pp->ldon_points_mmc, // " ldon_points_mmc, " - pp->ldon_points_ruj, // " ldon_points_ruj, " - pp->ldon_points_tak, // " ldon_points_tak, " - pp->ldon_points_available, // " ldon_points_available, " - pp->tribute_time_remaining, // " tribute_time_remaining, " - pp->showhelm, // " show_helm, " - pp->career_tribute_points, // " career_tribute_points, " - pp->tribute_points, // " tribute_points, " - pp->tribute_active, // " tribute_active, " - pp->endurance, // " endurance, " - pp->group_leadership_exp, // " group_leadership_exp, " - pp->raid_leadership_exp, // " raid_leadership_exp, " - pp->group_leadership_points, // " group_leadership_points, " - pp->raid_leadership_points, // " raid_leadership_points, " - pp->air_remaining, // " air_remaining, " - pp->PVPKills, // " pvp_kills, " - pp->PVPDeaths, // " pvp_deaths, " - pp->PVPCurrentPoints, // " pvp_current_points, " - pp->PVPCareerPoints, // " pvp_career_points, " - pp->PVPBestKillStreak, // " pvp_best_kill_streak, " - pp->PVPWorstDeathStreak, // " pvp_worst_death_streak, " - pp->PVPCurrentKillStreak, // " pvp_current_kill_streak, " - pp->aapoints_spent, // " aa_points_spent, " - pp->expAA, // " aa_exp, " - pp->aapoints, // " aa_points, " - pp->groupAutoconsent, // " group_auto_consent, " - pp->raidAutoconsent, // " raid_auto_consent, " - pp->guildAutoconsent, // " guild_auto_consent, " - pp->RestTimer, // " RestTimer) " - m_epp->aa_effects, - m_epp->perAA, - m_epp->expended_aa - ); - auto results = database.QueryDatabase(query); - LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterData %i, done... Took %f seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); - return true; -} - -bool ZoneDatabase::SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp){ - if (pp->copper < 0) { pp->copper = 0; } - if (pp->silver < 0) { pp->silver = 0; } - if (pp->gold < 0) { pp->gold = 0; } - if (pp->platinum < 0) { pp->platinum = 0; } - if (pp->copper_bank < 0) { pp->copper_bank = 0; } - if (pp->silver_bank < 0) { pp->silver_bank = 0; } - if (pp->gold_bank < 0) { pp->gold_bank = 0; } - if (pp->platinum_bank < 0) { pp->platinum_bank = 0; } - if (pp->platinum_cursor < 0) { pp->platinum_cursor = 0; } - if (pp->gold_cursor < 0) { pp->gold_cursor = 0; } - if (pp->silver_cursor < 0) { pp->silver_cursor = 0; } - if (pp->copper_cursor < 0) { pp->copper_cursor = 0; } - std::string query = StringFormat( - "REPLACE INTO `character_currency` (id, platinum, gold, silver, copper," - "platinum_bank, gold_bank, silver_bank, copper_bank," - "platinum_cursor, gold_cursor, silver_cursor, copper_cursor, " - "radiant_crystals, career_radiant_crystals, ebon_crystals, career_ebon_crystals)" - "VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u)", - character_id, - pp->platinum, - pp->gold, - pp->silver, - pp->copper, - pp->platinum_bank, - pp->gold_bank, - pp->silver_bank, - pp->copper_bank, - pp->platinum_cursor, - pp->gold_cursor, - pp->silver_cursor, - pp->copper_cursor, - pp->currentRadCrystals, - pp->careerRadCrystals, - pp->currentEbonCrystals, - pp->careerEbonCrystals); - auto results = database.QueryDatabase(query); - LogFile->write(EQEMuLog::Debug, "Saving Currency for character ID: %i, done", character_id); - 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); - auto results = QueryDatabase(rquery); - LogFile->write(EQEMuLog::Debug, "Saving AA for character ID: %u, aa_id: %u current_level: %u", character_id, aa_id, current_level); - return true; -} - -bool ZoneDatabase::SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ - if (spell_id > SPDAT_RECORDS){ return false; } - std::string query = StringFormat("REPLACE INTO `character_memmed_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, slot_id, spell_id); - QueryDatabase(query); - return true; -} - -bool ZoneDatabase::SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ - if (spell_id > SPDAT_RECORDS){ return false; } - std::string query = StringFormat("REPLACE INTO `character_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, slot_id, spell_id); - QueryDatabase(query); - return true; -} - -bool ZoneDatabase::DeleteCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ - std::string query = StringFormat("DELETE FROM `character_spells` WHERE `slot_id` = %u AND `id` = %u", slot_id, character_id); - QueryDatabase(query); - return true; -} - -bool ZoneDatabase::DeleteCharacterDisc(uint32 character_id, uint32 slot_id){ - std::string query = StringFormat("DELETE FROM `character_disciplines` WHERE `slot_id` = %u AND `id` = %u", slot_id, character_id); - QueryDatabase(query); - return true; -} - -bool ZoneDatabase::DeleteCharacterBandolier(uint32 character_id, uint32 band_id){ - std::string query = StringFormat("DELETE FROM `character_bandolier` WHERE `bandolier_id` = %u AND `id` = %u", band_id, character_id); - QueryDatabase(query); - return true; -} - -bool ZoneDatabase::DeleteCharacterLeadershipAAs(uint32 character_id){ - std::string query = StringFormat("DELETE FROM `character_leadership_abilities` WHERE `id` = %u", character_id); - QueryDatabase(query); - return true; -} - -bool ZoneDatabase::DeleteCharacterAAs(uint32 character_id){ - std::string query = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u", character_id); - QueryDatabase(query); - return true; -} - -bool ZoneDatabase::DeleteCharacterDye(uint32 character_id){ - std::string query = StringFormat("DELETE FROM `character_material` WHERE `id` = %u", character_id); - QueryDatabase(query); - return true; -} - -bool ZoneDatabase::DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ - std::string query = StringFormat("DELETE FROM `character_memmed_spells` WHERE `slot_id` = %u AND `id` = %u", slot_id, character_id); - QueryDatabase(query); - return true; -} - -bool ZoneDatabase::NoRentExpired(const char* name){ - std::string query = StringFormat("SELECT (UNIX_TIMESTAMP(NOW()) - last_login) FROM `character_data` WHERE name = '%s'", name); - auto results = QueryDatabase(query); - if (!results.Success()) - return false; - - if (results.RowCount() != 1) - return false; - - auto row = results.begin(); - uint32 seconds = atoi(row[0]); - - return (seconds>1800); -} - -/* Searches npctable for matching id, and returns the item if found, - * or nullptr otherwise. If id passed is 0, loads all npc_types for - * the current zone, returning the last item added. - */ -const NPCType* ZoneDatabase::GetNPCType (uint32 id) { - const NPCType *npc=nullptr; - - // If NPC is already in tree, return it. - auto itr = zone->npctable.find(id); - if(itr != zone->npctable.end()) - return itr->second; - - // Otherwise, get NPCs from database. - - - // If id is 0, load all npc_types for the current zone, - // according to spawn2. - std::string query = StringFormat("SELECT npc_types.id, npc_types.name, npc_types.level, npc_types.race, " - "npc_types.class, npc_types.hp, npc_types.mana, npc_types.gender, " - "npc_types.texture, npc_types.helmtexture, npc_types.size, " - "npc_types.loottable_id, npc_types.merchant_id, npc_types.alt_currency_id, " - "npc_types.adventure_template_id, npc_types.trap_template, npc_types.attack_speed, " - "npc_types.STR, npc_types.STA, npc_types.DEX, npc_types.AGI, npc_types._INT, " - "npc_types.WIS, npc_types.CHA, npc_types.MR, npc_types.CR, npc_types.DR, " - "npc_types.FR, npc_types.PR, npc_types.Corrup, npc_types.PhR," - "npc_types.mindmg, npc_types.maxdmg, npc_types.attack_count, npc_types.special_abilities," - "npc_types.npc_spells_id, npc_types.npc_spells_effects_id, npc_types.d_meele_texture1," - "npc_types.d_meele_texture2, npc_types.ammo_idfile, npc_types.prim_melee_type," - "npc_types.sec_melee_type, npc_types.ranged_type, npc_types.runspeed, npc_types.findable," - "npc_types.trackable, npc_types.hp_regen_rate, npc_types.mana_regen_rate, " - "npc_types.aggroradius, npc_types.assistradius, npc_types.bodytype, npc_types.npc_faction_id, " - "npc_types.face, npc_types.luclin_hairstyle, npc_types.luclin_haircolor, " - "npc_types.luclin_eyecolor, npc_types.luclin_eyecolor2, npc_types.luclin_beardcolor," - "npc_types.luclin_beard, npc_types.drakkin_heritage, npc_types.drakkin_tattoo, " - "npc_types.drakkin_details, npc_types.armortint_id, " - "npc_types.armortint_red, npc_types.armortint_green, npc_types.armortint_blue, " - "npc_types.see_invis, npc_types.see_invis_undead, npc_types.lastname, " - "npc_types.qglobal, npc_types.AC, npc_types.npc_aggro, npc_types.spawn_limit, " - "npc_types.see_hide, npc_types.see_improved_hide, npc_types.ATK, npc_types.Accuracy, " - "npc_types.Avoidance, npc_types.slow_mitigation, npc_types.maxlevel, npc_types.scalerate, " - "npc_types.private_corpse, npc_types.unique_spawn_by_name, npc_types.underwater, " - "npc_types.emoteid, npc_types.spellscale, npc_types.healscale, npc_types.no_target_hotkey," - "npc_types.raid_target, npc_types.attack_delay FROM npc_types WHERE id = %d", id); - - auto results = QueryDatabase(query); - if (!results.Success()) { - std::cerr << "Error loading NPCs from database. Bad query: " << results.ErrorMessage() << std::endl; - return nullptr; - } - - - for (auto row = results.begin(); row != results.end(); ++row) { - NPCType *tmpNPCType; - tmpNPCType = new NPCType; - memset (tmpNPCType, 0, sizeof *tmpNPCType); - - tmpNPCType->npc_id = atoi(row[0]); - - strn0cpy(tmpNPCType->name, row[1], 50); - - tmpNPCType->level = atoi(row[2]); - tmpNPCType->race = atoi(row[3]); - tmpNPCType->class_ = atoi(row[4]); - tmpNPCType->max_hp = atoi(row[5]); - tmpNPCType->cur_hp = tmpNPCType->max_hp; - tmpNPCType->Mana = atoi(row[6]); - tmpNPCType->gender = atoi(row[7]); - tmpNPCType->texture = atoi(row[8]); - tmpNPCType->helmtexture = atoi(row[9]); - tmpNPCType->size = atof(row[10]); - tmpNPCType->loottable_id = atoi(row[11]); - tmpNPCType->merchanttype = atoi(row[12]); - tmpNPCType->alt_currency_type = atoi(row[13]); - tmpNPCType->adventure_template = atoi(row[14]); - tmpNPCType->trap_template = atoi(row[15]); - tmpNPCType->attack_speed = atof(row[16]); - tmpNPCType->STR = atoi(row[17]); - tmpNPCType->STA = atoi(row[18]); - tmpNPCType->DEX = atoi(row[19]); - tmpNPCType->AGI = atoi(row[20]); - tmpNPCType->INT = atoi(row[21]); - tmpNPCType->WIS = atoi(row[22]); - tmpNPCType->CHA = atoi(row[23]); - tmpNPCType->MR = atoi(row[24]); - tmpNPCType->CR = atoi(row[25]); - tmpNPCType->DR = atoi(row[26]); - tmpNPCType->FR = atoi(row[27]); - tmpNPCType->PR = atoi(row[28]); - tmpNPCType->Corrup = atoi(row[29]); - tmpNPCType->PhR = atoi(row[30]); - tmpNPCType->min_dmg = atoi(row[31]); - tmpNPCType->max_dmg = atoi(row[32]); - tmpNPCType->attack_count = atoi(row[33]); - - if (row[34] != nullptr) - strn0cpy(tmpNPCType->special_abilities, row[34], 512); - else - tmpNPCType->special_abilities[0] = '\0'; - - tmpNPCType->npc_spells_id = atoi(row[35]); - tmpNPCType->npc_spells_effects_id = atoi(row[36]); - tmpNPCType->d_meele_texture1 = atoi(row[37]); - tmpNPCType->d_meele_texture2 = atoi(row[38]); - strn0cpy(tmpNPCType->ammo_idfile, row[39], 30); - tmpNPCType->prim_melee_type = atoi(row[40]); - tmpNPCType->sec_melee_type = atoi(row[41]); - tmpNPCType->ranged_type = atoi(row[42]); - tmpNPCType->runspeed= atof(row[43]); - tmpNPCType->findable = atoi(row[44]) == 0? false : true; - tmpNPCType->trackable = atoi(row[45]) == 0? false : true; - tmpNPCType->hp_regen = atoi(row[46]); - tmpNPCType->mana_regen = atoi(row[47]); - - // set defaultvalue for aggroradius - tmpNPCType->aggroradius = (int32)atoi(row[48]); - if (tmpNPCType->aggroradius <= 0) - tmpNPCType->aggroradius = 70; - - tmpNPCType->assistradius = (int32)atoi(row[49]); - if (tmpNPCType->assistradius <= 0) - tmpNPCType->assistradius = tmpNPCType->aggroradius; - - if (row[50] && strlen(row[50])) - tmpNPCType->bodytype = (uint8)atoi(row[50]); - else - tmpNPCType->bodytype = 0; - - tmpNPCType->npc_faction_id = atoi(row[51]); - - tmpNPCType->luclinface = atoi(row[52]); - tmpNPCType->hairstyle = atoi(row[53]); - tmpNPCType->haircolor = atoi(row[54]); - tmpNPCType->eyecolor1 = atoi(row[55]); - tmpNPCType->eyecolor2 = atoi(row[56]); - tmpNPCType->beardcolor = atoi(row[57]); - tmpNPCType->beard = atoi(row[58]); - tmpNPCType->drakkin_heritage = atoi(row[59]); - tmpNPCType->drakkin_tattoo = atoi(row[60]); - tmpNPCType->drakkin_details = atoi(row[61]); - - uint32 armor_tint_id = atoi(row[62]); - - tmpNPCType->armor_tint[0] = (atoi(row[63]) & 0xFF) << 16; - tmpNPCType->armor_tint[0] |= (atoi(row[64]) & 0xFF) << 8; - tmpNPCType->armor_tint[0] |= (atoi(row[65]) & 0xFF); - tmpNPCType->armor_tint[0] |= (tmpNPCType->armor_tint[0]) ? (0xFF << 24) : 0; - - if (armor_tint_id == 0) - for (int index = MaterialChest; index <= EmuConstants::MATERIAL_END; index++) - tmpNPCType->armor_tint[index] = tmpNPCType->armor_tint[0]; - else if (tmpNPCType->armor_tint[0] == 0) - { - std::string armortint_query = StringFormat("SELECT red1h, grn1h, blu1h, " - "red2c, grn2c, blu2c, " - "red3a, grn3a, blu3a, " - "red4b, grn4b, blu4b, " - "red5g, grn5g, blu5g, " - "red6l, grn6l, blu6l, " - "red7f, grn7f, blu7f, " - "red8x, grn8x, blu8x, " - "red9x, grn9x, blu9x " - "FROM npc_types_tint WHERE id = %d", - armor_tint_id); - auto armortint_results = QueryDatabase(armortint_query); - if (!armortint_results.Success() || armortint_results.RowCount() == 0) - armor_tint_id = 0; - else { - auto armorTint_row = armortint_results.begin(); - - for (int index = EmuConstants::MATERIAL_BEGIN; index <= EmuConstants::MATERIAL_END; index++) { - tmpNPCType->armor_tint[index] = atoi(armorTint_row[index * 3]) << 16; - tmpNPCType->armor_tint[index] |= atoi(armorTint_row[index * 3 + 1]) << 8; - tmpNPCType->armor_tint[index] |= atoi(armorTint_row[index * 3 + 2]); - tmpNPCType->armor_tint[index] |= (tmpNPCType->armor_tint[index]) ? (0xFF << 24) : 0; - } - } - } else - armor_tint_id = 0; - - tmpNPCType->see_invis = atoi(row[66]); - tmpNPCType->see_invis_undead = atoi(row[67]) == 0? false: true; // Set see_invis_undead flag - if (row[68] != nullptr) - strn0cpy(tmpNPCType->lastname, row[68], 32); - - tmpNPCType->qglobal = atoi(row[69]) == 0? false: true; // qglobal - tmpNPCType->AC = atoi(row[70]); - tmpNPCType->npc_aggro = atoi(row[71]) == 0? false: true; - tmpNPCType->spawn_limit = atoi(row[72]); - tmpNPCType->see_hide = atoi(row[73]) == 0? false: true; - tmpNPCType->see_improved_hide = atoi(row[74]) == 0? false: true; - tmpNPCType->ATK = atoi(row[75]); - tmpNPCType->accuracy_rating = atoi(row[76]); - tmpNPCType->avoidance_rating = atoi(row[77]); - tmpNPCType->slow_mitigation = atoi(row[78]); - tmpNPCType->maxlevel = atoi(row[79]); - tmpNPCType->scalerate = atoi(row[80]); - tmpNPCType->private_corpse = atoi(row[81]) == 1 ? true: false; - tmpNPCType->unique_spawn_by_name = atoi(row[82]) == 1 ? true: false; - tmpNPCType->underwater = atoi(row[83]) == 1 ? true: false; - tmpNPCType->emoteid = atoi(row[84]); - tmpNPCType->spellscale = atoi(row[85]); - tmpNPCType->healscale = atoi(row[86]); - tmpNPCType->no_target_hotkey = atoi(row[87]) == 1 ? true: false; - tmpNPCType->raid_target = atoi(row[88]) == 0 ? false: true; - tmpNPCType->attack_delay = atoi(row[89]); - - // If NPC with duplicate NPC id already in table, - // free item we attempted to add. - if (zone->npctable.find(tmpNPCType->npc_id) != zone->npctable.end()) { - std::cerr << "Error loading duplicate NPC " << tmpNPCType->npc_id << std::endl; - delete tmpNPCType; - return nullptr; - } - - zone->npctable[tmpNPCType->npc_id]=tmpNPCType; - npc = tmpNPCType; - } - - return npc; -} - -const NPCType* ZoneDatabase::GetMercType(uint32 id, uint16 raceid, uint32 clientlevel) { - - //need to save based on merc_npc_type & client level - uint32 merc_type_id = id * 100 + clientlevel; - - // If NPC is already in tree, return it. - auto itr = zone->merctable.find(merc_type_id); - if(itr != zone->merctable.end()) - return itr->second; - - //If the NPC type is 0, return nullptr. (sanity check) - if(id == 0) - return nullptr; - - // Otherwise, get NPCs from database. - // If id is 0, load all npc_types for the current zone, - // according to spawn2. - std::string query = StringFormat("SELECT vwMercNpcTypes.merc_npc_type_id, vwMercNpcTypes.name, " - "vwMercNpcTypes.level, vwMercNpcTypes.race_id, vwMercNpcTypes.class_id, " - "vwMercNpcTypes.hp, vwMercNpcTypes.mana, vwMercNpcTypes.gender, " - "vwMercNpcTypes.texture, vwMercNpcTypes.helmtexture, vwMercNpcTypes.attack_speed, " - "vwMercNpcTypes.STR, vwMercNpcTypes.STA, vwMercNpcTypes.DEX, vwMercNpcTypes.AGI, " - "vwMercNpcTypes._INT, vwMercNpcTypes.WIS, vwMercNpcTypes.CHA, vwMercNpcTypes.MR, " - "vwMercNpcTypes.CR, vwMercNpcTypes.DR, vwMercNpcTypes.FR, vwMercNpcTypes.PR, " - "vwMercNpcTypes.Corrup, vwMercNpcTypes.mindmg, vwMercNpcTypes.maxdmg, " - "vwMercNpcTypes.attack_count, vwMercNpcTypes.special_abilities, " - "vwMercNpcTypes.d_meele_texture1, vwMercNpcTypes.d_meele_texture2, " - "vwMercNpcTypes.prim_melee_type, vwMercNpcTypes.sec_melee_type, " - "vwMercNpcTypes.runspeed, vwMercNpcTypes.hp_regen_rate, vwMercNpcTypes.mana_regen_rate, " - "vwMercNpcTypes.bodytype, vwMercNpcTypes.armortint_id, " - "vwMercNpcTypes.armortint_red, vwMercNpcTypes.armortint_green, vwMercNpcTypes.armortint_blue, " - "vwMercNpcTypes.AC, vwMercNpcTypes.ATK, vwMercNpcTypes.Accuracy, vwMercNpcTypes.spellscale, " - "vwMercNpcTypes.healscale FROM vwMercNpcTypes " - "WHERE merc_npc_type_id = %d AND clientlevel = %d AND race_id = %d", - id, clientlevel, raceid); //dual primary keys. one is ID, one is level. - auto results = QueryDatabase(query); - if (!results.Success()) { - std::cerr << "Error loading NPCs from database. Bad query: " << results.ErrorMessage() << std::endl; - return nullptr; - } - - const NPCType *npc; - - // Process each row returned. - for (auto row = results.begin(); row != results.end(); ++row) { - NPCType *tmpNPCType; - tmpNPCType = new NPCType; - memset (tmpNPCType, 0, sizeof *tmpNPCType); - - tmpNPCType->npc_id = atoi(row[0]); - - strn0cpy(tmpNPCType->name, row[1], 50); - - tmpNPCType->level = atoi(row[2]); - tmpNPCType->race = atoi(row[3]); - tmpNPCType->class_ = atoi(row[4]); - tmpNPCType->max_hp = atoi(row[5]); - tmpNPCType->cur_hp = tmpNPCType->max_hp; - tmpNPCType->Mana = atoi(row[6]); - tmpNPCType->gender = atoi(row[7]); - tmpNPCType->texture = atoi(row[8]); - tmpNPCType->helmtexture = atoi(row[9]); - tmpNPCType->attack_speed = atof(row[10]); - tmpNPCType->STR = atoi(row[11]); - tmpNPCType->STA = atoi(row[12]); - tmpNPCType->DEX = atoi(row[13]); - tmpNPCType->AGI = atoi(row[14]); - tmpNPCType->INT = atoi(row[15]); - tmpNPCType->WIS = atoi(row[16]); - tmpNPCType->CHA = atoi(row[17]); - tmpNPCType->MR = atoi(row[18]); - tmpNPCType->CR = atoi(row[19]); - tmpNPCType->DR = atoi(row[20]); - tmpNPCType->FR = atoi(row[21]); - tmpNPCType->PR = atoi(row[22]); - tmpNPCType->Corrup = atoi(row[23]); - tmpNPCType->min_dmg = atoi(row[24]); - tmpNPCType->max_dmg = atoi(row[25]); - tmpNPCType->attack_count = atoi(row[26]); - - if (row[27] != nullptr) - strn0cpy(tmpNPCType->special_abilities, row[27], 512); - else - tmpNPCType->special_abilities[0] = '\0'; - - tmpNPCType->d_meele_texture1 = atoi(row[28]); - tmpNPCType->d_meele_texture2 = atoi(row[29]); - tmpNPCType->prim_melee_type = atoi(row[30]); - tmpNPCType->sec_melee_type = atoi(row[31]); - tmpNPCType->runspeed= atof(row[32]); - - tmpNPCType->hp_regen = atoi(row[33]); - tmpNPCType->mana_regen = atoi(row[34]); - - tmpNPCType->aggroradius = RuleI(Mercs, AggroRadius); - - if (row[35] && strlen(row[35])) - tmpNPCType->bodytype = (uint8)atoi(row[35]); - else - tmpNPCType->bodytype = 1; - - uint32 armor_tint_id = atoi(row[36]); - tmpNPCType->armor_tint[0] = (atoi(row[37]) & 0xFF) << 16; - tmpNPCType->armor_tint[0] |= (atoi(row[38]) & 0xFF) << 8; - tmpNPCType->armor_tint[0] |= (atoi(row[39]) & 0xFF); - tmpNPCType->armor_tint[0] |= (tmpNPCType->armor_tint[0]) ? (0xFF << 24) : 0; - - if (armor_tint_id == 0) - for (int index = MaterialChest; index <= EmuConstants::MATERIAL_END; index++) - tmpNPCType->armor_tint[index] = tmpNPCType->armor_tint[0]; - else if (tmpNPCType->armor_tint[0] == 0) { - std::string armorTint_query = StringFormat("SELECT red1h, grn1h, blu1h, " - "red2c, grn2c, blu2c, " - "red3a, grn3a, blu3a, " - "red4b, grn4b, blu4b, " - "red5g, grn5g, blu5g, " - "red6l, grn6l, blu6l, " - "red7f, grn7f, blu7f, " - "red8x, grn8x, blu8x, " - "red9x, grn9x, blu9x " - "FROM npc_types_tint WHERE id = %d", - armor_tint_id); - auto armorTint_results = QueryDatabase(armorTint_query); - if (!results.Success() || results.RowCount() == 0) - armor_tint_id = 0; - else { - auto armorTint_row = results.begin(); - - for (int index = EmuConstants::MATERIAL_BEGIN; index <= EmuConstants::MATERIAL_END; index++) { - tmpNPCType->armor_tint[index] = atoi(armorTint_row[index * 3]) << 16; - tmpNPCType->armor_tint[index] |= atoi(armorTint_row[index * 3 + 1]) << 8; - tmpNPCType->armor_tint[index] |= atoi(armorTint_row[index * 3 + 2]); - tmpNPCType->armor_tint[index] |= (tmpNPCType->armor_tint[index]) ? (0xFF << 24) : 0; - } - } - } else - armor_tint_id = 0; - - tmpNPCType->AC = atoi(row[40]); - tmpNPCType->ATK = atoi(row[41]); - tmpNPCType->accuracy_rating = atoi(row[42]); - tmpNPCType->scalerate = RuleI(Mercs, ScaleRate); - tmpNPCType->spellscale = atoi(row[43]); - tmpNPCType->healscale = atoi(row[4]); - - // If NPC with duplicate NPC id already in table, - // free item we attempted to add. - if (zone->merctable.find(tmpNPCType->npc_id * 100 + clientlevel) != zone->merctable.end()) { - delete tmpNPCType; - return nullptr; - } - - zone->merctable[tmpNPCType->npc_id * 100 + clientlevel]=tmpNPCType; - npc = tmpNPCType; - } - - return npc; -} - -bool ZoneDatabase::LoadMercInfo(Client *client) { - - std::string query = StringFormat("SELECT MercID, Slot, Name, TemplateID, SuspendedTime, " - "IsSuspended, TimerRemaining, Gender, MercSize, StanceID, HP, Mana, " - "Endurance, Face, LuclinHairStyle, LuclinHairColor, " - "LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, LuclinBeard, " - "DrakkinHeritage, DrakkinTattoo, DrakkinDetails " - "FROM mercs WHERE OwnerCharacterID = '%i' ORDER BY Slot", client->CharacterID()); - auto results = QueryDatabase(query); - if (!results.Success()) - return false; - - if(results.RowCount() == 0) - return false; - - for (auto row = results.begin(); row != results.end(); ++row) { - uint8 slot = atoi(row[1]); - - if(slot >= MAXMERCS) - continue; - - client->GetMercInfo(slot).mercid = atoi(row[0]); - client->GetMercInfo(slot).slot = slot; - snprintf(client->GetMercInfo(slot).merc_name, 64, "%s", row[2]); - client->GetMercInfo(slot).MercTemplateID = atoi(row[3]); - client->GetMercInfo(slot).SuspendedTime = atoi(row[4]); - client->GetMercInfo(slot).IsSuspended = atoi(row[5]) == 1 ? true : false; - client->GetMercInfo(slot).MercTimerRemaining = atoi(row[6]); - client->GetMercInfo(slot).Gender = atoi(row[7]); - client->GetMercInfo(slot).MercSize = atof(row[8]); - client->GetMercInfo(slot).State = 5; - client->GetMercInfo(slot).Stance = atoi(row[9]); - client->GetMercInfo(slot).hp = atoi(row[10]); - client->GetMercInfo(slot).mana = atoi(row[11]); - client->GetMercInfo(slot).endurance = atoi(row[12]); - client->GetMercInfo(slot).face = atoi(row[13]); - client->GetMercInfo(slot).luclinHairStyle = atoi(row[14]); - client->GetMercInfo(slot).luclinHairColor = atoi(row[15]); - client->GetMercInfo(slot).luclinEyeColor = atoi(row[16]); - client->GetMercInfo(slot).luclinEyeColor2 = atoi(row[17]); - client->GetMercInfo(slot).luclinBeardColor = atoi(row[18]); - client->GetMercInfo(slot).luclinBeard = atoi(row[19]); - client->GetMercInfo(slot).drakkinHeritage = atoi(row[20]); - client->GetMercInfo(slot).drakkinTattoo = atoi(row[21]); - client->GetMercInfo(slot).drakkinDetails = atoi(row[22]); - } - - return true; -} - -bool ZoneDatabase::LoadCurrentMerc(Client *client) { - - uint8 slot = client->GetMercSlot(); - - if(slot > MAXMERCS) - return false; - - std::string query = StringFormat("SELECT MercID, Name, TemplateID, SuspendedTime, " - "IsSuspended, TimerRemaining, Gender, MercSize, StanceID, HP, " - "Mana, Endurance, Face, LuclinHairStyle, LuclinHairColor, " - "LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, " - "LuclinBeard, DrakkinHeritage, DrakkinTattoo, DrakkinDetails " - "FROM mercs WHERE OwnerCharacterID = '%i' AND Slot = '%u'", - client->CharacterID(), slot); - auto results = database.QueryDatabase(query); - - if(!results.Success()) - return false; - - if(results.RowCount() == 0) - return false; - - - for (auto row = results.begin(); row != results.end(); ++row) { - client->GetMercInfo(slot).mercid = atoi(row[0]); - client->GetMercInfo(slot).slot = slot; - snprintf(client->GetMercInfo(slot).merc_name, 64, "%s", row[1]); - client->GetMercInfo(slot).MercTemplateID = atoi(row[2]); - client->GetMercInfo(slot).SuspendedTime = atoi(row[3]); - client->GetMercInfo(slot).IsSuspended = atoi(row[4]) == 1? true: false; - client->GetMercInfo(slot).MercTimerRemaining = atoi(row[5]); - client->GetMercInfo(slot).Gender = atoi(row[6]); - client->GetMercInfo(slot).MercSize = atof(row[7]); - client->GetMercInfo(slot).State = atoi(row[8]); - client->GetMercInfo(slot).hp = atoi(row[9]); - client->GetMercInfo(slot).mana = atoi(row[10]); - client->GetMercInfo(slot).endurance = atoi(row[11]); - client->GetMercInfo(slot).face = atoi(row[12]); - client->GetMercInfo(slot).luclinHairStyle = atoi(row[13]); - client->GetMercInfo(slot).luclinHairColor = atoi(row[14]); - client->GetMercInfo(slot).luclinEyeColor = atoi(row[15]); - client->GetMercInfo(slot).luclinEyeColor2 = atoi(row[16]); - client->GetMercInfo(slot).luclinBeardColor = atoi(row[17]); - client->GetMercInfo(slot).luclinBeard = atoi(row[18]); - client->GetMercInfo(slot).drakkinHeritage = atoi(row[19]); - client->GetMercInfo(slot).drakkinTattoo = atoi(row[20]); - client->GetMercInfo(slot).drakkinDetails = atoi(row[21]); - } - - return true; -} - -bool ZoneDatabase::SaveMerc(Merc *merc) { - Client *owner = merc->GetMercOwner(); - - if(!owner) - return false; - - if(merc->GetMercID() == 0) - { - // New merc record - std::string query = StringFormat("INSERT INTO mercs " - "(OwnerCharacterID, Slot, Name, TemplateID, " - "SuspendedTime, IsSuspended, TimerRemaining, " - "Gender, MercSize, StanceID, HP, Mana, Endurance, Face, " - "LuclinHairStyle, LuclinHairColor, LuclinEyeColor, " - "LuclinEyeColor2, LuclinBeardColor, LuclinBeard, " - "DrakkinHeritage, DrakkinTattoo, DrakkinDetails) " - "VALUES('%u', '%u', '%s', '%u', '%u', '%u', '%u', " - "'%u', '%u', '%f', '%u', '%u', '%u', '%i', '%i', '%i', " - "'%i', '%i', '%i', '%i', '%i', '%i', '%i')", - merc->GetMercCharacterID(), owner->GetNumMercs(), - merc->GetCleanName(), merc->GetMercTemplateID(), - owner->GetMercInfo().SuspendedTime, merc->IsSuspended(), - owner->GetMercInfo().MercTimerRemaining, merc->GetGender(), - merc->GetSize(), merc->GetStance(), merc->GetHP(), - merc->GetMana(), merc->GetEndurance(), merc->GetLuclinFace(), - merc->GetHairStyle(), merc->GetHairColor(), merc->GetEyeColor1(), - merc->GetEyeColor2(), merc->GetBeardColor(), - merc->GetBeard(), merc->GetDrakkinHeritage(), - merc->GetDrakkinTattoo(), merc->GetDrakkinDetails()); - - auto results = database.QueryDatabase(query); - if(!results.Success()) { - owner->Message(13, results.ErrorMessage().c_str()); - return false; - } else if (results.RowsAffected() != 1) { - owner->Message(13, "Unable to save merc to the database."); - return false; - } - - merc->SetMercID(results.LastInsertedID()); - merc->UpdateMercInfo(owner); - database.SaveMercBuffs(merc); - return true; - } - - // Update existing merc record - std::string query = StringFormat("UPDATE mercs SET OwnerCharacterID = '%u', Slot = '%u', " - "Name = '%s', TemplateID = '%u', SuspendedTime = '%u', " - "IsSuspended = '%u', TimerRemaining = '%u', Gender = '%u', MercSize = '%f', " - "StanceID = '%u', HP = '%u', Mana = '%u', Endurance = '%u', " - "Face = '%i', LuclinHairStyle = '%i', LuclinHairColor = '%i', " - "LuclinEyeColor = '%i', LuclinEyeColor2 = '%i', LuclinBeardColor = '%i', " - "LuclinBeard = '%i', DrakkinHeritage = '%i', DrakkinTattoo = '%i', " - "DrakkinDetails = '%i' WHERE MercID = '%u'", - merc->GetMercCharacterID(), owner->GetMercSlot(), merc->GetCleanName(), - merc->GetMercTemplateID(), owner->GetMercInfo().SuspendedTime, - merc->IsSuspended(), owner->GetMercInfo().MercTimerRemaining, - merc->GetGender(), merc->GetSize(), merc->GetStance(), merc->GetHP(), - merc->GetMana(), merc->GetEndurance(), merc->GetLuclinFace(), - merc->GetHairStyle(), merc->GetHairColor(), merc->GetEyeColor1(), - merc->GetEyeColor2(), merc->GetBeardColor(), merc->GetBeard(), - merc->GetDrakkinHeritage(), merc->GetDrakkinTattoo(), merc->GetDrakkinDetails(), - merc->GetMercID()); - - auto results = database.QueryDatabase(query); - if (!results.Success()) { - owner->Message(13, results.ErrorMessage().c_str()); - return false; - } else if (results.RowsAffected() != 1) { - owner->Message(13, "Unable to save merc to the database."); - return false; - } - - merc->UpdateMercInfo(owner); - database.SaveMercBuffs(merc); - - return true; -} - -void ZoneDatabase::SaveMercBuffs(Merc *merc) { - - Buffs_Struct *buffs = merc->GetBuffs(); - - // Remove any existing buff saves - std::string query = StringFormat("DELETE FROM merc_buffs WHERE MercId = %u", merc->GetMercID()); - auto results = database.QueryDatabase(query); - if(!results.Success()) { - LogFile->write(EQEMuLog::Error, "Error While Deleting Merc Buffs before save: %s", results.ErrorMessage().c_str()); - return; - } - - for (int buffCount = 0; buffCount <= BUFF_COUNT; buffCount++) { - if(buffs[buffCount].spellid == 0 || buffs[buffCount].spellid == SPELL_UNKNOWN) - continue; - - int IsPersistent = buffs[buffCount].persistant_buff? 1: 0; - - query = StringFormat("INSERT INTO merc_buffs (MercId, SpellId, CasterLevel, DurationFormula, " - "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);", - 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, - CalculateDiseaseCounters(buffs[buffCount].spellid) > 0 ? buffs[buffCount].counters : 0, - CalculateCurseCounters(buffs[buffCount].spellid) > 0 ? buffs[buffCount].counters : 0, - CalculateCorruptionCounters(buffs[buffCount].spellid) > 0 ? buffs[buffCount].counters : 0, - buffs[buffCount].numhits, buffs[buffCount].melee_rune, buffs[buffCount].magic_rune, - buffs[buffCount].dot_rune, buffs[buffCount].caston_x, IsPersistent, buffs[buffCount].caston_y, - buffs[buffCount].caston_z, buffs[buffCount].ExtraDIChance); - results = database.QueryDatabase(query); - if(!results.Success()) { - LogFile->write(EQEMuLog::Error, "Error Saving Merc Buffs: %s", results.ErrorMessage().c_str()); - break; - } - } -} - -void ZoneDatabase::LoadMercBuffs(Merc *merc) { - Buffs_Struct *buffs = merc->GetBuffs(); - uint32 max_slots = merc->GetMaxBuffSlots(); - - - bool BuffsLoaded = false; - 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 merc_buffs WHERE MercId = %u", - merc->GetMercID()); - auto results = database.QueryDatabase(query); - if(!results.Success()) { - LogFile->write(EQEMuLog::Error, "Error Loading Merc Buffs: %s", results.ErrorMessage().c_str()); - return; - } - - int buffCount = 0; - for (auto row = results.begin(); row != results.end(); ++row, ++buffCount) { - if(buffCount == BUFF_COUNT) - break; - - 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]); - - if(CalculateDiseaseCounters(buffs[buffCount].spellid) > 0) - buffs[buffCount].counters = atoi(row[5]); - - if(CalculateCurseCounters(buffs[buffCount].spellid) > 0) - buffs[buffCount].counters = atoi(row[6]); - - if(CalculateCorruptionCounters(buffs[buffCount].spellid) > 0) - buffs[buffCount].counters = atoi(row[7]); - - buffs[buffCount].numhits = atoi(row[8]); - buffs[buffCount].melee_rune = atoi(row[9]); - buffs[buffCount].magic_rune = atoi(row[10]); - buffs[buffCount].dot_rune = atoi(row[11]); - buffs[buffCount].caston_x = atoi(row[12]); - buffs[buffCount].casterid = 0; - - bool IsPersistent = atoi(row[13])? true: false; - - buffs[buffCount].caston_y = atoi(row[13]); - buffs[buffCount].caston_z = atoi(row[14]); - buffs[buffCount].ExtraDIChance = atoi(row[15]); - - buffs[buffCount].persistant_buff = IsPersistent; - - } - - query = StringFormat("DELETE FROM merc_buffs WHERE MercId = %u", merc->GetMercID()); - results = database.QueryDatabase(query); - if(!results.Success()) - LogFile->write(EQEMuLog::Error, "Error Loading Merc Buffs: %s", results.ErrorMessage().c_str()); - -} - -bool ZoneDatabase::DeleteMerc(uint32 merc_id) { - - if(merc_id == 0) - return false; - - // TODO: These queries need to be ran together as a transaction.. ie, - // if one or more fail then they all will fail to commit to the database. - // ...Not all mercs will have buffs, so why is it required that both deletes succeed? - std::string query = StringFormat("DELETE FROM merc_buffs WHERE MercId = '%u'", merc_id); - auto results = database.QueryDatabase(query); - if(!results.Success()) - { - LogFile->write(EQEMuLog::Error, "Error Deleting Merc Buffs: %s", results.ErrorMessage().c_str()); - } - - query = StringFormat("DELETE FROM mercs WHERE MercID = '%u'", merc_id); - results = database.QueryDatabase(query); - if(!results.Success()) - { - LogFile->write(EQEMuLog::Error, "Error Deleting Merc: %s", results.ErrorMessage().c_str()); - return false; - } - - return true; -} - -void ZoneDatabase::LoadMercEquipment(Merc *merc) { - - std::string query = StringFormat("SELECT item_id FROM merc_inventory " - "WHERE merc_subtype_id = (" - "SELECT merc_subtype_id FROM merc_subtypes " - "WHERE class_id = '%u' AND tier_id = '%u') " - "AND min_level <= %u AND max_level >= %u", - merc->GetClass(), merc->GetTierID(), - merc->GetLevel(), merc->GetLevel()); - auto results = database.QueryDatabase(query); - if(!results.Success()) { - LogFile->write(EQEMuLog::Error, "Error Loading Merc Inventory: %s", results.ErrorMessage().c_str()); - return; - } - - int itemCount = 0; - for(auto row = results.begin(); row != results.end(); ++row) { - if (itemCount == EmuConstants::EQUIPMENT_SIZE) - break; - - if(atoi(row[0]) == 0) - continue; - - merc->AddItem(itemCount, atoi(row[0])); - itemCount++; - } -} - -uint8 ZoneDatabase::GetGridType(uint32 grid, uint32 zoneid ) { - - std::string query = StringFormat("SELECT type FROM grid WHERE id = %i AND zoneid = %i", grid, zoneid); - auto results = QueryDatabase(query); - if (!results.Success()) { - std::cerr << "Error in GetGridType query '" << query << "' " << results.ErrorMessage() << std::endl; - return 0; - } - - if (results.RowCount() != 1) - return 0; - - auto row = results.begin(); - - return atoi(row[0]); -} - -void ZoneDatabase::SaveMerchantTemp(uint32 npcid, uint32 slot, uint32 item, uint32 charges){ - - std::string query = StringFormat("REPLACE INTO merchantlist_temp (npcid, slot, itemid, charges) " - "VALUES(%d, %d, %d, %d)", npcid, slot, item, charges); - auto results = QueryDatabase(query); - if (!results.Success()) - std::cerr << "Error in SaveMerchantTemp query '" << query << "' " << results.ErrorMessage() << std::endl; -} - -void ZoneDatabase::DeleteMerchantTemp(uint32 npcid, uint32 slot){ - - std::string query = StringFormat("DELETE FROM merchantlist_temp WHERE npcid=%d AND slot=%d", npcid, slot); - auto results = QueryDatabase(query); - if (!results.Success()) - std::cerr << "Error in DeleteMerchantTemp query '" << query << "' " << results.ErrorMessage() << std::endl; - -} - -bool ZoneDatabase::UpdateZoneSafeCoords(const char* zonename, const xyz_location& location) { - - std::string query = StringFormat("UPDATE zone SET safe_x='%f', safe_y='%f', safe_z='%f' " - "WHERE short_name='%s';", - location.m_X, location.m_Y, location.m_Z, zonename); - auto results = QueryDatabase(query); - if (!results.Success() || results.RowsAffected() == 0) - return false; - - return true; -} - -uint8 ZoneDatabase::GetUseCFGSafeCoords() -{ - const std::string query = "SELECT value FROM variables WHERE varname='UseCFGSafeCoords'"; - auto results = QueryDatabase(query); - if (!results.Success()) { - std::cerr << "Error in GetUseCFGSafeCoords query '" << query << "' " << results.ErrorMessage() << std::endl; - return 0; - } - - if (results.RowCount() != 1) - return 0; - - auto row = results.begin(); - - return atoi(row[0]); -} - -//New functions for timezone -uint32 ZoneDatabase::GetZoneTZ(uint32 zoneid, uint32 version) { - - std::string query = StringFormat("SELECT timezone FROM zone WHERE zoneidnumber = %i " - "AND (version = %i OR version = 0) ORDER BY version DESC", - zoneid, version); - auto results = QueryDatabase(query); - if (!results.Success()) { - std::cerr << "Error in GetZoneTZ query '" << query << "' " << results.ErrorMessage() << std::endl; - return 0; - } - - if (results.RowCount() == 0) - return 0; - - auto row = results.begin(); - return atoi(row[0]); -} - -bool ZoneDatabase::SetZoneTZ(uint32 zoneid, uint32 version, uint32 tz) { - - std::string query = StringFormat("UPDATE zone SET timezone = %i " - "WHERE zoneidnumber = %i AND version = %i", - tz, zoneid, version); - auto results = QueryDatabase(query); - if (!results.Success()) { - std::cerr << "Error in SetZoneTZ query '" << query << "' " << results.ErrorMessage() << std::endl; - return false; - } - - return results.RowsAffected() == 1; -} - -void ZoneDatabase::RefreshGroupFromDB(Client *client){ - if(!client) - return; - - Group *group = client->GetGroup(); - - if(!group) - return; - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupUpdate2_Struct)); - GroupUpdate2_Struct* gu = (GroupUpdate2_Struct*)outapp->pBuffer; - gu->action = groupActUpdate; - - strcpy(gu->yourname, client->GetName()); - GetGroupLeadershipInfo(group->GetID(), gu->leadersname, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &gu->leader_aas); - gu->NPCMarkerID = group->GetNPCMarkerID(); - - int index = 0; - - std::string query = StringFormat("SELECT name FROM group_id WHERE groupid = %d", group->GetID()); - auto results = QueryDatabase(query); - if (!results.Success()) - { - printf("Error in group update query: %s\n", results.ErrorMessage().c_str()); - } - else - { - for (auto row = results.begin(); row != results.end(); ++row) { - if(index >= 6) - continue; - - if(strcmp(client->GetName(), row[0]) == 0) - continue; - - strcpy(gu->membername[index], row[0]); - index++; - } - } - - client->QueuePacket(outapp); - safe_delete(outapp); - - if(client->GetClientVersion() >= EQClientSoD) { - group->NotifyMainTank(client, 1); - group->NotifyPuller(client, 1); - } - - group->NotifyMainAssist(client, 1); - group->NotifyMarkNPC(client); - group->NotifyAssistTarget(client); - group->NotifyTankTarget(client); - group->NotifyPullerTarget(client); - group->SendMarkedNPCsToMember(client); - -} - -uint8 ZoneDatabase::GroupCount(uint32 groupid) { - - std::string query = StringFormat("SELECT count(charid) FROM group_id WHERE groupid = %d", groupid); - auto results = QueryDatabase(query); - if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::GroupCount query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); - return 0; - } - - if (results.RowCount() == 0) - return 0; - - auto row = results.begin(); - - return atoi(row[0]); -} - -uint8 ZoneDatabase::RaidGroupCount(uint32 raidid, uint32 groupid) { - - std::string query = StringFormat("SELECT count(charid) FROM raid_members " - "WHERE raidid = %d AND groupid = %d;", raidid, groupid); - auto results = QueryDatabase(query); - - if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::RaidGroupCount query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); - return 0; - } - - if (results.RowCount() == 0) - return 0; - - auto row = results.begin(); - - return atoi(row[0]); - } - -int32 ZoneDatabase::GetBlockedSpellsCount(uint32 zoneid) -{ - std::string query = StringFormat("SELECT count(*) FROM blocked_spells WHERE zoneid = %d", zoneid); - auto results = QueryDatabase(query); - if (!results.Success()) { - std::cerr << "Error in GetBlockedSpellsCount query '" << query << "' " << results.ErrorMessage() << std::endl; - return -1; - } - - if (results.RowCount() == 0) - return -1; - - auto row = results.begin(); - - return atoi(row[0]); -} - -bool ZoneDatabase::LoadBlockedSpells(int32 blockedSpellsCount, ZoneSpellsBlocked* into, uint32 zoneid) -{ - LogFile->write(EQEMuLog::Status, "Loading Blocked Spells from database..."); - - std::string query = StringFormat("SELECT id, spellid, type, x, y, z, x_diff, y_diff, z_diff, message " - "FROM blocked_spells WHERE zoneid = %d ORDER BY id ASC", zoneid); - auto results = QueryDatabase(query); - if (!results.Success()) { - std::cerr << "Error in LoadBlockedSpells query '" << query << "' " << results.ErrorMessage() << std::endl; - return false; - } - - if (results.RowCount() == 0) - return true; - - int32 index = 0; - for(auto row = results.begin(); row != results.end(); ++row, ++index) { - if(index >= blockedSpellsCount) { - std::cerr << "Error, Blocked Spells Count of " << blockedSpellsCount << " exceeded." << std::endl; - break; - } - - memset(&into[index], 0, sizeof(ZoneSpellsBlocked)); - into[index].spellid = atoi(row[1]); - into[index].type = atoi(row[2]); - into[index].m_Location = xyz_location(atof(row[3]), atof(row[4]), atof(row[5])); - into[index].m_Difference = xyz_location(atof(row[6]), atof(row[7]), atof(row[8])); - strn0cpy(into[index].message, row[9], 255); - } - - return true; -} - -int ZoneDatabase::getZoneShutDownDelay(uint32 zoneID, uint32 version) -{ - std::string query = StringFormat("SELECT shutdowndelay FROM zone " - "WHERE zoneidnumber = %i AND (version=%i OR version=0) " - "ORDER BY version DESC", zoneID, version); - auto results = QueryDatabase(query); - if (!results.Success()) { - std::cerr << "Error in getZoneShutDownDelay query '" << query << "' " << results.ErrorMessage().c_str() << std::endl; - return (RuleI(Zone, AutoShutdownDelay)); - } - - if (results.RowCount() == 0) { - std::cerr << "Error in getZoneShutDownDelay no result '" << query << "' " << std::endl; - return (RuleI(Zone, AutoShutdownDelay)); - } - - auto row = results.begin(); - - return atoi(row[0]); -} - -uint32 ZoneDatabase::GetKarma(uint32 acct_id) -{ - std::string query = StringFormat("SELECT `karma` FROM `account` WHERE `id` = '%i' LIMIT 1", acct_id); - auto results = QueryDatabase(query); - if (!results.Success()) - return 0; - - auto row = results.begin(); - - return atoi(row[0]); -} - -void ZoneDatabase::UpdateKarma(uint32 acct_id, uint32 amount) -{ - std::string query = StringFormat("UPDATE account SET karma = %i WHERE id = %i", amount, acct_id); - auto results = QueryDatabase(query); - if (!results.Success()) - std::cerr << "Error in UpdateKarma query '" << query << "' " << results.ErrorMessage().c_str() << std::endl; - -} - -void ZoneDatabase::ListAllInstances(Client* client, uint32 charid) -{ - if(!client) - return; - - std::string query = StringFormat("SELECT instance_list.id, zone, version " - "FROM instance_list JOIN instance_list_player " - "ON instance_list.id = instance_list_player.id " - "WHERE instance_list_player.charid = %lu", - (unsigned long)charid); - auto results = QueryDatabase(query); - if (!results.Success()) - return; - - char name[64]; - database.GetCharName(charid, name); - client->Message(0, "%s is part of the following instances:", name); - - for (auto row = results.begin(); row != results.end(); ++row) { - client->Message(0, "%s - id: %lu, version: %lu", database.GetZoneName(atoi(row[1])), - (unsigned long)atoi(row[0]), (unsigned long)atoi(row[2])); - } -} - -void ZoneDatabase::QGlobalPurge() -{ - const std::string query = "DELETE FROM quest_globals WHERE expdate < UNIX_TIMESTAMP()"; - database.QueryDatabase(query); -} - -void ZoneDatabase::InsertDoor(uint32 ddoordbid, uint16 ddoorid, const char* ddoor_name, const xyz_heading& position, uint8 dopentype, uint16 dguildid, uint32 dlockpick, uint32 dkeyitem, uint8 ddoor_param, uint8 dinvert, int dincline, uint16 dsize){ - - std::string query = StringFormat("REPLACE INTO doors (id, doorid, zone, version, name, " - "pos_x, pos_y, pos_z, heading, opentype, guild, lockpick, " - "keyitem, door_param, invert_state, incline, size) " - "VALUES('%i', '%i', '%s', '%i', '%s', '%f', '%f', " - "'%f', '%f', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i')", - ddoordbid, ddoorid, zone->GetShortName(), zone->GetInstanceVersion(), - ddoor_name, position.m_X, position.m_Y, position.m_Z, position.m_Heading, - dopentype, dguildid, dlockpick, dkeyitem, ddoor_param, dinvert, dincline, dsize); - auto results = QueryDatabase(query); - if (!results.Success()) - std::cerr << "Error in InsertDoor" << query << "' " << results.ErrorMessage() << std::endl; -} - -void ZoneDatabase::LoadAltCurrencyValues(uint32 char_id, std::map ¤cy) { - - std::string query = StringFormat("SELECT currency_id, amount " - "FROM character_alt_currency " - "WHERE char_id = '%u'", char_id); - auto results = QueryDatabase(query); - if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "Error in LoadAltCurrencyValues query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); - return; - } - - for (auto row = results.begin(); row != results.end(); ++row) - currency[atoi(row[0])] = atoi(row[1]); - -} - -void ZoneDatabase::UpdateAltCurrencyValue(uint32 char_id, uint32 currency_id, uint32 value) { - - std::string query = StringFormat("REPLACE INTO character_alt_currency (char_id, currency_id, amount) " - "VALUES('%u', '%u', '%u')", char_id, currency_id, value); - database.QueryDatabase(query); - -} - -void ZoneDatabase::SaveBuffs(Client *client) { - - std::string query = StringFormat("DELETE FROM `character_buffs` WHERE `character_id` = '%u'", client->CharacterID()); - database.QueryDatabase(query); - - uint32 buff_count = client->GetMaxBuffSlots(); - Buffs_Struct *buffs = client->GetBuffs(); - - for (int index = 0; index < buff_count; index++) { - if(buffs[index].spellid == SPELL_UNKNOWN) - continue; - - 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) " - "VALUES('%u', '%u', '%u', '%u', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', " - "'%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); - auto results = QueryDatabase(query); - if (!results.Success()) - LogFile->write(EQEMuLog::Error, "Error in SaveBuffs query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); - - } -} - -void ZoneDatabase::LoadBuffs(Client *client) { - - Buffs_Struct *buffs = client->GetBuffs(); - uint32 max_slots = client->GetMaxBuffSlots(); - - 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()) { - LogFile->write(EQEMuLog::Error, "Error in LoadBuffs query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); - return; - } - - 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; - - Client *caster = entity_list.GetClientByName(row[3]); - uint32 caster_level = atoi(row[2]); - uint32 ticsremaining = atoul(row[4]); - uint32 counters = atoul(row[5]); - uint32 numhits = atoul(row[6]); - uint32 melee_rune = atoul(row[7]); - uint32 magic_rune = atoul(row[8]); - uint8 persistent = atoul(row[9]); - uint32 dot_rune = atoul(row[10]); - int32 caston_x = atoul(row[11]); - int32 caston_y = atoul(row[12]); - int32 caston_z = atoul(row[13]); - int32 ExtraDIChance = atoul(row[14]); - - buffs[slot_id].spellid = spell_id; - 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; - strcpy(buffs[slot_id].caster_name, ""); - buffs[slot_id].client = false; - } - - 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].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; - - } - - max_slots = client->GetMaxBuffSlots(); - for(int index = 0; index < max_slots; ++index) { - if(!IsValidSpell(buffs[index].spellid)) - continue; - - for(int effectIndex = 0; effectIndex < 12; ++effectIndex) { - - if (spells[buffs[index].spellid].effectid[effectIndex] == SE_Charm) { - buffs[index].spellid = SPELL_UNKNOWN; - break; - } - - if (spells[buffs[index].spellid].effectid[effectIndex] == SE_Illusion) { - if(buffs[index].persistant_buff) - break; - - buffs[index].spellid = SPELL_UNKNOWN; - break; - } - } - } -} - -void ZoneDatabase::SavePetInfo(Client *client) -{ - PetInfo *petinfo = nullptr; - - std::string query = StringFormat("DELETE FROM `character_pet_buffs` WHERE `char_id` = %u", client->CharacterID()); - auto results = database.QueryDatabase(query); - if (!results.Success()) - return; - - query = StringFormat("DELETE FROM `character_pet_inventory` WHERE `char_id` = %u", client->CharacterID()); - results = database.QueryDatabase(query); - if (!results.Success()) - return; - - for (int pet = 0; pet < 2; pet++) { - petinfo = client->GetPetInfo(pet); - if (!petinfo) - continue; - - query = StringFormat("INSERT INTO `character_pet_info` " - "(`char_id`, `pet`, `petname`, `petpower`, `spell_id`, `hp`, `mana`, `size`) " - "VALUES (%u, %u, '%s', %i, %u, %u, %u, %f) " - "ON DUPLICATE KEY UPDATE `petname` = '%s', `petpower` = %i, `spell_id` = %u, " - "`hp` = %u, `mana` = %u, `size` = %f", - client->CharacterID(), pet, petinfo->Name, petinfo->petpower, petinfo->SpellID, - petinfo->HP, petinfo->Mana, petinfo->size, // and now the ON DUPLICATE ENTRIES - petinfo->Name, petinfo->petpower, petinfo->SpellID, petinfo->HP, petinfo->Mana, petinfo->size); - results = database.QueryDatabase(query); - if (!results.Success()) - return; - query.clear(); - - // pet buffs! - for (int index = 0; index < RuleI(Spells, MaxTotalSlotsPET); index++) { - if (petinfo->Buffs[index].spellid == SPELL_UNKNOWN || petinfo->Buffs[index].spellid == 0) - continue; - 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, %u, %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)", - client->CharacterID(), pet, index, petinfo->Buffs[index].spellid, - petinfo->Buffs[index].level, petinfo->Buffs[index].duration, - petinfo->Buffs[index].counters); - } - database.QueryDatabase(query); - query.clear(); - - // pet inventory! - for (int index = EmuConstants::EQUIPMENT_BEGIN; index <= EmuConstants::EQUIPMENT_END; index++) { - if (!petinfo->Items[index]) - continue; - - if (query.length() == 0) - query = StringFormat("INSERT INTO `character_pet_inventory` " - "(`char_id`, `pet`, `slot`, `item_id`) " - "VALUES (%u, %u, %u, %u)", - client->CharacterID(), pet, index, petinfo->Items[index]); - else - query += StringFormat(", (%u, %u, %u, %u)", client->CharacterID(), pet, index, petinfo->Items[index]); - } - database.QueryDatabase(query); - } -} - -void ZoneDatabase::RemoveTempFactions(Client *client) { - - std::string query = StringFormat("DELETE FROM faction_values " - "WHERE temp = 1 AND char_id = %u", - client->CharacterID()); - auto results = QueryDatabase(query); - if (!results.Success()) - std::cerr << "Error in RemoveTempFactions query '" << query << "' " << results.ErrorMessage() << std::endl; - -} - -void ZoneDatabase::LoadPetInfo(Client *client) { - - // Load current pet and suspended pet - PetInfo *petinfo = client->GetPetInfo(0); - PetInfo *suspended = client->GetPetInfo(1); - - 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()) { - LogFile->write(EQEMuLog::Error, "Error in LoadPetInfo query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); - return; - } - - PetInfo *pi; - 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; - - strncpy(pi->Name,row[1],64); - pi->petpower = atoi(row[2]); - pi->SpellID = atoi(row[3]); - pi->HP = atoul(row[4]); - pi->Mana = atoul(row[5]); - 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()) { - LogFile->write(EQEMuLog::Error, "Error in LoadPetInfo query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); - 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 - uint32 ticsremaining = atoul(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()) { - LogFile->write(EQEMuLog::Error, "Error in LoadPetInfo query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); - 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; - - int slot = atoi(row[1]); - if (slot < EmuConstants::EQUIPMENT_BEGIN || slot > EmuConstants::EQUIPMENT_END) - continue; - - pi->Items[slot] = atoul(row[2]); - } - -} - -bool ZoneDatabase::GetFactionData(FactionMods* fm, uint32 class_mod, uint32 race_mod, uint32 deity_mod, int32 faction_id) { - if (faction_id <= 0 || faction_id > (int32) max_faction) - return false; - - if (faction_array[faction_id] == 0){ - return false; - } - - fm->base = faction_array[faction_id]->base; - - if(class_mod > 0) { - char str[32]; - sprintf(str, "c%u", class_mod); - - std::map::const_iterator iter = faction_array[faction_id]->mods.find(str); - if(iter != faction_array[faction_id]->mods.end()) { - fm->class_mod = iter->second; - } else { - fm->class_mod = 0; - } - } else { - fm->class_mod = 0; - } - - if(race_mod > 0) { - char str[32]; - sprintf(str, "r%u", race_mod); - - std::map::iterator iter = faction_array[faction_id]->mods.find(str); - if(iter != faction_array[faction_id]->mods.end()) { - fm->race_mod = iter->second; - } else { - fm->race_mod = 0; - } - } else { - fm->race_mod = 0; - } - - if(deity_mod > 0) { - char str[32]; - sprintf(str, "d%u", deity_mod); - - std::map::iterator iter = faction_array[faction_id]->mods.find(str); - if(iter != faction_array[faction_id]->mods.end()) { - fm->deity_mod = iter->second; - } else { - fm->deity_mod = 0; - } - } else { - fm->deity_mod = 0; - } - - return true; -} - -//o-------------------------------------------------------------- -//| Name: GetFactionName; Dec. 16 -//o-------------------------------------------------------------- -//| Notes: Retrieves the name of the specified faction .Returns false on failure. -//o-------------------------------------------------------------- -bool ZoneDatabase::GetFactionName(int32 faction_id, char* name, uint32 buflen) { - if ((faction_id <= 0) || faction_id > int32(max_faction) ||(faction_array[faction_id] == 0)) - return false; - if (faction_array[faction_id]->name[0] != 0) { - strn0cpy(name, faction_array[faction_id]->name, buflen); - return true; - } - return false; - -} - -//o-------------------------------------------------------------- -//| Name: GetNPCFactionList; Dec. 16, 2001 -//o-------------------------------------------------------------- -//| Purpose: Gets a list of faction_id's and values bound to the npc_id. Returns false on failure. -//o-------------------------------------------------------------- -bool ZoneDatabase::GetNPCFactionList(uint32 npcfaction_id, int32* faction_id, int32* value, uint8* temp, int32* primary_faction) { - if (npcfaction_id <= 0) { - if (primary_faction) - *primary_faction = npcfaction_id; - return true; - } - const NPCFactionList* nfl = GetNPCFactionEntry(npcfaction_id); - if (!nfl) - return false; - if (primary_faction) - *primary_faction = nfl->primaryfaction; - for (int i=0; ifactionid[i]; - value[i] = nfl->factionvalue[i]; - temp[i] = nfl->factiontemp[i]; - } - return true; -} - -//o-------------------------------------------------------------- -//| Name: SetCharacterFactionLevel; Dec. 20, 2001 -//o-------------------------------------------------------------- -//| Purpose: Update characters faction level with specified faction_id to specified value. Returns false on failure. -//o-------------------------------------------------------------- -bool ZoneDatabase::SetCharacterFactionLevel(uint32 char_id, int32 faction_id, int32 value, uint8 temp, faction_map &val_list) -{ - - std::string query = StringFormat("DELETE FROM faction_values " - "WHERE char_id=%i AND faction_id = %i", - char_id, faction_id); - auto results = QueryDatabase(query); - if (!results.Success()) { - std::cerr << "Error in SetCharacterFactionLevel query '" << query << "' " << results.ErrorMessage() << std::endl; - return false; - } - - if(value == 0) - return true; - - if(temp == 2) - temp = 0; - - if(temp == 3) - temp = 1; - - query = StringFormat("INSERT INTO faction_values (char_id, faction_id, current_value, temp) " - "VALUES (%i, %i, %i, %i)", char_id, faction_id, value, temp); - results = QueryDatabase(query); - if (!results.Success()) { - std::cerr << "Error in SetCharacterFactionLevel query '" << query << "' " << results.ErrorMessage() << std::endl; - return false; - } - - if (results.RowsAffected() == 0) - return false; - - val_list[faction_id] = value; - return true; -} - -bool ZoneDatabase::LoadFactionData() -{ - std::string query = "SELECT MAX(id) FROM faction_list"; - auto results = QueryDatabase(query); - if (!results.Success()) { - std::cerr << "Error in LoadFactionData '" << query << "' " << results.ErrorMessage() << std::endl; - return false; - } - - if (results.RowCount() == 0) - return false; - - auto row = results.begin(); - - max_faction = atoi(row[0]); - faction_array = new Faction*[max_faction+1]; - for(unsigned int index=0; indexname, row[1], 50); - faction_array[index]->base = atoi(row[2]); - - query = StringFormat("SELECT `mod`, `mod_name` FROM `faction_list_mod` WHERE faction_id = %u", index); - auto modResults = QueryDatabase(query); - if (!modResults.Success()) - continue; - - for (auto modRow = modResults.begin(); modRow != modResults.end(); ++modRow) - faction_array[index]->mods[modRow[1]] = atoi(modRow[0]); - } - - return true; -} - -bool ZoneDatabase::GetFactionIdsForNPC(uint32 nfl_id, std::list *faction_list, int32* primary_faction) { - if (nfl_id <= 0) { - std::list::iterator cur,end; - cur = faction_list->begin(); - end = faction_list->end(); - for(; cur != end; ++cur) { - struct NPCFaction* tmp = *cur; - safe_delete(tmp); - } - - faction_list->clear(); - if (primary_faction) - *primary_faction = nfl_id; - return true; - } - const NPCFactionList* nfl = GetNPCFactionEntry(nfl_id); - if (!nfl) - return false; - if (primary_faction) - *primary_faction = nfl->primaryfaction; - - std::list::iterator cur,end; - cur = faction_list->begin(); - end = faction_list->end(); - for(; cur != end; ++cur) { - struct NPCFaction* tmp = *cur; - safe_delete(tmp); - } - faction_list->clear(); - for (int i=0; ifactionid[i]) { - pFac = new struct NPCFaction; - pFac->factionID = nfl->factionid[i]; - pFac->value_mod = nfl->factionvalue[i]; - pFac->npc_value = nfl->factionnpcvalue[i]; - pFac->temp = nfl->factiontemp[i]; - faction_list->push_back(pFac); - } - } - return true; -} - -/* Corpse Queries */ - -bool ZoneDatabase::DeleteGraveyard(uint32 zone_id, uint32 graveyard_id) { - std::string query = StringFormat( "UPDATE `zone` SET `graveyard_id` = 0 WHERE `zone_idnumber` = %u AND `version` = 0", zone_id); - auto results = QueryDatabase(query); - - query = StringFormat("DELETE FROM `graveyard` WHERE `id` = %u", graveyard_id); - auto results2 = QueryDatabase(query); - - if (results.Success() && results2.Success()){ - return true; - } - - return false; -} - -uint32 ZoneDatabase::AddGraveyardIDToZone(uint32 zone_id, uint32 graveyard_id) { - std::string query = StringFormat( - "UPDATE `zone` SET `graveyard_id` = %u WHERE `zone_idnumber` = %u AND `version` = 0", - graveyard_id, zone_id - ); - auto results = QueryDatabase(query); - return zone_id; -} - -uint32 ZoneDatabase::CreateGraveyardRecord(uint32 graveyard_zone_id, const xyz_heading& position) { - std::string query = StringFormat("INSERT INTO `graveyard` " - "SET `zone_id` = %u, `x` = %1.1f, `y` = %1.1f, `z` = %1.1f, `heading` = %1.1f", - graveyard_zone_id, position.m_X, position.m_Y, position.m_Z, position.m_Heading); - auto results = QueryDatabase(query); - if (results.Success()) - return results.LastInsertedID(); - - return 0; -} -uint32 ZoneDatabase::SendCharacterCorpseToGraveyard(uint32 dbid, uint32 zone_id, uint16 instance_id, const xyz_heading& position) { - std::string query = StringFormat("UPDATE `character_corpses` " - "SET `zone_id` = %u, `instance_id` = 0, " - "`x` = %1.1f, `y` = %1.1f, `z` = %1.1f, `heading` = %1.1f, " - "`was_at_graveyard` = 1 " - "WHERE `id` = %d", - zone_id, position.m_X, position.m_Y, position.m_Z, position.m_Heading, dbid); - QueryDatabase(query); - return dbid; -} - -uint32 ZoneDatabase::GetCharacterCorpseDecayTimer(uint32 corpse_db_id){ - std::string query = StringFormat("SELECT(UNIX_TIMESTAMP() - UNIX_TIMESTAMP(time_of_death)) FROM `character_corpses` WHERE `id` = %d AND NOT `time_of_death` = 0", corpse_db_id); - auto results = QueryDatabase(query); - auto row = results.begin(); - if (results.Success() && results.RowsAffected() != 0){ -<<<<<<< HEAD - return atoll(row[0]); -======= - return atoul(row[0]); ->>>>>>> master - } - return 0; -} - -uint32 ZoneDatabase::UpdateCharacterCorpse(uint32 db_id, uint32 char_id, const char* char_name, uint32 zone_id, uint16 instance_id, PlayerCorpse_Struct* dbpc, const xyz_heading& position, bool is_rezzed) { - std::string query = StringFormat("UPDATE `character_corpses` " - "SET `charname` = '%s', `zone_id` = %u, `instance_id` = %u, `charid` = %d, " - "`x` = %1.1f,`y` = %1.1f,`z` = %1.1f, `heading` = %1.1f, " - "`is_locked` = %d, `exp` = %u, `size` = %f, `level` = %u, " - "`race` = %u, `gender` = %u, `class` = %u, `deity` = %u, " - "`texture` = %u, `helm_texture` = %u, `copper` = %u, " - "`silver` = %u, `gold` = %u, `platinum` = %u, `hair_color` = %u, " - "`beard_color` = %u, `eye_color_1` = %u, `eye_color_2` = %u, " - "`hair_style` = %u, `face` = %u, `beard` = %u, `drakkin_heritage` = %u, " - "`drakkin_tattoo` = %u, `drakkin_details` = %u, `wc_1` = %u, " - "`wc_2` = %u, `wc_3` = %u, `wc_4` = %u, `wc_5` = %u, `wc_6` = %u, " - "`wc_7` = %u, `wc_8` = %u, `wc_9` = %u " - "WHERE `id` = %u", - EscapeString(char_name).c_str(), zone_id, instance_id, char_id, - position.m_X, position.m_Y, position.m_Z, position.m_Heading, - dbpc->locked, dbpc->exp, dbpc->size, dbpc->level, dbpc->race, - dbpc->gender, dbpc->class_, dbpc->deity, dbpc->texture, - dbpc->helmtexture, dbpc->copper, dbpc->silver, dbpc->gold, - dbpc->plat, dbpc->haircolor, dbpc->beardcolor, dbpc->eyecolor1, - dbpc->eyecolor2, dbpc->hairstyle, dbpc->face, dbpc->beard, - dbpc->drakkin_heritage, dbpc->drakkin_tattoo, dbpc->drakkin_details, - dbpc->item_tint[0].color, dbpc->item_tint[1].color, dbpc->item_tint[2].color, - dbpc->item_tint[3].color, dbpc->item_tint[4].color, dbpc->item_tint[5].color, - dbpc->item_tint[6].color, dbpc->item_tint[7].color, dbpc->item_tint[8].color, - db_id); - auto results = QueryDatabase(query); - - return db_id; -} - -void ZoneDatabase::MarkCorpseAsRezzed(uint32 db_id) { - std::string query = StringFormat("UPDATE `character_corpses` SET `is_rezzed` = 1 WHERE `id` = %i", db_id); - auto results = QueryDatabase(query); -} - -uint32 ZoneDatabase::SaveCharacterCorpse(uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, PlayerCorpse_Struct* dbpc, const xyz_heading& position) { - /* Dump Basic Corpse Data */ - std::string query = StringFormat("INSERT INTO `character_corpses` " - "SET `charname` = '%s', `zone_id` = %u, `instance_id` = %u, `charid` = %d," - "`x` = %1.1f, `y` = %1.1f, `z` = %1.1f, `heading` = %1.1f," - "`time_of_death` = NOW(), `is_buried` = 0, `is_locked` = %d," - "`exp` = %u, `size` = %f, `level` = %u, `race` = %u, `gender` = %u," - "`class` = %u, `deity` = %u, `texture` = %u, `helm_texture` = %u," - "`copper` = %u, `silver` = %u,`gold` = %u,`platinum` = %u," - "`hair_color` = %u, `beard_color` = %u, `eye_color_1` = %u," - "`eye_color_2` = %u, `hair_style` = %u, `face` = %u," - "`beard` = %u, `drakkin_heritage` = %u, `drakkin_tattoo` = %u," - "`drakkin_details` = %u, `wc_1` = %u, `wc_2` = %u," - "`wc_3` = %u, `wc_4` = %u, `wc_5` = %u, `wc_6` = %u," - "`wc_7` = %u,`wc_8` = %u,`wc_9` = %u", - EscapeString(charname).c_str(), zoneid, instanceid, charid, - position.m_X, position.m_Y, position.m_Z, position.m_Heading, - dbpc->locked, dbpc->exp, dbpc->size, dbpc->level, dbpc->race, - dbpc->gender, dbpc->class_, dbpc->deity, dbpc->texture, - dbpc->helmtexture, dbpc->copper, dbpc->silver, dbpc->gold, - dbpc->plat, dbpc->haircolor, dbpc->beardcolor, dbpc->eyecolor1, - dbpc->eyecolor2, dbpc->hairstyle, dbpc->face, dbpc->beard, - dbpc->drakkin_heritage, dbpc->drakkin_tattoo, dbpc->drakkin_details, - dbpc->item_tint[0].color, dbpc->item_tint[1].color, dbpc->item_tint[2].color, - dbpc->item_tint[3].color, dbpc->item_tint[4].color, dbpc->item_tint[5].color, - dbpc->item_tint[6].color, dbpc->item_tint[7].color, dbpc->item_tint[8].color); - auto results = QueryDatabase(query); - uint32 last_insert_id = results.LastInsertedID(); - - /* Dump Items from Inventory */ - uint8 first_entry = 0; - for (unsigned int i = 0; i < dbpc->itemcount; i++) { - if (first_entry != 1){ - 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, attuned) \n" - " VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, 0) \n", - 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].aug_5 - ); - first_entry = 1; - } - else{ - query = query + StringFormat(", (%u, %u, %u, %u, %u, %u, %u, %u, %u, 0) \n", - 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].aug_5 - ); - } - } - auto sc_results = QueryDatabase(query); - return last_insert_id; -} - -uint32 ZoneDatabase::GetCharacterBuriedCorpseCount(uint32 char_id) { - std::string query = StringFormat("SELECT COUNT(*) FROM `character_corpses` WHERE `charid` = '%u' AND `is_buried` = 1", char_id); - auto results = QueryDatabase(query); - - for (auto row = results.begin(); row != results.end(); ++row) { - return atoi(row[0]); - } - return 0; -} - -uint32 ZoneDatabase::GetCharacterCorpseCount(uint32 char_id) { - std::string query = StringFormat("SELECT COUNT(*) FROM `character_corpses` WHERE `charid` = '%u'", char_id); - auto results = QueryDatabase(query); - - for (auto row = results.begin(); row != results.end(); ++row) { - return atoi(row[0]); - } - return 0; -} - -uint32 ZoneDatabase::GetCharacterCorpseID(uint32 char_id, uint8 corpse) { - std::string query = StringFormat("SELECT `id` FROM `character_corpses` WHERE `charid` = '%u'", char_id); - auto results = QueryDatabase(query); - - for (auto row = results.begin(); row != results.end(); ++row) { - for (int i = 0; i < corpse; i++) { - return atoul(row[0]); - } - } - return 0; -} - -uint32 ZoneDatabase::GetCharacterCorpseItemCount(uint32 corpse_id){ - std::string query = StringFormat("SELECT COUNT(*) FROM character_corpse_items WHERE `corpse_id` = %u", - corpse_id - ); - auto results = QueryDatabase(query); - auto row = results.begin(); - if (results.Success() && results.RowsAffected() != 0){ - return atoi(row[0]); - } - return 0; -} - -uint32 ZoneDatabase::GetCharacterCorpseItemAt(uint32 corpse_id, uint16 slotid) { - Corpse* tmp = LoadCharacterCorpse(corpse_id); - uint32 itemid = 0; - - if (tmp) { - itemid = tmp->GetWornItem(slotid); - tmp->DepopPlayerCorpse(); - } - return itemid; -} - -bool ZoneDatabase::LoadCharacterCorpseData(uint32 corpse_id, PlayerCorpse_Struct* pcs){ - std::string query = StringFormat( - "SELECT \n" - "is_locked, \n" - "exp, \n" - "size, \n" - "`level`, \n" - "race, \n" - "gender, \n" - "class, \n" - "deity, \n" - "texture, \n" - "helm_texture, \n" - "copper, \n" - "silver, \n" - "gold, \n" - "platinum, \n" - "hair_color, \n" - "beard_color, \n" - "eye_color_1, \n" - "eye_color_2, \n" - "hair_style, \n" - "face, \n" - "beard, \n" - "drakkin_heritage,\n" - "drakkin_tattoo, \n" - "drakkin_details, \n" - "wc_1, \n" - "wc_2, \n" - "wc_3, \n" - "wc_4, \n" - "wc_5, \n" - "wc_6, \n" - "wc_7, \n" - "wc_8, \n" - "wc_9 \n" - "FROM \n" - "character_corpses\n" - "WHERE `id` = %u LIMIT 1\n", - corpse_id - ); - auto results = QueryDatabase(query); - uint16 i = 0; - for (auto row = results.begin(); row != results.end(); ++row) { - pcs->locked = atoi(row[i++]); // is_locked, - pcs->exp = atoul(row[i++]); // exp, - pcs->size = atoi(row[i++]); // size, - pcs->level = atoi(row[i++]); // `level`, - pcs->race = atoi(row[i++]); // race, - pcs->gender = atoi(row[i++]); // gender, - pcs->class_ = atoi(row[i++]); // class, - pcs->deity = atoi(row[i++]); // deity, - pcs->texture = atoi(row[i++]); // texture, - pcs->helmtexture = atoi(row[i++]); // helm_texture, - pcs->copper = atoul(row[i++]); // copper, - pcs->silver = atoul(row[i++]); // silver, - pcs->gold = atoul(row[i++]); // gold, - pcs->plat = atoul(row[i++]); // platinum, - pcs->haircolor = atoi(row[i++]); // hair_color, - pcs->beardcolor = atoi(row[i++]); // beard_color, - pcs->eyecolor1 = atoi(row[i++]); // eye_color_1, - pcs->eyecolor2 = atoi(row[i++]); // eye_color_2, - pcs->hairstyle = atoi(row[i++]); // hair_style, - pcs->face = atoi(row[i++]); // face, - pcs->beard = atoi(row[i++]); // beard, - pcs->drakkin_heritage = atoul(row[i++]); // drakkin_heritage, - pcs->drakkin_tattoo = atoul(row[i++]); // drakkin_tattoo, - pcs->drakkin_details = atoul(row[i++]); // drakkin_details, - pcs->item_tint[0].color = atoul(row[i++]); // wc_1, - pcs->item_tint[1].color = atoul(row[i++]); // wc_2, - pcs->item_tint[2].color = atoul(row[i++]); // wc_3, - pcs->item_tint[3].color = atoul(row[i++]); // wc_4, - pcs->item_tint[4].color = atoul(row[i++]); // wc_5, - pcs->item_tint[5].color = atoul(row[i++]); // wc_6, - pcs->item_tint[6].color = atoul(row[i++]); // wc_7, - pcs->item_tint[7].color = atoul(row[i++]); // wc_8, - pcs->item_tint[8].color = atoul(row[i++]); // wc_9 - } - query = StringFormat( - "SELECT \n" - "equip_slot, \n" - "item_id, \n" - "charges, \n" - "aug_1, \n" - "aug_2, \n" - "aug_3, \n" - "aug_4, \n" - "aug_5, \n" - "attuned \n" - "FROM \n" - "character_corpse_items \n" - "WHERE `corpse_id` = %u\n" - , - corpse_id - ); - results = QueryDatabase(query); - - i = 0; - pcs->itemcount = results.RowCount(); - uint16 r = 0; - for (auto row = results.begin(); row != results.end(); ++row) { - memset(&pcs->items[i], 0, sizeof (player_lootitem::ServerLootItem_Struct)); - pcs->items[i].equip_slot = atoi(row[r++]); // equip_slot, - pcs->items[i].item_id = atoul(row[r++]); // item_id, - pcs->items[i].charges = atoi(row[r++]); // charges, - pcs->items[i].aug_1 = atoi(row[r++]); // aug_1, - pcs->items[i].aug_2 = atoi(row[r++]); // aug_2, - pcs->items[i].aug_3 = atoi(row[r++]); // aug_3, - pcs->items[i].aug_4 = atoi(row[r++]); // aug_4, - pcs->items[i].aug_5 = atoi(row[r++]); // aug_5, - r = 0; - i++; - } - - return true; -} - -Corpse* ZoneDatabase::SummonBuriedCharacterCorpses(uint32 char_id, uint32 dest_zone_id, uint16 dest_instance_id, const xyz_heading& position) { - Corpse* corpse = nullptr; - std::string query = StringFormat("SELECT `id`, `charname`, `time_of_death`, `is_rezzed` " - "FROM `character_corpses` " - "WHERE `charid` = '%u' AND `is_buried` = 1 " - "ORDER BY `time_of_death` LIMIT 1", - char_id); - auto results = QueryDatabase(query); - - for (auto row = results.begin(); row != results.end(); ++row) { -<<<<<<< HEAD - corpse = Corpse::LoadFromDBData( - atoll(row[0]), // uint32 in_dbid -======= - NewCorpse = Corpse::LoadCharacterCorpseEntity( - atoul(row[0]), // uint32 in_dbid ->>>>>>> master - char_id, // uint32 in_charid - row[1], // char* in_charname - position, - row[2], // char* time_of_death - atoi(row[3]) == 1, // bool rezzed - false // bool was_at_graveyard - ); -<<<<<<< HEAD - if (!corpse) - continue; - - entity_list.AddCorpse(corpse); - corpse->SetDecayTimer(RuleI(Character, CorpseDecayTimeMS)); - corpse->Spawn(); - if (!UnburyCharacterCorpse(corpse->GetDBID(), dest_zone_id, dest_instance_id, position)) - LogFile->write(EQEMuLog::Error, "Unable to unbury a summoned player corpse for character id %u.", char_id); -======= - if (NewCorpse) { - entity_list.AddCorpse(NewCorpse); - NewCorpse->SetDecayTimer(RuleI(Character, CorpseDecayTimeMS)); - NewCorpse->Spawn(); - if (!UnburyCharacterCorpse(NewCorpse->GetCorpseDBID(), dest_zone_id, dest_instance_id, dest_x, dest_y, dest_z, dest_heading)) - LogFile->write(EQEMuLog::Error, "Unable to unbury a summoned player corpse for character id %u.", char_id); - } ->>>>>>> master - } - - return corpse; -} - -bool ZoneDatabase::SummonAllCharacterCorpses(uint32 char_id, uint32 dest_zone_id, uint16 dest_instance_id, const xyz_heading& position) { - Corpse* NewCorpse = 0; - int CorpseCount = 0; - - std::string query = StringFormat( - "UPDATE character_corpses SET zone_id = %i, instance_id = %i, x = %f, y = %f, z = %f, heading = %f, is_buried = 0, was_at_graveyard = 0 WHERE charid = %i", - dest_zone_id, dest_instance_id, position.m_X, position.m_Y, position.m_Z, position.m_Heading, char_id - ); - auto results = QueryDatabase(query); - - query = StringFormat( - "SELECT `id`, `charname`, `time_of_death`, `is_rezzed` FROM `character_corpses` WHERE `charid` = '%u'" - "ORDER BY time_of_death", - char_id); - results = QueryDatabase(query); - - for (auto row = results.begin(); row != results.end(); ++row) { - NewCorpse = Corpse::LoadCharacterCorpseEntity( - atoul(row[0]), - char_id, - row[1], - position, - row[2], - atoi(row[3]) == 1, - false); - if (NewCorpse) { - entity_list.AddCorpse(NewCorpse); - NewCorpse->SetDecayTimer(RuleI(Character, CorpseDecayTimeMS)); - NewCorpse->Spawn(); - ++CorpseCount; - } - else{ - LogFile->write(EQEMuLog::Error, "Unable to construct a player corpse for character id %u.", char_id); - } - } - - return (CorpseCount > 0); -} - -bool ZoneDatabase::UnburyCharacterCorpse(uint32 db_id, uint32 new_zone_id, uint16 new_instance_id, const xyz_heading& position) { - std::string query = StringFormat("UPDATE `character_corpses` " - "SET `is_buried` = 0, `zone_id` = %u, `instance_id` = %u, " - "`x` = %f, `y` = %f, `z` = %f, `heading` = %f, " - "`time_of_death` = Now(), `was_at_graveyard` = 0 " - "WHERE `id` = %u", - new_zone_id, new_instance_id, - position.m_X, position.m_Y, position.m_Z, position.m_Heading, db_id); - auto results = QueryDatabase(query); - if (results.Success() && results.RowsAffected() != 0) - return true; - - return false; -} - -Corpse* ZoneDatabase::LoadCharacterCorpse(uint32 player_corpse_id) { - Corpse* NewCorpse = 0; - std::string query = StringFormat( - "SELECT `id`, `charid`, `charname`, `x`, `y`, `z`, `heading`, `time_of_death`, `is_rezzed`, `was_at_graveyard` FROM `character_corpses` WHERE `id` = '%u' LIMIT 1", - player_corpse_id - ); - auto results = QueryDatabase(query); - for (auto row = results.begin(); row != results.end(); ++row) { -<<<<<<< HEAD - auto position = xyz_heading(atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6])); - NewCorpse = Corpse::LoadFromDBData( - atoll(row[0]), // id uint32 in_dbid - atoll(row[1]), // charid uint32 in_charid -======= - NewCorpse = Corpse::LoadCharacterCorpseEntity( - atoul(row[0]), // id uint32 in_dbid - atoul(row[1]), // charid uint32 in_charid ->>>>>>> master - row[2], // char_name - position, - row[7], // time_of_death char* time_of_death - atoi(row[8]) == 1, // is_rezzed bool rezzed - atoi(row[9]) // was_at_graveyard bool was_at_graveyard - ); - entity_list.AddCorpse(NewCorpse); - } - return NewCorpse; -} - -bool ZoneDatabase::LoadCharacterCorpses(uint32 zone_id, uint16 instance_id) { - std::string query; - if (!RuleB(Zone, EnableShadowrest)){ - query = StringFormat("SELECT id, charid, charname, x, y, z, heading, time_of_death, is_rezzed, was_at_graveyard FROM character_corpses WHERE zone_id='%u' AND instance_id='%u'", zone_id, instance_id); - } - else{ - query = StringFormat("SELECT id, charid, charname, x, y, z, heading, time_of_death, is_rezzed, 0 as was_at_graveyard FROM character_corpses WHERE zone_id='%u' AND instance_id='%u' AND is_buried=0", zone_id, instance_id); - } - - auto results = QueryDatabase(query); - for (auto row = results.begin(); row != results.end(); ++row) { -<<<<<<< HEAD - auto position = xyz_heading(atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6])); - entity_list.AddCorpse( - Corpse::LoadFromDBData( - atoll(row[0]), // id uint32 in_dbid - atoll(row[1]), // charid uint32 in_charid - row[2], // char_name - position, -======= - entity_list.AddCorpse( - Corpse::LoadCharacterCorpseEntity( - atoul(row[0]), // id uint32 in_dbid - atoul(row[1]), // charid uint32 in_charid - row[2], // char_name - atof(row[3]), // x float in_x - atof(row[4]), // y float in_y - atof(row[5]), // z float in_z - atof(row[6]), // heading float in_heading ->>>>>>> master - row[7], // time_of_death char* time_of_death - atoi(row[8]) == 1, // is_rezzed bool rezzed - atoi(row[9])) - ); - } - - return true; -} - -uint32 ZoneDatabase::GetFirstCorpseID(uint32 char_id) { - std::string query = StringFormat("SELECT `id` FROM `character_corpses` WHERE `charid` = '%u' AND `is_buried` = 0 ORDER BY `time_of_death` LIMIT 1", char_id); - auto results = QueryDatabase(query); - for (auto row = results.begin(); row != results.end(); ++row) { - return atoi(row[0]); - } - return 0; -} - -<<<<<<< HEAD -bool ZoneDatabase::ClearCorpseItems(uint32 db_id){ - std::string query = StringFormat("DELETE FROM `character_corpse_items` WHERE `corpse_id` = %u", db_id); - auto results = QueryDatabase(query); - if (results.Success() && results.RowsAffected() != 0){ - return true; - } - return false; -} - -======= ->>>>>>> master -bool ZoneDatabase::DeleteItemOffCharacterCorpse(uint32 db_id, uint32 equip_slot, uint32 item_id){ - std::string query = StringFormat("DELETE FROM `character_corpse_items` WHERE `corpse_id` = %u AND equip_slot = %u AND item_id = %u", db_id, equip_slot, item_id); - auto results = QueryDatabase(query); - if (results.Success() && results.RowsAffected() != 0){ - return true; - } - return false; -} - -bool ZoneDatabase::BuryCharacterCorpse(uint32 db_id) { - std::string query = StringFormat("UPDATE `character_corpses` SET `is_buried` = 1 WHERE `id` = %u", db_id); - auto results = QueryDatabase(query); - if (results.Success() && results.RowsAffected() != 0){ - return true; - } - return false; -} - -bool ZoneDatabase::BuryAllCharacterCorpses(uint32 char_id) { - std::string query = StringFormat("SELECT `id` FROM `character_corpses` WHERE `charid` = %u", char_id); - auto results = QueryDatabase(query); - for (auto row = results.begin(); row != results.end(); ++row) { - BuryCharacterCorpse(atoi(row[0])); - return true; - } - return false; -} - -bool ZoneDatabase::DeleteCharacterCorpse(uint32 db_id) { - std::string query = StringFormat("DELETE FROM `character_corpses` WHERE `id` = %d", db_id); - auto results = QueryDatabase(query); - if (results.Success() && results.RowsAffected() != 0){ - return true; - } - return false; -} diff --git a/zone/zonedb.h.orig b/zone/zonedb.h.orig deleted file mode 100644 index a7814cd45..000000000 --- a/zone/zonedb.h.orig +++ /dev/null @@ -1,519 +0,0 @@ -#ifndef ZONEDB_H_ -#define ZONEDB_H_ - -#include "../common/shareddb.h" -#include "../common/eq_packet_structs.h" -<<<<<<< HEAD -#include "../common/loottable.h" -#include "zonedump.h" -#include "position.h" -======= ->>>>>>> master -#include "../common/faction.h" - -class Client; -class Corpse; -class Merc; -class NPC; -class Petition; -class Spawn2; -class SpawnGroupList; -class ItemInst; -struct CharacterEventLog_Struct; -struct Door; -struct ExtendedProfile_Struct; -struct NPCType; -struct PlayerCorpse_Struct; -struct ZonePoint; -struct npcDecayTimes_Struct; -template class LinkedList; - -//#include "doors.h" - -struct wplist { - int index; - float x; - float y; - float z; - int pause; - float heading; -}; - -#pragma pack(1) -struct DBnpcspells_entries_Struct { - int16 spellid; - uint16 type; - uint8 minlevel; - uint8 maxlevel; - int16 manacost; - int32 recast_delay; - int16 priority; - int16 resist_adjust; -}; -#pragma pack() - -#pragma pack(1) -struct DBnpcspellseffects_entries_Struct { - int16 spelleffectid; - uint8 minlevel; - uint8 maxlevel; - int32 base; - int32 limit; - int32 max; -}; -#pragma pack() - -struct DBnpcspells_Struct { - uint32 parent_list; - uint16 attack_proc; - uint8 proc_chance; - uint16 range_proc; - int16 rproc_chance; - uint16 defensive_proc; - int16 dproc_chance; - uint32 numentries; - uint32 fail_recast; - uint32 engaged_no_sp_recast_min; - uint32 engaged_no_sp_recast_max; - uint8 engaged_beneficial_self_chance; - uint8 engaged_beneficial_other_chance; - uint8 engaged_detrimental_chance; - uint32 pursue_no_sp_recast_min; - uint32 pursue_no_sp_recast_max; - uint8 pursue_detrimental_chance; - uint32 idle_no_sp_recast_min; - uint32 idle_no_sp_recast_max; - uint8 idle_beneficial_chance; - DBnpcspells_entries_Struct entries[0]; -}; - -struct DBnpcspellseffects_Struct { - uint32 parent_list; - uint32 numentries; - DBnpcspellseffects_entries_Struct entries[0]; -}; - -struct DBTradeskillRecipe_Struct { - SkillUseTypes tradeskill; - int16 skill_needed; - uint16 trivial; - bool nofail; - bool replace_container; - std::vector< std::pair > onsuccess; - std::vector< std::pair > onfail; - std::vector< std::pair > salvage; - std::string name; - uint8 must_learn; - bool has_learnt; - uint32 madecount; - uint32 recipe_id; - bool quest; -}; - -struct PetRecord { - uint32 npc_type; // npc_type id for the pet data to use - bool temporary; - int16 petpower; - uint8 petcontrol; // What kind of control over the pet is possible (Animation, familiar, ...) - uint8 petnaming; // How to name the pet (Warder, pet, random name, familiar, ...) - bool monsterflag; // flag for if a random monster appearance should get picked - uint32 equipmentset; // default equipment for the pet -}; - -// Actual pet info for a client. -struct PetInfo { - uint16 SpellID; - int16 petpower; - uint32 HP; - uint32 Mana; - float size; - SpellBuff_Struct Buffs[BUFF_COUNT]; - uint32 Items[EmuConstants::EQUIPMENT_SIZE]; - char Name[64]; -}; - -struct ZoneSpellsBlocked { - uint32 spellid; - int8 type; - xyz_location m_Location; - xyz_location m_Difference; - char message[256]; -}; - -struct TraderCharges_Struct { - uint32 ItemID[80]; - int32 SerialNumber[80]; - uint32 ItemCost[80]; - int32 Charges[80]; -}; - -const int MaxMercStanceID = 9; - -struct MercStanceInfo { - uint8 ProficiencyID; - uint8 ClassID; - uint32 StanceID; - uint8 IsDefault; -}; - -struct MercTemplate { - uint32 MercTemplateID; - uint32 MercType; // From dbstr_us.txt - Apprentice (330000100), Journeyman (330000200), Master (330000300) - uint32 MercSubType; // From dbstr_us.txt - 330020105^23^Race: Guktan
Type: Healer
Confidence: High
Proficiency: Apprentice, Tier V... - uint16 RaceID; - uint8 ClassID; - uint32 MercNPCID; - uint8 ProficiencyID; - uint8 TierID; - uint8 CostFormula; // To determine cost to client - uint32 ClientVersion; // Only send valid mercs per expansion - uint8 MercNameType; // Determines if merc gets random name or default text - char MercNamePrefix[25]; - char MercNameSuffix[25]; - uint32 Stances[MaxMercStanceID]; -}; - -struct MercInfo { - uint32 mercid; - uint8 slot; - char merc_name[64]; - uint32 MercTemplateID; - const MercTemplate* myTemplate; - uint32 SuspendedTime; - bool IsSuspended; - uint32 MercTimerRemaining; - uint8 Gender; - float MercSize; - int32 State; - uint32 Stance; - int32 hp; - int32 mana; - int32 endurance; - uint8 face; - uint8 luclinHairStyle; - uint8 luclinHairColor; - uint8 luclinEyeColor; - uint8 luclinEyeColor2; - uint8 luclinBeardColor; - uint8 luclinBeard; - uint32 drakkinHeritage; - uint32 drakkinTattoo; - uint32 drakkinDetails; -}; - -struct MercSpellEntry { - uint8 proficiencyid; - uint16 spellid; // <= 0 = no spell - uint32 type; // 0 = never, must be one (and only one) of the defined values - int16 stance; // 0 = all, + = only this stance, - = all except this stance - uint8 minlevel; - uint8 maxlevel; - int16 slot; - uint16 proc_chance; - uint32 time_cancast; // when we can cast this spell next -}; - -struct ClientMercEntry { - uint32 id; - uint32 npcid; -}; - -class ZoneDatabase : public SharedDatabase { - typedef std::list ItemList; -public: - ZoneDatabase(); - ZoneDatabase(const char* host, const char* user, const char* passwd, const char* database,uint32 port); - virtual ~ZoneDatabase(); - - /* Objects and World Containers */ - void LoadWorldContainer(uint32 parentid, ItemInst* container); - void SaveWorldContainer(uint32 zone_id, uint32 parent_id, const ItemInst* container); - void DeleteWorldContainer(uint32 parent_id,uint32 zone_id); - uint32 AddObject(uint32 type, uint32 icon, const Object_Struct& object, const ItemInst* inst); - void UpdateObject(uint32 id, uint32 type, uint32 icon, const Object_Struct& object, const ItemInst* inst); - void DeleteObject(uint32 id); - Ground_Spawns* LoadGroundSpawns(uint32 zone_id, int16 version, Ground_Spawns* gs); - - /* Traders */ - void SaveTraderItem(uint32 char_id,uint32 itemid,uint32 uniqueid, int32 charges,uint32 itemcost,uint8 slot); - void UpdateTraderItemCharges(int char_id, uint32 ItemInstID, int32 charges); - void UpdateTraderItemPrice(int CharID, uint32 ItemID, uint32 Charges, uint32 NewPrice); - void DeleteTraderItem(uint32 char_id); - void DeleteTraderItem(uint32 char_id,uint16 slot_id); - - ItemInst* LoadSingleTraderItem(uint32 char_id, int uniqueid); - Trader_Struct* LoadTraderItem(uint32 char_id); - TraderCharges_Struct* LoadTraderItemWithCharges(uint32 char_id); - - /* Buyer/Barter */ - void AddBuyLine(uint32 CharID, uint32 BuySlot, uint32 ItemID, const char *ItemName, uint32 Quantity, uint32 Price); - void RemoveBuyLine(uint32 CharID, uint32 BuySlot); - void DeleteBuyLines(uint32 CharID); - void UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity); - - /* General Character Related Stuff */ - bool SetServerFilters(char* name, ServerSideFilters_Struct *ssfs); - uint32 GetServerFilters(char* name, ServerSideFilters_Struct *ssfs); - - void SaveBuffs(Client *c); - void LoadBuffs(Client *c); - void LoadPetInfo(Client *c); - void SavePetInfo(Client *c); - void RemoveTempFactions(Client *c); - - /* Character Data Loaders */ - bool LoadCharacterFactionValues(uint32 character_id, faction_map & val_list); - bool LoadCharacterSpellBook(uint32 character_id, PlayerProfile_Struct* pp); - bool LoadCharacterMemmedSpells(uint32 character_id, PlayerProfile_Struct* pp); - bool LoadCharacterLanguages(uint32 character_id, PlayerProfile_Struct* pp); - bool LoadCharacterDisciplines(uint32 character_id, PlayerProfile_Struct* pp); - bool LoadCharacterSkills(uint32 character_id, PlayerProfile_Struct* pp); - bool LoadCharacterData(uint32 character_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp); - bool LoadCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); - bool LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Struct* pp); - bool LoadCharacterMaterialColor(uint32 character_id, PlayerProfile_Struct* pp); - bool LoadCharacterBandolier(uint32 character_id, PlayerProfile_Struct* pp); - bool LoadCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp); - bool LoadCharacterPotions(uint32 character_id, PlayerProfile_Struct* pp); - bool LoadCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp); - - /* Character Data Saves */ - bool SaveCharacterBindPoint(uint32 character_id, uint32 zone_id, uint32 instance_id, const xyz_heading& 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 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); - bool SaveCharacterSkill(uint32 character_id, uint32 skill_id, uint32 value); - bool SaveCharacterLanguage(uint32 character_id, uint32 lang_id, uint32 value); - bool SaveCharacterDisc(uint32 character_id, uint32 slot_id, uint32 disc_id); - bool SaveCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp); - bool SaveCharacterBandolier(uint32 character_id, uint8 bandolier_id, uint8 bandolier_slot, uint32 item_id, uint32 icon, const char* bandolier_name); - bool SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, uint32 item_id, uint32 icon); - bool SaveCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp); - - /* Character Data Deletes */ - bool DeleteCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); - bool DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); - bool DeleteCharacterDisc(uint32 character_id, uint32 slot_id); - bool DeleteCharacterBandolier(uint32 character_id, uint32 band_id); - bool DeleteCharacterLeadershipAAs(uint32 character_id); - bool DeleteCharacterAAs(uint32 character_id); - bool DeleteCharacterDye(uint32 character_id); - - /* Character Inventory */ - bool NoRentExpired(const char* name); - - /* Corpses */ - bool DeleteItemOffCharacterCorpse(uint32 db_id, uint32 equip_slot, uint32 item_id); - uint32 GetCharacterCorpseItemCount(uint32 corpse_id); - bool LoadCharacterCorpseData(uint32 corpse_id, PlayerCorpse_Struct* pcs); - Corpse* LoadCharacterCorpse(uint32 player_corpse_id); - Corpse* SummonBuriedCharacterCorpses(uint32 char_id, uint32 dest_zoneid, uint16 dest_instanceid, const xyz_heading& position); - void MarkCorpseAsRezzed(uint32 dbid); - bool GetDecayTimes(npcDecayTimes_Struct* npcCorpseDecayTimes); - bool BuryCharacterCorpse(uint32 dbid); - bool BuryAllCharacterCorpses(uint32 charid); - bool DeleteCharacterCorpse(uint32 dbid); - bool SummonAllCharacterCorpses(uint32 char_id, uint32 dest_zoneid, uint16 dest_instanceid, const xyz_heading& position); - bool SummonAllGraveyardCorpses(uint32 cur_zoneid, uint32 dest_zoneid, uint16 dest_instanceid, const xyz_heading& position); - bool UnburyCharacterCorpse(uint32 dbid, uint32 new_zoneid, uint16 dest_instanceid, const xyz_heading& position); - bool LoadCharacterCorpses(uint32 iZoneID, uint16 iInstanceID); - bool DeleteGraveyard(uint32 zone_id, uint32 graveyard_id); - uint32 GetCharacterCorpseDecayTimer(uint32 corpse_db_id); - uint32 GetCharacterBuriedCorpseCount(uint32 char_id); - uint32 SendCharacterCorpseToGraveyard(uint32 dbid, uint32 zoneid, uint16 instanceid, const xyz_heading& position); - uint32 CreateGraveyardRecord(uint32 graveyard_zoneid, const xyz_heading& position); - uint32 AddGraveyardIDToZone(uint32 zone_id, uint32 graveyard_id); - uint32 SaveCharacterCorpse(uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, PlayerCorpse_Struct* dbpc, const xyz_heading& position); - uint32 UpdateCharacterCorpse(uint32 dbid, uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, PlayerCorpse_Struct* dbpc, const xyz_heading& position, bool rezzed = false); - uint32 GetFirstCorpseID(uint32 char_id); - uint32 GetCharacterCorpseCount(uint32 char_id); - uint32 GetCharacterCorpseID(uint32 char_id, uint8 corpse); - uint32 GetCharacterCorpseItemAt(uint32 corpse_id, uint16 slotid); - uint32 GetPlayerCorpseTimeLeft(uint8 corpse, uint8 type); - - /* Faction */ - bool GetNPCFactionList(uint32 npcfaction_id, int32* faction_id, int32* value, uint8* temp, int32* primary_faction = 0); - bool GetFactionData(FactionMods* fd, uint32 class_mod, uint32 race_mod, uint32 deity_mod, int32 faction_id); //needed for factions Dec, 16 2001 - bool GetFactionName(int32 faction_id, char* name, uint32 buflen); // needed for factions Dec, 16 2001 - bool GetFactionIdsForNPC(uint32 nfl_id, std::list *faction_list, int32* primary_faction = 0); // improve faction handling - bool SetCharacterFactionLevel(uint32 char_id, int32 faction_id, int32 value, uint8 temp, faction_map &val_list); // needed for factions Dec, 16 2001 - bool LoadFactionData(); - - /* AAs */ - bool LoadAAEffects(); - bool LoadAAEffects2(); - bool LoadSwarmSpells(); - SendAA_Struct*GetAASkillVars(uint32 skill_id); - uint8 GetTotalAALevels(uint32 skill_id); - uint32 GetSizeAA(); - uint32 CountAAs(); - void LoadAAs(SendAA_Struct **load); - uint32 CountAAEffects(); - void FillAAEffects(SendAA_Struct* aa_struct); - - /* Zone related */ - bool GetZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct *data, bool &can_bind, bool &can_combat, bool &can_levitate, bool &can_castoutdoor, bool &is_city, bool &is_hotzone, bool &allow_mercs, uint8 &zone_type, int &ruleset, char **map_filename); - bool SaveZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct* zd); - bool LoadStaticZonePoints(LinkedList* zone_point_list,const char* zonename, uint32 version); - bool UpdateZoneSafeCoords(const char* zonename, const xyz_location& location); - uint8 GetUseCFGSafeCoords(); - int getZoneShutDownDelay(uint32 zoneID, uint32 version); - - /* Spawns and Spawn Points */ - bool LoadSpawnGroups(const char* zone_name, uint16 version, SpawnGroupList* spawn_group_list); - bool LoadSpawnGroupsByID(int spawngroupid, SpawnGroupList* spawn_group_list); - bool PopulateZoneSpawnList(uint32 zoneid, LinkedList &spawn2_list, int16 version, uint32 repopdelay = 0); - Spawn2* LoadSpawn2(LinkedList &spawn2_list, uint32 spawn2id, uint32 timeleft); - bool CreateSpawn2(Client *c, uint32 spawngroup, const char* zone, const xyz_heading& position, uint32 respawn, uint32 variance, uint16 condition, int16 cond_value); - void UpdateSpawn2Timeleft(uint32 id, uint16 instance_id,uint32 timeleft); - uint32 GetSpawnTimeLeft(uint32 id, uint16 instance_id); - void UpdateSpawn2Status(uint32 id, uint8 new_status); - - /* Grids/Paths */ - uint32 GetFreeGrid(uint16 zoneid); - void DeleteGrid(Client *c, uint32 sg2, uint32 grid_num, bool grid_too, uint16 zoneid); - void DeleteWaypoint(Client *c, uint32 grid_num, uint32 wp_num, uint16 zoneid); - void AddWP(Client *c, uint32 gridid, uint32 wpnum, const xyz_heading& position, uint32 pause, uint16 zoneid); - uint32 AddWPForSpawn(Client *c, uint32 spawn2id, const xyz_heading& position, uint32 pause, int type1, int type2, uint16 zoneid); - void ModifyGrid(Client *c, bool remove, uint32 id, uint8 type = 0, uint8 type2 = 0, uint16 zoneid = 0); - void ModifyWP(Client *c, uint32 grid_id, uint32 wp_num, const xyz_location& location, uint32 script = 0, uint16 zoneid = 0); - uint8 GetGridType(uint32 grid, uint32 zoneid); - uint8 GetGridType2(uint32 grid, uint16 zoneid); - bool GetWaypoints(uint32 grid, uint16 zoneid, uint32 num, wplist* wp); - void AssignGrid(Client *client, const xy_location& location, uint32 id); - int GetHighestGrid(uint32 zoneid); - int GetHighestWaypoint(uint32 zoneid, uint32 gridid); - - /* NPCs */ - - uint32 NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn = 0, uint32 extra = 0); // 0 = Create 1 = Add; 2 = Update; 3 = Remove; 4 = Delete - uint32 CreateNewNPCCommand(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 extra); - uint32 AddNewNPCSpawnGroupCommand(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 respawnTime); - uint32 DeleteSpawnLeaveInNPCTypeTable(const char* zone, Client *client, NPC* spawn); - uint32 DeleteSpawnRemoveFromNPCTypeTable(const char* zone, uint32 zone_version, Client *client, NPC* spawn); - uint32 AddSpawnFromSpawnGroup(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID); - uint32 AddNPCTypes(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID); - uint32 UpdateNPCTypeAppearance(Client *client, NPC* spawn); - bool SetSpecialAttkFlag(uint8 id, const char* flag); - bool GetPetEntry(const char *pet_type, PetRecord *into); - bool GetPoweredPetEntry(const char *pet_type, int16 petpower, PetRecord *into); - bool GetBasePetItems(int32 equipmentset, uint32 *items); - void AddLootTableToNPC(NPC* npc, uint32 loottable_id, ItemList* itemlist, uint32* copper, uint32* silver, uint32* gold, uint32* plat); - void AddLootDropToNPC(NPC* npc, uint32 lootdrop_id, ItemList* itemlist, uint8 droplimit, uint8 mindrop); - uint32 GetMaxNPCSpellsID(); - uint32 GetMaxNPCSpellsEffectsID(); - - DBnpcspells_Struct* GetNPCSpells(uint32 iDBSpellsID); - DBnpcspellseffects_Struct* GetNPCSpellsEffects(uint32 iDBSpellsEffectsID); - const NPCType* GetNPCType(uint32 id); - - /* Mercs */ - const NPCType* GetMercType(uint32 id, uint16 raceid, uint32 clientlevel); - void LoadMercEquipment(Merc *merc); - void SaveMercBuffs(Merc *merc); - void LoadMercBuffs(Merc *merc); - bool LoadMercInfo(Client *c); - bool LoadCurrentMerc(Client *c); - bool SaveMerc(Merc *merc); - bool DeleteMerc(uint32 merc_id); - - /* Petitions */ - void UpdateBug(BugStruct* bug); - void UpdateBug(PetitionBug_Struct* bug); - void DeletePetitionFromDB(Petition* wpet); - void UpdatePetitionToDB(Petition* wpet); - void InsertPetitionToDB(Petition* wpet); - void RefreshPetitionsFromDB(); - - /* Merchants */ - void SaveMerchantTemp(uint32 npcid, uint32 slot, uint32 item, uint32 charges); - void DeleteMerchantTemp(uint32 npcid, uint32 slot); - - /* Tradeskills */ - bool GetTradeRecipe(const ItemInst* container, uint8 c_type, uint32 some_id, uint32 char_id, DBTradeskillRecipe_Struct *spec); - bool GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id, uint32 char_id, DBTradeskillRecipe_Struct *spec); - uint32 GetZoneForage(uint32 ZoneID, uint8 skill); /* for foraging */ - uint32 GetZoneFishing(uint32 ZoneID, uint8 skill, uint32 &npc_id, uint8 &npc_chance); - void UpdateRecipeMadecount(uint32 recipe_id, uint32 char_id, uint32 madecount); - bool EnableRecipe(uint32 recipe_id); - bool DisableRecipe(uint32 recipe_id); - - /* Tribute */ - bool LoadTributes(); - - /* Doors */ - bool DoorIsOpen(uint8 door_id,const char* zone_name); - void SetDoorPlace(uint8 value,uint8 door_id,const char* zone_name); - bool LoadDoors(int32 iDoorCount, Door *into, const char *zone_name, int16 version); - bool CheckGuildDoor(uint8 doorid,uint16 guild_id, const char* zone); - bool SetGuildDoor(uint8 doorid,uint16 guild_id, const char* zone); - uint32 GetGuildEQID(uint32 guilddbid); - void UpdateDoorGuildID(int doorid, int guild_id); - int32 GetDoorsCount(uint32* oMaxID, const char *zone_name, int16 version); - int32 GetDoorsCountPlusOne(const char *zone_name, int16 version); - int32 GetDoorsDBCountPlusOne(const char *zone_name, int16 version); - void InsertDoor(uint32 did, uint16 ddoorid, const char* ddoor_name, const xyz_heading& position, uint8 dopentype, uint16 dguildid, uint32 dlockpick, uint32 dkeyitem, uint8 ddoor_param, uint8 dinvert, int dincline, uint16 dsize); - - /* Blocked Spells */ - int32 GetBlockedSpellsCount(uint32 zoneid); - bool LoadBlockedSpells(int32 blockedSpellsCount, ZoneSpellsBlocked* into, uint32 zoneid); - - /* Traps */ - bool LoadTraps(const char* zonename, int16 version); - char* GetTrapMessage(uint32 trap_id); - - /* Time */ - uint32 GetZoneTZ(uint32 zoneid, uint32 version); - bool SetZoneTZ(uint32 zoneid, uint32 version, uint32 tz); - - /* Group */ - void RefreshGroupFromDB(Client *c); - uint8 GroupCount(uint32 groupid); - - /* Raid */ - uint8 RaidGroupCount(uint32 raidid, uint32 groupid); - - /* Instancing */ - void ListAllInstances(Client* c, uint32 charid); - - /* QGlobals */ - void QGlobalPurge(); - - /* Alternate Currency */ - void LoadAltCurrencyValues(uint32 char_id, std::map ¤cy); - void UpdateAltCurrencyValue(uint32 char_id, uint32 currency_id, uint32 value); - - /* - * Misc stuff. - * PLEASE DO NOT ADD TO THIS COLLECTION OF CRAP UNLESS YOUR METHOD - * REALLY HAS NO BETTER SECTION - */ - bool logevents(const char* accountname,uint32 accountid,uint8 status,const char* charname,const char* target, const char* descriptiontype, const char* description,int event_nid); - void GetEventLogs(const char* name,char* target,uint32 account_id=0,uint8 eventid=0,char* detail=0,char* timestamp=0, CharacterEventLog_Struct* cel=0); - uint32 GetKarma(uint32 acct_id); - void UpdateKarma(uint32 acct_id, uint32 amount); - - /* Things which really dont belong here... */ - int16 CommandRequirement(const char* commandname); - -protected: - void ZDBInitVars(); - - uint32 max_faction; - Faction** faction_array; - uint32 npc_spells_maxid; - uint32 npc_spellseffects_maxid; - DBnpcspells_Struct** npc_spells_cache; - bool* npc_spells_loadtried; - DBnpcspellseffects_Struct** npc_spellseffects_cache; - bool* npc_spellseffects_loadtried; - uint8 door_isopen_array[255]; -}; - -extern ZoneDatabase database; - -#endif /*ZONEDB_H_*/ -