/* * Ascent MMORPG Server * Copyright (C) 2005-2007 Ascent Team * * 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, either version 3 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; 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, see . * */ #include "StdAfx.h" Player::Player ( uint32 high, uint32 low ) { m_objectTypeId = TYPEID_PLAYER; m_valuesCount = PLAYER_END; m_uint32Values = _fields; memset(m_uint32Values, 0,(PLAYER_END)*sizeof(uint32)); m_updateMask.SetCount(PLAYER_END); SetUInt32Value( OBJECT_FIELD_TYPE,TYPE_PLAYER|TYPE_UNIT|TYPE_OBJECT); SetUInt32Value( OBJECT_FIELD_GUID,low); SetUInt32Value( OBJECT_FIELD_GUID+1,high); m_wowGuid.Init(GetGUID()); iActivePet = 0; resurrector = 0; SpellCrtiticalStrikeRatingBonus=0; SpellHasteRatingBonus = 0; m_lifetapbonus = 0; info = NULL; // Playercreate info bSafeFall = false; SoulStone = 0; SoulStoneReciever = 0; bReincarnation = false; m_furorChance = 0; Seal = 0; judgespell = 0; m_session = 0; TrackingSpell = 0; m_status = 0; offhand_dmg_mod = 0.5; m_runSpeed = PLAYER_NORMAL_RUN_SPEED; m_isMoving = false; m_ShapeShifted = 0; m_curTarget = 0; m_curSelection = 0; m_lootGuid = 0; m_Summon = NULL; m_PetNumberMax = 0; m_lastShotTime = 0; m_H_regenTimer = 0; m_P_regenTimer = 0; m_onTaxi = false; m_taxi_pos_x = 0; m_taxi_pos_y = 0; m_taxi_pos_z = 0; m_taxi_ride_time = 0; m_fallTime = 0; // Attack related variables m_blockfromspellPCT = 0; m_blockfromspell = 0; m_critfromspell = 0; m_spellcritfromspell = 0; m_dodgefromspell = 0; m_parryfromspell = 0; m_hitfromspell = 0; m_hitfrommeleespell = 0; m_meleeattackspeedmod = 0; m_rangedattackspeedmod = 0; m_healthfromspell = 0; m_manafromspell = 0; m_healthfromitems = 0; m_manafromitems = 0; m_talentresettimes = 0; m_nextSave = getMSTime() + sWorld.getIntRate(INTRATE_SAVE); m_currentSpell = NULL; m_resurrectHealth = m_resurrectMana = 0; m_Group = NULL; m_SubGroup = 0; m_GroupInviter = 0; Lfgcomment = ""; for(int i=0;i<3;i++) { LfgType[i]=0; LfgDungeonId[i]=0; } m_Autojoin = true; m_AutoAddMem = false; m_invitersGuid = 0; m_currentMovement = MOVE_UNROOT; m_isGmInvisible = false; //DK m_invitersGuid = 0; //Trade ResetTradeVariables(); mTradeTarget = 0; //Duel DuelingWith = NULL; m_duelCountdownTimer = 0; m_duelStatus = 0; m_duelState = 2; // finished //WayPoint waypointunit = NULL; //PVP //PvPTimeoutEnabled = false; //Tutorials for ( int aX = 0 ; aX < 8 ; aX++ ) m_Tutorials[ aX ] = 0x00; m_lastRestUpdate = 0; m_lootGuid = 0; m_banned = false; //Bind possition m_bind_pos_x = 0; m_bind_pos_y = 0; m_bind_pos_z = 0; m_bind_mapid = 0; m_bind_zoneid = 0; // Rest m_timeLogoff = 0; m_isResting = 0; m_restState = 0; m_restAmount = 0; m_afk_reason = ""; m_playedtime[0] = 0; m_playedtime[1] = 0; m_playedtime[2] = (uint32)time(NULL); m_AllowAreaTriggerPort = true; // Battleground m_bgEntryPointMap = 0; m_bgEntryPointX = 0; m_bgEntryPointY = 0; m_bgEntryPointZ = 0; m_bgEntryPointO = 0; m_bgQueueType = 0; m_bgQueueInstanceId = 0; m_bgIsQueued = false; m_bg = 0; m_bgHasFlag = false; m_bgEntryPointInstance = 0; // gm stuff //m_invincible = false; bGMTagOn = false; CooldownCheat = false; CastTimeCheat = false; PowerCheat = false; GodModeCheat = false; FlyCheat = false; //FIX for professions weapon_proficiency = 0x4000;//2^14 //FIX for shit like shirt etc armor_proficiency = 1; m_bUnlimitedBreath = false; m_UnderwaterState = 0; m_UnderwaterTime = 60000; m_UnderwaterMaxTime = 60000; m_UnderwaterLastDmg = getMSTime(); m_SwimmingTime = 0; m_BreathDamageTimer = 0; //transport shit m_TransporterGUID = 0; m_TransporterX = 0.0f; m_TransporterY = 0.0f; m_TransporterZ = 0.0f; m_TransporterO = 0.0f; m_TransporterUnk = 0.0f; m_lockTransportVariables= false; // Autoshot variables m_AutoShotStartX = 0; m_AutoShotStartY = 0; m_AutoShotStartZ = 0; m_AutoShotTarget = 0; m_onAutoShot = false; m_AutoShotDuration = 0; m_AutoShotAttackTimer = 0; m_AutoShotSpell = NULL; m_AttackMsgTimer = 0; timed_quest_slot = 0; m_GM_SelectedGO = NULL; for(uint32 x = 0;x < 7; x++) { FlatResistanceModifierPos[x] = 0; FlatResistanceModifierNeg[x] = 0; BaseResistanceModPctPos[x] = 0; BaseResistanceModPctNeg[x] = 0; ResistanceModPctPos[x] = 0; ResistanceModPctNeg[x] = 0; SpellDmgDoneByInt[x] = 0; SpellHealDoneByInt[x] = 0; SpellDmgDoneBySpr[x] = 0; SpellHealDoneBySpr[x] = 0; SpellDelayResist[x] = 0; } for(uint32 x = 0; x < 5; x++) { FlatStatModPos[x] = 0; FlatStatModNeg[x] = 0; StatModPctPos[x] = 0; StatModPctNeg[x] = 0; TotalStatModPctPos[x] = 0; TotalStatModPctNeg[x] = 0; } for(uint32 x = 0; x < 12; x++) { IncreaseDamageByType[x] = 0; IncreaseDamageByTypePCT[x] = 0; IncreaseCricticalByTypePCT[x] = 0; } PctIgnoreRegenModifier = 0.0f; m_retainedrage = 0; DetectedRange = 0; m_targetIcon = 0; bShouldHaveLootableOnCorpse = false; m_MountSpellId = 0; bHasBindDialogOpen = false; m_CurrentCharm = NULL; m_CurrentTransporter = NULL; m_SummonedObject = NULL; m_currentLoot = (uint64)NULL; pctReputationMod = 0; roll = 0; mUpdateCount = 0; mCreationCount = 0; bCreationBuffer.reserve(50000); bUpdateBuffer.reserve(50000);//ought to be > than enough ;) mOutOfRangeIds.reserve(20000); mOutOfRangeIdCount = 0; bProcessPending = false; for(int i = 0; i < 25; ++i) m_questlog[i] = NULL; m_ItemInterface = new ItemInterface(this); CurrentGossipMenu = NULL; ResetHeartbeatCoords(); cannibalize = false; m_AreaID = 0; m_actionsDirty = false; cannibalizeCount = 0; rageFromDamageDealt = 0; m_honorToday = 0; m_honorYesterday = 0; m_honorPoints = 0; m_killsToday = 0; m_killsYesterday = 0; m_killsLifetime = 0; m_honorless = false; m_lastSeenWeather = 0; m_attacking = false; myCorpse = 0; bCorpseCreateable = true; blinked = false; m_speedhackChances = 1; m_explorationTimer = getMSTime(); linkTarget = 0; stack_cheat = false; myGuild = 0; m_pvpTimer = 0; m_cooldownTimer = getMSTime() + 10000; GlobalCooldown = 0; m_lastHonorResetTime = 0; memset(&mActions, 0, sizeof(ActionButton) * 120); tutorialsDirty = true; m_TeleportState = 1; m_beingPushed = false; m_charter = 0; flying_aura = 0; resend_speed = false; rename_pending = false; iInstanceType = 0; memset(reputationByListId, 0, sizeof(FactionReputation*) * 128); m_comboTarget = 0; m_comboPoints = 0; chat_disabled_until = 0; SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER, 0.0f); SetFloatValue(UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER, 0.0f); UpdateLastSpeeds(); m_resist_critical[0]=m_resist_critical[1]=0; for (uint32 x =0;x<3;x++) { m_resist_hit[x]=0; } ok_to_remove = false; trigger_on_stun = 0; trigger_on_stun_chance = 100; m_modphyscritdmgPCT = 0; m_RootedCritChanceBonus = 0; m_ModInterrMRegenPCT = 0; m_RegenManaOnSpellResist=0; m_rap_mod_pct = 0; m_modblockvalue = 0; m_summoner = m_summonInstanceId = m_summonMapId = 0; m_lastMoveType = 0; m_tempSummon = 0; m_spellcomboPoints = 0; m_pendingBattleground = 0; m_deathVision = false; m_retainComboPoints = false; last_heal_spell = NULL; m_playerInfo = NULL; m_sentTeleportPosition.ChangeCoords(999999.0f,999999.0f,999999.0f); m_speedChangeCounter=1; memset(&m_bgScore,0,sizeof(BGScore)); } Player::~Player ( ) { Player ** ref; for(ReferenceSet::iterator itr = m_references.begin(); itr != m_references.end(); ++itr) { ref = *itr; if(*ref == this) *ref = NULL; } m_references.clear(); if(!ok_to_remove) { printf("Player deleted from non-logoutplayer!\n"); OutputCrashLogLine("Player deleted from non-logoutplayer!"); #ifdef WIN32 CStackWalker sw; sw.ShowCallstack(); #endif objmgr.RemovePlayer(this); } if(m_session) m_session->SetPlayer(0); Player * pTarget; if(mTradeTarget != 0) { pTarget = GetTradeTarget(); if(pTarget) pTarget->mTradeTarget = 0; } if(m_Summon) m_Summon->ClearPetOwner(); mTradeTarget = 0; if(DuelingWith != 0) DuelingWith->DuelingWith = 0; DuelingWith = 0; CleanupGossipMenu(); ASSERT(!IsInWorld()); sEventMgr.RemoveEvents(this); // delete m_talenttree CleanupChannels(); for(int i = 0; i < 25; ++i) { if(m_questlog[i] != NULL) { delete m_questlog[i]; } } // clean up ITEMCOOLDOWN stuff if (m_itemcooldown.size()) { ItemCooldownSet::iterator itr; for (itr = m_itemcooldown.begin(); itr != m_itemcooldown.end();itr++) { delete (*itr); } } if(m_Group != NULL) m_Group->RemovePlayer(m_playerInfo, this, true); for(SplineMap::iterator itr = _splineMap.begin(); itr != _splineMap.end(); ++itr) delete itr->second; if(m_ItemInterface) delete m_ItemInterface; for(ReputationMap::iterator itr = m_reputation.begin(); itr != m_reputation.end(); ++itr) delete itr->second; m_objectTypeId = TYPEID_UNUSED; } inline uint32 GetSpellForLanguage(uint32 SkillID) { switch(SkillID) { case SKILL_LANG_COMMON: return 668; break; case SKILL_LANG_ORCISH: return 669; break; case SKILL_LANG_TAURAHE: return 670; break; case SKILL_LANG_DARNASSIAN: return 671; break; case SKILL_LANG_DWARVEN: return 672; break; case SKILL_LANG_THALASSIAN: return 813; break; case SKILL_LANG_DRACONIC: return 814; break; case SKILL_LANG_DEMON_TONGUE: return 815; break; case SKILL_LANG_TITAN: return 816; break; case SKILL_LANG_OLD_TONGUE: return 817; break; case SKILL_LANG_GNOMISH: return 7430; break; case SKILL_LANG_TROLL: return 7341; break; case SKILL_LANG_GUTTERSPEAK: return 17737; break; case SKILL_LANG_DRAENEI: return 29932; break; } return 0; } ///==================================================================== /// Create /// params: p_newChar /// desc: data from client to create a new character ///==================================================================== bool Player::Create(WorldPacket& data ) { uint8 race,class_,gender,skin,face,hairStyle,hairColor,facialHair,outfitId; // unpack data into member variables data >> m_name; // correct capitalization CapitalizeString(m_name); data >> race >> class_ >> gender >> skin >> face; data >> hairStyle >> hairColor >> facialHair >> outfitId; info = objmgr.GetPlayerCreateInfo(race, class_); if(!info) { // info not found... disconnect sCheatLog.writefromsession(m_session, "tried to create invalid player with race %u and class %u", race, class_); m_session->Disconnect(); return false; } // check that the account CAN create TBC characters, if we're making some if(race >= RACE_BLOODELF && !m_session->HasFlag(ACCOUNT_FLAG_XPACK_01)) { sCheatLog.writefromsession(m_session, "tried to create player with race %u and class %u but no expansion flags", race, class_); m_session->Disconnect(); return false; } m_mapId = info->mapId; m_zoneId = info->zoneId; m_position.ChangeCoords(info->positionX, info->positionY, info->positionZ); m_bind_pos_x = info->positionX; m_bind_pos_y = info->positionY; m_bind_pos_z = info->positionZ; m_bind_mapid = info->mapId; m_bind_zoneid = info->zoneId; m_isResting = 0; m_restAmount = 0; m_restState = 0; memset(m_taximask, 0, sizeof(uint32)*8); // set race dbc myRace = sCharRaceStore.LookupEntry(race); myClass = sCharClassStore.LookupEntry(class_); if(!myRace || !myClass) { // information not found sCheatLog.writefromsession(m_session, "tried to create invalid player with race %u and class %u, dbc info not found", race, class_); m_session->Disconnect(); return false; } if(myRace->team_id == 7) m_team = 0; else m_team = 1; sLog.outString("Account %s creating a %s %s %s", m_session->GetAccountName().c_str(), gender ? "Female" : "Male", sCharRaceStore.LookupString(myRace->name2), sCharClassStore.LookupString(myClass->name)); uint8 powertype = myClass->power_type; // Automatically add the race's taxi hub to the character's taximask at creation time ( 1 << (taxi_node_id-1) ) memset(m_taximask,0,sizeof(m_taximask)); switch(race) { case RACE_TAUREN: m_taximask[0]= 1 << (22-1); break; case RACE_HUMAN: m_taximask[0]= 1 << ( 2-1); break; case RACE_DWARF: m_taximask[0]= 1 << ( 6-1); break; case RACE_GNOME: m_taximask[0]= 1 << ( 6-1); break; case RACE_ORC: m_taximask[0]= 1 << (23-1); break; case RACE_TROLL: m_taximask[0]= 1 << (23-1); break; case RACE_UNDEAD: m_taximask[0]= 1 << (11-1); break; case RACE_NIGHTELF: m_taximask[0]= 1 << (27-1); break; case RACE_BLOODELF: m_taximask[2]= 1 << (18-1); break; case RACE_DRAENEI: m_taximask[2]= 1 << (30-1); break; } // Set Starting stats for char SetFloatValue(OBJECT_FIELD_SCALE_X, ((race==RACE_TAUREN)?1.3f:1.0f)); SetUInt32Value(UNIT_FIELD_HEALTH, info->health); SetUInt32Value(UNIT_FIELD_POWER1, info->mana ); //SetUInt32Value(UNIT_FIELD_POWER2, 0 ); // this gets devided by 10 SetUInt32Value(UNIT_FIELD_POWER3, info->focus ); SetUInt32Value(UNIT_FIELD_POWER4, info->energy ); SetUInt32Value(UNIT_FIELD_MAXHEALTH, info->health); SetUInt32Value(UNIT_FIELD_MAXPOWER1, info->mana ); SetUInt32Value(UNIT_FIELD_MAXPOWER2, info->rage ); SetUInt32Value(UNIT_FIELD_MAXPOWER3, info->focus ); SetUInt32Value(UNIT_FIELD_MAXPOWER4, info->energy ); //THIS IS NEEDED SetUInt32Value(UNIT_FIELD_BASE_HEALTH, info->health); SetUInt32Value(UNIT_FIELD_BASE_MANA, info->mana ); SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, info->factiontemplate ); SetUInt32Value(UNIT_FIELD_LEVEL, 1 ); SetUInt32Value(UNIT_FIELD_BYTES_0, ( ( race ) | ( class_ << 8 ) | ( gender << 16 ) | ( powertype << 24 ) ) ); //UNIT_FIELD_BYTES_1 (standstate) | (unk1) | (unk2) | (attackstate) if(class_ == WARRIOR) SetShapeShift(FORM_BATTLESTANCE); SetUInt32Value(UNIT_FIELD_BYTES_2, (0x28 << 8) ); SetFlag(UNIT_FIELD_FLAGS , U_FIELD_FLAG_PLAYER_CONTROLLED ); SetUInt32Value(UNIT_FIELD_STAT0, info->strength ); SetUInt32Value(UNIT_FIELD_STAT1, info->ability ); SetUInt32Value(UNIT_FIELD_STAT2, info->stamina ); SetUInt32Value(UNIT_FIELD_STAT3, info->intellect ); SetUInt32Value(UNIT_FIELD_STAT4, info->spirit ); SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, 0.388999998569489f ); SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f ); if(race != 10) { SetUInt32Value(UNIT_FIELD_DISPLAYID, info->displayId + gender ); SetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID, info->displayId + gender ); } else { SetUInt32Value(UNIT_FIELD_DISPLAYID, info->displayId - gender ); SetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID, info->displayId - gender ); } //SetFloatValue(UNIT_FIELD_MINDAMAGE, info->mindmg ); //SetFloatValue(UNIT_FIELD_MAXDAMAGE, info->maxdmg ); SetUInt32Value(UNIT_FIELD_ATTACK_POWER, info->attackpower ); SetUInt32Value(PLAYER_BYTES, ((skin) | (face << 8) | (hairStyle << 16) | (hairColor << 24))); //PLAYER_BYTES_2 GM ON/OFF BANKBAGSLOTS RESTEDSTATE // SetUInt32Value(PLAYER_BYTES_2, (facialHair | (0xEE << 8) | (0x01 << 16) | (0x02 << 24))); SetUInt32Value(PLAYER_BYTES_2, (facialHair /*| (0xEE << 8)*/ | (0x02 << 24)));//no bank slot by default! //PLAYER_BYTES_3 DRUNKENSTATE PVPRANK SetUInt32Value(PLAYER_BYTES_3, ((gender) | (0x00 << 8) | (0x00 << 16) | (GetPVPRank() << 24))); SetUInt32Value(PLAYER_NEXT_LEVEL_XP, 400); SetUInt32Value(PLAYER_FIELD_BYTES, 0x08 ); SetUInt32Value(PLAYER_CHARACTER_POINTS2,2); SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f); SetUInt32Value(PLAYER_FIELD_MAX_LEVEL, sWorld.Expansion1LevelCap); for(uint32 x=0;x<7;x++) SetFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT+x, 1.00); SetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, 0xEEEEEEEE); m_StableSlotCount = 0; Item *item; for(std::list::iterator sp = info->spell_list.begin();sp!=info->spell_list.end();sp++) { mSpells.insert((*sp)); } m_FirstLogin = true; skilllineentry * se; for(std::list::iterator ss = info->skills.begin(); ss!=info->skills.end(); ss++) { se = sSkillLineStore.LookupEntry(ss->skillid); if(se->type != SKILL_TYPE_LANGUAGE) _AddSkillLine(se->id, ss->currentval, ss->maxval); } //Chances depend on stats must be in this order! // UpdateStats(); //UpdateChances(); _InitialReputation(); // Add actionbars for(std::list::iterator itr = info->actionbars.begin();itr!=info->actionbars.end();++itr) { setAction(itr->button, itr->action, itr->type, itr->misc); } for(std::list::iterator is = info->items.begin(); is!=info->items.end(); is++) { if ( (*is).protoid != 0) { item=objmgr.CreateItem((*is).protoid,this); if(item) { item->SetUInt32Value(ITEM_FIELD_STACK_COUNT,(*is).amount); if((*is).slotSafeAddItem(item, INVENTORY_SLOT_NOT_SET, (*is).slot); else GetItemInterface()->AddItemToFreeSlot(item); } } } load_health = m_uint32Values[UNIT_FIELD_HEALTH]; load_mana = m_uint32Values[UNIT_FIELD_POWER1]; return true; } void Player::Update( uint32 p_time ) { if(!IsInWorld()) return; Unit::Update( p_time ); uint32 mstime = getMSTime(); if(m_attacking) { // Check attack timer. if(mstime >= m_attackTimer) _EventAttack(false); if(m_duelWield && mstime >= m_attackTimer_1) _EventAttack(true); } if(m_onAutoShot) { if(m_AutoShotAttackTimer > p_time) m_AutoShotAttackTimer -= p_time; else EventRepeatSpell(); } else if(m_AutoShotAttackTimer > 0) { if(m_AutoShotAttackTimer > p_time) m_AutoShotAttackTimer -= p_time; else m_AutoShotAttackTimer = 0; } // Breathing if(m_UnderwaterState & UNDERWATERSTATE_UNDERWATER) { // keep subtracting timer if(m_UnderwaterTime) { // not taking dmg yet if(p_time >= m_UnderwaterTime) m_UnderwaterTime = 0; else m_UnderwaterTime -= p_time; } if(!m_UnderwaterTime) { // check last damage dealt timestamp, and if enough time has elapsed deal damage if(mstime >= m_UnderwaterLastDmg) { uint32 damage = m_uint32Values[UNIT_FIELD_MAXHEALTH] / 10; WorldPacket data(SMSG_ENVIRONMENTALDAMAGELOG, 21); data << GetGUID() << uint8(DAMAGE_DROWNING) << damage << uint64(0); SendMessageToSet(&data, true); DealDamage(this, damage, 0, 0, 0); m_UnderwaterLastDmg = mstime + 1000; } } } else { // check if we're not on a full breath timer if(m_UnderwaterTime < m_UnderwaterMaxTime) { // regenning m_UnderwaterTime += (p_time * 10); if(m_UnderwaterTime >= m_UnderwaterMaxTime) { m_UnderwaterTime = m_UnderwaterMaxTime; StopMirrorTimer(1); } } } // Lava Damage if(m_UnderwaterState & UNDERWATERSTATE_LAVA) { // check last damage dealt timestamp, and if enough time has elapsed deal damage if(mstime >= m_UnderwaterLastDmg) { uint32 damage = m_uint32Values[UNIT_FIELD_MAXHEALTH] / 5; WorldPacket data(SMSG_ENVIRONMENTALDAMAGELOG, 21); data << GetGUID() << uint8(DAMAGE_LAVA) << damage << uint64(0); SendMessageToSet(&data, true); DealDamage(this, damage, 0, 0, 0); m_UnderwaterLastDmg = mstime + 1000; } } // Autosave if(mstime >= m_nextSave) SaveToDB(false); if(m_CurrentTransporter && !m_lockTransportVariables) { // Update our position, using trnasporter X/Y float c_tposx = m_CurrentTransporter->GetPositionX() + m_TransporterX; float c_tposy = m_CurrentTransporter->GetPositionY() + m_TransporterY; float c_tposz = m_CurrentTransporter->GetPositionZ() + m_TransporterZ; SetPosition(c_tposx, c_tposy, c_tposz, GetOrientation(), false); } // Exploration if(mstime >= m_explorationTimer) { _EventExploration(); m_explorationTimer = mstime + 3000; } // Cooldowns if(mstime >= m_cooldownTimer) { UpdateCooldowns(); m_cooldownTimer = mstime + 10000; } if(m_pvpTimer) { if(p_time >= m_pvpTimer) { RemovePvPFlag(); m_pvpTimer = 0; } else m_pvpTimer -= p_time; } } void Player::EventDismount(uint32 money, float x, float y, float z) { ModUInt32Value( PLAYER_FIELD_COINAGE , -(int32)money ); SetPosition(x, y, z, true); SetTaxiState(false); SetTaxiPath(NULL); UnSetTaxiPos(); m_taxi_ride_time = 0; //uint32 modelid = GetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID); SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID , 0); RemoveFlag( UNIT_FIELD_FLAGS, U_FIELD_FLAG_MOUNT_SIT ); if (HasFlag(UNIT_FIELD_FLAGS, U_FIELD_FLAG_LOCK_PLAYER )) RemoveFlag( UNIT_FIELD_FLAGS, U_FIELD_FLAG_LOCK_PLAYER ); SetPlayerSpeed(RUN, m_runSpeed); sEventMgr.RemoveEvents(this, EVENT_PLAYER_TAXI_INTERPOLATE); // Save to database on dismount SaveToDB(false); // If we have multiple "trips" to do, "jump" on the next one :p if(m_taxiPaths.size()) { TaxiPath * p = *m_taxiPaths.begin(); m_taxiPaths.erase(m_taxiPaths.begin()); TaxiStart(p, taxi_model_id, 0); } } void Player::_EventAttack(bool offhand) { if (m_currentSpell) { m_currentSpell->cancel(); setAttackTimer(500, offhand); return; } Unit *pVictim = 0; if(m_curSelection) pVictim = GetMapMgr()->GetUnit(m_curSelection); //Can't find victim, stop attacking if (!pVictim) { sLog.outDetail("Player::Update: No valid current selection to attack, stopping attack\n"); setHRegenTimer(5000); //prevent clicking off creature for a quick heal EventAttackStop(); return; } if (!canReachWithAttack(pVictim)) { if(m_AttackMsgTimer != 1) { m_session->OutPacket(SMSG_ATTACKSWING_NOTINRANGE); m_AttackMsgTimer = 1; } setAttackTimer(300, offhand); } else if(!isInFront(pVictim)) { // We still have to do this one. if(m_AttackMsgTimer != 2) { m_session->OutPacket(SMSG_ATTACKSWING_BADFACING); m_AttackMsgTimer = 2; } setAttackTimer(300, offhand); } else { m_AttackMsgTimer = 0; // Set to weapon time. setAttackTimer(0, offhand); //pvp timeout reset if(pVictim->IsPlayer()) { if (static_cast(pVictim)->cannibalize) { sEventMgr.RemoveEvents(pVictim, EVENT_CANNIBALIZE); pVictim->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); static_cast(pVictim)->cannibalize = false; } } if(this->IsStealth()) { SetStealth(0); } if (!GetOnMeleeSpell()) { Strike(pVictim,(uint32)offhand,NULL,0,0,0, false); } else { SpellEntry *spellInfo = sSpellStore.LookupEntry(GetOnMeleeSpell()); SetOnMeleeSpell(0); Spell *spell = new Spell(this,spellInfo,true,NULL); SpellCastTargets targets; targets.m_unitTarget = GetSelection(); spell->prepare(&targets); } } } void Player::_EventCharmAttack() { if(!m_CurrentCharm) return; Unit *pVictim = NULL; if(m_curSelection == 0) { sEventMgr.RemoveEvents(this, EVENT_PLAYER_CHARM_ATTACK); return; } pVictim= GetMapMgr()->GetUnit(m_curSelection); //Can't find victim, stop attacking if (!pVictim) { sLog.outError( "WORLD: "I64FMT" doesn't exist.",m_curSelection); sLog.outDetail("Player::Update: No valid current selection to attack, stopping attack\n"); this->setHRegenTimer(5000); //prevent clicking off creature for a quick heal clearStateFlag(UF_ATTACKING); EventAttackStop(); } else { if (!m_CurrentCharm->canReachWithAttack(pVictim)) { if(m_AttackMsgTimer == 0) { //m_session->OutPacket(SMSG_ATTACKSWING_NOTINRANGE); m_AttackMsgTimer = 2000; // 2 sec till next msg. } // Shorten, so there isnt a delay when the client IS in the right position. sEventMgr.ModifyEventTimeLeft(this, EVENT_PLAYER_CHARM_ATTACK, 100); } else if(!m_CurrentCharm->isInFront(pVictim)) { if(m_AttackMsgTimer == 0) { m_session->OutPacket(SMSG_ATTACKSWING_BADFACING); m_AttackMsgTimer = 2000; // 2 sec till next msg. } // Shorten, so there isnt a delay when the client IS in the right position. sEventMgr.ModifyEventTimeLeft(this, EVENT_PLAYER_CHARM_ATTACK, 100); } else { //if(pVictim->GetTypeId() == TYPEID_UNIT) // pVictim->GetAIInterface()->StopMovement(5000); //pvp timeout reset /*if(pVictim->IsPlayer()) { if(((Player*)pVictim)->DuelingWith == NULL)//Dueling doesn't trigger PVP ((Player*)pVictim)->PvPTimeoutUpdate(false); //update targets timer if(DuelingWith == NULL)//Dueling doesn't trigger PVP PvPTimeoutUpdate(false); //update casters timer }*/ if (!m_CurrentCharm->GetOnMeleeSpell()) { m_CurrentCharm->Strike(pVictim,0,NULL,0,0,0, false); } else { SpellEntry *spellInfo = sSpellStore.LookupEntry(m_CurrentCharm->GetOnMeleeSpell()); m_CurrentCharm->SetOnMeleeSpell(0); Spell *spell = new Spell(m_CurrentCharm,spellInfo,true,NULL); SpellCastTargets targets; targets.m_unitTarget = GetSelection(); spell->prepare(&targets); //delete spell; // deleted automatically, no need to do this. } } } } void Player::EventAttackStart() { m_attacking = true; if(!sEventMgr.HasEvent(this,EVENT_ATTACK_TIMEOUT)) //do not add excesive attack events sEventMgr.AddEvent(this,&Player::EventAttackStop,EVENT_ATTACK_TIMEOUT,PLAYER_ATTACK_TIMEOUT_INTERVAL,1,0); //attack timeout on no attack after 5 seconds if(m_MountSpellId) RemoveAura(m_MountSpellId); } void Player::EventAttackStop() { if(m_CurrentCharm != NULL) sEventMgr.RemoveEvents(this, EVENT_PLAYER_CHARM_ATTACK); sEventMgr.RemoveEvents(this,EVENT_ATTACK_TIMEOUT); //have no idea why we need an attack timeout event setAttackTarget(NULL); // nice and simple! m_attacking = false; } void Player::_EventExploration() { if (isDead()) return; if (!IsInWorld()) return; if(m_position.x > _maxX || m_position.x < _minX || m_position.y > _maxY || m_position.y < _minY) return; if(GetMapMgr()->GetCellByCoords(GetPositionX(),GetPositionY()) == NULL) return; uint16 AreaId = GetMapMgr()->GetAreaID(GetPositionX(),GetPositionY()); if(!AreaId || AreaId == 0xFFFF) return; AreaTable * at = sAreaStore.LookupEntry(AreaId); if(at == 0) return; /*char areaname[200]; if(at) { strcpy(areaname, sAreaStore.LookupString((uint32)at->name)); } else { strcpy(areaname, "UNKNOWN"); } sChatHandler.BlueSystemMessageToPlr(this,areaname);*/ int offset = at->explorationFlag / 32; offset += PLAYER_EXPLORED_ZONES_1; uint32 val = (uint32)(1 << (at->explorationFlag % 32)); uint32 currFields = GetUInt32Value(offset); if(AreaId != m_AreaID) { m_AreaID = AreaId; UpdatePvPArea(); } if(at->ZoneId != 0 && m_zoneId != at->ZoneId) ZoneUpdate(at->ZoneId); // Check for a restable area if(at->AreaFlags & AREA_CITY_AREA || at->AreaFlags & AREA_CITY) { // check faction if((at->category == AREAC_ALLIANCE_TERRITORY && GetTeam() == 0) || (at->category == AREAC_HORDE_TERRITORY && GetTeam() == 1) ) { if(!m_isResting) ApplyPlayerRestState(true); } else if(at->category != AREAC_ALLIANCE_TERRITORY && at->category != AREAC_HORDE_TERRITORY) { if(!m_isResting) ApplyPlayerRestState(true); } else { if(m_isResting) ApplyPlayerRestState(false); } } else { //second AT check for subzones. if(at->ZoneId) { AreaTable * at2 = sAreaStore.LookupEntry(at->ZoneId); if(at2 && at2->AreaFlags & AREA_CITY_AREA || at2 && at2->AreaFlags & AREA_CITY) { if((at2->category == AREAC_ALLIANCE_TERRITORY && GetTeam() == 0) || (at2->category == AREAC_HORDE_TERRITORY && GetTeam() == 1) ) { if(!m_isResting) ApplyPlayerRestState(true); } else if(at2->category != AREAC_ALLIANCE_TERRITORY && at2->category != AREAC_HORDE_TERRITORY) { if(!m_isResting) ApplyPlayerRestState(true); } else { if(m_isResting) ApplyPlayerRestState(false); } } } else { if(m_isResting) ApplyPlayerRestState(false); } } if( !(currFields & val) && !GetTaxiState() && !m_TransporterGUID)//Unexplored Area // bur: we dont want to explore new areas when on taxi { SetUInt32Value(offset, (uint32)(currFields | val)); uint32 explore_xp = at->level * 10; WorldPacket data(SMSG_EXPLORATION_EXPERIENCE, 8); data << at->AreaId << explore_xp; m_session->SendPacket(&data); if(getLevel() < GetUInt32Value(PLAYER_FIELD_MAX_LEVEL) && explore_xp) GiveXP(explore_xp, 0, false); } } void Player::EventDeath() { if (m_state & UF_ATTACKING) EventAttackStop(); if (m_onTaxi) sEventMgr.RemoveEvents(this, EVENT_PLAYER_TAXI_DISMOUNT); if(!IS_INSTANCE(GetMapId()) && !sEventMgr.HasEvent(this,EVENT_PLAYER_FORECED_RESURECT)) //Should never be true sEventMgr.AddEvent(this,&Player::RepopRequestedPlayer,EVENT_PLAYER_FORECED_RESURECT,PLAYER_FORCED_RESURECT_INTERVAL,1,0); //in case he forgets to release spirit (afk or something) } void Player::BuildEnumData( WorldPacket * p_data ) { *p_data << GetGUID(); *p_data << m_name; //uint32 bytes = GetUInt32Value(UNIT_FIELD_BYTES_0); //#ifdef USING_BIG_ENDIAN //SetUInt32Value(UNIT_FIELD_BYTES_0, swap32(UNIT_FIELD_BYTES_0)); //#endif *p_data << GetByte(UNIT_FIELD_BYTES_0,0);//uint8(bytes & 0xff); // race *p_data << GetByte(UNIT_FIELD_BYTES_0,1);//uint8((bytes >> 8) & 0xff); // class *p_data << GetByte(UNIT_FIELD_BYTES_0,2);//uint8((bytes >> 16) & 0xff); // gender //#ifdef USING_BIG_ENDIAN //SetUInt32Value(UNIT_FIELD_BYTES_0, swap32(UNIT_FIELD_BYTES_0)); //#endif *p_data << GetUInt32Value(PLAYER_BYTES); /* bytes = GetUInt32Value(PLAYER_BYTES); *p_data << uint8(bytes & 0xff); //skin *p_data << uint8((bytes >> 8) & 0xff); //face *p_data << uint8((bytes >> 16) & 0xff); //hairstyle *p_data << uint8((bytes >> 24) & 0xff); //haircolor */ ///bytes = GetUInt32Value(PLAYER_BYTES_2); #ifdef USING_BIG_ENDIAN *p_data << GetByte(PLAYER_BYTES_2,3);//uint8(bytes & 0xff); //facialhair #else *p_data << GetByte(PLAYER_BYTES_2,0);//uint8(bytes & 0xff); //facialhair #endif *p_data << uint8(getLevel()); //level *p_data << m_zoneId; *p_data << m_mapId; *p_data << m_position; *p_data << GetUInt32Value(PLAYER_GUILDID);// guild if(rename_pending) *p_data << uint32(0x00A04342); // wtf blizz? :P else if(m_banned) *p_data << (uint32)7; // Banned (cannot login) else if(isDead()) *p_data << (uint32)8704; // Dead (displaying as Ghost) else *p_data << (uint32)1; // Alive *p_data << (uint8)m_restState; // rest state // pet stuff CreatureInfo *info = NULL; if(getClass()==WARLOCK || getClass()==HUNTER) { QueryResult *result = CharacterDatabase.Query("SELECT entryid FROM playerpets WHERE ownerguid=%u AND active=1", GetGUIDLow()); if(result) { info = CreatureNameStorage.LookupEntry(result->Fetch()[0].GetUInt32()); delete result; } } if(info) //PET INFO uint32 displayid, uint32 level, uint32 familyid *p_data << uint32(info->DisplayID) << uint32(getLevel()) << uint32(info->Family); else *p_data << uint32(0) << uint32(0) << uint32(0); for (uint32 i = 0; i < EQUIPMENT_SLOT_END ; i++)//max equipment slot is 18....this is strange { if (GetItemInterface()->GetInventoryItem(i) != NULL) { *p_data << (uint32)GetItemInterface()->GetInventoryItem(i)->GetProto()->DisplayInfoID; *p_data << (uint8)GetItemInterface()->GetInventoryItem(i)->GetProto()->InventoryType; } else { *p_data << (uint32)0; *p_data << (uint8)0; } } //blizz send 20 slots for some reason(or no reason :P) *p_data << (uint32)0; *p_data << (uint8)0; } /// This function sends the message displaying the purple XP gain for the char /// It assumes you will send out an UpdateObject packet at a later time. void Player::GiveXP(uint32 xp, const uint64 &guid, bool allowbonus) { WorldPacket data(SMSG_LOG_XPGAIN, 21); uint32 restxp = xp; if ( xp < 1 ) return; if(!GetSession()->CanUseCommand('p')) if(getLevel() >= GetUInt32Value(PLAYER_FIELD_MAX_LEVEL)) return; if(m_restState == RESTSTATE_RESTED && allowbonus) { restxp = SubtractRestXP(xp); xp += restxp; } UpdateRestState(); if (guid == 0) { data << uint64(0); data << uint32(xp); data << uint8(0); data << uint32(restxp); data << float(1.0f); // static data.. Seems to always be 1.0f } else { data << guid; data << uint32(xp); // Normal XP data << uint8(0); // Unknown.. seems to always be 0 data << uint32(restxp); // "Rest XP", is equal to XP for no rest xp message data << float(1.0f); // static data.. Seems to always be 1.0f } GetSession()->SendPacket(&data); /*uint32 curXP = GetUInt32Value(PLAYER_XP); uint32 nextLvlXP = GetUInt32Value(PLAYER_NEXT_LEVEL_XP); uint32 newXP = curXP + xp; uint32 level = GetUInt32Value(UNIT_FIELD_LEVEL); bool levelup = false; if(m_Summon != NULL && m_Summon->GetUInt32Value(UNIT_CREATED_BY_SPELL) == 0) m_Summon->GiveXP(xp); uint32 TotalHealthGain = 0, TotalManaGain = 0; uint32 cl=getClass(); // Check for level-up while (newXP >= nextLvlXP) { levelup = true; // Level-Up! newXP -= nextLvlXP; // reset XP to 0, but add extra from this xp add level ++; // increment the level if( level > 9) { //Give Talent Point uint32 curTalentPoints = GetUInt32Value(PLAYER_CHARACTER_POINTS1); SetUInt32Value(PLAYER_CHARACTER_POINTS1,curTalentPoints+1); } }*/ if(m_Summon != NULL && m_Summon->GetUInt32Value(UNIT_CREATED_BY_SPELL) == 0) m_Summon->GiveXP(xp); int32 newxp = m_uint32Values[PLAYER_XP] + xp; int32 nextlevelxp = lvlinfo->XPToNextLevel; uint32 level = m_uint32Values[UNIT_FIELD_LEVEL]; LevelInfo * li; bool levelup = false; while(newxp >= nextlevelxp && newxp > 0) { ++level; li = objmgr.GetLevelInfo(getRace(), getClass(), level); newxp -= nextlevelxp; nextlevelxp = li->XPToNextLevel; levelup = true; if(level > 9) ModUInt32Value(PLAYER_CHARACTER_POINTS1, 1); if(level >= GetUInt32Value(PLAYER_FIELD_MAX_LEVEL)) break; } if(level > GetUInt32Value(PLAYER_FIELD_MAX_LEVEL)) level = GetUInt32Value(PLAYER_FIELD_MAX_LEVEL); if(levelup) { m_playedtime[0] = 0; //Reset the "Current level played time" SetUInt32Value(UNIT_FIELD_LEVEL, level); LevelInfo * oldlevel = lvlinfo; lvlinfo = objmgr.GetLevelInfo(getRace(), getClass(), level); CalculateBaseStats(); // Generate Level Info Packet data.Initialize(SMSG_LEVELUP_INFO); data << level; data << uint32(lvlinfo->HP - oldlevel->HP); data << uint32(lvlinfo->Mana - oldlevel->Mana); // grep: these are probably the other powers :) data << uint32(0); data << uint32(0); data << uint32(0); data << uint32(0); // Append stat differences data << uint32(lvlinfo->Stat[0] - oldlevel->Stat[0]); data << uint32(lvlinfo->Stat[1] - oldlevel->Stat[1]); data << uint32(lvlinfo->Stat[2] - oldlevel->Stat[2]); data << uint32(lvlinfo->Stat[3] - oldlevel->Stat[3]); data << uint32(lvlinfo->Stat[4] - oldlevel->Stat[4]); // Send to client GetSession()->SendPacket(&data); _UpdateMaxSkillCounts(); UpdateStats(); //UpdateChances(); // Set next level conditions SetUInt32Value(PLAYER_NEXT_LEVEL_XP, lvlinfo->XPToNextLevel); // Set stats for(uint32 i = 0; i < 5; ++i) { BaseStats[i] = lvlinfo->Stat[i]; CalcStat(i); } //set full hp and mana SetUInt32Value(UNIT_FIELD_HEALTH,GetUInt32Value(UNIT_FIELD_MAXHEALTH)); SetUInt32Value(UNIT_FIELD_POWER1,GetUInt32Value(UNIT_FIELD_MAXPOWER1)); sSocialMgr.SendUpdateToFriends( this ); } // Set the update bit SetUInt32Value(PLAYER_XP, newxp); HandleProc(PROC_ON_GAIN_EXPIERIENCE, this, NULL); m_procCounter = 0; } void Player::smsg_InitialSpells() { uint16 spellCount = mSpells.size(); uint16 itemCount = m_itemcooldown.size(); WorldPacket data(SMSG_INITIAL_SPELLS, 5 + (spellCount * 4) + (itemCount * 4) ); data << uint8(0); data << uint16(spellCount); // spell count SpellSet::iterator itr; for (itr = mSpells.begin(); itr != mSpells.end(); ++itr) { // todo: check out when we should send 0x0 and when we should send 0xeeee // this is not slot,values is always eeee or 0,seems to be cooldown data << uint16(*itr); // spell id data << uint16(0xeeee); } if (itemCount) { data << uint16(itemCount); // item / spell count ItemCooldownSet::iterator itr; for (itr = m_itemcooldown.begin(); itr != m_itemcooldown.end(); ++itr) { //ItemCooldown * testje = (*itr); // for debug data << uint16((*itr)->SpellID); // spell id data << uint16((*itr)->ItemEntry); // item entry data << uint16((*itr)->SpellCategory); // spell Category // if no spell category do it manualy if (!(*itr)->SpellCategory) { data << uint32((*itr)->CooldownTimeStamp - now()); data << uint32(0); } else { data << uint32(0); data << uint32((*itr)->CooldownTimeStamp - now()); // Current time time remaining of the cooldown in ms } } } else { data << uint16(0); } GetSession()->SendPacket(&data); //Log::getSingleton( ).outDetail( "CHARACTER: Sent Initial Spells" ); } /* Saves ItemCooldowns checks for invalid items and deletes them from the list and don't save them */ void Player::_SaveItemCooldown() { if(CooldownCheat) return; // if we have nothing to save why save? if (m_itemcooldown.size() == 0) return; CharacterDatabase.Execute("DELETE FROM playercooldownitems WHERE OwnerGuid = %u", GetGUIDLow() ); uint32 entrys_to_insert=0; std::stringstream query; query << "INSERT INTO playercooldownitems (OwnerGuid, ItemEntry, SpellID, SpellCategory, CooldownTimeStamp, Cooldown) VALUES "; ItemCooldownSet::iterator itr, it2, itrend; itrend = m_itemcooldown.end(); for (itr = m_itemcooldown.begin(); itr != m_itemcooldown.end(); ) { if (now() > (*itr)->CooldownTimeStamp) // if item is invalid { ItemCooldown * temp = (*itr); // get a temp pointer it2 = itr; ++itr; if(temp) { m_itemcooldown.erase(it2); // remove the object of the list delete temp; // delete its mem, using the temp pointer } continue; } if(entrys_to_insert>0) query << ","; query << "("<< GetGUIDLow() << "," << (*itr)->ItemEntry << "," << (*itr)->SpellID << "," << (*itr)->SpellCategory << "," << (*itr)->CooldownTimeStamp << "," << (*itr)->Cooldown << ")"; ++itr; entrys_to_insert++; } //only execute if we have entrys to insert if(entrys_to_insert) CharacterDatabase.Execute( query.str().c_str() ); } void Player::_SaveSpellCoolDownSecurity() { if(CooldownCheat) return; // if we have nothing to save, then why save? if (SpellCooldownMap.size() == 0) return; CharacterDatabase.Execute("DELETE FROM playercooldownsecurity WHERE OwnerGuid = %u", GetGUIDLow() ); SpellCooldownHolderMap::iterator itr, it2, itrend; std::stringstream query; query << "INSERT INTO playercooldownsecurity (OwnerGuid, SpellID, TimeStamp) VALUES "; itrend = SpellCooldownMap.end(); uint32 SpellID; uint32 TimeStamp; uint32 ts = now(); uint8 hascooldowns=0; for (itr = SpellCooldownMap.begin(); itr != SpellCooldownMap.end();) { SpellID = itr->first; TimeStamp = itr->second; if (ts > TimeStamp) // if cooldown is valid { it2 = itr; ++itr; SpellCooldownMap.erase(it2); continue; } if(hascooldowns) query << ","; query << "(" << GetGUIDLow() << "," << SpellID << "," << TimeStamp << ")"; ++itr; hascooldowns++; } if(hascooldowns) CharacterDatabase.Execute( query.str().c_str( ) ); } void Player::_SavePet() { // Remove any existing info CharacterDatabase.Execute("DELETE FROM playerpets WHERE ownerguid=%u", GetGUIDLow()); if(m_Summon) // update PlayerPets array with current pet's info { m_Summon->UpdatePetInfo(false); if(!m_Summon->Summon) // is a pet { // save pet spellz PetSpellMap::iterator itr = m_Summon->mSpells.begin(); uint32 pn = m_Summon->m_PetNumber; CharacterDatabase.Execute("DELETE FROM playerpetspells WHERE petnumber=%u", pn); for(; itr != m_Summon->mSpells.end(); ++itr) { CharacterDatabase.Execute("INSERT INTO playerpetspells VALUES(%u, %u, %u, %u)", GetGUIDLow(), pn, itr->first->Id, itr->second); } } } std::stringstream ss; for(std::map::iterator itr = m_Pets.begin(); itr != m_Pets.end(); itr++) { ss.rdbuf()->str(""); ss << "INSERT INTO playerpets VALUES('" << GetGUIDLow() << "','" << itr->second->number << "','" << CharacterDatabase.EscapeString(itr->second->name) << "','" << itr->second->entry << "','" << itr->second->fields << "','" << itr->second->xp << "','" << itr->second->active << "','" << itr->second->level << "','" << itr->second->happiness << "','" << itr->second->actionbar << "','" << itr->second->happinessupdate << "','" << itr->second->summon << "','" << itr->second->autocastspell << "','" << itr->second->loyaltypts << "','" << itr->second->loyaltyupdate << "')"; CharacterDatabase.Execute(ss.str().c_str()); } } void Player::_SavePetSpells() { // Remove any existing CharacterDatabase.Execute("DELETE FROM playersummonspells WHERE ownerguid=%u", GetGUIDLow()); // Save summon spells map >::iterator itr = SummonSpells.begin(); for(; itr != SummonSpells.end(); ++itr) { set::iterator it = itr->second.begin(); for(; it != itr->second.end(); ++it) { CharacterDatabase.Execute("INSERT INTO playersummonspells VALUES(%u, %u, %u)", GetGUIDLow(), itr->first, (*it)); } } } void Player::AddSummonSpell(uint32 Entry, uint32 SpellID) { SummonSpells[Entry].insert(SpellID); } void Player::RemoveSummonSpell(uint32 Entry, uint32 SpellID) { map >::iterator itr = SummonSpells.find(Entry); if(itr != SummonSpells.end()) { itr->second.erase(SpellID); if(itr->second.size() == 0) SummonSpells.erase(itr); } } set* Player::GetSummonSpells(uint32 Entry) { map >::iterator itr = SummonSpells.find(Entry); if(itr != SummonSpells.end()) { return &(itr->second); } return 0; } /* Loads ItemCooldowns checks for invalid items and deletes them from the db */ void Player::_LoadItemCooldown() { QueryResult *result = CharacterDatabase.Query("SELECT * FROM playercooldownitems WHERE OwnerGuid=%u",GetGUIDLow()); if(result) { // TODO is there a better way to do this? do { // if the current item does not have cooldown delete it from db Field *fields = result->Fetch(); uint32 TempSpellID = fields[2].GetUInt32(); uint32 TempSpellCategory = fields[3].GetUInt32(); uint32 TempTimestamp = fields[4].GetUInt32(); uint32 TempCooldown = fields[5].GetUInt32(); uint32 DiffTimestamp = TempTimestamp - now(); if (now() > TempTimestamp || (now() < TempTimestamp && DiffTimestamp > TempCooldown)) //if timestamp overflow or dif time is larget than 7 days { CharacterDatabase.Execute( "DELETE FROM playercooldownitems WHERE OwnerGuid=%u AND ItemEntry=%u", GetGUIDLow(),fields[1].GetUInt32()); } else // only add items to list that still have cooldown { ItemCooldown *be = new ItemCooldown; be->ItemEntry = fields[1].GetUInt32(); be->SpellID = TempSpellID; be->SpellCategory = TempSpellCategory; be->CooldownTimeStamp = TempTimestamp; be->Cooldown = TempCooldown; m_itemcooldown.insert(be); } } while( result->NextRow() ); delete result; } } void Player::_LoadSpellCoolDownSecurity() { QueryResult *result = CharacterDatabase.Query("SELECT * FROM playercooldownsecurity WHERE OwnerGuid=%u",GetGUIDLow()); if(result) { do { // if the current item does not have cooldown delete it from db Field *fields = result->Fetch(); uint32 SpellID = fields[1].GetUInt32(); uint32 Timestamp = fields[2].GetUInt32(); uint32 DiffTimestamp = Timestamp - now(); SpellEntry *spellInfo = sSpellStore.LookupEntry( SpellID ); if (now() + spellInfo->RecoveryTime > Timestamp && // cooldown did not expired somehow (not taking into care cooldown modifiers!) now() < Timestamp + spellInfo->RecoveryTime ) // cooldown does not starts in future (not taking into care cooldown modifiers!) { AddCooldown(SpellID,DiffTimestamp); } // if (now() > Timestamp || (now() < Timestamp && DiffTimestamp > Timestamp)) else // only add spells to list that still have cooldown { //if timestamp overflow or diff time is larger than 7 days CharacterDatabase.WaitExecute( "DELETE FROM playercooldownsecurity WHERE OwnerGuid = %u AND SpellID = %u", GetGUIDLow(), SpellID ); } } while( result->NextRow() ); delete result; } } void Player::_LoadPet() { QueryResult *result = CharacterDatabase.Query( "SELECT * FROM playerpets WHERE ownerguid=%u ORDER BY petnumber",GetGUIDLow()); if(!result)return; m_PetNumberMax=0; do { Field *fields = result->Fetch(); fields = result->Fetch(); PlayerPet *pet = new PlayerPet; pet->number = fields[1].GetUInt32(); pet->name = fields[2].GetString(); pet->entry = fields[3].GetUInt32(); pet->fields = fields[4].GetString(); pet->xp = fields[5].GetUInt32(); pet->active = fields[6].GetUInt32(); pet->level = fields[7].GetUInt32(); pet->happiness = fields[8].GetUInt32(); pet->actionbar = fields[9].GetString(); pet->happinessupdate = fields[10].GetUInt32(); pet->summon = fields[11].GetUInt32(); pet->autocastspell = fields[12].GetUInt32(); pet->loyaltypts = fields[13].GetUInt32(); pet->loyaltyupdate = fields[14].GetUInt32(); m_Pets[pet->number] = pet; if(pet->active) { if(iActivePet) // how the hell can this happen printf("pet warning - >1 active pet.. weird.."); else iActivePet = pet->number; } if(pet->number > m_PetNumberMax) m_PetNumberMax = pet->number; }while(result->NextRow()); delete result; } void Player::SpawnPet(uint32 pet_number) { std::map::iterator itr = m_Pets.find(pet_number); if(itr == m_Pets.end()) { sLog.outError("PET SYSTEM: "I64FMT" Tried to load invalid pet %d", GetGUID(), pet_number); return; } Pet *pPet = objmgr.CreatePet(); pPet->SetInstanceID(GetInstanceID()); pPet->LoadFromDB(this, itr->second); } void Player::_LoadPetSpells() { std::stringstream query; std::map* >::iterator itr; uint32 entry = 0; uint32 spell = 0; query << "SELECT * FROM playersummonspells where ownerguid='" << GetGUIDLow() << "' ORDER BY entryid"; QueryResult *result = CharacterDatabase.Query( query.str().c_str() ); if(result) { do { Field *fields = result->Fetch(); entry = fields[1].GetUInt32(); spell = fields[2].GetUInt32(); AddSummonSpell(entry, spell); } while( result->NextRow() ); delete result; } } void Player::addSpell(uint32 spell_id) { SpellSet::iterator iter = mSpells.find(spell_id); if(iter != mSpells.end()) return; mSpells.insert(spell_id); if(IsInWorld()) m_session->OutPacket(SMSG_LEARNED_SPELL, 4, &spell_id); // Check if we're a deleted spell iter = mDeletedSpells.find(spell_id); if(iter != mDeletedSpells.end()) mDeletedSpells.erase(iter); // Check if we're logging in. if(!IsInWorld()) return; // Add the skill line for this spell if we don't already have it. skilllinespell * sk = objmgr.GetSpellSkill(spell_id); if(sk && !_HasSkillLine(sk->skilline)) { skilllineentry * skill = sSkillLineStore.LookupEntry(sk->skilline); if(!skill) { _AddSkillLine(sk->skilline, 1, 1); return; } uint32 max = 5 * getLevel(); switch(skill->type) { case SKILL_TYPE_WEAPON: case SKILL_TYPE_SECONDARY: case SKILL_TYPE_LANGUAGE: case SKILL_TYPE_PROFESSION: return; break; default: { if(skill->id != SKILL_POISONS && skill->id != SKILL_LOCKPICKING) max = 1; }break; } if(skill->type==SKILL_TYPE_PROFESSION) ModUInt32Value(PLAYER_CHARACTER_POINTS2,-1); _AddSkillLine(sk->skilline, 1, max); } } //=================================================================================================================== // Set Create Player Bits -- Sets bits required for creating a player in the updateMask. // Note: Doesn't set Quest or Inventory bits // updateMask - the updatemask to hold the set bits //=================================================================================================================== void Player::_SetCreateBits(UpdateMask *updateMask, Player *target) const { if(target == this) { Object::_SetCreateBits(updateMask, target); } else { UpdateMask mask; mask.SetCount(m_valuesCount); _SetVisibleBits(&mask, target); for(uint16 index = 0; index < m_valuesCount; index++) { if(GetUInt32Value(index) != 0 && mask.GetBit(index)) updateMask->SetBit(index); } } } void Player::_SetUpdateBits(UpdateMask *updateMask, Player *target) const { if(target == this) { Object::_SetUpdateBits(updateMask, target); } else { UpdateMask mask; mask.SetCount(m_valuesCount); _SetVisibleBits(&mask, target); Object::_SetUpdateBits(updateMask, target); *updateMask &= mask; } } void Player::_SetVisibleBits(UpdateMask *updateMask, Player *target) const { updateMask->SetBit(OBJECT_FIELD_GUID); updateMask->SetBit(OBJECT_FIELD_TYPE); updateMask->SetBit(OBJECT_FIELD_SCALE_X); updateMask->SetBit(UNIT_FIELD_SUMMON); updateMask->SetBit(UNIT_FIELD_SUMMON+1); updateMask->SetBit(UNIT_FIELD_TARGET); updateMask->SetBit(UNIT_FIELD_TARGET+1); updateMask->SetBit(UNIT_FIELD_HEALTH); updateMask->SetBit(UNIT_FIELD_POWER1); updateMask->SetBit(UNIT_FIELD_POWER2); updateMask->SetBit(UNIT_FIELD_POWER3); updateMask->SetBit(UNIT_FIELD_POWER4); updateMask->SetBit(UNIT_FIELD_POWER5); updateMask->SetBit(UNIT_FIELD_MAXHEALTH); updateMask->SetBit(UNIT_FIELD_MAXPOWER1); updateMask->SetBit(UNIT_FIELD_MAXPOWER2); updateMask->SetBit(UNIT_FIELD_MAXPOWER3); updateMask->SetBit(UNIT_FIELD_MAXPOWER4); updateMask->SetBit(UNIT_FIELD_MAXPOWER5); updateMask->SetBit(UNIT_FIELD_LEVEL); updateMask->SetBit(UNIT_FIELD_FACTIONTEMPLATE); updateMask->SetBit(UNIT_FIELD_BYTES_0); updateMask->SetBit(UNIT_FIELD_FLAGS); for(uint32 i = UNIT_FIELD_AURA; i < UNIT_FIELD_AURASTATE; i ++) updateMask->SetBit(i); updateMask->SetBit(UNIT_FIELD_BASEATTACKTIME); updateMask->SetBit(UNIT_FIELD_BASEATTACKTIME+1); updateMask->SetBit(UNIT_FIELD_BOUNDINGRADIUS); updateMask->SetBit(UNIT_FIELD_COMBATREACH); updateMask->SetBit(UNIT_FIELD_DISPLAYID); updateMask->SetBit(UNIT_FIELD_NATIVEDISPLAYID); updateMask->SetBit(UNIT_FIELD_MOUNTDISPLAYID); updateMask->SetBit(UNIT_FIELD_BYTES_1); updateMask->SetBit(UNIT_FIELD_MOUNTDISPLAYID); updateMask->SetBit(UNIT_FIELD_PETNUMBER); updateMask->SetBit(UNIT_FIELD_PET_NAME_TIMESTAMP); updateMask->SetBit(UNIT_FIELD_CHANNEL_OBJECT); updateMask->SetBit(UNIT_FIELD_CHANNEL_OBJECT+1); updateMask->SetBit(UNIT_CHANNEL_SPELL); updateMask->SetBit(UNIT_DYNAMIC_FLAGS); updateMask->SetBit(PLAYER_FLAGS); updateMask->SetBit(PLAYER_BYTES); updateMask->SetBit(PLAYER_BYTES_2); updateMask->SetBit(PLAYER_BYTES_3); updateMask->SetBit(PLAYER_GUILD_TIMESTAMP); updateMask->SetBit(PLAYER_DUEL_TEAM); updateMask->SetBit(PLAYER_DUEL_ARBITER); updateMask->SetBit(PLAYER_DUEL_ARBITER+1); updateMask->SetBit(PLAYER_GUILDID); updateMask->SetBit(PLAYER_GUILDRANK); for(uint16 i = 0; i < EQUIPMENT_SLOT_END; i++) { updateMask->SetBit((uint16)(PLAYER_VISIBLE_ITEM_1_0 + (i*16))); // visual items for other players updateMask->SetBit((uint16)(PLAYER_VISIBLE_ITEM_1_0+1 + (i*16))); // visual items for other players } /* fuck i hate const - burlex */ if(target && target->GetGroup() == const_cast(this)->GetGroup() && const_cast(this)->GetSubGroup() == target->GetSubGroup()) { /* quest fields are the same for party members */ for(uint32 i = PLAYER_QUEST_LOG_1_01; i < PLAYER_QUEST_LOG_25_2; ++i) updateMask->SetBit(i); } } void Player::DestroyForPlayer( Player *target ) const { Unit::DestroyForPlayer( target ); } void Player::SaveToDB(bool bNewCharacter /* =false */) { uint32 start_time = getMSTime(); //Calc played times uint32 playedt = (uint32)time(NULL) - m_playedtime[2]; m_playedtime[0] += playedt; m_playedtime[1] += playedt; m_playedtime[2] += playedt; std::stringstream ss; ss << "REPLACE INTO characters VALUES (" << GetGUIDLow() << ", " << GetSession()->GetAccountId() << "," // stat saving << "'" << m_name << "', " << uint32(getRace()) << "," << uint32(getClass()) << "," << uint32(getGender()) << "," << uint32(getLevel()) << "," << m_uint32Values[PLAYER_XP] << "," // dump exploration data << "'"; for(uint32 i = 0; i < 64; ++i) ss << m_uint32Values[PLAYER_EXPLORED_ZONES_1 + i] << ","; ss << "','"; // dump skill data /*for(uint32 i=PLAYER_SKILL_INFO_1_1;isecond.Skill->type != SKILL_TYPE_LANGUAGE) { ss << itr->first << ";" << itr->second.CurrentValue << ";" << itr->second.MaximumValue << ";"; } } uint32 player_flags = m_uint32Values[PLAYER_FLAGS]; { // Remove un-needed and problematic player flags from being saved :p if(player_flags & PLAYER_FLAG_PARTY_LEADER) player_flags &= ~PLAYER_FLAG_PARTY_LEADER; if(player_flags & PLAYER_FLAG_AFK) player_flags &= ~PLAYER_FLAG_AFK; if(player_flags & PLAYER_FLAG_DND) player_flags &= ~PLAYER_FLAG_DND; if(player_flags & PLAYER_FLAG_GM) player_flags &= ~PLAYER_FLAG_GM; if(player_flags & PLAYER_FLAG_PVP_TOGGLE) player_flags &= ~PLAYER_FLAG_PVP_TOGGLE; if(player_flags & PLAYER_FLAG_FREE_FOR_ALL_PVP) player_flags &= ~PLAYER_FLAG_FREE_FOR_ALL_PVP; } ss << "', " << m_uint32Values[PLAYER_FIELD_WATCHED_FACTION_INDEX] << "," << m_uint32Values[PLAYER_CHOSEN_TITLE] << "," << m_uint32Values[PLAYER__FIELD_KNOWN_TITLES] << "," << m_uint32Values[PLAYER_FIELD_COINAGE] << "," << m_uint32Values[PLAYER_AMMO_ID] << "," << m_uint32Values[PLAYER_CHARACTER_POINTS2] << "," << m_uint32Values[PLAYER_CHARACTER_POINTS1] << "," << load_health << "," << load_mana << "," << uint32(GetPVPRank()) << "," << m_uint32Values[PLAYER_BYTES] << "," << m_uint32Values[PLAYER_BYTES_2] << "," << player_flags << "," << m_uint32Values[PLAYER_FIELD_BYTES] << "," << m_position.x << ", " << m_position.y << ", " << m_position.z << ", " << m_position.o << ", " << m_mapId << ", " << m_zoneId << ", '"; for(uint32 i = 0; i < 8; i++ ) ss << m_taximask[i] << " "; ss << "', " << m_banned << ", '" << CharacterDatabase.EscapeString(m_banreason) << "', " << (uint32)time(NULL) << ","; //online state if(GetSession()->_loggingOut) { ss << "0,"; }else { ss << "1,"; } ss << m_bind_pos_x << ", " << m_bind_pos_y << ", " << m_bind_pos_z << ", " << m_bind_mapid << ", " << m_bind_zoneid << ", " << uint32(m_isResting) << ", " << uint32(m_restState) << ", " << uint32(m_restAmount) << ", '" << uint32(m_playedtime[0]) << " " << uint32(m_playedtime[1]) << " " << uint32(playedt) << " ', " << uint32(m_deathState) << ", " << m_talentresettimes << ", " << m_FirstLogin << ", " << rename_pending; Guild *pGuild; PlayerInfo * pMember; if(GetGuildId() && (pGuild = objmgr.GetGuild(GetGuildId())) && (pMember = pGuild->GetGuildMember( GetGUID() ))) { ss << ",'" << CharacterDatabase.EscapeString(pMember->publicNote) << "','"; ss << CharacterDatabase.EscapeString(pMember->officerNote) << "'," << GetGuildId() << "," << GetGuildRank(); }else { ss << ",'','',0,0"; } ss << "," << (m_charter ? m_charter->GetID() : uint32(0)) << "," << (uint32)m_StableSlotCount << ","; // instances ss << m_instanceId << ", " << m_bgEntryPointMap << ", " << m_bgEntryPointX << ", " << m_bgEntryPointY << ", " << m_bgEntryPointZ << ", " << m_bgEntryPointO << ", " << m_bgEntryPointInstance << ", "; // taxi if(m_onTaxi) { ss << m_CurrentTaxiPath->GetID() << ", "; ss << lastNode << ", "; ss << GetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID); } else { ss << "0, 0, 0"; } ss << "," << (m_CurrentTransporter ? m_CurrentTransporter->GetEntry() : (uint32)0); ss << ",'" << m_TransporterX << "','" << m_TransporterY << "','" << m_TransporterZ << "'"; ss << ",'"; // Dump spell data to stringstream SpellSet::iterator spellItr = mSpells.begin(); for(; spellItr != mSpells.end(); ++spellItr) { ss << uint32(*spellItr) << ","; } ss << "','"; // Dump deleted spell data to stringstream spellItr = mDeletedSpells.begin(); for(; spellItr != mDeletedSpells.end(); ++spellItr) { ss << uint32(*spellItr) << ","; } ss << "','"; // Dump reputation data ReputationMap::iterator iter = m_reputation.begin(); for(; iter != m_reputation.end(); ++iter) { ss << int32(iter->first) << "," << int32(iter->second->flag) << "," << int32(iter->second->baseStanding) << "," << int32(iter->second->standing) << ","; } ss << "','"; // Add player action bars for(uint32 i = 0; i < 120; ++i) { ss << uint32(mActions[i].Action) << "," << uint32(mActions[i].Misc) << "," << uint32(mActions[i].Type) << ","; } ss << "','"; if(!bNewCharacter) SaveAuras(ss); // ss << LoadAuras; ss << "','"; // Add player finished quests set::iterator fq = m_finishedQuests.begin(); for(; fq != m_finishedQuests.end(); ++fq) { ss << (*fq) << ","; } ss << "', "; ss << m_lastHonorResetTime << ", "; ss << m_killsToday << ", " << m_killsYesterday << ", " << m_killsLifetime << ", "; ss << m_honorToday << ", " << m_honorYesterday << ", "; ss << m_honorPoints << ", "; ss << iInstanceType << ")"; if(bNewCharacter) CharacterDatabase.WaitExecute(ss.str().c_str()); else CharacterDatabase.Execute( ss.str().c_str() ); //Save Other related player stuff // Inventory GetItemInterface()->mSaveItemsToDatabase(bNewCharacter); // save quest progress _SaveQuestLogEntry(); // Tutorials _SaveTutorials(); // GM Ticket objmgr.SaveGMTicket(GetGUID()); // Cooldown Items _SaveItemCooldown(); // Spell Cooldowns security _SaveSpellCoolDownSecurity(); // Pets if(getClass() == HUNTER || getClass() == WARLOCK) { _SavePet(); _SavePetSpells(); } sLog.outDetail("[Player] Saved player %s ["I64FMT"], took %ums", GetName(), GetGUID(), getMSTime() - start_time); m_nextSave = getMSTime() + sWorld.getIntRate(INTRATE_SAVE); } void Player::_SaveQuestLogEntry() { for(std::set::iterator itr = m_removequests.begin(); itr != m_removequests.end(); ++itr) CharacterDatabase.Execute("DELETE FROM questlog WHERE player_guid=%u AND quest_id=%u", GetGUIDLow(), (*itr)); m_removequests.clear(); for(int i = 0; i < 25; ++i) { if(m_questlog[i] != NULL) m_questlog[i]->SaveToDB(); } } bool Player::canCast(SpellEntry *m_spellInfo) { if (m_spellInfo->EquippedItemClass != 0) { if(this->GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_MAINHAND)) { if(this->GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_MAINHAND)->GetProto()->Class == m_spellInfo->EquippedItemClass) { if (m_spellInfo->EquippedItemSubClass != 0) { if (m_spellInfo->EquippedItemSubClass != 173555 && m_spellInfo->EquippedItemSubClass != 96 && m_spellInfo->EquippedItemSubClass != 262156) { if (pow(2.0,(this->GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_MAINHAND)->GetProto()->SubClass) != m_spellInfo->EquippedItemSubClass)) return false; } } } } else if(m_spellInfo->EquippedItemSubClass == 173555) return false; if (this->GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_RANGED)) { if(this->GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_RANGED)->GetProto()->Class == m_spellInfo->EquippedItemClass) { if (m_spellInfo->EquippedItemSubClass != 0) { if (m_spellInfo->EquippedItemSubClass != 173555 && m_spellInfo->EquippedItemSubClass != 96 && m_spellInfo->EquippedItemSubClass != 262156) { if (pow(2.0,(this->GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_RANGED)->GetProto()->SubClass) != m_spellInfo->EquippedItemSubClass)) return false; } } } } else if (m_spellInfo->EquippedItemSubClass == 262156) return false; } return true; } bool Player::LoadFromDB(uint32 guid) { uint32 field_index = 2; #define get_next_field fields[field_index++] QueryResult *result = CharacterDatabase.Query("SELECT * FROM characters WHERE guid=%u AND banned=0 AND forced_rename_pending = 0",guid); if(!result) { printf("Player login query failed., guid %u\n", (unsigned int)guid); return false; } Field *fields = result->Fetch(); if(fields[1].GetUInt32() != m_session->GetAccountId()) { sCheatLog.writefromsession(m_session, "player tried to load character not belonging to them (guid %u, on account %u)", fields[0].GetUInt32(), fields[1].GetUInt32()); delete result; return false; } // Load name m_name = get_next_field.GetString(); // Load race/class from fields setRace(get_next_field.GetUInt8()); setClass(get_next_field.GetUInt8()); setGender(get_next_field.GetUInt8()); // set race dbc myRace = sCharRaceStore.LookupEntry(getRace()); myClass = sCharClassStore.LookupEntry(getClass()); if(!myClass || !myRace) { // bad character printf("guid %u failed to login, no race or class dbc found. (race %u class %u)\n", (unsigned int)guid, (unsigned int)getRace(), (unsigned int)getClass()); return false; } if(myRace->team_id == 7) m_team = 0; else m_team = 1; SetNoseLevel(); // set power type SetPowerType(myClass->power_type); // obtain player create info info = objmgr.GetPlayerCreateInfo(getRace(), getClass()); assert(info); // set level m_uint32Values[UNIT_FIELD_LEVEL] = get_next_field.GetUInt32(); /*if(m_uint32Values[UNIT_FIELD_LEVEL] > sWorld.LevelCap) m_uint32Values[UNIT_FIELD_LEVEL] = sWorld.LevelCap;*/ // obtain level/stats information lvlinfo = objmgr.GetLevelInfo(getRace(), getClass(), getLevel()); if(!lvlinfo) { printf("guid %u level %u class %u race %u levelinfo not found!\n", (unsigned int)guid, (unsigned int)getLevel(), (unsigned int)getClass(), (unsigned int)getRace()); return false; } CalculateBaseStats(); // set xp m_uint32Values[PLAYER_XP] = get_next_field.GetUInt32(); // Process exploration data. uint32 Counter = 0; char * end; char * start = (char*)get_next_field.GetString();//buff; while(Counter <64) { end = strchr(start,','); if(!end)break; *end=0; SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + Counter, atol(start)); start = end +1; Counter++; } // Process skill data. Counter = 0; start = (char*)get_next_field.GetString();//buff; // new format uint32 field = PLAYER_SKILL_INFO_1_1; const ItemProf * prof; if(!strchr(start, ' ') && !strchr(start,';')) { /* no skills - reset to defaults */ for(std::list::iterator ss = info->skills.begin(); ss!=info->skills.end(); ss++) { if(ss->skillid && ss->currentval && ss->maxval && !::GetSpellForLanguage(ss->skillid)) _AddSkillLine(ss->skillid, ss->currentval, ss->maxval); } } else { char * f = strdup(start); start = f; if(!strchr(start,';')) { /* old skill format.. :< */ uint32 v1,v2,v3; PlayerSkill sk; for(;;) { end = strchr(start, ' '); if(!end) break; *end = 0; v1 = atol(start); start = end + 1; end = strchr(start, ' '); if(!end) break; *end = 0; v2 = atol(start); start = end + 1; end = strchr(start, ' '); if(!end) break; v3 = atol(start); start = end + 1; if(v1 & 0xffff) { sk.Reset(v1 & 0xffff); sk.CurrentValue = v2 & 0xffff; sk.MaximumValue = (v2 >> 16) & 0xffff; m_skills.insert( make_pair(sk.Skill->id, sk) ); } } } else { uint32 v1,v2,v3; PlayerSkill sk; for(;;) { end = strchr(start, ';'); if(!end) break; *end = 0; v1 = atol(start); start = end + 1; end = strchr(start, ';'); if(!end) break; *end = 0; v2 = atol(start); start = end + 1; end = strchr(start, ';'); if(!end) break; v3 = atol(start); start = end + 1; /* add the skill */ sk.Reset(v1); sk.CurrentValue = v2; sk.MaximumValue = v3; m_skills.insert(make_pair(v1, sk)); } } free(f); } for(SkillMap::iterator itr = m_skills.begin(); itr != m_skills.end(); ++itr) { if(itr->first == SKILL_RIDING) { itr->second.CurrentValue = itr->second.MaximumValue; } prof = GetProficiencyBySkill(itr->first); if(prof) { if(prof->itemclass==4) armor_proficiency|=prof->subclass; else weapon_proficiency|=prof->subclass; } } // set the rest of the shit uint32 PvPRanks[] = { PVPTITLE_NONE, PVPTITLE_PRIVATE, PVPTITLE_CORPORAL, PVPTITLE_SERGEANT, PVPTITLE_MASTER_SERGEANT, PVPTITLE_SERGEANT_MAJOR, PVPTITLE_KNIGHT, PVPTITLE_KNIGHT_LIEUTENANT, PVPTITLE_KNIGHT_CAPTAIN, PVPTITLE_KNIGHT_CHAMPION, PVPTITLE_LIEUTENANT_COMMANDER, PVPTITLE_COMMANDER, PVPTITLE_MARSHAL, PVPTITLE_FIELD_MARSHAL, PVPTITLE_GRAND_MARSHAL, PVPTITLE_SCOUT, PVPTITLE_GRUNT, PVPTITLE_HSERGEANT, PVPTITLE_SENIOR_SERGEANT, PVPTITLE_FIRST_SERGEANT, PVPTITLE_STONE_GUARD, PVPTITLE_BLOOD_GUARD, PVPTITLE_LEGIONNAIRE, PVPTITLE_CENTURION, PVPTITLE_CHAMPION, PVPTITLE_LIEUTENANT_GENERAL, PVPTITLE_GENERAL, PVPTITLE_WARLORD, PVPTITLE_HIGH_WARLORD }; m_uint32Values[PLAYER_FIELD_WATCHED_FACTION_INDEX] = get_next_field.GetUInt32(); m_uint32Values[PLAYER_CHOSEN_TITLE] = get_next_field.GetUInt32(); field_index++; m_uint32Values[PLAYER_FIELD_COINAGE] = get_next_field.GetUInt32(); m_uint32Values[PLAYER_AMMO_ID] = get_next_field.GetUInt32(); m_uint32Values[PLAYER_CHARACTER_POINTS2] = get_next_field.GetUInt32(); m_uint32Values[PLAYER_CHARACTER_POINTS1] = get_next_field.GetUInt32(); load_health = get_next_field.GetUInt32(); load_mana = get_next_field.GetUInt32(); SetUInt32Value(UNIT_FIELD_HEALTH, load_health); uint8 pvprank = get_next_field.GetUInt8(); SetUInt32Value(PLAYER_BYTES, get_next_field.GetUInt32()); SetUInt32Value(PLAYER_BYTES_2, get_next_field.GetUInt32()); SetUInt32Value(PLAYER_BYTES_3, getGender() | (pvprank << 24)); uint32 offset = 13 * GetTeam(); SetUInt32Value(PLAYER__FIELD_KNOWN_TITLES, PvPRanks[GetPVPRank() + offset]); SetUInt32Value(PLAYER_FLAGS, get_next_field.GetUInt32()); SetUInt32Value(PLAYER_FIELD_BYTES, get_next_field.GetUInt32()); m_position.x = get_next_field.GetFloat(); m_position.y = get_next_field.GetFloat(); m_position.z = get_next_field.GetFloat(); m_position.o = get_next_field.GetFloat(); m_mapId = get_next_field.GetUInt32(); m_zoneId = get_next_field.GetUInt32(); // Calculate the base stats now they're all loaded for(uint32 i = 0; i < 5; ++i) CalcStat(i); // for(uint32 x = PLAYER_SPELL_CRIT_PERCENTAGE1; x < PLAYER_SPELL_CRIT_PERCENTAGE06 + 1; ++x) /// SetFloatValue(x, 0.0f); for(uint32 x = PLAYER_FIELD_MOD_DAMAGE_DONE_PCT; x < PLAYER_FIELD_MOD_HEALING_DONE_POS; ++x) SetFloatValue(x, 1.0f); // Normal processing... // UpdateMaxSkills(); UpdateStats(); //UpdateChances(); // Initialize 'normal' fields SetFloatValue(OBJECT_FIELD_SCALE_X, ((getRace()==RACE_TAUREN)?1.3f:1.0f)); //SetUInt32Value(UNIT_FIELD_POWER2, 0); SetUInt32Value(UNIT_FIELD_POWER3, info->focus); SetUInt32Value(UNIT_FIELD_POWER4, info->energy ); SetUInt32Value(UNIT_FIELD_MAXPOWER2, info->rage ); SetUInt32Value(UNIT_FIELD_MAXPOWER3, info->focus ); SetUInt32Value(UNIT_FIELD_MAXPOWER4, info->energy ); if(getClass() == WARRIOR) SetShapeShift(FORM_BATTLESTANCE); SetUInt32Value(UNIT_FIELD_BYTES_2, (0x28 << 8) ); SetFlag(UNIT_FIELD_FLAGS , U_FIELD_FLAG_PLAYER_CONTROLLED ); SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, 0.388999998569489f ); SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f ); if(getRace() != 10) { SetUInt32Value(UNIT_FIELD_DISPLAYID, info->displayId + getGender() ); SetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID, info->displayId + getGender() ); } else { SetUInt32Value(UNIT_FIELD_DISPLAYID, info->displayId - getGender() ); SetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID, info->displayId - getGender() ); } SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f); SetUInt32Value(PLAYER_FIELD_MAX_LEVEL, (GetSession()->HasFlag(ACCOUNT_FLAG_XPACK_01)?sWorld.Expansion1LevelCap:sWorld.LevelCap)); SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, info->factiontemplate); LoadTaxiMask( get_next_field.GetString() ); m_banned = get_next_field.GetUInt32(); //Character ban m_banreason = get_next_field.GetString(); m_timeLogoff = get_next_field.GetUInt32(); field_index++; m_bind_pos_x = get_next_field.GetFloat(); m_bind_pos_y = get_next_field.GetFloat(); m_bind_pos_z = get_next_field.GetFloat(); m_bind_mapid = get_next_field.GetUInt32(); m_bind_zoneid = get_next_field.GetUInt32(); m_isResting = get_next_field.GetUInt8(); m_restState = get_next_field.GetUInt8(); m_restAmount = get_next_field.GetUInt32(); std::string tmpStr = get_next_field.GetString(); m_playedtime[0] = (uint32)atoi((const char*)strtok((char*)tmpStr.c_str()," ")); m_playedtime[1] = (uint32)atoi((const char*)strtok(NULL," ")); m_deathState = (DeathState)get_next_field.GetUInt32(); m_talentresettimes = get_next_field.GetUInt32(); m_FirstLogin = get_next_field.GetBool(); rename_pending = get_next_field.GetBool(); field_index++; field_index++; //uint32 guildid = get_next_field.GetUInt32(); //uint32 guildrank = get_next_field.GetUInt32(); SetGuildId(get_next_field.GetUInt32()); SetUInt32Value(PLAYER_GUILDRANK,get_next_field.GetUInt32()); uint32 cid = get_next_field.GetUInt32(); if(cid) m_charter = objmgr.GetCharter(cid); m_StableSlotCount = get_next_field.GetUInt32(); m_instanceId = get_next_field.GetUInt32(); m_bgEntryPointMap = get_next_field.GetUInt32(); m_bgEntryPointX = get_next_field.GetFloat(); m_bgEntryPointY = get_next_field.GetFloat(); m_bgEntryPointZ = get_next_field.GetFloat(); m_bgEntryPointO = get_next_field.GetFloat(); m_bgEntryPointInstance = get_next_field.GetUInt32(); uint32 taxipath = get_next_field.GetUInt32(); TaxiPath *path = NULL; if(taxipath) { path = sTaxiMgr.GetTaxiPath(taxipath); lastNode = get_next_field.GetUInt32(); if(path) { SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, get_next_field.GetUInt32()); SetTaxiPath(path); m_onTaxi = true; } else field_index++; } else { field_index++; field_index++; } m_TransporterGUID = get_next_field.GetUInt32(); if(m_TransporterGUID) { Transporter * t = objmgr.GetTransporterByEntry(m_TransporterGUID); m_TransporterGUID = t ? t->GetGUID() : 0; } m_TransporterX = get_next_field.GetFloat(); m_TransporterY = get_next_field.GetFloat(); m_TransporterZ = get_next_field.GetFloat(); // Load Spells from CSV data. start = (char*)get_next_field.GetString();//buff; while(true) { end = strchr(start,','); if(!end)break; *end=0; mSpells.insert(atol(start)); start = end +1; } start = (char*)get_next_field.GetString();//buff; while(true) { end = strchr(start,','); if(!end)break; *end=0; mDeletedSpells.insert(atol(start)); start = end +1; } // Load Reputatation CSV Data start =(char*) get_next_field.GetString(); FactionDBC * factdbc ; FactionReputation * rep; uint32 id; int32 basestanding; int32 standing; uint8 fflag; while(true) { end = strchr(start,','); if(!end)break; *end=0; id = atol(start); start = end +1; end = strchr(start,','); if(!end)break; *end=0; fflag = atol(start); start = end +1; end = strchr(start,','); if(!end)break; *end=0; basestanding = atoi(start);//atol(start); start = end +1; end = strchr(start,','); if(!end)break; *end=0; standing = atoi(start);// atol(start); start = end +1; // listid stuff factdbc = sFactionStore.LookupEntry(id); if(!factdbc) continue; rep = new FactionReputation; rep->baseStanding = basestanding; rep->standing = standing; rep->flag = fflag; m_reputation[id]=rep; // do listid stuff if(factdbc->RepListId >= 0) reputationByListId[factdbc->RepListId] = rep; } if(!m_reputation.size()) _InitialReputation(); // Load saved actionbars start = (char*)get_next_field.GetString(); Counter =0; while(Counter < 120) { end = strchr(start,','); if(!end)break; *end=0; mActions[Counter].Action = atol(start); start = end +1; end = strchr(start,','); if(!end)break; *end=0; mActions[Counter].Misc = atol(start); start = end +1; end = strchr(start,','); if(!end)break; *end=0; mActions[Counter++].Type = atol(start); start = end +1; } //LoadAuras = get_next_field.GetString(); start = (char*)get_next_field.GetString();//buff; do { end = strchr(start,','); if(!end)break; *end=0; LoginAura la; la.id = atol(start); start = end +1; end = strchr(start,','); if(!end)break; *end=0; la.dur = atol(start); start = end +1; loginauras.push_back(la); } while(true); // Load saved finished quests start = (char*)get_next_field.GetString(); while(true) { end = strchr(start,','); if(!end)break; *end=0; m_finishedQuests.insert(atol(start)); start = end +1; } uint32 last_update = get_next_field.GetUInt32(); m_killsToday = get_next_field.GetUInt32(); m_killsYesterday = get_next_field.GetUInt32(); m_killsLifetime = get_next_field.GetUInt32(); m_honorToday = get_next_field.GetUInt32(); m_honorYesterday = get_next_field.GetUInt32(); m_honorPoints = get_next_field.GetUInt32(); iInstanceType = get_next_field.GetUInt32(); uint32 next_update = HonorHandler::GetNextUpdateTime(); if(last_update <= next_update) HonorHandler::DailyFieldMove(this); else HonorHandler::RecalculateHonorFields(this); delete result; for(uint32 x=0;x<5;x++) BaseStats[x]=GetUInt32Value(UNIT_FIELD_STAT0+x); _setFaction(); //class fixes switch(getClass()) { case PALADIN: armor_proficiency|=(1<<7);//LIBRAM break; case DRUID: armor_proficiency|=(1<<8);//IDOL break; case SHAMAN: armor_proficiency|=(1<<9);//TOTEM break; case WARLOCK: case HUNTER: _LoadPetSpells(); _LoadPet(); break; } if(m_session->CanUseCommand('c')) _AddLanguages(true); else _AddLanguages(false); OnlineTime = time(NULL); if(GetGuildId()) SetUInt32Value(PLAYER_GUILD_TIMESTAMP, time(NULL)); return true; #undef get_next_field } void Player::LoadFromDB_Light(Field *fields, uint32 guid) { uint32 field_index = 1; #define get_next_field fields[field_index++] // set level m_uint32Values[UNIT_FIELD_LEVEL] = get_next_field.GetUInt32(); // Set guid fields // m_uint32Values[OBJECT_FIELD_GUID] = guid; //m_uint32Values[OBJECT_FIELD_GUID+1] = 0; // Load race/class from fields setRace(get_next_field.GetUInt8()); setClass(get_next_field.GetUInt8()); setGender(get_next_field.GetUInt8()); SetUInt32Value(PLAYER_BYTES, get_next_field.GetUInt32()); SetUInt32Value(PLAYER_BYTES_2, get_next_field.GetUInt32()); SetUInt32Value(PLAYER_GUILDID, get_next_field.GetUInt32()); SetNoseLevel(); // Load name m_name = get_next_field.GetString(); float x = get_next_field.GetFloat(); float y = get_next_field.GetFloat(); float z = get_next_field.GetFloat(); m_position.ChangeCoords(x, y, z); m_mapId = get_next_field.GetUInt32(); m_zoneId = get_next_field.GetUInt32(); m_banned = get_next_field.GetUInt32(); m_restState = get_next_field.GetUInt8(); m_deathState = (DeathState)get_next_field.GetUInt32(); _LoadInventoryLight(); // set race dbc myRace = sCharRaceStore.LookupEntry(getRace()); myClass = sCharClassStore.LookupEntry(getClass()); // set race dbc myRace = sCharRaceStore.LookupEntry(getRace()); myClass = sCharClassStore.LookupEntry(getClass()); if(!myClass || !myRace) { // bad character printf("guid %u failed to login, no race or class dbc found. (race %u class %u)\n", (unsigned int)guid, (unsigned int)getRace(), (unsigned int)getClass()); return; } if(myRace->team_id == 7) m_team = 0; else m_team = 1; rename_pending = get_next_field.GetBool(); #undef get_next_field } void Player::LoadPropertiesFromDB() { _LoadTutorials(); GetItemInterface()->mLoadItemsFromDatabase(); _LoadQuestLogEntry(); // _LoadItemCooldown(); _LoadSpellCoolDownSecurity(); // init Faction _setFaction(); } void Player::_LoadInventoryLight() { // Inventory std::stringstream invq; invq << "SELECT * FROM playeritems WHERE ownerguid=" << GetGUIDLow(); invq << " and containerslot=-1 and slot < 19";//EQUIPMENT_SLOT_END QueryResult *result = CharacterDatabase.Query( invq.str().c_str() ); if(result) { do { Field *fields = result->Fetch(); Item* item; uint32 entry = fields[2].GetUInt32(); ItemPrototype * proto = ItemPrototypeStorage.LookupEntry(entry); if(proto) { item=new Item(HIGHGUID_ITEM,fields[1].GetUInt64()); item->LoadFromDB(fields, 0, true); int8 slot=fields[11].GetInt8(); GetItemInterface()->SafeAddItem(item, INVENTORY_SLOT_NOT_SET, slot); } /*else { sDatabase.Execute("DELETE FROM playeritems WHERE guid ="I64FMTD, fields[1].GetUInt64()); }*/ } while( result->NextRow() ); delete result; } } bool Player::HasSpell(uint32 spell) { return mSpells.find(spell) != mSpells.end(); } uint32 Player::GetMaxLearnedSpellLevel(uint32 spell) { SpellEntry *spinfo=sSpellStore.LookupEntry(spell); if(!spinfo) return 0; uint32 max_level=0; SpellSet::iterator iter; for(iter= mSpells.begin();iter != mSpells.end();iter++) { //get hash name for this spell SpellEntry *spinfo2 = sSpellStore.LookupEntry(*iter); if(!spinfo2) return 0; //nasty error here. Very impossible to happen (memory corruption or something if(spinfo2->NameHash == spinfo->NameHash) if(max_levelspellLevel) max_level = spinfo2->spellLevel; } return max_level; } void Player::_LoadQuestLogEntry() { QueryResult *result = CharacterDatabase.Query("SELECT * FROM questlog WHERE player_guid=%u", GetGUIDLow()); QuestLogEntry *entry; Quest *quest; Field *fields; uint32 questid; uint32 baseindex; // clear all fields for(int i = 0; i < 25; ++i) { baseindex = PLAYER_QUEST_LOG_1_1 + (i * 3); SetUInt32Value(baseindex + 0, 0); SetUInt32Value(baseindex + 1, 0); SetUInt32Value(baseindex + 2, 0); } int slot = 0; if(result) { do { fields = result->Fetch(); questid = fields[2].GetUInt32(); quest = QuestStorage.LookupEntry(questid); slot = fields[3].GetUInt32(); ASSERT(slot != -1); // remove on next save if bad quest if(!quest) { m_removequests.insert(questid); continue; } if(m_questlog[slot] != 0) continue; entry = new QuestLogEntry; entry->Init(quest, this, slot); entry->LoadFromDB(fields); entry->UpdatePlayerFields(); } while(result->NextRow()); delete result; } } QuestLogEntry* Player::GetQuestLogForEntry(uint32 quest) { for(int i = 0; i < 25; ++i) { if(m_questlog[i] == ((QuestLogEntry*)0x00000001)) m_questlog[i] = NULL; if(m_questlog[i] != NULL) { if(m_questlog[i]->GetQuest() && m_questlog[i]->GetQuest()->id == quest) return m_questlog[i]; } } return NULL; /*uint32 x = PLAYER_QUEST_LOG_1_1; uint32 y = 0; for(; x < PLAYER_VISIBLE_ITEM_1_CREATOR && y < 25; x += 3, y++) { if(m_uint32Values[x] == quest) return m_questlog[y]; } return NULL;*/ } void Player::SetQuestLogSlot(QuestLogEntry *entry, uint32 slot) { if(entry == (QuestLogEntry*)0x00000001) { #ifdef WIN32 OutputCrashLogLine("bad quest log:"); CStackWalker ws; ws.ShowCallstack(); return; #endif } m_questlog[slot] = entry; } void Player::DeleteFromDB() { sSocialMgr.RemovePlayer(this); CharacterDatabase.WaitExecute("DELETE FROM characters WHERE guid = %u", GetGUIDLow()); Corpse * c=objmgr.GetCorpseByOwner(GetGUIDLow()); if(c) CharacterDatabase.Execute("DELETE FROM corpses WHERE guid = %u", c->GetGUIDLow()); CharacterDatabase.Execute("DELETE FROM playeritems WHERE ownerguid=%u",GetGUIDLow()); CharacterDatabase.Execute("DELETE FROM gm_tickets WHERE guid = %u", GetGUIDLow()); CharacterDatabase.Execute("DELETE FROM playerpets WHERE ownerguid = %u", GetGUIDLow()); CharacterDatabase.Execute("DELETE FROM playerpetspells WHERE ownerguid = %u", GetGUIDLow()); CharacterDatabase.Execute("DELETE FROM playersummonspells WHERE ownerguid = %u", GetGUIDLow()); CharacterDatabase.Execute("DELETE FROM tutorials WHERE playerId = %u", GetGUIDLow()); CharacterDatabase.Execute("DELETE FROM questlog WHERE player_guid = %u", GetGUIDLow()); CharacterDatabase.Execute("DELETE FROM playercooldownitems WHERE OwnerGuid = %u", GetGUIDLow()); CharacterDatabase.Execute("DELETE FROM mailbox WHERE player_guid = %u", GetGUIDLow()); //Zehamster: Delete own lists and people having plr in their lists AND clear storage sSocialMgr.RemovePlayer( this ); } void Player::AddToWorld() { FlyCheat = false; // check transporter if(m_TransporterGUID && m_CurrentTransporter) { SetPosition(m_CurrentTransporter->GetPositionX() + m_TransporterX, m_CurrentTransporter->GetPositionY() + m_TransporterY, m_CurrentTransporter->GetPositionZ() + m_TransporterZ, GetOrientation(), false); } // If we join an invalid instance and get booted out, this will prevent our stats from doubling :P if(IsInWorld()) return; m_beingPushed = true; Unit::AddToWorld(); // Add failed. if(m_mapMgr == NULL) { m_beingPushed = false; return; } if(m_session) m_session->SetInstance(m_mapMgr->GetInstanceID()); } void Player::OnPushToWorld() { if(m_TeleportState == 2) // Worldport Ack OnWorldPortAck(); m_beingPushed = false; AddItemsToWorld(); m_lockTransportVariables = false; // delay the unlock movement packet WorldPacket * data = new WorldPacket(SMSG_MOVE_UNLOCK_MOVEMENT, 4); *data << uint32(0); delayedPackets.add(data); sWorld.mInWorldPlayerCount++; // Update PVP Situation LoginPvPSetup(); Unit::OnPushToWorld(); if(m_FirstLogin) { sHookInterface.OnFirstEnterWorld(this); m_FirstLogin = false; } sHookInterface.OnEnterWorld(this); if(m_TeleportState == 1) // First world enter CompleteLoading(); m_TeleportState = 0; if(flying_aura && m_mapId != 530) { RemoveAura(flying_aura); flying_aura = 0; } ResetHeartbeatCoords(); m_lastMoveType = 0; /* send weather */ sWeatherMgr.SendWeather(this); if(m_mapMgr && m_mapMgr->m_battleground) m_mapMgr->m_battleground->PortPlayer(this, true); SetUInt32Value(UNIT_FIELD_HEALTH, load_health); SetUInt32Value(UNIT_FIELD_POWER1, load_mana); } void Player::ResetHeartbeatCoords() { _lastHeartbeatX = _lastHeartbeatY = _lastHeartbeatZ = 0; _lastHeartbeatTime = 0; } void Player::RemoveFromWorld() { load_health = m_uint32Values[UNIT_FIELD_HEALTH]; load_mana = m_uint32Values[UNIT_FIELD_POWER1]; if(m_bg) m_bg->RemovePlayer(this, true); if(m_tempSummon) { m_tempSummon->RemoveFromWorld(false); if(m_tempSummon) m_tempSummon->SafeDelete(); m_tempSummon = 0; SetUInt64Value(UNIT_FIELD_SUMMON, 0); } // Cancel trade if it's active. Player * pTarget; if(mTradeTarget != 0) { pTarget = GetTradeTarget(); if(pTarget) pTarget->ResetTradeVariables(); ResetTradeVariables(); } //clear buyback GetItemInterface()->EmptyBuyBack(); for(uint32 x=0;x<4;x++) { if(m_TotemSlots[x]) m_TotemSlots[x]->TotemExpire(); } ResetHeartbeatCoords(); ClearSplinePackets(); if(m_Summon) { m_Summon->GetAIInterface()->SetPetOwner(0); m_Summon->Remove(false, true, false); } if(m_SummonedObject) { if(m_SummonedObject->GetInstanceID() != GetInstanceID()) { sEventMgr.AddEvent(m_SummonedObject, &Object::Delete, EVENT_GAMEOBJECT_EXPIRE, 100, 1,0); }else { if(m_SummonedObject->GetTypeId() == TYPEID_PLAYER) { OutputCrashLogLine("SummonedObject = Player!"); #ifdef WIN32 CStackWalker ws; ws.ShowCallstack(); #endif } else { if(m_SummonedObject->IsInWorld()) { m_SummonedObject->RemoveFromWorld(); } delete m_SummonedObject; } } m_SummonedObject = NULL; } if(IsInWorld()) { clearAttackers(true); RemoveItemsFromWorld(); Unit::RemoveFromWorld(); } sWorld.mInWorldPlayerCount--; } // TODO: perhaps item should just have a list of mods, that will simplify code void Player::_ApplyItemMods(Item *item, int8 slot,bool apply,bool justdrokedown) { ASSERT(item); ItemPrototype *proto = item->GetProto(); //fast check to skip mod applying if the item doesnt meat the requirements. if(item->GetUInt32Value(ITEM_FIELD_DURABILITY)==0 && item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY) && justdrokedown==false) { return; } //check for rnd prop uint32 rndprop=item->GetUInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID); if(rndprop) { RandomProps *rp= sRandomPropStore.LookupEntry(rndprop); if(rp) for (int k=0;k<3;k++) { if (rp->spells[k] != 0) { if(apply) { EnchantEntry * ee = sEnchantStore.LookupEntry(rp->spells[k]); int32 Slot = item->HasEnchantment(ee->Id); if(Slot < 0) { Slot = item->FindFreeEnchantSlot(ee); item->AddEnchantment(ee, 0, true, false, true, Slot); } else item->ApplyEnchantmentBonus(Slot, true); }else { EnchantEntry * ee = sEnchantStore.LookupEntry(rp->spells[k]); int32 Slot = item->HasEnchantment(ee->Id); if(Slot >= 0) { //item->RemoveEnchantment(Slot); item->ApplyEnchantmentBonus(Slot, false); } } } } } //Items Set check uint32 setid=proto->ItemSet; if(setid) { ItemSetEntry *set=sItemSetStore.LookupEntry(setid); ASSERT(set); ItemSet* Set=NULL; std::list::iterator i; for(i=m_itemsets.begin();i!=m_itemsets.end();i++) { if(i->setid==setid) { Set = &(*i); break; } } if(apply) { if(!Set) { Set=new ItemSet; memset(Set,0,sizeof(ItemSet)); Set->itemscount=1; Set->setid=setid; } else Set->itemscount++; if(!set->RequiredSkillID || (_GetSkillLineCurrent(set->RequiredSkillID,true) >= set->RequiredSkillAmt)) { for(uint32 x=0;x<8;x++) { if(Set->itemscount==set->itemscount[x]) {//cast new spell SpellEntry *info= sSpellStore.LookupEntry(set->SpellID[x]); Spell * spell=new Spell(this,info,true,NULL); SpellCastTargets targets; targets.m_unitTarget = this->GetGUID(); spell->prepare(&targets); if(info->RequiredShapeShift && (getClass()==DRUID || getClass()==WARRIOR)) m_SSSPecificSpells.insert(info->Id); } } } if(i==m_itemsets.end()) { m_itemsets.push_back(*Set); delete Set; } } else { if(Set) { for(uint32 x=0;x<8;x++) if(Set->itemscount == set->itemscount[x]) { this->RemoveAura(set->SpellID[x],GetGUID()); if(m_SSSPecificSpells.size()) m_SSSPecificSpells.erase(set->SpellID[x]); } if(!(--Set->itemscount)) m_itemsets.erase(i); } } } // Resistances if (proto->Armor) { if(apply) BaseResistance[0]+=proto->Armor; else BaseResistance[0]-=proto->Armor; CalcResistance(0); } //FIXME: can there be negative resistances from items? if (proto->FireRes) { if(apply)FlatResistanceModifierPos[2]+=proto->FireRes; else FlatResistanceModifierPos[2]-=proto->FireRes; CalcResistance(2); } if (proto->NatureRes) { if(apply)FlatResistanceModifierPos[3]+=proto->NatureRes; else FlatResistanceModifierPos[3]-=proto->NatureRes; CalcResistance(3); } if (proto->FrostRes) { if(apply)FlatResistanceModifierPos[4]+=proto->FrostRes; else FlatResistanceModifierPos[4]-=proto->FrostRes; CalcResistance(4); } if (proto->ShadowRes) { if(apply)FlatResistanceModifierPos[5]+=proto->ShadowRes; else FlatResistanceModifierPos[5]-=proto->ShadowRes; CalcResistance(5); } if (proto->ArcaneRes) { if(apply)FlatResistanceModifierPos[6]+=proto->ArcaneRes; else FlatResistanceModifierPos[6]-=proto->ArcaneRes; CalcResistance(6); } // Stats for (int i = 0; i < 10; i++) { int32 val = proto->Stats[i].Value; if(val == 0) continue; ModifyBonuses(proto->Stats[i].Type,apply?val:-val); } if(proto->Damage[0].Min) { if( proto->InventoryType == INVTYPE_RANGED || proto->InventoryType == INVTYPE_RANGEDRIGHT || proto->InventoryType == INVTYPE_THROWN ) { BaseRangedDamage[0]+=apply ? proto->Damage[0].Min : -proto->Damage[0].Min; BaseRangedDamage[1]+=apply ? proto->Damage[0].Max : -proto->Damage[0].Max; } else { if(slot==EQUIPMENT_SLOT_OFFHAND) { BaseOffhandDamage[0]=apply ? proto->Damage[0].Min : 0; BaseOffhandDamage[1]=apply ? proto->Damage[0].Max : 0; } else { BaseDamage[0]=apply ? proto->Damage[0].Min : 1; BaseDamage[1]=apply ? proto->Damage[0].Max : 1; } } } // Misc if (apply) { // Apply all enchantment bonuses item->ApplyEnchantmentBonuses(); for (int k = 0; k < 5;k++) { if (item->GetProto()->Spells[k].Trigger == 1) { SpellEntry* spells = sSpellStore.LookupEntry(item->GetProto()->Spells[k].Id); Spell *spell = new Spell(this, spells ,true,NULL); SpellCastTargets targets; targets.m_unitTarget = this->GetGUID(); spell->castedItemId = item->GetEntry(); spell->prepare(&targets); if(spells->RequiredShapeShift && (getClass()==DRUID || getClass()==WARRIOR)) m_SSSPecificSpells.insert(spells->Id); } else if(item->GetProto()->Spells[k].Trigger == 2) { ProcTriggerSpell ts; ts.origId=0; ts.spellId=item->GetProto()->Spells[k].Id; ts.procChance=5; ts.caster=this->GetGUID(); ts.procFlags=PROC_ON_MELEE_ATTACK; ts.deleted = false; this->m_procSpells.push_front(ts); } } } else { // Remove all enchantment bonuses item->RemoveEnchantmentBonuses(); for (int k = 0; k < 5;k++) { if (item->GetProto()->Spells[k].Trigger == 1) { this->RemoveAura(item->GetProto()->Spells[k].Id); if(m_SSSPecificSpells.size()) m_SSSPecificSpells.erase(item->GetProto()->Spells[k].Id); }else if(item->GetProto()->Spells[k].Trigger == 2) { std::list::iterator i; for(i=m_procSpells.begin();i!=m_procSpells.end();i++) if((*i).spellId==item->GetProto()->Spells[k].Id) { //m_procSpells.erase(i); i->deleted = true; break; } } } } UpdateStats(); } void Player::SetMovement(uint8 pType, uint32 flag) { WorldPacket data(13); switch(pType) { case MOVE_ROOT: { data.SetOpcode(SMSG_FORCE_MOVE_ROOT); data << GetNewGUID(); data << flag; m_currentMovement = MOVE_ROOT; }break; case MOVE_UNROOT: { data.SetOpcode(SMSG_FORCE_MOVE_UNROOT); data << GetNewGUID(); data << flag; m_currentMovement = MOVE_UNROOT; }break; case MOVE_WATER_WALK: { data.SetOpcode(SMSG_MOVE_WATER_WALK); data << GetNewGUID(); data << flag; }break; case MOVE_LAND_WALK: { data.SetOpcode(SMSG_MOVE_LAND_WALK); data << GetNewGUID(); data << flag; }break; default:break; } if(data.size() > 0) SendMessageToSet(&data, true); } void Player::SetPlayerSpeed(uint8 SpeedType, float value) { WorldPacket data(18); data << GetNewGUID(); data << m_speedChangeCounter++; if(SpeedType == RUN) // nfi what this is.. :/ data << uint8(1); data << value; switch(SpeedType) { case RUN: { if(value == m_lastRunSpeed) return; data.SetOpcode(SMSG_FORCE_RUN_SPEED_CHANGE); m_runSpeed = value; m_lastRunSpeed = value; }break; case RUNBACK: { if(value == m_lastRunBackSpeed) return; data.SetOpcode(SMSG_FORCE_RUN_BACK_SPEED_CHANGE); m_backWalkSpeed = value; m_lastRunBackSpeed = value; }break; case SWIM: { if(value == m_lastSwimSpeed) return; data.SetOpcode(SMSG_FORCE_SWIM_SPEED_CHANGE); m_swimSpeed = value; m_lastSwimSpeed = value; }break; case SWIMBACK: { if(value == m_lastBackSwimSpeed) break; data.SetOpcode(MSG_MOVE_SET_SWIM_BACK_SPEED); m_backSwimSpeed = value; m_lastBackSwimSpeed = value; }break; case FLY: { if(value == m_lastFlySpeed) return; data.SetOpcode(SMSG_FORCE_MOVE_SET_FLY_SPEED); m_flySpeed = value; m_lastFlySpeed = value; }break; default:return; } SendMessageToSet(&data , true); // dont mess up on these ResetHeartbeatCoords(); } void Player::BuildPlayerRepop() { SetUInt32Value( UNIT_FIELD_HEALTH, 1 ); //8326 --for all races but ne, 9036 20584--ne SpellCastTargets tgt; tgt.m_unitTarget=this->GetGUID(); if(getRace()==RACE_NIGHTELF) { SpellEntry *inf=sSpellStore.LookupEntry(20584); Spell*sp=new Spell(this,inf,true,NULL); sp->prepare(&tgt); inf=sSpellStore.LookupEntry(9036); sp=new Spell(this,inf,true,NULL); sp->prepare(&tgt); } else { SpellEntry *inf=sSpellStore.LookupEntry(8326); Spell*sp=new Spell(this,inf,true,NULL); sp->prepare(&tgt); } StopMirrorTimer(0); StopMirrorTimer(1); StopMirrorTimer(2); SetFlag(PLAYER_FLAGS, 0x10); SetMovement(MOVE_UNROOT, 1); SetMovement(MOVE_WATER_WALK, 1); } void Player::RepopRequestedPlayer() { if(myCorpse) { GetSession()->SendNotification(NOTIFICATION_MESSAGE_NO_PERMISSION); return; } MapInfo * pMapinfo; sEventMgr.RemoveEvents(this,EVENT_PLAYER_FORECED_RESURECT); //in case somebody resurrected us before this event happened // Set death state to corpse, that way players will lose visibility setDeathState(CORPSE); // Update visibility, that way people wont see running corpses :P UpdateVisibility(); // If we're in battleground, remove the skinnable flag.. has bad effects heheh if(HasFlag(UNIT_FIELD_FLAGS, U_FIELD_FLAG_SKINNABLE)) RemoveFlag(UNIT_FIELD_FLAGS, U_FIELD_FLAG_SKINNABLE); bool corpse = (m_bg != NULL) ? m_bg->CreateCorpse(this) : true; if(corpse) CreateCorpse(); BuildPlayerRepop(); pMapinfo = WorldMapInfoStorage.LookupEntry(GetMapId()); if(pMapinfo) { if(pMapinfo->type == INSTANCE_NULL || pMapinfo->type == INSTANCE_PVP) { RepopAtGraveyard(GetPositionX(),GetPositionY(),GetPositionZ(),GetMapId()); } else { RepopAtGraveyard(pMapinfo->repopx, pMapinfo->repopy, pMapinfo->repopz, pMapinfo->repopmapid); } } else { RepopAtGraveyard(GetPositionX(),GetPositionY(),GetPositionZ(),GetMapId()); } if(corpse) { SpawnCorpseBody(); /* Send Spirit Healer Location */ WorldPacket data(SMSG_SPIRIT_HEALER_POS, 16); data << m_mapId << m_position; m_session->SendPacket(&data); } } void Player::ResurrectPlayer() { sEventMgr.RemoveEvents(this,EVENT_PLAYER_FORECED_RESURECT); //in case somebody resurected us before this event happened if(m_resurrectHealth) SetUInt32Value(UNIT_FIELD_HEALTH, min(m_resurrectHealth, m_uint32Values[UNIT_FIELD_MAXHEALTH])); if(m_resurrectMana) SetUInt32Value(UNIT_FIELD_POWER1, min(m_resurrectMana, m_uint32Values[UNIT_FIELD_MAXPOWER1])); m_resurrectHealth = m_resurrectMana = 0; SpawnCorpseBones(); if(getRace()==RACE_NIGHTELF) { RemoveAura(20584); RemoveAura(9036); }else RemoveAura(8326); RemoveFlag(PLAYER_FLAGS, 0x10); setDeathState(ALIVE); UpdateVisibility(); if(resurrector && IsInWorld()) { Player * p= objmgr.GetPlayer(resurrector); resurrector=0; if(p == 0) return; //_Relocate(p->GetMapMgr()->GetMapId(), p->GetPosition(),false,false); SafeTeleport(p->GetMapId(),p->GetInstanceID(),p->GetPosition()); } SetMovement(MOVE_LAND_WALK, 1); } void Player::KillPlayer() { setDeathState(JUST_DIED); // Battleground stuff if(m_bg) m_bg->HookOnPlayerDeath(this); EventDeath(); m_session->OutPacket(SMSG_CANCEL_COMBAT); m_session->OutPacket(SMSG_CANCEL_AUTO_REPEAT); SetMovement(MOVE_ROOT, 0); StopMirrorTimer(0); StopMirrorTimer(1); StopMirrorTimer(2); SetFlag( UNIT_FIELD_FLAGS, 0x08 ); //player death animation, also can be used with DYNAMIC_FLAGS SetUInt32Value( UNIT_DYNAMIC_FLAGS, 0x00 ); if(this->getClass() == WARRIOR) //rage resets on death SetUInt32Value(UNIT_FIELD_POWER2, 0); sHookInterface.OnDeath(this); } void Player::CreateCorpse() { Corpse *pCorpse; uint32 _uf, _pb, _pb2, _cfb1, _cfb2; objmgr.DelinkPlayerCorpses(this); if(!this->bCorpseCreateable) { bCorpseCreateable = true; // for next time return; // no corpse allowed! } pCorpse = objmgr.CreateCorpse(); pCorpse->SetInstanceID(GetInstanceID()); pCorpse->Create(this, GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); _uf = GetUInt32Value(UNIT_FIELD_BYTES_0); _pb = GetUInt32Value(PLAYER_BYTES); _pb2 = GetUInt32Value(PLAYER_BYTES_2); uint8 race = (uint8)(_uf); uint8 skin = (uint8)(_pb); uint8 face = (uint8)(_pb >> 8); uint8 hairstyle = (uint8)(_pb >> 16); uint8 haircolor = (uint8)(_pb >> 24); uint8 facialhair = (uint8)(_pb2); _cfb1 = ((0x00) | (race << 8) | (0x00 << 16) | (skin << 24)); _cfb2 = ((face) | (hairstyle << 8) | (haircolor << 16) | (facialhair << 24)); pCorpse->SetZoneId( GetZoneId() ); pCorpse->SetUInt32Value( CORPSE_FIELD_BYTES_1, _cfb1 ); pCorpse->SetUInt32Value( CORPSE_FIELD_BYTES_2, _cfb2 ); pCorpse->SetUInt32Value( CORPSE_FIELD_FLAGS, 4 ); pCorpse->SetUInt32Value( CORPSE_FIELD_DISPLAY_ID, GetUInt32Value(UNIT_FIELD_DISPLAYID) ); if(m_bg) { // remove our lootable flags if(HasFlag(UNIT_DYNAMIC_FLAGS, U_DYN_FLAG_LOOTABLE)) RemoveFlag(UNIT_DYNAMIC_FLAGS, U_DYN_FLAG_LOOTABLE); if(HasFlag(UNIT_FIELD_FLAGS,U_FIELD_FLAG_SKINNABLE)) RemoveFlag(UNIT_FIELD_FLAGS,U_FIELD_FLAG_SKINNABLE); loot.gold = 0; pCorpse->generateLoot(); if(bShouldHaveLootableOnCorpse) { pCorpse->SetUInt32Value(CORPSE_FIELD_DYNAMIC_FLAGS, 1); // sets it so you can loot the plyr } else { // hope this works pCorpse->SetUInt32Value(CORPSE_FIELD_FLAGS, 60); } // now that our corpse is created, don't do it again bShouldHaveLootableOnCorpse = false; } else { pCorpse->loot.gold = 0; } uint32 iDisplayID = 0; uint16 iIventoryType = 0; uint32 _cfi = 0; for (int8 i = 0; i < EQUIPMENT_SLOT_END; i++) { if(GetItemInterface()->GetInventoryItem(i)) { iDisplayID = GetItemInterface()->GetInventoryItem(i)->GetProto()->DisplayInfoID; iIventoryType = (uint16)GetItemInterface()->GetInventoryItem(i)->GetProto()->InventoryType; _cfi = (uint16(iDisplayID)) | (iIventoryType)<< 24; pCorpse->SetUInt32Value(CORPSE_FIELD_ITEM + i,_cfi); } } //save corpse in db for future use pCorpse->SaveToDB(); } void Player::SpawnCorpseBody() { Corpse *pCorpse; pCorpse = objmgr.GetCorpseByOwner(this->GetGUIDLow()); if(pCorpse && !pCorpse->IsInWorld()) { if(bShouldHaveLootableOnCorpse && pCorpse->GetUInt32Value(CORPSE_FIELD_DYNAMIC_FLAGS) != 1) pCorpse->SetUInt32Value(CORPSE_FIELD_DYNAMIC_FLAGS, 1); // sets it so you can loot the plyr if(m_mapMgr == 0) pCorpse->AddToWorld(); else pCorpse->PushToWorld(m_mapMgr); } myCorpse = pCorpse; } void Player::SpawnCorpseBones() { Corpse *pCorpse; pCorpse = objmgr.GetCorpseByOwner(GetGUIDLow()); myCorpse = 0; if(pCorpse) { if (pCorpse->IsInWorld() && pCorpse->GetCorpseState() == CORPSE_STATE_BODY) { if(pCorpse->GetInstanceID() != GetInstanceID()) { sEventMgr.AddEvent(pCorpse, &Corpse::SpawnBones, EVENT_CORPSE_SPAWN_BONES, 100, 1,0); } else pCorpse->SpawnBones(); } else { //Cheater! } } } void Player::DeathDurabilityLoss(double percent) { m_session->OutPacket(SMSG_DURABILITY_DAMAGE_DEATH); uint32 pDurability; uint32 pMaxDurability; int32 pNewDurability; for (int8 i = 0; i < EQUIPMENT_SLOT_END; i++) { if(GetItemInterface()->GetInventoryItem(i)) { pMaxDurability = GetItemInterface()->GetInventoryItem(i)->GetUInt32Value(ITEM_FIELD_MAXDURABILITY); pDurability = GetItemInterface()->GetInventoryItem(i)->GetUInt32Value(ITEM_FIELD_DURABILITY); if(pDurability) { pNewDurability = (uint32)(pMaxDurability*percent); pNewDurability = (pDurability - pNewDurability); if(pNewDurability < 0) pNewDurability = 0; if(pNewDurability <= 0) { ApplyItemMods(GetItemInterface()->GetInventoryItem(i), i, false, true); } GetItemInterface()->GetInventoryItem(i)->SetUInt32Value(ITEM_FIELD_DURABILITY,(uint32)pNewDurability); GetItemInterface()->GetInventoryItem(i)->m_isDirty = true; } } } } void Player::RepopAtGraveyard(float ox, float oy, float oz, uint32 mapid) { bool first = true; //float closestX = 0, closestY = 0, closestZ = 0, closestO = 0; StorageContainerIterator * itr; LocationVector src(ox, oy, oz); LocationVector dest(0, 0, 0, 0); LocationVector temp; float closest_dist = 999999.0f; float dist; if(m_bg && m_bg->HookHandleRepop(this)) { return; } else { uint32 areaid = sWorldCreator.GetMap(mapid)->GetAreaID(ox,oy); AreaTable * at = sAreaStore.LookupEntry(areaid); if(!at) return; uint32 mzone = ( at->ZoneId ? at->ZoneId : at->AreaId); itr = GraveyardStorage.MakeIterator(); while(!itr->AtEnd()) { GraveyardTeleport *pGrave = itr->Get(); if((pGrave->MapId == mapid && pGrave->ZoneId == mzone && pGrave->FactionID == GetTeam() || pGrave->MapId == mapid && pGrave->ZoneId == mzone && pGrave->FactionID == 3) || (pGrave->MapId == mapid && pGrave->AdjacentZoneId == mzone && pGrave->FactionID == GetTeam() || pGrave->MapId == mapid && pGrave->AdjacentZoneId == mzone && pGrave->FactionID == 3)) { temp.ChangeCoords(pGrave->X, pGrave->Y, pGrave->Z); dist = src.DistanceSq(temp); if( first || dist < closest_dist ) { first = false; closest_dist = dist; dest = temp; } } if(!itr->Inc()) break; } itr->Destruct(); } if(sHookInterface.OnRepop(this) && dest.x != 0 && dest.y != 0 && dest.z != 0) { SafeTeleport(mapid, 0, dest); } // //correct method as it works on official server, and does not require any damn sql // //no factions! no zones! no sqls! 1word: blizz-like // float closestX , closestY , closestZ ; // uint32 entries=sWorldSafeLocsStore.GetNumRows(); // GraveyardEntry*g; // uint32 mymapid=mapid // float mx=ox,my=oy; // float last_distance=9e10; // // for(uint32 x=0;xmapid!=mymapid)continue; // float distance=(mx-g->x)*(mx-g->x)+(my-g->y)*(my-g->y); // if(distancex; // closestY=g->y; // closestZ=g->z; // last_distance=distance; // } // // // } // if(last_distance<1e10) //#endif } void Player::JoinedChannel(Channel *c) { m_channels.push_back(c); } void Player::LeftChannel(Channel *c) { m_channels.remove(c); } void Player::CleanupChannels() { list::iterator i; for(i = m_channels.begin(); i != m_channels.end(); i++) (*i)->Leave(this,false); } void Player::SendInitialActions() { #ifndef USING_BIG_ENDIAN m_session->OutPacket(SMSG_ACTION_BUTTONS, 480, &mActions); #else /* we can't do this the fast way on ppc, due to endianness */ WorldPacket data(SMSG_ACTION_BUTTONS, 480); for(uint32 i = 0; i < 480; ++i) { data << mActions[i].Action << mActions[i].Type << mActions[i].Misc; } m_session->SendPacket(&data); #endif } void Player::setAction(uint8 button, uint16 action, uint8 type, uint8 misc) { assert(button < 120); mActions[button].Action = action; mActions[button].Type = type; mActions[button].Misc = misc; } //Groupcheck bool Player::IsGroupMember(Player *plyr) { if(m_Group != NULL) return m_Group->HasMember(plyr); else return false; } int32 Player::GetOpenQuestSlot() { for (uint32 i = 0; i < 25; ++i) if (m_questlog[i] == NULL) return i; return -1; } void Player::AddToFinishedQuests(uint32 quest_id) { //maybe that shouldn't be an assert, but i'll leave it for now //ASSERT(m_finishedQuests.find(quest_id) == m_finishedQuests.end()); //Removed due to crash //If it failed though, then he's probably cheating. if (m_finishedQuests.find(quest_id) != m_finishedQuests.end()) return; m_finishedQuests.insert(quest_id); } bool Player::HasFinishedQuest(uint32 quest_id) { return (m_finishedQuests.find(quest_id) != m_finishedQuests.end()); } //From Mangos Project void Player::_LoadTutorials() { QueryResult *result = CharacterDatabase.Query("SELECT * FROM tutorials WHERE playerId=%u",GetGUIDLow()); if(result) { Field *fields = result->Fetch(); for (int iI=0; iI<8; iI++) m_Tutorials[iI] = fields[iI + 1].GetUInt32(); delete result; } tutorialsDirty = false; } void Player::_SaveTutorials() { if(tutorialsDirty) { CharacterDatabase.Execute("REPLACE INTO tutorials VALUES('%u','%u','%u','%u','%u','%u','%u','%u','%u')", GetGUIDLow(), m_Tutorials[0], m_Tutorials[1], m_Tutorials[2], m_Tutorials[3], m_Tutorials[4], m_Tutorials[5], m_Tutorials[6], m_Tutorials[7]); tutorialsDirty = false; } } uint32 Player::GetTutorialInt(uint32 intId ) { ASSERT( intId < 8 ); return m_Tutorials[intId]; } void Player::SetTutorialInt(uint32 intId, uint32 value) { if(intId >= 8) return; ASSERT( (intId < 8) ); m_Tutorials[intId] = value; tutorialsDirty = true; } //Player stats calculation for saving at lvl up, etc /*void Player::CalcBaseStats() {//((Player*)this)->getClass() == HUNTER || //TODO take into account base stats at create uint32 AP, RAP; //Save AttAck power if(getClass() == ROGUE || getClass() == HUNTER) { AP = GetBaseUInt32Value(UNIT_FIELD_STAT0) + GetBaseUInt32Value(UNIT_FIELD_STAT1); RAP = (GetBaseUInt32Value(UNIT_FIELD_STAT1) * 2); SetBaseUInt32Value(UNIT_FIELD_ATTACK_POWER, AP); SetBaseUInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER, RAP); } else { AP = (GetBaseUInt32Value(UNIT_FIELD_STAT0) * 2); RAP = (GetBaseUInt32Value(UNIT_FIELD_STAT1) * 2); SetBaseUInt32Value(UNIT_FIELD_ATTACK_POWER, AP); SetBaseUInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER, RAP); } }*/ void Player::UpdateHit(int32 hit) { /*std::list::iterator i; Affect::ModList::const_iterator j; Affect *aff; uint32 in = hit; for (i = GetAffectBegin(); i != GetAffectEnd(); i++) { aff = *i; for (j = aff->GetModList().begin();j != aff->GetModList().end(); j++) { Modifier mod = (*j); if ((mod.GetType() == SPELL_AURA_MOD_HIT_CHANCE)) { SpellEntry *spellInfo = sSpellStore.LookupEntry(aff->GetSpellId()); if (this->canCast(spellInfo)) in += mod.GetAmount(); } } } SetHitFromSpell(in);*/ } void Player::UpdateChances() { int clss = (int)getClass(); const float baseDodge[12] = { 0, 0, 0.75, 0.64, 0, 3, 0, 1.75, 3.25, 2, 0, 0.75 }; const float dodgeRatio[12] = { 0, 30, 30, 40, 21, 30, 0, 30, 30, 30, 0, 30 }; float tmp = baseDodge[clss] + (GetUInt32Value( UNIT_FIELD_STAT1) / dodgeRatio[clss]) + this->GetDodgeFromSpell(); tmp+=CalcRating(2);//dodge rating SetFloatValue(PLAYER_DODGE_PERCENTAGE,min(tmp,95.0)); tmp = 5.0f + GetUInt32Value(UNIT_FIELD_STAT0) / 22.0 + this->GetBlockFromSpell(); tmp+=CalcRating(4);//block rating SetFloatValue(PLAYER_BLOCK_PERCENTAGE,min(tmp,95.0)); tmp = 5.0f + this->GetParryFromSpell(); tmp+=CalcRating(3); SetFloatValue(PLAYER_PARRY_PERCENTAGE,max(0,min(tmp,95.0))); //let us not use negative parry. Some spells decrease it /* The formula is generated as follows: [agility] / [crit constant*] + [skill modifier] + [bonuses] The crit constant is class and level dependent and for a level 70 character as follows: * Rogue [40] * Druid [25.00] * Hunter [40] * Mage [25.00] * Paladin [25.00] * Priest [25.00] * Shaman [25.00] * Warlock [24.69] * Warrior [33] */ switch(clss) { case ROGUE: tmp = 5.0f + (GetUInt32Value(UNIT_FIELD_STAT1) / 40.00); break; case HUNTER: tmp = 5.0f + (GetUInt32Value(UNIT_FIELD_STAT1) / 40.00); break; case WARRIOR: tmp = 5.0f + (GetUInt32Value(UNIT_FIELD_STAT1) / 33.00); break; default: tmp = 5.0f + (GetUInt32Value(UNIT_FIELD_STAT1) / 25.00); break; } //std::list::iterator i = tocritchance.begin(); map::iterator i = tocritchance.begin(); Item*it = GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_MAINHAND); float b=0; for(;i!=tocritchance.end();++i) { //-1 = any weapon if((i->second.wclass==(uint32)-1) || (it && (1 << it->GetProto()->SubClass & i->second.subclass))) { b+=i->second.value; } } float cr=tmp+CalcRating(8)+b; SetFloatValue(PLAYER_CRIT_PERCENTAGE,min(cr,95.0)); float rcr=tmp+CalcRating(9); SetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE,min(rcr,95.0)); //TODO: Correct spell crit chance calc. spellcritperc = (GetUInt32Value(UNIT_FIELD_STAT3) / 60.0f) + this->GetSpellCritFromSpell(); spellcritperc+=CalcRating(10); UpdateChanceFields(); } void Player::UpdateChanceFields() { // Update spell crit values in fields for(uint32 i = 0; i < 7; ++i) { SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1 + i, SpellCritChanceSchool[i]+spellcritperc); } } void Player::UpdateAttackSpeed() { uint32 speed=2000; Item *weap ; if(GetShapeShift()==FORM_CAT)//cat form { speed = 1000; }else if(GetShapeShift()==FORM_BEAR || GetShapeShift()==FORM_DIREBEAR) { speed = 2500; } else //regular if(!disarmed) { weap=GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_MAINHAND); if(weap) speed=weap->GetProto()->Delay; } SetUInt32Value(UNIT_FIELD_BASEATTACKTIME, (uint32)(speed*(100.0 - ((float)m_meleeattackspeedmod) - CalcRating(17))/100.0)); weap=GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_OFFHAND); if(weap && weap->GetProto()->Class==2)//weapon { speed=weap->GetProto()->Delay; SetUInt32Value(UNIT_FIELD_BASEATTACKTIME_01, (uint32)(speed*(100.0 - ((float)m_meleeattackspeedmod) - CalcRating(17))/100.0)); } weap=GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_RANGED); if(weap) { speed=weap->GetProto()->Delay; SetUInt32Value(UNIT_FIELD_RANGEDATTACKTIME, (uint32)(speed*(100.0 - ((float)m_rangedattackspeedmod) - CalcRating(18))/100.0)); } } void Player::UpdateStats() { UpdateAttackSpeed(); //formulas from wowwiki int32 AP = 0, RAP = 0; uint32 str,agi,lev; str=GetUInt32Value(UNIT_FIELD_STAT0); agi=GetUInt32Value(UNIT_FIELD_STAT1); lev=getLevel(); // Attack power uint32 cl=getClass(); switch (cl) { case DRUID: AP=str*2-20; if(GetShapeShift()==FORM_CAT) AP += agi + lev * 2; if(GetShapeShift() == FORM_BEAR || GetShapeShift() == FORM_DIREBEAR) AP += lev * 3; break; case ROGUE: //AP = lev * 2 + str + agi - 20; //RAP = lev + agi * 2 - 20; // AP = str + agi - 20; AP = lev * 2 + str + agi - 20; RAP = lev + agi - 10; break; case HUNTER: //AP = lev* 2 + str + agi - 20; //RAP = (lev + agi)*2 - 20; AP = str + agi - 20; RAP = lev * 2 + agi - 10; break; case SHAMAN: AP= (lev+str)*2 - 20; break; case PALADIN: //AP = lev * 3 + str * 2 - 20; // AP = str * 2 - 20; AP = lev * 3 + str * 2 - 20; break; case WARRIOR: // AP = lev * 3 + str * 2 - 20; //RAP = (lev+agi)*2 - 20; // AP = str * 2 - 20; AP = lev * 3 + str * 2 - 20; RAP = lev + agi - 20; break; default://mage,priest,warlock AP = str-10; } /* modifiers */ RAP += int32(float(float(m_rap_mod_pct) * float(float(m_uint32Values[UNIT_FIELD_STAT3]) / 100.0f))); if(RAP <0) RAP=0; if(AP < 0) AP=0; SetUInt32Value(UNIT_FIELD_ATTACK_POWER, AP); SetUInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER, RAP); int32 hp=GetUInt32Value(UNIT_FIELD_BASE_HEALTH); int32 bonus=(GetUInt32Value(UNIT_FIELD_POSSTAT2)-GetUInt32Value(UNIT_FIELD_NEGSTAT2))*10+m_healthfromspell+m_healthfromitems; int32 res=hp+bonus; int32 oldmaxhp=GetUInt32Value(UNIT_FIELD_MAXHEALTH); if(resres) SetUInt32Value(UNIT_FIELD_HEALTH,res); else if ( (cl==DRUID) && (GetShapeShift() == FORM_BEAR || GetShapeShift() == FORM_DIREBEAR) ) { res=(int32) (float)GetUInt32Value(UNIT_FIELD_MAXHEALTH)*(float)GetUInt32Value(UNIT_FIELD_HEALTH)/oldmaxhp; SetUInt32Value(UNIT_FIELD_HEALTH,res); } if(cl!=WARRIOR&&cl!=ROGUE) { // MP int32 mana = GetUInt32Value(UNIT_FIELD_BASE_MANA); bonus=(GetUInt32Value(UNIT_FIELD_POSSTAT3)-GetUInt32Value(UNIT_FIELD_NEGSTAT3))*15+m_manafromspell +m_manafromitems ; res=mana+bonus; if(resres) SetUInt32Value(UNIT_FIELD_POWER1,res); } /////////////////////RATINGS STUFF///////////////// float newb=CalcRating(19); if(newb!=SpellHasteRatingBonus) { ModFloatValue(UNIT_MOD_CAST_SPEED,(SpellHasteRatingBonus-newb)/100.0); SpellHasteRatingBonus=newb; } float NewShit = CalcRating(10); if(NewShit !=SpellCrtiticalStrikeRatingBonus) { for(uint32 i = 0; i < 7; ++i) { SpellCritChanceSchool[i]+=(NewShit-SpellCrtiticalStrikeRatingBonus); } SpellCrtiticalStrikeRatingBonus=NewShit; } ////////////////////RATINGS STUFF////////////////////// UpdateChances(); CalcDamage(); } void Player::AddRestXP(uint32 amount) { if(GetUInt32Value(UNIT_FIELD_LEVEL) >= GetUInt32Value(PLAYER_FIELD_MAX_LEVEL)) // Save CPU, don't waste time on this if you're >= 60 return; m_restAmount += amount; SetUInt32Value(PLAYER_REST_STATE_EXPERIENCE, (uint32)(m_restAmount*0.5)); UpdateRestState(); } uint32 Player::SubtractRestXP(uint32 amount) { if(GetUInt32Value(UNIT_FIELD_LEVEL) >= GetUInt32Value(PLAYER_FIELD_MAX_LEVEL)) // Save CPU, don't waste time on this if you're >= 70 return 0; uint32 amt = amount; int32 tmp = m_restAmount - amount; int32 pos = m_restAmount - (amount*2); if(pos < 0) pos = 0; if(tmp < 0) { amt = m_restAmount; m_restAmount = 0; } else m_restAmount -= amount; SetUInt32Value(PLAYER_REST_STATE_EXPERIENCE, pos); UpdateRestState(); return amt; } uint32 Player::CalculateRestXP(uint32 seconds) { float rate = sWorld.getRate(RATE_RESTXP); float xp = 0; if(seconds < 60) { xp = 1 * rate; } else { xp = ((seconds / 60) * rate); } return uint32(xp); } void Player::EventPlayerRest() { if(GetUInt32Value(UNIT_FIELD_LEVEL) >= GetUInt32Value(PLAYER_FIELD_MAX_LEVEL)) // Save CPU, don't waste time on this if you're >= 70 { EventMgr::getSingleton().RemoveEvents(this, EVENT_PLAYER_REST); return; } // Rest timer float diff = difftime(time(NULL),m_lastRestUpdate); m_lastRestUpdate = (uint32)time(NULL); uint32 RestXP = CalculateRestXP((uint32)diff); sLog.outDebug("REST: Adding %d rest XP for %.0f seconds of rest time", RestXP, diff); AddRestXP(RestXP); } void Player::UpdateRestState() { if(m_restAmount && GetUInt32Value(UNIT_FIELD_LEVEL) < GetUInt32Value(PLAYER_FIELD_MAX_LEVEL)) m_restState = RESTSTATE_RESTED; else m_restState = RESTSTATE_NORMAL; // Update needle position SetUInt32Value(PLAYER_BYTES_2, ((GetUInt32Value(PLAYER_BYTES_2) & 0x00FFFFFF) | (m_restState << 24))); } void Player::ApplyPlayerRestState(bool apply) { if(apply) { m_restState = RESTSTATE_RESTED; m_isResting = true; SetFlag(PLAYER_FLAGS, PLAYER_FLAG_RESTING); //put zzz icon UpdateRestState(); m_lastRestUpdate = (uint32)time(NULL); if(GetUInt32Value(UNIT_FIELD_LEVEL) >= GetUInt32Value(PLAYER_FIELD_MAX_LEVEL)) // Save CPU, don't waste time on this if you're >= 70 return; sEventMgr.AddEvent(this, &Player::EventPlayerRest, EVENT_PLAYER_REST, (uint32)60000, 0, EVENT_FLAG_DO_NOT_EXECUTE_IN_WORLD_CONTEXT); } else { m_isResting = false; RemoveFlag(PLAYER_FLAGS,PLAYER_FLAG_RESTING); //remove zzz icon sEventMgr.RemoveEvents(this, EVENT_PLAYER_REST); UpdateRestState(); } } void Player::UpdateCooldowns() { if(CooldownCheat) return; uint32 mstime = getMSTime(); map::iterator itr; map::iterator next; for(itr = SpellCooldownMap.begin();itr!=SpellCooldownMap.end();) { next = itr++; if(mstime >= next->second) SpellCooldownMap.erase(next); } for(itr = SpellCooldownCategoryMap.begin();itr!=SpellCooldownCategoryMap.end();) { next = itr++; if(mstime >= next->second) SpellCooldownCategoryMap.erase(next); } if(mstime > GlobalCooldown) GlobalCooldown = 0; } void Player::AddCategoryCooldown(uint32 cat, uint32 tm) { if(CooldownCheat) return; map::iterator itr = SpellCooldownCategoryMap.find(cat); uint32 mstime = getMSTime(); if(itr != SpellCooldownCategoryMap.end()) { itr->second = mstime + tm; } else { SpellCooldownCategoryMap.insert( make_pair( cat, mstime + tm ) ); } } void Player::AddCooldown(uint32 cat, uint32 tm) { if(CooldownCheat) return; map::iterator itr = SpellCooldownMap.find(cat); uint32 mstime = now(); if(itr != SpellCooldownMap.end()) { itr->second = mstime + tm; } else { SpellCooldownMap.insert( make_pair( cat, mstime + tm ) ); } } // I gues I need to look over this again // rename this function into AddRecoverSpellCooldown or something void Player::AddRecoverCooldown(SpellEntry * spellInfo) { if(CooldownCheat) return; // if we have a cooldown larger then 1 minute if (spellInfo->RecoveryTime > 1 * 60 * 1000 || spellInfo->CategoryRecoveryTime > 1 * 60 * 1000) { ItemCooldown * item = new ItemCooldown; int32 cooltime; item->ItemEntry = 0; // SpellCoolDowns have no itemid item->SpellID = spellInfo->Id; // spellId item->SpellCategory = spellInfo->Category; // category // if we have a standard cooldown time // double check this if (spellInfo->RecoveryTime) { cooltime = spellInfo->RecoveryTime; } else { cooltime = spellInfo->CategoryRecoveryTime; } SM_FIValue(this->SM_FCooldownTime, &cooltime, spellInfo->SpellGroupType); SM_PIValue(this->SM_PCooldownTime, &cooltime, spellInfo->SpellGroupType); item->Cooldown = cooltime; item->CooldownTimeStamp = now() + cooltime; m_itemcooldown.insert(item); } } void Player::AddGlobalCooldown(uint32 tm) { if(CooldownCheat) return; GlobalCooldown = getMSTime() + tm; } #define CORPSE_VIEW_DISTANCE 900 // 30*30 bool Player::CanSee(Object* obj) { if (obj == this) return true; uint32 object_type = obj->GetTypeId(); if (!isAlive()) { if(myCorpse && myCorpse->GetDistance2dSq(this) < sWorld.m_UpdateDistance) // We're close enough to our corpse { if(myCorpse->GetDistanceSq(obj) <= CORPSE_VIEW_DISTANCE) { // We can see any object within a certain distance from our corpse. ;) return true; } } // If we got here, we're further away from our corpse than we can see. switch(object_type) { case TYPEID_DYNAMICOBJECT: return false; //------------------------------------------------------------------ case TYPEID_UNIT: { if ( ((Unit*)obj)->IsSpiritHealer() ) return true; else return false; } //------------------------------------------------------------------ case TYPEID_PLAYER: { // if we're in corpse state, we can't see anything but other dead players. // otherwise, we can still see everything if(getDeathState() != CORPSE || m_deathVision) return true; else return false; } //------------------------------------------------------------------ default: return true; } } else//alive { switch(object_type) { case TYPEID_PLAYER: { Player *pObj = static_cast(obj); // gm invisible only applies if we're not a gm! if (pObj->m_isGmInvisible && !m_session->GetPermissionCount()) return false; // if the player is dead, but not in corpse state yet it means that // they're lying on the ground dead, we need to be able to see them // so that we can resurrect, etc. however, if the player is in corpse // phase it means they just repopped or are running in deathworld // and we have to destroy/can't see them. if(pObj->getDeathState() == CORPSE) return false; if(pObj->m_invisible) return false; if(!pObj->m_stealth) return true; if(pObj->trackStealth) return true; else { //stealth if(this->GetSubGroup()) if(this->GetSubGroup() == ((Player*)(obj))->GetSubGroup()) //is in 1 party -> can see return true; if(this->GetGUID()==((Player*)(obj))->stalkedby) return true; if((pObj)->isInFront(this)) return false; float visibility = (float)GetStealthDetect()/(float)((Player*)(obj))->GetStealthLevel() ; float invisRange = visibility * 3 + GetFloatValue (UNIT_FIELD_BOUNDINGRADIUS) +obj->GetFloatValue (UNIT_FIELD_BOUNDINGRADIUS); if (bGMTagOn || GetDistance2dSq (obj) <= invisRange * invisRange) return true; else return false; } } //------------------------------------------------------------------ case TYPEID_UNIT: { if(static_cast(obj)->m_invisible) return false; if ( ((Unit*)obj)->IsSpiritHealer() ) return false; uint32 val = ((Unit*)obj)->m_invisibityFlag; if(val > INVISIBILTY_FLAG_NONE) { if (val < INVISIBILTY_FLAG_TOTAL) { float r = GetInvisibiltyDetection(static_cast(val))/GetUInt32Value(PLAYER_FIELD_MAX_LEVEL); if (GetDistance2dSq (obj) < r * r) return true; else return false; } } else return true; } //------------------------------------------------------------------ case TYPEID_GAMEOBJECT://some go's are stealthed { if(static_cast(obj)->invisible) { uint64 owner = obj->GetUInt64Value(OBJECT_FIELD_CREATED_BY); if(this->GetGUID() == owner) return true; SubGroup * pGroup = GetGroup() ? GetGroup()->GetSubGroup(GetSubGroup()) : 0; if(pGroup) return pGroup->HasMember(owner); else { float r = GetInvisibiltyDetection(static_cast(obj)->invisibilityFlag)/sWorld.Expansion1LevelCap; if (GetDistance2dSq (obj) < r * r) return true; else return false; } } else return true; } //------------------------------------------------------------------ default: return true; } } } void Player::AddInRangeObject(Object* pObj) { //Send taxi move if we're on a taxi if ((GetTaxiState()) && (pObj->GetTypeId() == TYPEID_PLAYER)) { uint32 ntime = getMSTime(); if (ntime > m_taxi_ride_time) m_CurrentTaxiPath->SendMoveForTime(this, (Player*)pObj, ntime - m_taxi_ride_time); else m_CurrentTaxiPath->SendMoveForTime(this, (Player*)pObj, m_taxi_ride_time - ntime); } Unit::AddInRangeObject(pObj); //if the object is a unit send a move packet if they have a destination if(pObj->GetTypeId() == TYPEID_UNIT) { //add an event to send move update have to send guid as pointer was causing a crash :( //sEventMgr.AddEvent(((Creature*)pObj)->GetAIInterface(), &AIInterface::SendCurrentMove, this->GetGUID(), EVENT_UNIT_SENDMOVE, 200, 1); ((Creature*)pObj)->GetAIInterface()->SendCurrentMove(this); } } void Player::OnRemoveInRangeObject(Object* pObj) { //if (/*!CanSee(pObj) && */IsVisible(pObj)) //{ //RemoveVisibleObject(pObj); //} if(m_tempSummon == pObj) { m_tempSummon->RemoveFromWorld(false); if(m_tempSummon) m_tempSummon->SafeDelete(); m_tempSummon = 0; SetUInt64Value(UNIT_FIELD_SUMMON, 0); } m_visibleObjects.erase(pObj); Unit::OnRemoveInRangeObject(pObj); if( pObj == m_CurrentCharm ) { this->UnPossess(); if(m_currentSpell) m_currentSpell->cancel(); // cancel the spell } if(pObj == m_Summon) { if(m_Summon->IsSummon()) { m_Summon->Dismiss(true); } else { m_Summon->Remove(true, true, false); } m_Summon = 0; } } void Player::ClearInRangeSet() { m_visibleObjects.clear(); Unit::ClearInRangeSet(); } void Player::EventCannibalize(uint32 amount) { uint32 amt = (GetUInt32Value(UNIT_FIELD_MAXHEALTH)*amount)/100; uint32 newHealth = GetUInt32Value(UNIT_FIELD_HEALTH) + amt; if(newHealth <= GetUInt32Value(UNIT_FIELD_MAXHEALTH)) SetUInt32Value(UNIT_FIELD_HEALTH, newHealth); else SetUInt32Value(UNIT_FIELD_HEALTH, GetUInt32Value(UNIT_FIELD_MAXHEALTH)); cannibalizeCount++; if(cannibalizeCount == 5) SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); WorldPacket data(SMSG_PERIODICAURALOG, 38); data << GetNewGUID(); // caster guid data << GetNewGUID(); // target guid data << (uint32)(20577); // spellid data << (uint32)1; // unknown?? need resource? data << (uint32)FLAG_PERIODIC_HEAL; // aura school data << amt; // amount of done to target / heal / damage data << (uint32)0; // unknown in some sniff this was 0x0F SendMessageToSet(&data, true); } void Player::EventReduceDrunk(bool full) { uint8 drunk = ((GetUInt32Value(PLAYER_BYTES_3) >> 24) & 0xFF); if(full) drunk = 0; else drunk -= 10; SetUInt32Value(PLAYER_BYTES_3, ((GetUInt32Value(PLAYER_BYTES_3) & 0xFFFF00FF) | (drunk << 8))); if(drunk == 0) sEventMgr.RemoveEvents(this, EVENT_PLAYER_REDUCEDRUNK); } void Player::LoadTaxiMask(const char* data) { vector tokens = StrSplit(data, " "); int index; vector::iterator iter; for (iter = tokens.begin(), index = 0; (index < 8) && (iter != tokens.end()); ++iter, ++index) { m_taximask[index] = atol((*iter).c_str()); } } bool Player::HasQuestForItem(uint32 itemid) { Quest *qst; for(uint32 i = 0; i < 25; ++i) { if(m_questlog[i] != NULL) { qst = m_questlog[i]->GetQuest(); if(!qst->count_required_item) continue; for(uint32 j = 0; j < qst->count_required_item && j < 4; ++j) if(qst->required_item[j] == itemid && (GetItemInterface()->GetItemCount(itemid) < qst->required_itemcount[j])) return true; } } return false; } /*Loot type MUST be 1-corpse, go 2-skinning/herbalism/minning 3-Fishing */ void Player::SendLoot(uint64 guid,uint8 loot_type) { if(!IsInWorld()) return; Loot * pLoot = NULL; if(UINT32_LOPART(GUID_HIPART(guid)) == HIGHGUID_UNIT) { Creature* pCreature = GetMapMgr()->GetCreature(guid); if(!pCreature)return; pLoot=&pCreature->loot; m_currentLoot = pCreature->GetGUID(); }else if(UINT32_LOPART(GUID_HIPART(guid)) == HIGHGUID_GAMEOBJECT) { GameObject* pGO = GetMapMgr()->GetGameObject(guid); if(!pGO)return; pGO->SetUInt32Value(GAMEOBJECT_STATE,0); pLoot=&pGO->loot; m_currentLoot = pGO->GetGUID(); } else if((UINT32_LOPART(GUID_HIPART(guid)) == HIGHGUID_PLAYER) ) { Player *p=GetMapMgr()->GetPlayer(guid); if(!p)return; pLoot=&p->loot; m_currentLoot = p->GetGUID(); } else if( (UINT32_LOPART(GUID_HIPART(guid)) == HIGHGUID_CORPSE)) { Corpse *pCorpse = objmgr.GetCorpse(guid); if(!pCorpse)return; pLoot=&pCorpse->loot; m_currentLoot = pCorpse->GetGUID(); } else if( (UINT32_LOPART(GUID_HIPART(guid)) == HIGHGUID_ITEM) ) { Item *pItem = GetItemInterface()->GetItemByGUID(guid); if(!pItem) return; pLoot = pItem->loot; m_currentLoot = pItem->GetGUID(); } if(!pLoot) { // something whack happened.. damn cheaters.. return; } // add to looter set pLoot->looters.insert(GetGUID()); WorldPacket data, data2(28); data.SetOpcode (SMSG_LOOT_RESPONSE); m_lootGuid = guid; data << guid; data << loot_type;//loot_type; data << pLoot->gold; data << (uint8) 0;//loot size reserve std::vector<__LootItem>::iterator iter=pLoot->items.begin(); uint32 count=0; uint8 slottype = 0; for(uint32 x=0;iter!=pLoot->items.end();iter++,x++) { if (iter->iItemsCount == 0) continue; ItemPrototype* itemProto =ItemPrototypeStorage.LookupEntry(iter->item.itemid); if (!itemProto) continue; //quest items check. type 4/5 //quest items that dont start quests. if((itemProto->Bonding == ITEM_BIND_QUEST) && !(itemProto->QuestId) && !HasQuestForItem(iter->item.itemid)) continue; if((itemProto->Bonding == ITEM_BIND_QUEST2) && !(itemProto->QuestId) && !HasQuestForItem(iter->item.itemid)) continue; //quest items that start quests need special check to avoid drops all the time. if((itemProto->Bonding == ITEM_BIND_QUEST) && (itemProto->QuestId) && GetQuestLogForEntry(itemProto->QuestId)) continue; if((itemProto->Bonding == ITEM_BIND_QUEST2) && (itemProto->QuestId) && GetQuestLogForEntry(itemProto->QuestId)) continue; if((itemProto->Bonding == ITEM_BIND_QUEST) && (itemProto->QuestId) && HasFinishedQuest(itemProto->QuestId)) continue; if((itemProto->Bonding == ITEM_BIND_QUEST2) && (itemProto->QuestId) && HasFinishedQuest(itemProto->QuestId)) continue; //check for starting item quests that need questlines. if((itemProto->QuestId && itemProto->Bonding != ITEM_BIND_QUEST && itemProto->Bonding != ITEM_BIND_QUEST2)) { bool HasRequiredQuests = true; Quest * pQuest = QuestStorage.LookupEntry(itemProto->QuestId); if(pQuest) { //check if its a questline. for(uint32 i = 0; i < pQuest->required_quests[4]; i++) { if(pQuest->required_quests[i]) { if(!HasFinishedQuest(pQuest->required_quests[i]) || GetQuestLogForEntry(pQuest->required_quests[i])) { HasRequiredQuests = false; break; } } } if(!HasRequiredQuests) continue; } } slottype = 0; if(m_Group != NULL) { switch(m_Group->GetMethod()) { case PARTY_LOOT_MASTER: slottype = 2; break; case PARTY_LOOT_GROUP: case PARTY_LOOT_RR: case PARTY_LOOT_NBG: slottype = 1; break; default: slottype = 0; break; } // only quality items are distributed if(itemProto->Quality < m_Group->GetThreshold()) { slottype = 0; } /* if all people passed anyone can loot it? :P */ if(iter->passed) slottype = 0; // All players passed on the loot } data << uint8(x); data << uint32(iter->item.itemid); data << uint32(iter->iItemsCount);//nr of items of this type data << uint32(iter->item.displayid); data << uint32(0) ; data << uint32(iter->iRandomProperty); data << slottype; // "still being rolled for" flag if(slottype == 1) { if(iter->roll == NULL && !iter->passed) { iter->roll = new LootRoll(60000, (m_Group != NULL ? m_Group->MemberCount() : 1), guid, x, iter->item.itemid, 0, iter->iRandomProperty, GetMapMgr()); data2.Initialize(SMSG_LOOT_START_ROLL); data2 << guid; data2 << x; data2 << uint32(iter->item.itemid); data2 << uint32(0); data2 << uint32(iter->iRandomProperty); data2 << uint32(60000); // countdown if(m_Group) { m_Group->Lock(); for(uint32 i = 0; i < m_Group->GetSubGroupCount(); ++i) { for(GroupMembersSet::iterator itr = m_Group->GetSubGroup(i)->GetGroupMembersBegin(); itr != m_Group->GetSubGroup(i)->GetGroupMembersEnd(); ++itr) { if(itr->player && itr->player->GetItemInterface()->CanReceiveItem(itemProto, iter->iItemsCount) == 0) { itr->player->GetSession()->SendPacket(&data2); } } } m_Group->Unlock(); } else { GetSession()->SendPacket(&data2); } } } count++; } data.wpos (13) ; data << (uint8)count; GetSession ()->SendPacket(&data); SetFlag(UNIT_FIELD_FLAGS,U_FIELD_ANIMATION_LOOTING); } void Player::EventAllowTiggerPort(bool enable) { m_AllowAreaTriggerPort = enable; } uint32 Player::CalcTalentResetCost(uint32 resetnum) { if(resetnum ==0 ) return 10000; else { if(resetnum>10) return 500000; else return resetnum*50000; } } void Player::SendTalentResetConfirm() { WorldPacket data(MSG_TALENT_WIPE_CONFIRM, 12); data << GetGUID(); data << CalcTalentResetCost(GetTalentResetTimes()); GetSession()->SendPacket(&data); } bool Player::CanShootRangedWeapon(uint32 spellid, Unit *target, bool autoshot) { SpellEntry *spellinfo = sSpellStore.LookupEntry(spellid); if(!spellinfo) return false; uint8 fail = 0; Item *itm = GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_RANGED); if(!itm) { fail = SPELL_FAILED_NO_AMMO; return false; } if(this->m_AutoShotStartX != GetPositionX() || this->m_AutoShotStartY != GetPositionY() || this->m_AutoShotStartZ != GetPositionZ()) { // We've moved //printf("Autoshot: Detected player movement. canceling.\n"); fail = SPELL_FAILED_INTERRUPTED; } if(m_curSelection != m_AutoShotTarget) { // Player has clicked off target. Fail spell. fail = SPELL_FAILED_INTERRUPTED; } if(GetCurrentSpell()) return false; if(!target || target->isDead()) return false; SpellRange * range = sSpellRange.LookupEntry(spellinfo->rangeIndex); float minrange = GetMinRange(range); float dist = CalcDistance(this, target); float maxr = GetMaxRange(range); // Check for close if(spellid != SPELL_RANGED_WAND)//no min limit for wands if(minrange > dist) fail = SPELL_FAILED_TOO_CLOSE; if(maxr < dist) { fail = SPELL_FAILED_OUT_OF_RANGE; } if(spellid == SPELL_RANGED_THROW) { if(itm) // no need for this if(itm->GetProto()) if(GetItemInterface()->GetItemCount(itm->GetProto()->ItemId) == 0) fail = SPELL_FAILED_NO_AMMO; } /* else { if(GetUInt32Value(PLAYER_AMMO_ID))//for wand if(this->GetItemInterface()->GetItemCount(GetUInt32Value(PLAYER_AMMO_ID)) == 0) fail = SPELL_FAILED_NO_AMMO; }*/ if(fail)// && fail != SPELL_FAILED_OUT_OF_RANGE) { SendCastResult(autoshot ? 75 : spellid, fail, 0); return false; } return true; } void Player::EventRepeatSpell() { if(!m_curSelection) return; Unit *target = GetMapMgr()->GetUnit(m_curSelection); if(!target || !this->CanShootRangedWeapon(m_AutoShotSpell->Id, target, true)) { m_AutoShotAttackTimer = m_AutoShotDuration; //avoid flooding client with error mesages return; } else { uint32 duration2 = this->GetUInt32Value(UNIT_FIELD_RANGEDATTACKTIME); //Ranged Ammo Haste if(m_AutoShotDuration != duration2) m_AutoShotDuration = duration2; m_AutoShotAttackTimer = m_AutoShotDuration; Spell *sp = new Spell(this, m_AutoShotSpell, true, NULL); SpellCastTargets tgt; tgt.m_unitTarget = m_curSelection; tgt.m_targetMask = TARGET_FLAG_UNIT; sp->prepare(&tgt); } } void Player::removeSpellByHashName(uint32 hash) { SpellSet::iterator it,iter; for(iter= mSpells.begin();iter != mSpells.end();) { it = iter++; uint32 SpellID = *it; SpellEntry *e = sSpellStore.LookupEntry(SpellID); if(e->NameHash == hash) { RemoveAura(SpellID,GetGUID()); m_session->OutPacket(SMSG_REMOVED_SPELL, 4, &SpellID); mSpells.erase(it); } } } bool Player::removeSpell(uint32 SpellID, bool MoveToDeleted, bool SupercededSpell, uint32 SupercededSpellID) { SpellSet::iterator iter = mSpells.find(SpellID); if(iter != mSpells.end()) { mSpells.erase(iter); RemoveAura(SpellID,GetGUID()); } else { return false; } if(MoveToDeleted) mDeletedSpells.insert(SpellID); if(!IsInWorld()) return true; if(SupercededSpell) { WorldPacket data(SMSG_SUPERCEDED_SPELL, 8); data << SupercededSpellID << SpellID; m_session->SendPacket(&data); } else { m_session->OutPacket(SMSG_REMOVED_SPELL, 4, &SpellID); } return true; } void Player::EventActivateGameObject(GameObject* obj) { obj->BuildFieldUpdatePacket(this, GAMEOBJECT_DYN_FLAGS, 1); } void Player::EventDeActivateGameObject(GameObject* obj) { obj->BuildFieldUpdatePacket(this, GAMEOBJECT_DYN_FLAGS, 0); } void Player::EventTimedQuestExpire(Quest *qst, QuestLogEntry *qle, uint32 log_slot) { WorldPacket fail; sQuestMgr.BuildQuestFailed(&fail, qst->id); GetSession()->SendPacket(&fail); CALL_QUESTSCRIPT_EVENT(qle, OnQuestCancel)(this); qle->Finish(); } void Player::SendInitialLogonPackets() { // Initial Packets... they seem to be re-sent on port. m_session->OutPacket(SMSG_SET_REST_START, 4, &m_timeLogoff); #ifndef USING_BIG_ENDIAN StackWorldPacket<32> data(SMSG_BINDPOINTUPDATE); #else WorldPacket data(SMSG_BINDPOINTUPDATE, 32); #endif data << m_bind_pos_x; data << m_bind_pos_y; data << m_bind_pos_z; data << m_bind_mapid; data << m_bind_zoneid; GetSession()->SendPacket( &data ); //Proficiences data.Initialize(SMSG_SET_PROFICIENCY); data << (uint8)4; data << armor_proficiency ; GetSession()->SendPacket(&data); #ifndef USING_BIG_ENDIAN data.Clear(); #else data.clear(); #endif data << (uint8)2; data << weapon_proficiency; GetSession()->SendPacket(&data); //Tutorial Flags data.Initialize( SMSG_TUTORIAL_FLAGS ); for (int i = 0; i < 8; i++) data << uint32( m_Tutorials[i] ); GetSession()->SendPacket(&data); //Initial Spells smsg_InitialSpells(); //Initial Actions SendInitialActions(); //Factions smsg_InitialFactions(); data.Initialize(SMSG_LOGIN_SETTIMESPEED); time_t minutes = sWorld.GetGameTime( ) / 60; time_t hours = minutes / 60; minutes %= 60; time_t gameTime = minutes + ( hours << 6 ); data << (uint32)gameTime; data << (float)0.0166666669777748f; // Normal Game Speed GetSession()->SendPacket( &data ); sLog.outDetail("WORLD: Sent initial logon packets for %s.", GetName()); } void Player::Reset_Spells() { PlayerCreateInfo *info = objmgr.GetPlayerCreateInfo(getRace(), getClass()); ASSERT(info); std::list spelllist; for(SpellSet::iterator itr = mSpells.begin(); itr!=mSpells.end(); itr++) { spelllist.push_back((*itr)); } for(std::list::iterator itr = spelllist.begin(); itr!=spelllist.end(); itr++) { removeSpell((*itr), false, false, 0); } for(std::list::iterator sp = info->spell_list.begin();sp!=info->spell_list.end();sp++) { if(*sp) { addSpell(*sp); } } } void Player::Reset_Talents() { unsigned int numRows = sTalentStore.GetNumRows(); for (unsigned int i = 0; i < numRows; i++) // Loop through all talents. { TalentEntry *tmpTalent = sTalentStore.LookupEntry(i); if(!tmpTalent) continue; //should not ocur //this is a normal talent (i hope ) for (int j = 0; j < 5; j++) { if (tmpTalent->RankID[j] != 0) { m_SSSPecificSpells.erase(tmpTalent->RankID[j]); SpellEntry *spellInfo; spellInfo = sSpellStore.LookupEntry( tmpTalent->RankID[j] ); if(spellInfo) { for(int k=0;k<3;k++) if(spellInfo->Effect[k] == SPELL_EFFECT_LEARN_SPELL) { removeSpell(spellInfo->EffectTriggerSpell[k], false, 0, 0); //remove higher ranks of this spell too (like earth shield lvl 1 is talent and the rest is thought from trainer) SpellEntry *spellInfo2; spellInfo2 = sSpellStore.LookupEntry( spellInfo->EffectTriggerSpell[k] ); if(spellInfo2) removeSpellByHashName(spellInfo2->NameHash); } //remove them all in 1 shot removeSpellByHashName(spellInfo->NameHash); } } else break; } } uint32 l=getLevel(); if(l>9) { SetUInt32Value(PLAYER_CHARACTER_POINTS1, l - 9); } else { SetUInt32Value(PLAYER_CHARACTER_POINTS1, 0); } } void Player::Reset_ToLevel1() { RemoveAllAuras(); // clear aura fields for(int i=UNIT_FIELD_AURA;ihealth); SetUInt32Value(UNIT_FIELD_POWER1, info->mana ); SetUInt32Value(UNIT_FIELD_POWER2, 0 ); // this gets devided by 10 SetUInt32Value(UNIT_FIELD_POWER3, info->focus ); SetUInt32Value(UNIT_FIELD_POWER4, info->energy ); SetUInt32Value(UNIT_FIELD_MAXHEALTH, info->health); SetUInt32Value(UNIT_FIELD_BASE_HEALTH, info->health); SetUInt32Value(UNIT_FIELD_BASE_MANA, info->mana); SetUInt32Value(UNIT_FIELD_MAXPOWER1, info->mana ); SetUInt32Value(UNIT_FIELD_MAXPOWER2, info->rage ); SetUInt32Value(UNIT_FIELD_MAXPOWER3, info->focus ); SetUInt32Value(UNIT_FIELD_MAXPOWER4, info->energy ); SetUInt32Value(UNIT_FIELD_STAT0, info->strength ); SetUInt32Value(UNIT_FIELD_STAT1, info->ability ); SetUInt32Value(UNIT_FIELD_STAT2, info->stamina ); SetUInt32Value(UNIT_FIELD_STAT3, info->intellect ); SetUInt32Value(UNIT_FIELD_STAT4, info->spirit ); SetUInt32Value(UNIT_FIELD_ATTACK_POWER, info->attackpower ); SetUInt32Value(PLAYER_CHARACTER_POINTS1,0); SetUInt32Value(PLAYER_CHARACTER_POINTS2,2); for(uint32 x=0;x<7;x++) SetFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT+x, 1.00); } void Player::CalcResistance(uint32 type) { int32 res; uint32 pos; uint32 neg; ASSERT(type < 7); pos=(BaseResistance[type]*BaseResistanceModPctPos[type])/100; neg=(BaseResistance[type]*BaseResistanceModPctNeg[type])/100; pos+=FlatResistanceModifierPos[type]; neg+=FlatResistanceModifierNeg[type]; res=BaseResistance[type]+pos-neg; if(type==0)res+=GetUInt32Value(UNIT_FIELD_STAT1)*2;//fix armor from agi if(res<0)res=0; pos+=(res*ResistanceModPctPos[type])/100; neg+=(res*ResistanceModPctNeg[type])/100; res=pos-neg+BaseResistance[type]; if(type==0)res+=GetUInt32Value(UNIT_FIELD_STAT1)*2;//fix armor from agi SetUInt32Value(UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE+type,pos); SetUInt32Value(UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE+type,neg); SetUInt32Value(UNIT_FIELD_RESISTANCES+type,res>0?res:0); } void Player::UpdateNearbyGameObjects() { for (Object::InRangeSet::iterator itr = GetInRangeSetBegin(); itr != GetInRangeSetEnd(); ++itr) { if((*itr)->GetTypeId() == TYPEID_GAMEOBJECT) { bool activate_quest_object = false; GameObject *go = ((GameObject*)*itr); QuestLogEntry *qle; GameObjectInfo *info; info = go->GetInfo(); bool deactivate = false; if(info && (info->goMap.size() || info->itemMap.size()) ) { for(GameObjectGOMap::iterator itr = go->GetInfo()->goMap.begin(); itr != go->GetInfo()->goMap.end(); ++itr) { if((qle = GetQuestLogForEntry(itr->first->id))) { for(uint32 i = 0; i < qle->GetQuest()->count_required_mob; ++i) { if(qle->GetQuest()->required_mob[i] == go->GetEntry() && qle->GetMobCount(i) < qle->GetQuest()->required_mobcount[i]) { activate_quest_object = true; break; } } if(activate_quest_object) break; } } if(!activate_quest_object) { for(GameObjectItemMap::iterator itr = go->GetInfo()->itemMap.begin(); itr != go->GetInfo()->itemMap.end(); ++itr) { for(std::map::iterator it2 = itr->second.begin(); it2 != itr->second.end(); ++it2) { if(GetItemInterface()->GetItemCount(it2->first) < it2->second) { activate_quest_object = true; break; } } if(activate_quest_object) break; } } if(!activate_quest_object) { deactivate = true; } } bool bPassed = !deactivate; if((*itr)->GetUInt32Value(GAMEOBJECT_TYPE_ID) == GAMEOBJECT_TYPE_QUESTGIVER) { if(((GameObject*)(*itr))->m_quests) { if(((GameObject*)(*itr))->m_quests->size() > 0) { std::list::iterator itr2 = ((GameObject*)(*itr))->m_quests->begin(); for(;itr2!=((GameObject*)(*itr))->m_quests->end();++itr2) { uint32 status = sQuestMgr.CalcQuestStatus(NULL, this, (*itr2)->qst, (*itr2)->type, false); if(status == QMGR_QUEST_AVAILABLE || status == QMGR_QUEST_REPEATABLE || status == QMGR_QUEST_FINISHED) { // Activate gameobject EventActivateGameObject((GameObject*)(*itr)); bPassed = true; break; } } } } } if(!bPassed) EventDeActivateGameObject((GameObject*)(*itr)); } } } void Player::EventTaxiInterpolate() { if(!m_CurrentTaxiPath) return; float x,y,z; uint32 ntime = getMSTime(); if (ntime > m_taxi_ride_time) m_CurrentTaxiPath->SetPosForTime(x, y, z, ntime - m_taxi_ride_time, &lastNode); else m_CurrentTaxiPath->SetPosForTime(x, y, z, m_taxi_ride_time - ntime, &lastNode); if(x < _minX || x > _maxX || y < _minY || y > _maxX) return; SetPosition(x,y,z,0); } void Player::TaxiStart(TaxiPath *path, uint32 modelid, uint32 start_node) { if(this->m_MountSpellId) RemoveAura(m_MountSpellId); //also remove morph spells if(GetUInt32Value(UNIT_FIELD_DISPLAYID)!=GetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID)) { RemoveAllAuraType(SPELL_AURA_TRANSFORM); RemoveAllAuraType(SPELL_AURA_MOD_SHAPESHIFT); } SetUInt32Value( UNIT_FIELD_MOUNTDISPLAYID, modelid ); SetFlag( UNIT_FIELD_FLAGS ,U_FIELD_FLAG_LOCK_PLAYER ); SetFlag( UNIT_FIELD_FLAGS, U_FIELD_FLAG_MOUNT_SIT ); SetTaxiPath(path); SetTaxiPos(); SetTaxiState(true); m_taxi_ride_time = getMSTime(); //uint32 traveltime = uint32(path->getLength() * TAXI_TRAVEL_SPEED); // 36.7407 float traveldist = 0; float lastx = 0, lasty = 0, lastz = 0; TaxiPathNode *firstNode = path->GetPathNode(start_node); uint32 add_time = 0; if(start_node) { TaxiPathNode *pn = path->GetPathNode(0); float dist = 0; lastx = pn->x; lasty = pn->y; lastz = pn->z; for(uint32 i = 1; i <= start_node; ++i) { pn = path->GetPathNode(i); if(!pn) { JumpToEndTaxiNode(path); return; } dist += CalcDistance(lastx, lasty, lastz, pn->x, pn->y, pn->z); lastx = pn->x; lasty = pn->y; lastz = pn->z; } add_time = uint32( dist * TAXI_TRAVEL_SPEED ); lastx = lasty = lastz = 0; } for(uint32 i = start_node; i < path->GetNodeCount(); ++i) { TaxiPathNode *pn = path->GetPathNode(i); if(!pn) { JumpToEndTaxiNode(path); return; } if(!lastx || !lasty || !lastz) { lastx = pn->x; lasty = pn->y; lastz = pn->z; } else { float dist = CalcDistance(lastx,lasty,lastz, pn->x,pn->y,pn->z); traveldist += dist; lastx = pn->x; lasty = pn->y; lastz = pn->z; } } uint32 traveltime = uint32(traveldist * TAXI_TRAVEL_SPEED); WorldPacket data(SMSG_MONSTER_MOVE, 38 + ( (path->GetNodeCount() - start_node) * 12 ) ); data << GetNewGUID(); data << firstNode->x << firstNode->y << firstNode->z; data << m_taxi_ride_time; data << uint8( 0 ); data << uint32( 0x00000300 ); data << uint32( traveltime ); m_taxi_ride_time -= add_time; data << uint32( path->GetNodeCount() - start_node ); // uint32 timer = 0, nodecount = 0; // TaxiPathNode *lastnode = NULL; for(uint32 i = start_node; i < path->GetNodeCount(); i++) { TaxiPathNode *pn = path->GetPathNode(i); if(!pn) { JumpToEndTaxiNode(path); return; } data << pn->x << pn->y << pn->z; } SendMessageToSet(&data, true); sEventMgr.AddEvent(this, &Player::EventTaxiInterpolate, EVENT_PLAYER_TAXI_INTERPOLATE, 900, 0,0); TaxiPathNode *pn = path->GetPathNode(path->GetNodeCount() - 1); sEventMgr.AddEvent(this, &Player::EventDismount, path->GetPrice(), pn->x, pn->y, pn->z, EVENT_PLAYER_TAXI_DISMOUNT, traveltime, 1,EVENT_FLAG_DO_NOT_EXECUTE_IN_WORLD_CONTEXT); } void Player::JumpToEndTaxiNode(TaxiPath * path) { // this should *always* be safe in case it cant build your position on the path! TaxiPathNode * pathnode = path->GetPathNode(path->GetNodeCount()-1); if(!pathnode) return; SetTaxiState(false); SetTaxiPath(NULL); UnSetTaxiPos(); m_taxi_ride_time = 0; SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID , 0); RemoveFlag( UNIT_FIELD_FLAGS, U_FIELD_FLAG_MOUNT_SIT ); RemoveFlag( UNIT_FIELD_FLAGS, U_FIELD_FLAG_LOCK_PLAYER ); SetPlayerSpeed(RUN, m_runSpeed); SafeTeleport(pathnode->mapid, 0, LocationVector(pathnode->x, pathnode->y, pathnode->z)); } void Player::RemoveSpellsFromLine(uint32 skill_line) { uint32 cnt = sSkillStore.GetNumRows(); for(uint32 i=0; i < cnt; i++) { skilllinespell * sp = sSkillStore.LookupEntry(i); if(sp) { if(sp->skilline == skill_line) { // Check ourselves for this spell, and remove it.. removeSpell(sp->spell, 0, 0, 0); } } } } void Player::CalcStat(uint32 type) { int32 res; ASSERT(type < 5); int32 pos=(BaseStats[type]*StatModPctPos[type])/100+FlatStatModPos[type]; int32 neg=(BaseStats[type]*StatModPctNeg[type])/100+FlatStatModNeg[type]; res=pos+BaseStats[type]-neg; pos+=(res*((Player*)this)->TotalStatModPctPos[type])/100; neg+=(res*((Player*)this)->TotalStatModPctNeg[type])/100; res=pos+BaseStats[type]-neg; SetUInt32Value(UNIT_FIELD_POSSTAT0+type,pos); SetUInt32Value(UNIT_FIELD_NEGSTAT0+type,neg); SetUInt32Value(UNIT_FIELD_STAT0+type,res>0?res:0); if(type==1) CalcResistance(0); } void Player::RegenerateMana(float RegenPct) { const static float ClassMultiplier[12]={ 0,0,0.2,0.25,0,0.25,0,0.2,0.25,0.2,0,0.2}; const static float ClassFlatMod[12]={ 0,0,15,15,0,12.5,0,17,12.5,15,0,15}; float amt; if (m_interruptRegen) return; uint32 cur = GetUInt32Value(UNIT_FIELD_POWER1); uint32 mm = GetUInt32Value(UNIT_FIELD_MAXPOWER1); if(cur >= mm)return; uint32 cl = getClass(); uint32 Spirit = GetUInt32Value(UNIT_FIELD_STAT4); amt = (Spirit*ClassMultiplier[cl]+ClassFlatMod[cl])*PctPowerRegenModifier[POWER_TYPE_MANA]; //Apply shit from conf file amt *= sWorld.getRate(RATE_POWER1)*RegenPct; //Near values from official // wowwiki says no faster mp while resting, anyways this is wrong it reduces instead of increasing. /*if(m_isResting) amt = amt * 0.7f; else amt = amt * 0.3f;*/ if(amt<=1.0)//this fixes regen like 0.98 cur++; else cur += float2int32(amt); SetUInt32Value(UNIT_FIELD_POWER1,(cur >= mm) ? mm : cur); } void Player::RegenerateHealth(bool inCombat) { const static float ClassMultiplier[12]={ 0,0.8,0.25,0.25,0.5,0.1,0,0.11,0.1,0.11,0,0.09}; const static float ClassFlatMod[12]={ 0,6,6,6,2,4,0,6,4,6,0,6.5}; float amt; uint32 cur = GetUInt32Value(UNIT_FIELD_HEALTH); uint32 mh = GetUInt32Value(UNIT_FIELD_MAXHEALTH); if(cur >= mh) return; uint32 cl = getClass(); uint32 Spirit = GetUInt32Value(UNIT_FIELD_STAT4); if(PctRegenModifier == 0.0f) amt = (Spirit*ClassMultiplier[cl]+ClassFlatMod[cl]); else amt = (Spirit*ClassMultiplier[cl]+ClassFlatMod[cl])*(1+PctRegenModifier); //Apply shit from conf file amt *= sWorld.getRate(RATE_HEALTH); //Near values from official // wowwiki: Health Regeneration is increased by 33% while sitting. if(m_isResting) amt = amt * 1.33f; if(m_interruptRegen) inCombat = true; if(inCombat) amt *= PctIgnoreRegenModifier; if(amt != 0) { if(amt > 0) { if(amt <= 1.0f)//this fixes regen like 0.98 cur++; else cur += float2int32(amt); SetUInt32Value(UNIT_FIELD_HEALTH,(cur>=mh) ? mh : cur); } else DealDamage(this, float2int32(amt), 0, 0, 0); } } void Player::LooseRage() { //Rage is lost at a rate of 3 rage every 3 seconds. //The Anger Management talent changes this to 2 rage every 3 seconds. uint32 cur = GetUInt32Value(UNIT_FIELD_POWER2); uint32 decayValue = 30; // default not modified if(cur) { uint32 cl=getClass(); switch(cl) { case WARRIOR: // Check for talent modifiers when the talent code is completed break; case DRUID: // For now, i don't think there is anything that might change druids // rage per decay anywhere break; } SetUInt32Value(UNIT_FIELD_POWER2, (cur <= decayValue) ? 0 : (cur - decayValue)); } } void Player::RegenerateEnergy(float RegenPct) { uint32 cur = GetUInt32Value(UNIT_FIELD_POWER4); uint32 mh = GetUInt32Value(UNIT_FIELD_MAXPOWER4); if(cur >= mh) return; float amt = 20.0 * PctPowerRegenModifier[POWER_TYPE_ENERGY]; cur += float2int32(amt); SetUInt32Value(UNIT_FIELD_POWER4,(cur>=mh) ? mh : cur); } uint32 Player::GeneratePetNumber() { uint32 val = m_PetNumberMax + 1; for (uint32 i = 1; i < m_PetNumberMax; i++) if(m_Pets.find(i) == m_Pets.end()) return i; // found a free one return val; } void Player::RemovePlayerPet(uint32 pet_number) { std::map::iterator itr = m_Pets.find(pet_number); if(itr != m_Pets.end()) { delete itr->second; m_Pets.erase(itr); } } void Player::_Relocate(uint32 mapid, const LocationVector & v, bool sendpending, bool force_new_world) { //this func must only be called when switching between maps! WorldPacket data(41); if(sendpending && mapid != m_mapId && force_new_world) { data.SetOpcode(SMSG_TRANSFER_PENDING); data << mapid; GetSession()->SendPacket(&data); } SetPlayerStatus(TRANSFER_PENDING); if(m_mapId != mapid || force_new_world) { if(IsInWorld()) { RemoveFromWorld(); } data.Initialize(SMSG_NEW_WORLD); data << (uint32)mapid << v << v.o; GetSession()->SendPacket( &data ); SetMapId(mapid); } else { // via teleport ack msg WorldPacket * data = BuildTeleportAckMsg(v); m_session->SendPacket(data); delete data; } m_sentTeleportPosition = v; SetPosition(v); ResetHeartbeatCoords(); } // Player::AddItemsToWorld // Adds all items to world, applies any modifiers for them. void Player::AddItemsToWorld() { for(int32 i = 0; i < INVENTORY_KEYRING_END; i++) { if(GetItemInterface()->GetInventoryItem(i)) { GetItemInterface()->GetInventoryItem(i)->PushToWorld(m_mapMgr); if(i < INVENTORY_SLOT_BAG_END) // only equipment slots get mods. { _ApplyItemMods(GetItemInterface()->GetInventoryItem(i), i, true); } if(GetItemInterface()->GetInventoryItem(i)->IsContainer() && GetItemInterface()->IsBagSlot(i)) { for(uint32 e=0; e < GetItemInterface()->GetInventoryItem(i)->GetProto()->ContainerSlots; e++) { Item *item = ((Container*)GetItemInterface()->GetInventoryItem(i))->GetItem(e); if(item) { item->PushToWorld(m_mapMgr); } } } } } } // Player::RemoveItemsFromWorld // Removes all items from world, reverses any modifiers. void Player::RemoveItemsFromWorld() { for(int8 i = 0; i < INVENTORY_KEYRING_END; i++) { if(GetItemInterface()->GetInventoryItem(i)) { if(i < INVENTORY_SLOT_BAG_END) // only equipment slots get mods. { _ApplyItemMods(GetItemInterface()->GetInventoryItem(i), i, false); } GetItemInterface()->GetInventoryItem(i)->RemoveFromWorld(); if(GetItemInterface()->GetInventoryItem(i)->IsContainer() && GetItemInterface()->IsBagSlot(i)) { for(uint32 e=0; e < GetItemInterface()->GetInventoryItem(i)->GetProto()->ContainerSlots; e++) { Item *item = ((Container*)GetItemInterface()->GetInventoryItem(i))->GetItem(e); if(item && item->IsInWorld()) { item->RemoveFromWorld(); } } } } } } uint32 Player::BuildCreateUpdateBlockForPlayer(ByteBuffer *data, Player *target ) { int count = 0; if(target == this) { // we need to send create objects for all items. count += GetItemInterface()->m_CreateForPlayer(data); } count += Unit::BuildCreateUpdateBlockForPlayer(data, target); return count; } void Player::Kick(uint32 delay /* = 0 */) { if(!delay) { m_KickDelay = 0; _Kick(); } else { m_KickDelay = delay; sEventMgr.AddEvent(this, &Player::_Kick, EVENT_PLAYER_KICK, 1000, 0, EVENT_FLAG_DO_NOT_EXECUTE_IN_WORLD_CONTEXT); } } void Player::_Kick() { if(!m_KickDelay) { sEventMgr.RemoveEvents(this, EVENT_PLAYER_KICK); // remove now GetSession()->LogoutPlayer(true); } else { if((m_KickDelay - 1000) < 500) { m_KickDelay = 0; } else { m_KickDelay -= 1000; } sChatHandler.BlueSystemMessageToPlr(this, "You will be removed from the server in %u seconds.", (uint32)(m_KickDelay/1000)); } } bool Player::HasDeletedSpell(uint32 spell) { return (mDeletedSpells.count(spell) > 0); } void Player::ClearCooldownForSpell(uint32 spell_id) { WorldPacket data(12); data.SetOpcode(SMSG_CLEAR_COOLDOWN); data << spell_id << GetGUID(); GetSession()->SendPacket(&data); // remove cooldown data from Server side lists SpellEntry * spe = sSpellStore.LookupEntry(spell_id); if(!spe) return; map::iterator itr; itr = SpellCooldownMap.find(spell_id); if(itr != SpellCooldownMap.end()) SpellCooldownMap.erase(itr); itr = SpellCooldownCategoryMap.find(spe->Category); if(itr != SpellCooldownCategoryMap.end()) SpellCooldownCategoryMap.erase(itr); ItemCooldownSet::iterator itr2, it2; for (itr2 = m_itemcooldown.begin(); itr2 != m_itemcooldown.end(); ) { ItemCooldown * temp = (*itr2); it2 = itr2++; if(temp) { if(temp->SpellID == spell_id || temp->SpellCategory == spe->Category) { m_itemcooldown.erase(it2); delete temp; } } } } void Player::ClearCooldownsOnLine(uint32 skill_line, uint32 called_from) { // found an easier way.. loop spells, check skill line SpellSet::const_iterator itr = mSpells.begin(); skilllinespell *sk; for(; itr != mSpells.end(); ++itr) { if((*itr) == called_from) // skip calling spell.. otherwise spammies! :D continue; sk = objmgr.GetSpellSkill((*itr)); if(sk && sk->skilline == skill_line) ClearCooldownForSpell((*itr)); } } void Player::PushUpdateData(ByteBuffer *data, uint32 updatecount) { // imagine the bytebuffer getting appended from 2 threads at once! :D _bufferS.Acquire(); /* this is a safe barrier. */ if(bUpdateBuffer.size() >= 40000) { /* force an update to push out our pending data */ ProcessPendingUpdates(); } mUpdateCount += updatecount; bUpdateBuffer.append(*data); // add to process queue if(m_mapMgr && !bProcessPending) { bProcessPending = true; m_mapMgr->PushToProcessed(this); } _bufferS.Release(); } void Player::PushOutOfRange(const WoWGuid & guid) { _bufferS.Acquire(); mOutOfRangeIds << guid; ++mOutOfRangeIdCount; // add to process queue if(m_mapMgr && !bProcessPending) { bProcessPending = true; m_mapMgr->PushToProcessed(this); } _bufferS.Release(); } void Player::PushCreationData(ByteBuffer *data, uint32 updatecount) { // imagine the bytebuffer getting appended from 2 threads at once! :D _bufferS.Acquire(); /* this is a safe barrier. */ if(bCreationBuffer.size() >= 40000) { /* force an update to push out our pending data */ ProcessPendingUpdates(); } mCreationCount += updatecount; bCreationBuffer.append(*data); // add to process queue if(m_mapMgr && !bProcessPending) { bProcessPending = true; m_mapMgr->PushToProcessed(this); } _bufferS.Release(); } void Player::ProcessPendingUpdates() { _bufferS.Acquire(); if(!bUpdateBuffer.size() && !mOutOfRangeIds.size() && !bCreationBuffer.size()) { _bufferS.Release(); return; } uint32 bBuffer_size = bCreationBuffer.size() + 10 + (mOutOfRangeIds.size() * 9); uint8 * update_buffer = NULL; //we got creation packets. Join them with out of range packets and skip update ones. if(bCreationBuffer.size()) { update_buffer = new uint8[bBuffer_size]; } uint32 c = 0; //build out of range updates if creation updates are queued if(bCreationBuffer.size()) { #ifdef USING_BIG_ENDIAN *(uint32*)&update_buffer[c] = swap32(uint32(((mOutOfRangeIds.size() > 0) ? (mCreationCount + 1) : mCreationCount))); c += 4; #else *(uint32*)&update_buffer[c] = ((mOutOfRangeIds.size() > 0) ? (mCreationCount + 1) : mCreationCount); c += 4; #endif update_buffer[c] = 1; ++c; // append any out of range updates if(mOutOfRangeIdCount) { update_buffer[c] = UPDATETYPE_OUT_OF_RANGE_OBJECTS; ++c; #ifdef USING_BIG_ENDIAN *(uint32*)&update_buffer[c] = swap32(mOutOfRangeIdCount); c += 4; #else *(uint32*)&update_buffer[c] = mOutOfRangeIdCount; c += 4; #endif memcpy(&update_buffer[c], mOutOfRangeIds.contents(), mOutOfRangeIds.size()); c += mOutOfRangeIds.size(); mOutOfRangeIds.clear(); mOutOfRangeIdCount = 0; } if(bCreationBuffer.size()) memcpy(&update_buffer[c], bCreationBuffer.contents(), bCreationBuffer.size()); c += bCreationBuffer.size(); // clear our update buffer bCreationBuffer.clear(); mCreationCount = 0; // compress update packet // while we said 350 before, i'm gonna make it 500 :D if(c < sWorld.compression_threshold || !CompressAndSendUpdateBuffer(c, update_buffer)) { // send uncompressed packet -> because we failed m_session->OutPacket(SMSG_UPDATE_OBJECT, c, update_buffer); } delete [] update_buffer; } uint32 aBuffer_size = bUpdateBuffer.size() + 10 + (mOutOfRangeIds.size() * 9); update_buffer = new uint8[aBuffer_size]; c = 0; #ifdef USING_BIG_ENDIAN *(uint32*)&update_buffer[c] = swap32(uint32(((mOutOfRangeIds.size() > 0) ? (mUpdateCount + 1) : mUpdateCount))); c += 4; #else *(uint32*)&update_buffer[c] = ((mOutOfRangeIds.size() > 0) ? (mUpdateCount + 1) : mUpdateCount); c += 4; #endif update_buffer[c] = 1; ++c; // append any out of range updates if(mOutOfRangeIdCount) { update_buffer[c] = UPDATETYPE_OUT_OF_RANGE_OBJECTS; ++c; #ifdef USING_BIG_ENDIAN *(uint32*)&update_buffer[c] = swap32(mOutOfRangeIdCount); c += 4; #else *(uint32*)&update_buffer[c] = mOutOfRangeIdCount; c += 4; #endif memcpy(&update_buffer[c], mOutOfRangeIds.contents(), mOutOfRangeIds.size()); c += mOutOfRangeIds.size(); mOutOfRangeIds.clear(); mOutOfRangeIdCount = 0; } if(bUpdateBuffer.size()) memcpy(&update_buffer[c], bUpdateBuffer.contents(), bUpdateBuffer.size()); c += bUpdateBuffer.size(); // clear our update buffer bUpdateBuffer.clear(); bProcessPending = false; mUpdateCount = 0; _bufferS.Release(); // compress update packet // while we said 350 before, i'm gonna make it 500 :D if(c < sWorld.compression_threshold || !CompressAndSendUpdateBuffer(c, update_buffer)) { // send uncompressed packet -> because we failed m_session->OutPacket(SMSG_UPDATE_OBJECT, c, update_buffer); } delete [] update_buffer; // send any delayed packets WorldPacket * pck; while(delayedPackets.size()) { pck = delayedPackets.next(); m_session->SendPacket(pck); delete pck; } // resend speed if needed if(resend_speed) { SetPlayerSpeed(RUN, m_runSpeed); SetPlayerSpeed(FLY, m_flySpeed); } } bool Player::CompressAndSendUpdateBuffer(uint32 size, const uint8* update_buffer) { uint32 destsize = size + size/10 + 16; int rate = sWorld.getIntRate(INTRATE_COMPRESSION); if(size > 30000) rate = 9; // max // set up stream z_stream stream; stream.zalloc = 0; stream.zfree = 0; stream.opaque = 0; if(deflateInit(&stream, rate) != Z_OK) { sLog.outError("deflateInit failed."); return false; } uint8 *buffer = new uint8[destsize]; memset(buffer,0,destsize); /* fix umr - burlex */ // set up stream pointers stream.next_out = (Bytef*)buffer+4; stream.avail_out = destsize; stream.next_in = (Bytef*)update_buffer; stream.avail_in = size; // call the actual process if(deflate(&stream, Z_NO_FLUSH) != Z_OK || stream.avail_in != 0) { sLog.outError("deflate failed."); delete [] buffer; return false; } // finish the deflate if(deflate(&stream, Z_FINISH) != Z_STREAM_END) { sLog.outError("deflate failed: did not end stream"); delete [] buffer; return false; } // finish up if(deflateEnd(&stream) != Z_OK) { sLog.outError("deflateEnd failed."); delete [] buffer; return false; } // fill in the full size of the compressed stream #ifdef USING_BIG_ENDIAN *(uint32*)&buffer[0] = swap32(size); #else *(uint32*)&buffer[0] = size; #endif // send it m_session->OutPacket(SMSG_COMPRESSED_UPDATE_OBJECT, stream.total_out + 4, buffer); // cleanup memory delete [] buffer; return true; } void Player::ClearAllPendingUpdates() { _bufferS.Acquire(); bProcessPending = false; mUpdateCount = 0; bUpdateBuffer.clear(); _bufferS.Release(); } void Player::AddSplinePacket(uint64 guid, ByteBuffer* packet) { SplineMap::iterator itr = _splineMap.find(guid); if(itr != _splineMap.end()) { delete itr->second; _splineMap.erase(itr); } _splineMap.insert( SplineMap::value_type( guid, packet ) ); } ByteBuffer* Player::GetAndRemoveSplinePacket(uint64 guid) { SplineMap::iterator itr = _splineMap.find(guid); if(itr != _splineMap.end()) { ByteBuffer *buf = itr->second; _splineMap.erase(itr); return buf; } return NULL; } void Player::ClearSplinePackets() { SplineMap::iterator it2; for(SplineMap::iterator itr = _splineMap.begin(); itr != _splineMap.end();) { it2 = itr; ++itr; delete it2->second; _splineMap.erase(it2); } _splineMap.clear(); } bool Player::ExitInstance() { if(!m_bgEntryPointX) return false; RemoveFromWorld(); SafeTeleport(m_bgEntryPointMap, m_bgEntryPointInstance, LocationVector( m_bgEntryPointX, m_bgEntryPointY, m_bgEntryPointZ, m_bgEntryPointO)); return true; } void Player::SaveEntryPoint(uint32 mapId) { if(IS_INSTANCE(GetMapId())) return; // dont save if we're not on the main continent. //otherwise we could end up in an endless loop :P MapInfo * pMapinfo = WorldMapInfoStorage.LookupEntry(mapId); if(pMapinfo) { m_bgEntryPointX = pMapinfo->repopx; m_bgEntryPointY = pMapinfo->repopy; m_bgEntryPointZ = pMapinfo->repopz; m_bgEntryPointO = GetOrientation(); m_bgEntryPointMap = pMapinfo->repopmapid; m_bgEntryPointInstance = GetInstanceID(); } else { m_bgEntryPointMap = 0; m_bgEntryPointX = 0; m_bgEntryPointY = 0; m_bgEntryPointZ = 0; m_bgEntryPointO = 0; m_bgEntryPointInstance = 0; } } void Player::CleanupGossipMenu() { if(CurrentGossipMenu) { delete CurrentGossipMenu; CurrentGossipMenu = NULL; } } void Player::Gossip_Complete() { GetSession()->OutPacket(SMSG_GOSSIP_COMPLETE, 0, NULL); CleanupGossipMenu(); } void Player::Gossip_SendPOI(float X, float Y, uint32 Icon, uint32 Flags, uint32 Data, const char* Name) { WorldPacket data(SMSG_GOSSIP_POI, 21+strlen(Name)); data << Flags << X << Y << Icon << Data << Name; GetSession()->SendPacket(&data); } void Player::ZoneUpdate(uint32 ZoneId) { m_zoneId = ZoneId; sHookInterface.OnZone(this, ZoneId); AreaTable * at = sAreaStore.LookupEntry(GetAreaID()); if(at->category == AREAC_SANCTUARY || at->AreaFlags & AREA_SANCTUARY) { Unit * pUnit = (GetSelection() == 0) ? 0 : (m_mapMgr ? m_mapMgr->GetUnit(GetSelection()) : 0); if(pUnit && DuelingWith != pUnit) { EventAttackStop(); smsg_AttackStop(pUnit); } if(m_currentSpell) { Unit * target = m_currentSpell->GetUnitTarget(); if(target && target != DuelingWith && target != this) m_currentSpell->cancel(); } } /*std::map::iterator iter = sWorld.mZoneIDToTable.find(ZoneId); if(iter == sWorld.mZoneIDToTable.end()) return; AreaTable *p = iter->second; if(p->AreaId != m_AreaID) { m_AreaID = p->AreaId; UpdatePVPStatus(m_AreaID); } sLog.outDetail("ZONE_UPDATE: Player %s entered zone %s", GetName(), sAreaStore.LookupString((int)p->name));*/ } void Player::SendTradeUpdate() { Player * pTarget = GetTradeTarget(); if(!pTarget) return; WorldPacket data(SMSG_TRADE_STATUS_EXTENDED, 500); data << uint8(1); data << uint32(2) << uint32(2); data << mTradeGold << uint32(0); // Items for(uint32 Index = 0; Index < 7; ++Index) { Item * pItem = mTradeItems[Index]; if(pItem != 0) { ItemPrototype * pProto = pItem->GetProto(); ASSERT(pProto != 0); data << uint8(Index); data << pProto->ItemId; data << pProto->DisplayInfoID; data << pItem->GetUInt32Value(ITEM_FIELD_STACK_COUNT); // Amount OK // Enchantment stuff data << uint32(0); // unknown data << pItem->GetUInt64Value(ITEM_FIELD_GIFTCREATOR); // gift creator OK data << pItem->GetUInt32Value(ITEM_FIELD_ENCHANTMENT); // Item Enchantment OK data << uint32(0); // unknown data << uint32(0); // unknown data << uint32(0); // unknown data << pItem->GetUInt64Value(ITEM_FIELD_CREATOR); // item creator OK data << pItem->GetUInt32Value(ITEM_FIELD_STACK_COUNT); // Spell Charges OK data << uint32(0); // seems like time stamp or something like that data << pItem->GetUInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID); data << pProto->LockId; // lock ID OK data << pItem->GetUInt32Value(ITEM_FIELD_MAXDURABILITY); data << pItem->GetUInt32Value(ITEM_FIELD_DURABILITY); } } pTarget->GetSession()->SendPacket(&data); } void Player::RequestDuel(Player *pTarget) { if (DuelingWith != NULL) return; // We Already Dueling or have Requested a Duel SetDuelState(DUEL_STATE_REQUESTED); //Setup Duel pTarget->DuelingWith = this; DuelingWith = pTarget; //Get Flags position float dist = CalcDistance(pTarget); dist = dist/2; //half way float x = (GetPositionX() + pTarget->GetPositionX()*dist)/(1+dist) + cos(GetOrientation()+(M_PI/2))*2; float y = (GetPositionY() + pTarget->GetPositionY()*dist)/(1+dist) + sin(GetOrientation()+(M_PI/2))*2; float z = (GetPositionZ() + pTarget->GetPositionZ()*dist)/(1+dist); //Create flag/arbiter GameObject* pGameObj = GetMapMgr()->CreateGameObject(); pGameObj->CreateFromProto(21680,GetMapId(), x, y, z, GetOrientation()); pGameObj->SetInstanceID(GetInstanceID()); //Spawn the Flag pGameObj->SetUInt64Value(OBJECT_FIELD_CREATED_BY, GetGUID()); pGameObj->SetUInt32Value(GAMEOBJECT_FACTION, GetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE)); pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, GetUInt32Value(UNIT_FIELD_LEVEL)); //Assign the Flag SetUInt64Value(PLAYER_DUEL_ARBITER,pGameObj->GetGUID()); pTarget->SetUInt64Value(PLAYER_DUEL_ARBITER,pGameObj->GetGUID()); pGameObj->PushToWorld(m_mapMgr); WorldPacket data(SMSG_DUEL_REQUESTED, 16); data << pGameObj->GetGUID(); data << GetGUID(); pTarget->GetSession()->SendPacket(&data); m_duelCountdownTimer = 3000; } void Player::DuelCountdown() { m_duelCountdownTimer -= 1000; if(DuelingWith == 0) return; if(m_duelCountdownTimer == 0) { // Start Duel. SetUInt32Value(UNIT_FIELD_POWER2, 0); DuelingWith->SetUInt32Value(UNIT_FIELD_POWER2, 0); //Give the players a Team DuelingWith->SetUInt32Value(PLAYER_DUEL_TEAM, 1); // Duel Requester SetUInt32Value(PLAYER_DUEL_TEAM, 2); SetDuelState(DUEL_STATE_STARTED); DuelingWith->SetDuelState(DUEL_STATE_STARTED); sEventMgr.AddEvent(this, &Player::DuelBoundaryTest, EVENT_PLAYER_DUEL_BOUNDARY_CHECK, 500, 0,0); sEventMgr.AddEvent(DuelingWith, &Player::DuelBoundaryTest, EVENT_PLAYER_DUEL_BOUNDARY_CHECK, 500, 0,0); } } void Player::DuelBoundaryTest() { //check if in bounds if(!IsInWorld()) return; GameObject * pGameObject = GetMapMgr()->GetGameObject(GetUInt32Value(PLAYER_DUEL_ARBITER)); if(!pGameObject) { EndDuel(DUEL_WINNER_RETREAT); return; } float Dist = CalcDistance((Object*)pGameObject); if(Dist > 75.0f) { // Out of bounds if(m_duelStatus == DUEL_STATUS_OUTOFBOUNDS) { // we already know, decrease timer by 500 m_duelCountdownTimer -= 500; if(m_duelCountdownTimer == 0) { // Times up :p DuelingWith->EndDuel(DUEL_WINNER_RETREAT); } } else { // we just went out of bounds // set timer m_duelCountdownTimer = 10000; // let us know m_session->OutPacket(SMSG_DUEL_OUTOFBOUNDS, 4, &m_duelCountdownTimer); m_duelStatus = DUEL_STATUS_OUTOFBOUNDS; } } else { // we're in range if(m_duelStatus == DUEL_STATUS_OUTOFBOUNDS) { // just came back in range m_session->OutPacket(SMSG_DUEL_INBOUNDS); m_duelStatus = DUEL_STATUS_INBOUNDS; } } } void Player::EndDuel(uint8 WinCondition) { if(m_duelState == DUEL_STATE_FINISHED) return; // Remove the events sEventMgr.RemoveEvents(this, EVENT_PLAYER_DUEL_COUNTDOWN); sEventMgr.RemoveEvents(this, EVENT_PLAYER_DUEL_BOUNDARY_CHECK); for(uint32 x = 0; x < MAX_AURAS; ++x) { if(!m_auras[x]) continue; if(m_auras[x]->WasCastInDuel()) m_auras[x]->Remove(); } m_duelState = DUEL_STATE_FINISHED; if(DuelingWith == 0) return; sEventMgr.RemoveEvents(DuelingWith, EVENT_PLAYER_DUEL_BOUNDARY_CHECK); sEventMgr.RemoveEvents(DuelingWith, EVENT_PLAYER_DUEL_COUNTDOWN); for(uint32 x = 0; x < MAX_AURAS; ++x) { if(!DuelingWith->m_auras[x]) continue; if(DuelingWith->m_auras[x]->WasCastInDuel()) DuelingWith->m_auras[x]->Remove(); } DuelingWith->m_duelState = DUEL_STATE_FINISHED; //Announce Winner WorldPacket data(SMSG_DUEL_WINNER, 500); data << uint8(WinCondition); data << GetName() << DuelingWith->GetName(); if(WinCondition == DUEL_WINNER_KNOCKOUT) { DuelingWith->Emote(EMOTE_ONESHOT_BEG); DuelingWith->Root(); sEventMgr.AddEvent(DuelingWith, &Unit::Unroot, EVENT_UNIT_UNROOT, 3000, 1,0); } SendMessageToSet(&data, true); //get Arbiter GameObject *arbiter = m_mapMgr ? GetMapMgr()->GetGameObject(GetUInt32Value(PLAYER_DUEL_ARBITER)) : 0; //Clear Duel Related Stuff SetUInt64Value(PLAYER_DUEL_ARBITER, 0); DuelingWith->SetUInt64Value(PLAYER_DUEL_ARBITER, 0); SetUInt32Value(PLAYER_DUEL_TEAM, 0); DuelingWith->SetUInt32Value(PLAYER_DUEL_TEAM, 0); if(arbiter) { //Despawn Arbiter /* arbiter->Despawn(10000); sObjHolder.Delete(arbiter);*/ //original code....for some reason we set timer to respawn arbiter // this is wrong i guess...reenable if i'm wrong arbiter->RemoveFromWorld(); delete arbiter; } EventAttackStop(); DuelingWith->EventAttackStop(); // Call off pet if(this->GetSummon()) { this->GetSummon()->setAttackTarget(NULL); this->GetSummon()->GetAIInterface()->SetUnitToFollow(this); this->GetSummon()->GetAIInterface()->HandleEvent(EVENT_FOLLOWOWNER, this->GetSummon(), 0); } // removing auras that kills players after if low HP /*RemoveNegativeAuras(); NOT NEEDED. External targets can always gank both duelers with DoTs. :D DuelingWith->RemoveNegativeAuras();*/ //Stop Players attacking so they don't kill the other player m_session->OutPacket(SMSG_CANCEL_COMBAT); DuelingWith->m_session->OutPacket(SMSG_CANCEL_COMBAT); if(getAttackTarget() == DuelingWith->GetGUID()) setAttackTarget(NULL); if(DuelingWith->getAttackTarget() == GetGUID()) DuelingWith->setAttackTarget(NULL); smsg_AttackStop(DuelingWith); DuelingWith->smsg_AttackStop(this); DuelingWith->DuelingWith = NULL; DuelingWith = NULL; } void Player::StopMirrorTimer(uint32 Type) { m_session->OutPacket(SMSG_STOP_MIRROR_TIMER, 4, &Type); } void Player::EventTeleport(uint32 mapid, float x, float y, float z) { SafeTeleport(mapid, 0, LocationVector(x, y, z)); } void Player::ApplyLevelInfo(LevelInfo* Info, uint32 Level) { // Apply level SetUInt32Value(UNIT_FIELD_LEVEL, Level); // Set next level conditions SetUInt32Value(PLAYER_NEXT_LEVEL_XP, Info->XPToNextLevel); // Set stats for(uint32 i = 0; i < 5; ++i) { BaseStats[i] = Info->Stat[i]; CalcStat(i); } // Set health / mana SetUInt32Value(UNIT_FIELD_HEALTH, Info->HP); SetUInt32Value(UNIT_FIELD_MAXHEALTH, Info->HP); SetUInt32Value(UNIT_FIELD_POWER1, Info->Mana); SetUInt32Value(UNIT_FIELD_MAXPOWER1, Info->Mana); // Calculate talentpoints uint32 TalentPoints = 0; if(Level >= 10) TalentPoints = Level - 9; SetUInt32Value(PLAYER_CHARACTER_POINTS1, TalentPoints); // Set base fields SetUInt32Value(UNIT_FIELD_BASE_HEALTH, Info->HP); SetUInt32Value(UNIT_FIELD_BASE_MANA, Info->Mana); _UpdateMaxSkillCounts(); UpdateStats(); //UpdateChances(); sLog.outDetail("Player %s set parameters to level %u", GetName(), Level); } void Player::BroadcastMessage(const char* Format, ...) { va_list l; va_start(l, Format); char Message[1024]; vsnprintf(Message, 1024, Format, l); va_end(l); WorldPacket * data = sChatHandler.FillSystemMessageData(Message); m_session->SendPacket(data); delete data; } const double BaseRating []= { 2.5,//weapon_skill_ranged!!!! 1.5,//defense=comba_r_1 12,//dodge 20,//parry=3 5,//block=4 10,//melee hit 10,//ranged hit 8,//spell hit=7 14,//melee critical strike=8 14,//ranged critical strike=9 14,//spell critical strike=10 0,// 0, 0, 25,//resilience=14 25,//resil .... meaning unknown 25,//resil .... meaning unknown 10,//MELEE_HASTE_RATING=17 10,//RANGED_HASTE_RATING=18 10,//spell_haste_rating = 19??? 2.5,//melee weapon skill==20 2.5,//melee second hand=21 }; float Player::CalcRating(uint32 index) { if(index<=10|| (index>=14 && index <=21)) { int rating = GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1+index); int l = getLevel(); if(l<10)l=10;//this is not dirty fix-> that's from wowwiki double cost; if(l < 60) cost=(l-8)/52.0; else cost=82.0/(262-3*l); return rating /(BaseRating[index] * cost); }else return 0; } bool Player::SafeTeleport(uint32 MapID, uint32 InstanceID, float X, float Y, float Z, float O) { return SafeTeleport(MapID, InstanceID, LocationVector(X, Y, Z, O)); } bool Player::SafeTeleport(uint32 MapID, uint32 InstanceID, const LocationVector & vec) { bool instance = false; if(InstanceID && (uint32)m_instanceId != InstanceID) { instance = true; this->SetInstanceID(InstanceID); } else if(m_mapId != MapID) { instance = true; } // make sure player does not drown when teleporting from under water if (m_UnderwaterState & UNDERWATERSTATE_UNDERWATER) m_UnderwaterState &= ~UNDERWATERSTATE_UNDERWATER; if(flying_aura && m_mapId != 530) { RemoveAura(flying_aura); flying_aura = 0; } // Lookup map info MapInfo * mi = WorldMapInfoStorage.LookupEntry(MapID); if(mi && mi->flags & WMI_INSTANCE_XPACK_01 && !m_session->HasFlag(ACCOUNT_FLAG_XPACK_01)) { WorldPacket msg(SMSG_BROADCAST_MSG, 50); msg << uint32(3) << "You must have The Burning Crusade Expansion to access this content." << uint8(0); m_session->SendPacket(&msg); return false; } _Relocate(MapID, vec, true, instance); return true; } void Player::SetGuildId(uint32 guildId) { myGuild = guildId ? objmgr.GetGuild(guildId) : 0; SetUInt32Value(PLAYER_GUILDID,guildId); } void Player::UpdatePvPArea() { AreaTable * at = sAreaStore.LookupEntry(m_AreaID); if(at == 0) return; // This is where all the magic happens :P if((at->category == AREAC_ALLIANCE_TERRITORY && GetTeam() == 0) || (at->category == AREAC_HORDE_TERRITORY && GetTeam() == 1)) { if(!HasFlag(PLAYER_FLAGS, PLAYER_FLAG_PVP_TOGGLE) && !m_pvpTimer) { // I'm flagged and I just walked into a zone of my type. Start the 5min counter. ResetPvPTimer(); } return; } else { //Enemy city check if(at->AreaFlags & AREA_CITY_AREA || at->AreaFlags & AREA_CITY) { if((at->category == AREAC_ALLIANCE_TERRITORY && GetTeam() == 1) || (at->category == AREAC_HORDE_TERRITORY && GetTeam() == 0)) { if(!IsPvPFlagged()) SetPvPFlag(); StopPvPTimer(); return; } } //fix for zone areas. if(at->ZoneId) { AreaTable * at2 = sAreaStore.LookupEntry(at->ZoneId); if(at2 && (at2->category == AREAC_ALLIANCE_TERRITORY && GetTeam() == 0) || at2 && (at2->category == AREAC_HORDE_TERRITORY && GetTeam() == 1)) { if(!HasFlag(PLAYER_FLAGS, PLAYER_FLAG_PVP_TOGGLE) && !m_pvpTimer) { // I'm flagged and I just walked into a zone of my type. Start the 5min counter. ResetPvPTimer(); } return; } //enemy territory check if(at2 && at2->AreaFlags & AREA_CITY_AREA || at2 && at2->AreaFlags & AREA_CITY) { if(at2 && (at2->category == AREAC_ALLIANCE_TERRITORY && GetTeam() == 1) || at2 && (at2->category == AREAC_HORDE_TERRITORY && GetTeam() == 0)) { if(!IsPvPFlagged()) SetPvPFlag(); StopPvPTimer(); return; } } } // I just walked into either an enemies town, or a contested zone. // Force flag me if i'm not already. if(at->category == AREAC_SANCTUARY || at->AreaFlags & AREA_SANCTUARY) { if(IsPvPFlagged()) RemovePvPFlag(); RemoveFlag(PLAYER_FLAGS, PLAYER_FLAG_FREE_FOR_ALL_PVP); StopPvPTimer(); } else { //contested territory if(sWorld.GetRealmType() == REALM_PVP) { //automaticaly sets pvp flag on contested territorys. if(!IsPvPFlagged()) SetPvPFlag(); StopPvPTimer(); } if(sWorld.GetRealmType() == REALM_PVE) { if(HasFlag(PLAYER_FLAGS, PLAYER_FLAG_PVP_TOGGLE)) { if(!IsPvPFlagged()) SetPvPFlag(); } else if(!HasFlag(PLAYER_FLAGS, PLAYER_FLAG_PVP_TOGGLE) && IsPvPFlagged() && !m_pvpTimer) { ResetPvPTimer(); } } if(at->AreaFlags & AREA_PVP_ARENA && !m_bg) /* ffa pvp arenas will come later */ { if(!IsPvPFlagged()) SetPvPFlag(); SetFlag(PLAYER_FLAGS, PLAYER_FLAG_FREE_FOR_ALL_PVP); } else { RemoveFlag(PLAYER_FLAGS, PLAYER_FLAG_FREE_FOR_ALL_PVP); } } } } void Player::BuildFlagUpdateForNonGroupSet(uint32 index, uint32 flag) { Object *curObj; for (Object::InRangeSet::iterator iter = GetInRangeSetBegin(); iter != GetInRangeSetEnd();) { curObj = *iter; iter++; if(curObj->IsPlayer()) { Group *pGroup = static_cast(curObj)->GetGroup(); if(pGroup && pGroup == GetGroup()) { } else { BuildFieldUpdatePacket(((Player*)curObj),index,flag); } } } } void Player::LoginPvPSetup() { // Make sure we know our area ID. _EventExploration(); if(isAlive()) CastSpell(this, PLAYER_HONORLESS_TARGET_SPELL, true); } void Player::PvPToggle() { if(sWorld.GetRealmType() == REALM_PVE) { if(m_pvpTimer > 0) { // Means that we typed /pvp while we were "cooling down". Stop the timer. StopPvPTimer(); SetFlag(PLAYER_FLAGS, PLAYER_FLAG_PVP_TOGGLE); if(!IsPvPFlagged()) SetPvPFlag(); } else { if(IsPvPFlagged()) { AreaTable * at = sAreaStore.LookupEntry(m_AreaID); if(at && at->AreaFlags & AREA_CITY_AREA || at && at->AreaFlags & AREA_CITY) { if(at && (at->category == AREAC_ALLIANCE_TERRITORY && GetTeam() == 1) || at && (at->category == AREAC_HORDE_TERRITORY && GetTeam() == 0)) { } else { // Start the "cooldown" timer. ResetPvPTimer(); } } else { // Start the "cooldown" timer. ResetPvPTimer(); } RemoveFlag(PLAYER_FLAGS, PLAYER_FLAG_PVP_TOGGLE); } else { // Move into PvP state. SetFlag(PLAYER_FLAGS, PLAYER_FLAG_PVP_TOGGLE); StopPvPTimer(); SetPvPFlag(); } } } else if(sWorld.GetRealmType() == REALM_PVP) { AreaTable * at = sAreaStore.LookupEntry(m_AreaID); if(at == 0) return; // This is where all the magic happens :P if((at->category == AREAC_ALLIANCE_TERRITORY && GetTeam() == 0) || (at->category == AREAC_HORDE_TERRITORY && GetTeam() == 1)) { if(m_pvpTimer > 0) { // Means that we typed /pvp while we were "cooling down". Stop the timer. StopPvPTimer(); SetFlag(PLAYER_FLAGS, PLAYER_FLAG_PVP_TOGGLE); if(!IsPvPFlagged()) SetPvPFlag(); } else { if(IsPvPFlagged()) { // Start the "cooldown" timer. ResetPvPTimer(); RemoveFlag(PLAYER_FLAGS, PLAYER_FLAG_PVP_TOGGLE); } else { // Move into PvP state. SetFlag(PLAYER_FLAGS, PLAYER_FLAG_PVP_TOGGLE); StopPvPTimer(); SetPvPFlag(); } } } else { if(at->ZoneId) { AreaTable * at2 = sAreaStore.LookupEntry(at->ZoneId); if(at2 && (at2->category == AREAC_ALLIANCE_TERRITORY && GetTeam() == 0) || at2 && (at2->category == AREAC_HORDE_TERRITORY && GetTeam() == 1)) { if(m_pvpTimer > 0) { // Means that we typed /pvp while we were "cooling down". Stop the timer. StopPvPTimer(); SetFlag(PLAYER_FLAGS, PLAYER_FLAG_PVP_TOGGLE); if(!IsPvPFlagged()) SetPvPFlag(); } else { if(IsPvPFlagged()) { // Start the "cooldown" timer. ResetPvPTimer(); RemoveFlag(PLAYER_FLAGS, PLAYER_FLAG_PVP_TOGGLE); } else { // Move into PvP state. SetFlag(PLAYER_FLAGS, PLAYER_FLAG_PVP_TOGGLE); StopPvPTimer(); SetPvPFlag(); } } return; } } if(!HasFlag(PLAYER_FLAGS, PLAYER_FLAG_PVP_TOGGLE)) SetFlag(PLAYER_FLAGS, PLAYER_FLAG_PVP_TOGGLE); else { RemoveFlag(PLAYER_FLAGS, PLAYER_FLAG_PVP_TOGGLE); } } } } bool Player::CanCastDueToCooldown(SpellEntry * spellid) { map::iterator itr; uint32 mstime = getMSTime(); // no point checking single spells unless they have // a cooldown if(spellid->RecoveryTime) { itr = SpellCooldownMap.find(spellid->Id); if(itr != SpellCooldownMap.end()) { if(mstime < itr->second) return false; } } if(spellid->Category) { itr = SpellCooldownCategoryMap.find(spellid->Category); if(itr != SpellCooldownCategoryMap.end()) { if(mstime < itr->second) return false; } } if(mstime < GlobalCooldown) return false; return true; } void Player::ResetPvPTimer() { m_pvpTimer = sWorld.getIntRate(INTRATE_PVPTIMER); } void Player::SaveHonorFields() { // Save honor fields only :P //Zehamster: we can do this with a single single query instead of 7 ;) CharacterDatabase.Execute("UPDATE characters SET lastDailyReset=%u,killsToday=%u,killsYesterday=%u,killsLifeTime=%u," "honorToday=%u,honorYesterday=%u,honorPoints=%u WHERE guid=%u", m_lastHonorResetTime, m_killsToday, m_killsYesterday, m_killsLifetime, m_honorToday, m_honorYesterday, m_honorPoints, GetGUIDLow()); } void Player::CalculateBaseStats() { if(!lvlinfo) return; memcpy(BaseStats, lvlinfo->Stat, sizeof(uint32) * 5); SetUInt32Value(UNIT_FIELD_MAXHEALTH, lvlinfo->HP); SetUInt32Value(UNIT_FIELD_BASE_HEALTH, lvlinfo->HP); SetUInt32Value(PLAYER_NEXT_LEVEL_XP, lvlinfo->XPToNextLevel); if(GetPowerType() == POWER_TYPE_MANA) { SetUInt32Value(UNIT_FIELD_BASE_MANA, lvlinfo->Mana); SetUInt32Value(UNIT_FIELD_MAXPOWER1, lvlinfo->Mana); } } void Player::CompleteLoading() { // cast passive initial spells -- grep note: these shouldnt require plyr to be in world SpellSet::iterator itr; SpellEntry *info; SpellCastTargets targets; targets.m_unitTarget = this->GetGUID(); targets.m_targetMask = 0x2; for (itr = mSpells.begin(); itr != mSpells.end(); ++itr) { info = sSpellStore.LookupEntry(*itr); if(info && (info->Attributes & ATTRIBUTES_PASSIVE) ) // passive { Spell * spell=new Spell(this,info,true,NULL); spell->prepare(&targets); if(info->RequiredShapeShift && (getClass()==DRUID || getClass()==WARRIOR)) m_SSSPecificSpells.insert(info->Id); } } std::list::iterator i = loginauras.begin(); for(;i!=loginauras.end(); i++) { //check if we already have this aura // if(this->HasActiveAura((*i).id)) // continue; //how many times do we intend to put this oura on us /* uint32 count_appearence=0; std::list::iterator i2 = i; for(;i2!=loginauras.end();i2++) if((*i).id==(*i2).id) { count_appearence++; } */ SpellEntry * sp = sSpellStore.LookupEntry((*i).id); Aura * a = new Aura(sp,(*i).dur,this,this); for(uint32 x =0;x<3;x++) { if(sp->Effect[x]==SPELL_EFFECT_APPLY_AURA) { a->AddMod(sp->EffectApplyAuraName[x],sp->EffectBasePoints[x]+1,sp->EffectMiscValue[x],x); } } this->AddAura(a); //FIXME: must save amt,pos/neg //Somehow we should restore number of appearence. Right now i have no idea how :( // if(count_appearence>1) // this->AddAuraVisual((*i).id,count_appearence-1,a->IsPositive()); } // this needs to be after the cast of passive spells, because it will cast ghost form, after the remove making it in ghost alive, if no corpse. //death system checkout if(GetUInt32Value(UNIT_FIELD_HEALTH) <= 0 && !HasFlag(PLAYER_FLAGS, PLAYER_FLAG_DEATH_WORLD_ENABLE)) { setDeathState(CORPSE); } else if(HasFlag(PLAYER_FLAGS, PLAYER_FLAG_DEATH_WORLD_ENABLE)) { // Check if we have an existing corpse. Corpse * corpse = objmgr.GetCorpseByOwner(GetGUIDLow()); if(corpse == 0) { sEventMgr.AddEvent(this, &Player::RepopAtGraveyard, GetPositionX(),GetPositionY(),GetPositionZ(), GetMapId(), EVENT_PLAYER_CHECKFORCHEATS, 1000, 1,EVENT_FLAG_DO_NOT_EXECUTE_IN_WORLD_CONTEXT); } else { // Set proper deathstate setDeathState(CORPSE); } } else if(getDeathState() == JUST_DIED && !HasActiveAura(8326)) { //RepopRequestedPlayer(); sEventMgr.AddEvent(this, &Player::RepopRequestedPlayer, EVENT_PLAYER_CHECKFORCHEATS, 1000, 1,EVENT_FLAG_DO_NOT_EXECUTE_IN_WORLD_CONTEXT); } if(iActivePet) SpawnPet(iActivePet); // only spawn if >0 if(GetTaxiState()) { // Create HAS to be sent before this! ProcessPendingUpdates(); TaxiStart(GetTaxiPath(), GetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID), lastNode); } // useless logon spell Spell *logonspell = new Spell(this, sSpellStore.LookupEntry(836), false, NULL); logonspell->prepare(&targets); // Banned if(IsBanned()) { Kick(10000); BroadcastMessage("This character is not allowed to play."); BroadcastMessage("You have been banned for: %s", GetBanReason().c_str()); } /* are we in a group? */ if(!m_Group && m_playerInfo->m_Group) { m_playerInfo->m_Group->AddMember(m_playerInfo, this, m_playerInfo->subGroup); } } void Player::OnWorldPortAck() { //only rezz if player is porting to a instance portal MapInfo *pMapinfo = WorldMapInfoStorage.LookupEntry(GetMapId()); if(isDead()) { if(pMapinfo) { if(pMapinfo->type != INSTANCE_NULL) ResurrectPlayer(); } } if(pMapinfo) { WorldPacket data(4); if(pMapinfo->HasFlag(WMI_INSTANCE_WELCOME) && GetMapMgr()) { std::string welcome_msg; welcome_msg = "Welcome to "; welcome_msg += pMapinfo->name; welcome_msg += "."; if(pMapinfo->type == INSTANCE_RAID || pMapinfo->type == INSTANCE_MULTIMODE && iInstanceType == MODE_HEROIC) { welcome_msg += "This instance is scheduled to reset on "; welcome_msg += asctime(localtime(&GetMapMgr()->RaidExpireTime)); } sChatHandler.SystemMessage(m_session, welcome_msg.c_str()); } if(pMapinfo->type == INSTANCE_NONRAID || pMapinfo->type == INSTANCE_MULTIMODE && iInstanceType == MODE_NORMAL) { data.SetOpcode(SMSG_INSTANCE_RESET_ACTIVATE); data << uint32(0x00); GetSession()->SendPacket(&data); sInstanceSavingManager.SavePlayerToInstance(this, pMapinfo->mapid); } else if(pMapinfo->type == INSTANCE_NULL) { data.SetOpcode(SMSG_INSTANCE_RESET_ACTIVATE); data << uint32(0x01); GetSession()->SendPacket(&data); } } ResetHeartbeatCoords(); } void Player::ModifyBonuses(uint32 type,int32 val) { switch (type) { case POWER: ModUInt32Value(UNIT_FIELD_MAXPOWER1,val); m_manafromitems += val; break; case HEALTH: ModUInt32Value(UNIT_FIELD_MAXHEALTH, val); m_healthfromitems += val; break; case AGILITY: // modify agility FlatStatModPos[1]+=val; CalcStat(1); break; case STRENGHT: //modify strength FlatStatModPos[0]+=val; CalcStat(0); break; case INTELLECT: //modify intellect FlatStatModPos[3]+=val; CalcStat(3); break; case SPIRIT: //modify spirit FlatStatModPos[4]+=val; CalcStat(4); break; case STAMINA: //modify stamina FlatStatModPos[2]+=val; CalcStat(2); break; case WEAPON_SKILL_RATING: { }break; case DEFENSE_RATING: { ModUInt32Value(PLAYER_FIELD_COMBAT_RATING_01, val); }break; case DODGE_RATING: { ModUInt32Value(PLAYER_FIELD_COMBAT_RATING_02, val); }break; case PARRY_RATING: { ModUInt32Value(PLAYER_FIELD_COMBAT_RATING_03, val); }break; case SHIELD_BLOCK_RATING: { ModUInt32Value(PLAYER_FIELD_COMBAT_RATING_04, val); }break; case MELEE_HIT_RATING: { ModUInt32Value(PLAYER_FIELD_COMBAT_RATING_05, val); }break; case RANGED_HIT_RATING: { ModUInt32Value(PLAYER_FIELD_COMBAT_RATING_06, val); }break; case SPELL_HIT_RATING: { ModUInt32Value(PLAYER_FIELD_COMBAT_RATING_07, val); }break; case MELEE_CRITICAL_STRIKE_RATING: { ModUInt32Value(PLAYER_FIELD_COMBAT_RATING_08,val); }break; case RANGED_CRITICAL_STRIKE_RATING: { ModUInt32Value(PLAYER_FIELD_COMBAT_RATING_09, val); }break; case SPELL_CRITICAL_STRIKE_RATING: { ModUInt32Value(PLAYER_FIELD_COMBAT_RATING_10, val); }break; case MELEE_HIT_AVOIDANCE_RATING: { }break; case RANGED_HIT_AVOIDANCE_RATING: { }break; case SPELL_HIT_AVOIDANCE_RATING: { }break; case MELEE_CRITICAL_AVOIDANCE_RATING: { }break; case RANGED_CRITICAL_AVOIDANCE_RATING: { }break; case SPELL_CRITICAL_AVOIDANCE_RATING: { }break; case MELEE_HASTE_RATING: { ModUInt32Value(PLAYER_FIELD_COMBAT_RATING_17, val); }break; case RANGED_HASTE_RATING: { ModUInt32Value(PLAYER_FIELD_COMBAT_RATING_18, val); }break; case SPELL_HASTE_RATING: { ModUInt32Value(PLAYER_FIELD_COMBAT_RATING_19, val); }break; case HIT_RATING: { ModUInt32Value(PLAYER_FIELD_COMBAT_RATING_05, val);//melee ModUInt32Value(PLAYER_FIELD_COMBAT_RATING_06, val);//ranged }break; case CRITICAL_STRIKE_RATING: { ModUInt32Value(PLAYER_FIELD_COMBAT_RATING_08,val);//melee ModUInt32Value(PLAYER_FIELD_COMBAT_RATING_09,val);//ranged }break; case HIT_AVOIDANCE_RATING: { }break; case CRITICAL_AVOIDANCE_RATING: { }break; case RESILIENCE_RATING: { //uses 3 fields ModUInt32Value(PLAYER_FIELD_COMBAT_RATING_16, val); ModUInt32Value(PLAYER_FIELD_COMBAT_RATING_15, val); ModUInt32Value(PLAYER_FIELD_COMBAT_RATING_14, val); }break; case HASTE_RATING: { ModUInt32Value(PLAYER_FIELD_COMBAT_RATING_17, val);//melee ModUInt32Value(PLAYER_FIELD_COMBAT_RATING_18, val);//ranged }break; } } bool Player::CanSignCharter(Charter * charter, Player * requester) { if(m_charter || IsInGuild() || requester->GetTeam() != GetTeam()) return false; else return true; } void Player::SaveAuras(stringstream &ss) { // Add player auras for(uint32 x=0;xm_spellProto->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA || aur->m_spellProto->Effect[i] == SPELL_EFFECT_ADD_FARSIGHT) { skip = true; break; } } // skipped spells due to bugs switch(aur->m_spellProto->Id) { case 12043: // Presence of mind case 11129: // Combustion case 16188: // Natures Swiftness case 17116: // Natures Swiftness case 34936: // Backlash case 35076: // Blessing of A'dal case 23333: // WSG case 23335: // WSG skip = true; break; } //disabled proc spells until proper loading is fixed. Some spells tend to block or not remove when restored if(aur->GetSpellProto()->procFlags) { // sLog.outDebug("skipping aura %d because has flags %d",aur->GetSpellId(),aur->GetSpellProto()->procFlags); skip = true; } //disabled proc spells until proper loading is fixed. We cannot recover the charges that were used up. Will implement later if(aur->GetSpellProto()->procCharges) { // sLog.outDebug("skipping aura %d because has proccharges %d",aur->GetSpellId(),aur->GetSpellProto()->procCharges); skip = true; } //we are going to cast passive spells anyway on login so no need to save auras for them if(aur->IsPassive() && !(aur->GetSpellProto()->AttributesEx & 1024)) skip = true; if(skip)continue; uint32 d=aur->GetTimeLeft(); if(d>3000) ss << aur->GetSpellId() << "," << d << ","; } } } void Player::SetShapeShift(uint8 ss) { SetByte(UNIT_FIELD_BYTES_1,2,ss); //remove auras that we should not have for(uint32 x =0;xGetSpellProto()->RequiredShapeShift) { if(!ss || !(((uint32)1 << (ss-1))&m_auras[x]->GetSpellProto()->RequiredShapeShift)) { m_auras[x]->Remove(); } } } if(m_SSSPecificSpells.size()) {//recalc modifiers std::set::iterator i; for(i=m_SSSPecificSpells.begin();i!=m_SSSPecificSpells.end();i++) { uint32 SpellId = *i; if(this->FindAura(SpellId)) continue; SpellEntry* spells = sSpellStore.LookupEntry(SpellId); Spell *spell = new Spell(this, spells ,true,NULL); SpellCastTargets targets; targets.m_unitTarget = this->GetGUID(); spell->prepare(&targets); } } } void Player::CalcDamage() { float delta; float r; int ss = GetShapeShift(); /////////////////MAIN HAND float ap_bonus = GetAP()/14000.0; delta = GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS)-GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG); if(IsInFeralForm()) { uint32 lev = getLevel(); /*if(ss==FORM_CAT) r = delta + ap_bonus * 1000.0; else r = delta + ap_bonus * 2500.0;*/ if(ss == FORM_CAT) r = lev + delta + ap_bonus * 1000.0; else r = lev + delta + ap_bonus * 2500.0; //SetFloatValue(UNIT_FIELD_MINDAMAGE,r); //SetFloatValue(UNIT_FIELD_MAXDAMAGE,r); SetFloatValue(UNIT_FIELD_MINDAMAGE,r * 0.9); SetFloatValue(UNIT_FIELD_MAXDAMAGE,r * 1.1); return; } //////no druid ss uint32 speed=2000; Item *it = GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_MAINHAND); if(!disarmed) { if(it) speed = it->GetProto()->Delay; } float bonus=ap_bonus*speed; float tmp = 1; map::iterator i; for(i = damagedone.begin();i!=damagedone.end();i++) { if((i->second.wclass == (uint32)-1) || //any weapon (it && ((1 << it->GetProto()->SubClass) & i->second.subclass) ) ) tmp+=i->second.value/100.0; } r = BaseDamage[0]+delta+bonus; r *= tmp; SetFloatValue(UNIT_FIELD_MINDAMAGE,r>0?r:0); r = BaseDamage[1]+delta+bonus; r *= tmp; SetFloatValue(UNIT_FIELD_MAXDAMAGE,r>0?r:0); uint32 cr=0; if(it) { if(((Player*)this)->m_wratings.size ()) { std::map::iterator itr=m_wratings.find(it->GetProto()->SubClass); if(itr!=m_wratings.end()) cr=itr->second; } } SetUInt32Value(PLAYER_FIELD_COMBAT_RATING_20,cr); ///////////////////////MAIN HAND end ////////sec hand start cr=0; it = ((Player*)this)->GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_OFFHAND); if(it) { if(!disarmed) { speed =it->GetProto()->Delay; } else speed = 2000; bonus = ap_bonus * speed; i = damagedone.begin(); tmp = 1; for(;i!=damagedone.end();i++) { if((i->second.wclass==(uint32)-1) || //any weapon (( (1 << it->GetProto()->SubClass) & i->second.subclass) ) ) tmp+=i->second.value/100.0; } r = (BaseOffhandDamage[0]+delta+bonus)*offhand_dmg_mod; r *= tmp; SetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE,r>0?r:0); r = (BaseOffhandDamage[1]+delta+bonus)*offhand_dmg_mod; r *= tmp; SetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE,r>0?r:0); if(m_wratings.size ()) { std::map::iterator itr=m_wratings.find(it->GetProto()->SubClass); if(itr!=m_wratings.end()) cr=itr->second; } } SetUInt32Value(PLAYER_FIELD_COMBAT_RATING_21,cr); /////////////second hand end ///////////////////////////RANGED cr=0; if((it = GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_RANGED))) { i = damagedone.begin(); tmp = 1; for(;i != damagedone.end();i++) { if( (i->second.wclass == (uint32)-1) || //any weapon ( ((1 << it->GetProto()->SubClass) & i->second.subclass) ) ) { tmp+=i->second.value/100.0; } } if(it->GetProto()->SubClass != 19)//wands do not have bonuses from RAP & ammo { ap_bonus = (GetUInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER)+(int32)GetUInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER_MODS))/14000.0; bonus = ap_bonus*it->GetProto()->Delay; if(GetUInt32Value(PLAYER_AMMO_ID)) { ItemPrototype * xproto=ItemPrototypeStorage.LookupEntry(GetUInt32Value(PLAYER_AMMO_ID)); if(xproto) { bonus+=((xproto->Damage[0].Min+xproto->Damage[0].Max)*it->GetProto()->Delay)/2000.0; } } }else bonus =0; r = BaseRangedDamage[0]+delta+bonus; r *= tmp; SetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE,r>0?r:0); r = BaseRangedDamage[1]+delta+bonus; r *= tmp; SetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE,r>0?r:0); if(m_wratings.size ()) { std::map::iterator i=m_wratings.find(it->GetProto()->SubClass); if(i != m_wratings.end()) cr=i->second; } } SetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1,cr); /////////////////////////////////RANGED end tmp = 1; for(i = damagedone.begin();i != damagedone.end();i++) if(i->second.wclass==(uint32)-1) //any weapon tmp += i->second.value/100.0; //display only modifiers for any weapon SetFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT ,tmp); } uint32 Player::GetMainMeleeDamage(uint32 AP_owerride) { float min_dmg,max_dmg; float delta; float r; int ss = GetShapeShift(); /////////////////MAIN HAND float ap_bonus; if(AP_owerride) ap_bonus = AP_owerride/14000.0; else ap_bonus = GetAP()/14000.0; delta = GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS)-GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG); if(IsInFeralForm()) { uint32 lev = getLevel(); if(ss == FORM_CAT) r = lev + delta + ap_bonus * 1000.0; else r = lev + delta + ap_bonus * 2500.0; min_dmg = r * 0.9; max_dmg = r * 1.1; return max((min_dmg + max_dmg)/2,0); } //////no druid ss uint32 speed=2000; Item *it = GetItemInterface()->GetInventoryItem(EQUIPMENT_SLOT_MAINHAND); if(!disarmed) { if(it) speed = it->GetProto()->Delay; } float bonus=ap_bonus*speed; float tmp = 1; map::iterator i; for(i = damagedone.begin();i!=damagedone.end();i++) { if((i->second.wclass == (uint32)-1) || //any weapon (it && ((1 << it->GetProto()->SubClass) & i->second.subclass) ) ) tmp+=i->second.value/100.0; } r = BaseDamage[0]+delta+bonus; r *= tmp; min_dmg = r * 0.9; r = BaseDamage[1]+delta+bonus; r *= tmp; max_dmg = r * 1.1; return max((min_dmg + max_dmg)/2,0); } void Player::EventPortToGM(Player *p) { SafeTeleport(p->GetMapId(),p->GetInstanceID(),p->GetPosition()); } void Player::UpdateComboPoints() { // fuck bytebuffers :D unsigned char buffer[10]; uint32 c = 2; // check for overflow if(m_comboPoints > 5) m_comboPoints = 5; if(m_comboPoints < 0) m_comboPoints = 0; if(m_comboTarget != 0) { Unit * target = (m_mapMgr != NULL) ? m_mapMgr->GetUnit(m_comboTarget) : NULL; if(!target || target->isDead() || GetSelection() != m_comboTarget) { buffer[0] = buffer[1] = 0; } else { c = FastGUIDPack(m_comboTarget, buffer, 0); buffer[c++] = m_comboPoints; } } else buffer[0] = buffer[1] = 0; m_session->OutPacket(SMSG_SET_COMBO_POINTS, c, buffer); } Unit *Player::GetSoloSpellTarget(uint32 spell_id) { if(m_mapMgr == 0) return NULL; SoloSpells::iterator iter=solospelltarget.find(spell_id); if(iter!=solospelltarget.end()) return GetMapMgr()->GetUnit(iter->second); return NULL; } void Player::SetSoloSpellTarget(uint32 spellid,uint64 newtarget) { if(newtarget) solospelltarget.insert(make_pair( spellid, newtarget )); else { SoloSpells::iterator iter=solospelltarget.find(spellid); if(iter!=solospelltarget.end()) { solospelltarget.erase(iter); return; } } } void Player::SendAreaTriggerMessage(const char * message, ...) { va_list ap; va_start(ap, message); char msg[500]; vsnprintf(msg, 500, message, ap); va_end(ap); WorldPacket data(SMSG_AREA_TRIGGER_MESSAGE, 6 + strlen(msg)); data << (uint32)0 << msg << (uint8)0x00; m_session->SendPacket(&data); } void Player::Set_Mute_on_player(uint32 until) { chat_disabled_until = until; if(!sEventMgr.HasEvent(this,EVENT_MUTE_PLAYER)) sEventMgr.AddEvent(this,&Player::Remove_Mute_on_player,EVENT_MUTE_PLAYER,chat_disabled_until,1,0); } void Player::Remove_Mute_on_player() { chat_disabled_until = 0; } void Player::removeSoulStone() { if(!this->SoulStone) return; uint32 sSoulStone = 0; switch(this->SoulStone) { case 3026: { sSoulStone = 20707; }break; case 20758: { sSoulStone = 20762; }break; case 20759: { sSoulStone = 20763; }break; case 20760: { sSoulStone = 20764; }break; case 20761: { sSoulStone = 20765; }break; } this->RemoveAura(sSoulStone); this->SoulStone = this->SoulStoneReciever = 0; //just incase } void Player::SoftDisconnect() { sEventMgr.RemoveEvents(this, EVENT_PLAYER_SOFT_DISCONNECT); WorldSession *session=GetSession(); session->LogoutPlayer(true); session->Disconnect(); } void Player::SetNoseLevel() { // Set the height of the player switch (getRace()) { case RACE_HUMAN: // female if (getGender()) m_noseLevel = 1.72; // male else m_noseLevel = 1.78; break; case RACE_ORC: if (getGender()) m_noseLevel = 1.82; else m_noseLevel = 1.98; break; case RACE_DWARF: if (getGender()) m_noseLevel = 1.27; else m_noseLevel = 1.4; break; case RACE_NIGHTELF: if (getGender()) m_noseLevel = 1.84; else m_noseLevel = 2.13; break; case RACE_UNDEAD: if (getGender()) m_noseLevel = 1.61; else m_noseLevel = 1.8; break; case RACE_TAUREN: if (getGender()) m_noseLevel = 2.48; else m_noseLevel = 2.01; break; case RACE_GNOME: if (getGender()) m_noseLevel = 1.06; else m_noseLevel = 1.04; break; case RACE_TROLL: if (getGender()) m_noseLevel = 2.02; else m_noseLevel = 1.93; break; case RACE_BLOODELF: if (getGender()) m_noseLevel = 1.83; else m_noseLevel = 1.93; break; case RACE_DRAENEI: if (getGender()) m_noseLevel = 2.09; else m_noseLevel = 2.36; break; } } void Player::Possess(Unit * pTarget) { if(m_Summon || m_CurrentCharm) return; m_CurrentCharm = pTarget; if(pTarget->GetTypeId() == TYPEID_UNIT) { // unit-only stuff. pTarget->setAItoUse(false); pTarget->GetAIInterface()->StopMovement(0); pTarget->m_redirectSpellPackets = this; } m_noInterrupt++; SetUInt64Value(UNIT_FIELD_CHARM, pTarget->GetGUID()); SetUInt64Value(PLAYER_FARSIGHT, pTarget->GetGUID()); pTarget->SetUInt64Value(UNIT_FIELD_CHARMEDBY, GetGUID()); pTarget->SetCharmTempVal(pTarget->GetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE)); pTarget->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, GetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE)); pTarget->SetFlag(UNIT_FIELD_FLAGS, U_FIELD_FLAG_PLAYER_CONTROLLED_CREATURE); SetFlag(UNIT_FIELD_FLAGS, U_FIELD_FLAG_LOCK_PLAYER); /* send "switch mover" packet */ WorldPacket data1(SMSG_DEATH_NOTIFY_OBSOLETE, 10); /* burlex: this should be renamed SMSG_SWITCH_ACTIVE_MOVER :P */ data1 << pTarget->GetNewGUID() << uint8(1); m_session->SendPacket(&data1); /* update target faction set */ pTarget->_setFaction(); pTarget->UpdateOppFactionSet(); list avail_spells; for(list::iterator itr = pTarget->GetAIInterface()->m_spells.begin(); itr != pTarget->GetAIInterface()->m_spells.end(); ++itr) { if((*itr)->agent == AGENT_SPELL) avail_spells.push_back((*itr)->spell->Id); } list::iterator itr = avail_spells.begin(); /* build + send pet_spells packet */ WorldPacket data(SMSG_PET_SPELLS, pTarget->GetAIInterface()->m_spells.size() * 4 + 20); data << pTarget->GetGUID(); data << uint32(0x00000000);//unk1 data << uint32(0x00000101);//unk2 // First spell is attack. data << uint32(PET_SPELL_ATTACK); // Send the actionbar for(uint32 i = 1; i < 10; ++i) { if(itr != avail_spells.end()) { data << uint16((*itr)) << uint16(DEFAULT_SPELL_STATE); ++itr; } else data << uint16(0) << uint8(0) << uint8(i+5); } // Send the rest of the spells. data << uint8(avail_spells.size()); for(itr = avail_spells.begin(); itr != avail_spells.end(); ++itr) data << uint16(*itr) << uint16(DEFAULT_SPELL_STATE); data << uint64(0); m_session->SendPacket(&data); } void Player::UnPossess() { if(m_Summon || !m_CurrentCharm) return; Unit * pTarget = m_CurrentCharm; m_CurrentCharm = 0; if(pTarget->GetTypeId() == TYPEID_UNIT) { // unit-only stuff. pTarget->setAItoUse(true); pTarget->m_redirectSpellPackets = 0; } m_noInterrupt--; SetUInt64Value(PLAYER_FARSIGHT, 0); SetUInt64Value(UNIT_FIELD_CHARM, 0); pTarget->SetUInt64Value(UNIT_FIELD_CHARMEDBY, 0); RemoveFlag(UNIT_FIELD_FLAGS, U_FIELD_FLAG_LOCK_PLAYER); pTarget->RemoveFlag(UNIT_FIELD_FLAGS, U_FIELD_FLAG_PLAYER_CONTROLLED_CREATURE); pTarget->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, pTarget->GetCharmTempVal()); pTarget->_setFaction(); pTarget->UpdateOppFactionSet(); /* send "switch mover" packet */ WorldPacket data(SMSG_DEATH_NOTIFY_OBSOLETE, 10); data << GetNewGUID() << uint8(1); m_session->SendPacket(&data); data.Initialize(SMSG_PET_SPELLS); data << uint64(0); m_session->SendPacket(&data); } //what is an Immobilize spell ? Have to add it later to spell effect handler void Player::EventStunOrImmobilize(Unit *proc_target) { if(trigger_on_stun) { if(trigger_on_stun_chance<100 && !Rand(trigger_on_stun_chance)) return; SpellEntry *spellInfo = sSpellStore.LookupEntry(trigger_on_stun); if(!spellInfo) return; Spell *spell = new Spell(this, spellInfo ,true, NULL); SpellCastTargets targets; /* if(spellInfo->procFlags & PROC_TAGRGET_ATTACKER) { if(!attacker) return; targets.m_unitTarget = attacker->GetGUID(); } else targets.m_unitTarget = GetGUID(); */ if(proc_target) targets.m_unitTarget = proc_target->GetGUID(); else targets.m_unitTarget = GetGUID(); spell->prepare(&targets); } } void Player::SummonRequest(uint32 Requestor, uint32 ZoneID, uint32 MapID, uint32 InstanceID, const LocationVector & Position) { m_summonInstanceId = InstanceID; m_summonPos = Position; m_summoner = Requestor; m_summonMapId = MapID; WorldPacket data(SMSG_SUMMON_REQUEST, 50); data << uint64(Requestor) << ZoneID << uint32(120000); // 2 minutes m_session->SendPacket(&data); } void Player::RemoveFromBattlegroundQueue() { if(!m_pendingBattleground) return; m_pendingBattleground->RemovePendingPlayer(this); sChatHandler.SystemMessage(m_session, "You were removed from the queue for the battleground for not joining after 2 minutes."); m_pendingBattleground = 0; } #ifdef CLUSTERING void Player::EventRemoveAndDelete() { } #endif void Player::_AddSkillLine(uint32 SkillLine, uint32 Current, uint32 Max) { ItemProf * prof; SkillMap::iterator itr = m_skills.find(SkillLine); if(itr != m_skills.end()) { if( (Current > itr->second.CurrentValue && Max >= itr->second.MaximumValue) || (Current == itr->second.CurrentValue && Max > itr->second.MaximumValue) ) { itr->second.CurrentValue = Current; itr->second.MaximumValue = Max; _UpdateSkillFields(); } } else { PlayerSkill inf; inf.Skill = sSkillLineStore.LookupEntry(SkillLine); inf.CurrentValue = Current; inf.MaximumValue = Max; inf.BonusValue = 0; m_skills.insert( make_pair( SkillLine, inf ) ); _UpdateSkillFields(); //Add to proficeincy if((prof=(ItemProf *)GetProficiencyBySkill(SkillLine))) { WorldPacket data(SMSG_SET_PROFICIENCY, 8); data << prof->itemclass; if(prof->itemclass==4) { armor_proficiency|=prof->subclass; data << armor_proficiency; }else { weapon_proficiency|=prof->subclass; data << weapon_proficiency; } GetSession()->SendPacket(&data); } } } void Player::_UpdateSkillFields() { uint32 f = PLAYER_SKILL_INFO_1_1; /* Set the valid skills */ for(SkillMap::iterator itr = m_skills.begin(); itr != m_skills.end(); ++itr) { ASSERT(f <= PLAYER_CHARACTER_POINTS1); if(itr->second.Skill->type == SKILL_TYPE_PROFESSION) SetUInt32Value(f++, itr->first | 0x10000); else SetUInt32Value(f++, itr->first); SetUInt32Value(f++, (itr->second.MaximumValue << 16) | itr->second.CurrentValue); SetUInt32Value(f++, itr->second.BonusValue); } /* Null out the rest of the fields */ for(; f < PLAYER_CHARACTER_POINTS1; ++f) { if(m_uint32Values[f] != 0) SetUInt32Value(f, 0); } } bool Player::_HasSkillLine(uint32 SkillLine) { return (m_skills.find(SkillLine) != m_skills.end()); } void Player::_AdvanceSkillLine(uint32 SkillLine, uint32 Count /* = 1 */) { SkillMap::iterator itr = m_skills.find(SkillLine); if(itr == m_skills.end()) { /* Add it */ _AddSkillLine(SkillLine, Count, getLevel() * 5); } else { /* Update it. */ if(itr->second.CurrentValue >= itr->second.MaximumValue) return; itr->second.CurrentValue += Count; if(itr->second.CurrentValue >= itr->second.MaximumValue) itr->second.CurrentValue = itr->second.MaximumValue; } _UpdateSkillFields(); } uint32 Player::_GetSkillLineMax(uint32 SkillLine) { SkillMap::iterator itr = m_skills.find(SkillLine); return (itr == m_skills.end()) ? 0 : itr->second.MaximumValue; } uint32 Player::_GetSkillLineCurrent(uint32 SkillLine, bool IncludeBonus /* = true */) { SkillMap::iterator itr = m_skills.find(SkillLine); if(itr == m_skills.end()) return 0; return (IncludeBonus ? itr->second.CurrentValue + itr->second.BonusValue : itr->second.CurrentValue); } void Player::_RemoveSkillLine(uint32 SkillLine) { SkillMap::iterator itr = m_skills.find(SkillLine); if(itr == m_skills.end()) return; m_skills.erase(itr); _UpdateSkillFields(); } void Player::_UpdateMaxSkillCounts() { bool dirty = false; uint32 new_max = getLevel() * 5; for(SkillMap::iterator itr = m_skills.begin(); itr != m_skills.end(); ++itr) { if(itr->second.Skill->type != SKILL_TYPE_WEAPON && itr->second.Skill->id != SKILL_POISONS && itr->second.Skill->id != SKILL_LOCKPICKING) { continue; } if(itr->second.MaximumValue != new_max) { dirty = true; itr->second.MaximumValue = new_max; } } if(dirty) _UpdateSkillFields(); } void Player::_ModifySkillBonus(uint32 SkillLine, int32 Delta) { SkillMap::iterator itr = m_skills.find(SkillLine); if(itr == m_skills.end()) return; itr->second.BonusValue += Delta; _UpdateSkillFields(); } void Player::_ModifySkillBonusByType(uint32 SkillType, int32 Delta) { bool dirty = false; for(SkillMap::iterator itr = m_skills.begin(); itr != m_skills.end(); ++itr) { if(itr->second.Skill->type == SkillType) { itr->second.BonusValue += Delta; dirty=true; } } if(dirty) _UpdateSkillFields(); } /** Maybe this formula needs to be checked? * - Burlex */ float PlayerSkill::GetSkillUpChance() { float diff = MaximumValue - CurrentValue; return (diff * 100.0f / float(MaximumValue)) / 3.0f; } void Player::_RemoveLanguages() { for(SkillMap::iterator itr = m_skills.begin(), it2; itr != m_skills.end();) { if(itr->second.Skill->type == SKILL_TYPE_LANGUAGE) { it2 = itr++; m_skills.erase(it2); } else ++itr; } } void PlayerSkill::Reset(uint32 Id) { MaximumValue = 0; CurrentValue = 0; BonusValue = 0; Skill = (Id == 0) ? NULL : sSkillLineStore.LookupEntry(Id); } void Player::_AddLanguages(bool All) { /** This function should only be used at login, and after _RemoveLanguages is called. * Otherwise weird stuff could happen :P * - Burlex */ PlayerSkill sk; skilllineentry * en; uint32 spell_id; static uint32 skills[] = { SKILL_LANG_COMMON, SKILL_LANG_ORCISH, SKILL_LANG_DWARVEN, SKILL_LANG_DARNASSIAN, SKILL_LANG_TAURAHE, SKILL_LANG_THALASSIAN, SKILL_LANG_TROLL, SKILL_LANG_GUTTERSPEAK, SKILL_LANG_DRAENEI, 0 }; if(All) { for(uint32 i = 0; skills[i] != 0; ++i) { sk.Reset(skills[i]); sk.MaximumValue = sk.CurrentValue = 300; m_skills.insert( make_pair(skills[i], sk) ); if((spell_id = ::GetSpellForLanguage(skills[i]))) addSpell(spell_id); } } else { for(list::iterator itr = info->skills.begin(); itr != info->skills.end(); ++itr) { en = sSkillLineStore.LookupEntry(itr->skillid); if(en->type == SKILL_TYPE_LANGUAGE) { sk.Reset(itr->skillid); sk.MaximumValue = sk.CurrentValue = 300; m_skills.insert( make_pair(itr->skillid, sk) ); if((spell_id = ::GetSpellForLanguage(itr->skillid))) addSpell(spell_id); } } } _UpdateSkillFields(); } float Player::GetSkillUpChance(uint32 id) { SkillMap::iterator itr = m_skills.find(id); if(itr == m_skills.end()) return 0.0f; return itr->second.GetSkillUpChance(); } void Player::_RemoveAllSkills() { m_skills.clear(); _UpdateSkillFields(); } void Player::_AdvanceAllSkills(uint32 count) { bool dirty=false; for(SkillMap::iterator itr = m_skills.begin(); itr != m_skills.end(); ++itr) { if(itr->second.CurrentValue != itr->second.MaximumValue) { itr->second.CurrentValue += count; if(itr->second.CurrentValue >= itr->second.MaximumValue) itr->second.CurrentValue = itr->second.MaximumValue; dirty=true; } } if(dirty) _UpdateSkillFields(); } void Player::_ModifySkillMaximum(uint32 SkillLine, uint32 NewMax) { SkillMap::iterator itr = m_skills.find(SkillLine); if(itr == m_skills.end()) return; if(NewMax > itr->second.MaximumValue) { if(SkillLine == SKILL_RIDING) itr->second.CurrentValue = NewMax; itr->second.MaximumValue = NewMax; _UpdateSkillFields(); } } /************************************************************************/ /* Spell Packet wharper Please keep this separated */ /************************************************************************/ void Player::SendCastResult(uint32 SpellId, uint8 ErrorMessage, uint32 Extra) { #ifndef USING_BIG_ENDIAN StackWorldPacket<9> data(SMSG_CAST_RESULT); #else WorldPacket data(SMSG_CAST_RESULT, 9); #endif data << SpellId; data << ErrorMessage; if (Extra) data << Extra; GetSession()->SendPacket(&data); } /************************************************************************/ /* End of SpellPacket Wharper */ /************************************************************************/