/* * 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" #include "Unit.h" using namespace std; //#define DEG2RAD (M_PI/180.0) #define M_PI 3.14159265358979323846 Object::Object() : m_position(0,0,0,0), m_spawnLocation(0,0,0,0) { m_mapId = 0; m_zoneId = 0; m_uint32Values = 0; m_objectUpdated = false; m_valuesCount = 0; //official Values m_walkSpeed = 2.5f; m_runSpeed = 7.0f; m_flySpeed = 7.0f; m_backFlySpeed = 4.5f; m_backWalkSpeed = 4.5f; // this should really be named m_backRunSpeed m_swimSpeed = 4.722222f; m_backSwimSpeed = 2.5f; m_turnRate = 3.141593f; m_mapMgr = 0; m_mapCell = 0; mSemaphoreTeleport = false; m_faction = NULL; m_factionDBC = NULL; m_instanceId = 0; Active = false; m_inQueue = false; } Object::~Object( ) { if(m_objectTypeId != TYPEID_ITEM) ASSERT(!m_inQueue); if (this->IsInWorld() && m_objectTypeId != TYPEID_ITEM && m_objectTypeId != TYPEID_CONTAINER) { #ifdef WIN32 CStackWalker ws; OutputCrashLogLine("Object deleted while in world."); ws.ShowCallstack(); #endif this->RemoveFromWorld(); } // for linux m_instanceId = -1; m_objectTypeId=TYPEID_UNUSED; } void Object::_Create( uint32 mapid, float x, float y, float z, float ang ) { m_mapId = mapid; m_position.ChangeCoords(x, y, z, ang); m_spawnLocation.ChangeCoords(x, y, z, ang); } uint32 Object::BuildCreateUpdateBlockForPlayer(ByteBuffer *data, Player *target) { uint8 flags = 0; uint32 flags2 = 0; uint8 updatetype = UPDATETYPE_CREATE_OBJECT; if(m_objectTypeId == TYPEID_CORPSE) { if(m_uint32Values[CORPSE_FIELD_DISPLAY_ID] == 0) return 0; } // any other case switch(m_objectTypeId) { // items + containers: 0x8 case TYPEID_ITEM: case TYPEID_CONTAINER: flags = 0x18; break; // player/unit: 0x68 (except self) case TYPEID_UNIT: flags = 0x70; break; case TYPEID_PLAYER: flags = 0x70; break; // gameobject/dynamicobject case TYPEID_GAMEOBJECT: case TYPEID_DYNAMICOBJECT: case TYPEID_CORPSE: flags = 0x58; break; // anyone else can get fucked and die! } if(target == this) { // player creating self flags |= 0x01; updatetype = UPDATETYPE_CREATE_YOURSELF; } // gameobject stuff if(m_objectTypeId == TYPEID_GAMEOBJECT) { switch(m_uint32Values[GAMEOBJECT_TYPE_ID]) { case GAMEOBJECT_TYPE_MO_TRANSPORT: { if(UINT32_LOPART(GetGUIDHigh()) != HIGHGUID_TRANSPORTER) return 0; // bad transporter else flags = 0x4A; }break; case GAMEOBJECT_TYPE_TRANSPORT: { /* deeprun tram, etc */ flags = 0x5A; }break; case GAMEOBJECT_TYPE_DUEL_ARBITER: { // duel flags have to stay as updatetype 3, otherwise // it won't animate updatetype = UPDATETYPE_CREATE_YOURSELF; }break; } } // build our actual update *data << updatetype; // we shouldn't be here, under any cercumstances, unless we have a wowguid.. ASSERT(m_wowGuid.GetNewGuidLen()); *data << m_wowGuid; *data << m_objectTypeId; _BuildMovementUpdate(data, flags, flags2, target); // we have dirty data, or are creating for ourself. UpdateMask updateMask; updateMask.SetCount( m_valuesCount ); _SetCreateBits( &updateMask, target ); // this will cache automatically if needed _BuildValuesUpdate( data, &updateMask, target ); // update count: 1 ;) return 1; } //That is dirty fix it actually creates update of 1 field with //the given value ignoring existing changes in fields and so on //usefull if we want update this field for certain players //NOTE: it does not change fields. This is also very fast method WorldPacket *Object::BuildFieldUpdatePacket( uint32 index,uint32 value) { // uint64 guidfields = GetGUID(); // uint8 guidmask = 0; WorldPacket * packet=new WorldPacket(1500); packet->SetOpcode( SMSG_UPDATE_OBJECT ); *packet << (uint32)1;//number of update/create blocks *packet << (uint8)0;//unknown *packet << (uint8) UPDATETYPE_VALUES; // update type == update *packet << GetNewGUID(); uint32 mBlocks = index/32+1; *packet << (uint8)mBlocks; for(uint32 dword_n=mBlocks-1;dword_n;dword_n--) *packet <<(uint32)0; *packet <<(((uint32)(1))<<(index%32)); *packet << value; return packet; } void Object::BuildFieldUpdatePacket(Player* Target, uint32 Index, uint32 Value) { ByteBuffer buf(500); buf << uint8(UPDATETYPE_VALUES); buf << GetNewGUID(); uint32 mBlocks = Index/32+1; buf << (uint8)mBlocks; for(uint32 dword_n=mBlocks-1;dword_n;dword_n--) buf <<(uint32)0; buf <<(((uint32)(1))<<(Index%32)); buf << Value; Target->PushUpdateData(&buf, 1); } void Object::BuildFieldUpdatePacket(ByteBuffer * buf, uint32 Index, uint32 Value) { *buf << uint8(UPDATETYPE_VALUES); *buf << GetNewGUID(); uint32 mBlocks = Index/32+1; *buf << (uint8)mBlocks; for(uint32 dword_n=mBlocks-1;dword_n;dword_n--) *buf <<(uint32)0; *buf <<(((uint32)(1))<<(Index%32)); *buf << Value; } uint32 Object::BuildValuesUpdateBlockForPlayer(ByteBuffer *data, Player *target) { // returns: update count *data << (uint8) UPDATETYPE_VALUES; // update type == update ASSERT(m_wowGuid.GetNewGuidLen()); *data << m_wowGuid; UpdateMask updateMask; updateMask.SetCount( m_valuesCount ); _SetUpdateBits( &updateMask, target ); _BuildValuesUpdate( data, &updateMask, target ); // 1 update. return 1; } uint32 Object::BuildValuesUpdateBlockForPlayer(ByteBuffer * buf, UpdateMask * mask ) { // returns: update count *buf << (uint8) UPDATETYPE_VALUES; // update type == update ASSERT(m_wowGuid.GetNewGuidLen()); *buf << m_wowGuid; _BuildValuesUpdate( buf, mask, 0 ); // 1 update. return 1; } void Object::DestroyForPlayer(Player *target) const { if(target->GetSession() == 0) return; ASSERT(target); WorldPacket data(SMSG_DESTROY_OBJECT, 8); data << GetGUID(); target->GetSession()->SendPacket( &data ); } /////////////////////////////////////////////////////////////// /// Build the Movement Data portion of the update packet /// Fills the data with this object's movement/speed info /// TODO: rewrite this stuff, document unknown fields and flags void Object::_BuildMovementUpdate(ByteBuffer * data, uint8 flags, uint32 flags2, Player* target ) { ByteBuffer *splinebuf = (m_objectTypeId == TYPEID_UNIT) ? target->GetAndRemoveSplinePacket(GetGUID()) : 0; *data << (uint8)flags; Player * pThis = 0; if(m_objectTypeId == TYPEID_PLAYER) { pThis = static_cast(this); if(target == this) { // Updating our last speeds. pThis->UpdateLastSpeeds(); } } if (flags & 0x20) { if(pThis && pThis->m_TransporterGUID != 0) flags2 |= 0x200; if(splinebuf) { flags2 |= 0x08000001; //1=move forward if(GetTypeId() == TYPEID_UNIT) { if(static_cast(this)->GetAIInterface()->m_moveRun == false) flags2 |= 0x100; //100=walk } } if(GetTypeId() == TYPEID_UNIT) { // Don't know what this is, but I've only seen it applied to spirit healers. // maybe some sort of invisibility flag? :/ switch(GetEntry()) { case 6491: // Spirit Healer case 13116: // Alliance Spirit Guide case 13117: // Horde Spirit Guide { flags2 |= 0x10000000; }break; } if(static_cast(this)->GetAIInterface()->IsFlying()) flags2 |= 0x800; } *data << (uint32)flags2; *data << getMSTime(); // this appears to be time in ms but can be any thing } if (flags & 0x40) { if(flags & 0x2) { *data << (float)m_position.x; *data << (float)m_position.y; *data << (float)m_position.z; *data << (float)m_position.o; } else { *data << m_position; *data << m_position.o; } if(flags & 0x20 && flags2 & 0x0200) { *data << pThis->m_TransporterGUID; *data << pThis->m_TransporterX << pThis->m_TransporterY << pThis->m_TransporterZ << pThis->m_TransporterO; *data << pThis->m_TransporterUnk; } } if (flags & 0x20) { *data << (uint32)0; } if (flags & 0x20 && flags2 & 0x2000) { *data << (float)0; *data << (float)1.0; *data << (float)0; *data << (float)0; } if (flags & 0x20) { *data << m_walkSpeed; // walk speed *data << m_runSpeed; // run speed *data << m_backWalkSpeed; // backwards walk speed *data << m_swimSpeed; // swim speed *data << m_backSwimSpeed; // backwards swim speed *data << m_flySpeed; // fly speed *data << m_backFlySpeed; // back fly speed *data << m_turnRate; // turn rate } if(splinebuf) { data->append(*splinebuf); delete splinebuf; } if(flags & 0x8) { /* burlex: i don't think this data really matters.. but I'm just gonna use these, since it may help us with debugging later on */ *data << GetInstanceID(); if(flags & 0x10) *data << GetEntry(); } else if(flags & 0x10) *data << GetEntry(); if(flags & 0x2) { *data << getMSTime(); } } //======================================================================================= // Creates an update block with the values of this object as // determined by the updateMask. //======================================================================================= void Object::_BuildValuesUpdate(ByteBuffer * data, UpdateMask *updateMask, Player* target) { bool activate_quest_object = false; bool reset = false; uint32 oldflags = 0; if(updateMask->GetBit(OBJECT_FIELD_GUID) && target) // We're creating. { Creature * pThis = static_cast(this); if(GetTypeId() == TYPEID_UNIT && pThis->Tagged && (pThis->loot.gold || pThis->loot.items.size())) { // Let's see if we're the tagger or not. oldflags = m_uint32Values[UNIT_DYNAMIC_FLAGS]; uint32 Flags = m_uint32Values[UNIT_DYNAMIC_FLAGS]; uint32 oldFlags = 0; if(pThis->TaggerGuid == target->GetGUID()) { // Our target is our tagger. oldFlags = U_DYN_FLAG_TAGGED_BY_OTHER; if(Flags & U_DYN_FLAG_TAGGED_BY_OTHER) Flags &= ~oldFlags; if(!(Flags & U_DYN_FLAG_LOOTABLE)) Flags |= U_DYN_FLAG_LOOTABLE; } else { // Target is not the tagger. oldFlags = U_DYN_FLAG_LOOTABLE; if(!(Flags & U_DYN_FLAG_TAGGED_BY_OTHER)) Flags |= U_DYN_FLAG_TAGGED_BY_OTHER; if(Flags & U_DYN_FLAG_LOOTABLE) Flags &= ~oldFlags; } m_uint32Values[UNIT_DYNAMIC_FLAGS] = Flags; updateMask->SetBit(UNIT_DYNAMIC_FLAGS); reset = true; } if(target && GetTypeId() == TYPEID_GAMEOBJECT) { GameObject *go = ((GameObject*)this); QuestLogEntry *qle; GameObjectInfo *info; if(go->HasQuests()) { activate_quest_object = true; } else { info = go->GetInfo(); if(info && (info->goMap.size() || info->itemMap.size()) ) { for(GameObjectGOMap::iterator itr = go->GetInfo()->goMap.begin(); itr != go->GetInfo()->goMap.end(); ++itr) { if((qle = target->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((qle = target->GetQuestLogForEntry(itr->first->id))) { if(target->GetItemInterface()->GetItemCount(it2->first) < it2->second) { activate_quest_object = true; break; } } } if(activate_quest_object) break; } } } } } } if(activate_quest_object) { oldflags = m_uint32Values[GAMEOBJECT_DYN_FLAGS]; if(!updateMask->GetBit(GAMEOBJECT_DYN_FLAGS)) updateMask->SetBit(GAMEOBJECT_DYN_FLAGS); m_uint32Values[GAMEOBJECT_DYN_FLAGS] = 1; reset = true; } WPAssert(updateMask && updateMask->GetCount() == m_valuesCount); uint32 bc; uint32 values_count; if(m_valuesCount>(2*0x20))//if number of blocks > 2-> unit and player+item container { bc=updateMask->GetUpdateBlockCount(); values_count=min(bc*32,m_valuesCount); }else { bc=updateMask->GetBlockCount(); values_count=m_valuesCount; } *data << (uint8)bc; data->append( updateMask->GetMask(), bc*4 ); for( uint32 index = 0; index < values_count; index ++ ) { if( updateMask->GetBit( index ) ) { switch(index) { case UNIT_FIELD_MAXHEALTH: { if(m_valuesCount < UNIT_END) *data << m_uint32Values[index]; else { switch(m_objectTypeId) { case TYPEID_PLAYER: *data << m_uint32Values[index]; break; case TYPEID_UNIT: { if(IsPet()) { *data << m_uint32Values[index]; break; } else { *data << (uint32)100; } } } } } break; case UNIT_FIELD_HEALTH: { if(m_valuesCount < UNIT_END) *data << m_uint32Values[index]; else { switch(m_objectTypeId) { case TYPEID_PLAYER: *data << m_uint32Values[index]; break; case TYPEID_UNIT: { if(IsPet()) { *data << m_uint32Values[index]; break; } else { uint32 pct = uint32(float( float(m_uint32Values[index]) / float(m_uint32Values[UNIT_FIELD_MAXHEALTH]) * 100.0f)); /* fix case where health value got rounded down and the client sees health as dead */ if(!pct && m_uint32Values[UNIT_FIELD_HEALTH] != 0) ++pct; *data << pct; } } } } } break; default: *data << m_uint32Values[ index ]; break; } } } if(reset) { switch(GetTypeId()) { case TYPEID_UNIT: m_uint32Values[UNIT_DYNAMIC_FLAGS] = oldflags; break; case TYPEID_GAMEOBJECT: m_uint32Values[GAMEOBJECT_DYN_FLAGS] = oldflags; break; } } } void Object::BuildHeartBeatMsg(WorldPacket *data) const { data->Initialize(MSG_MOVE_HEARTBEAT); *data << GetGUID(); *data << uint32(0); // flags *data << uint32(0); // mysterious value #1 *data << m_position; *data << m_position.o; } WorldPacket * Object::BuildTeleportAckMsg(const LocationVector & v) { /////////////////////////////////////// //Update player on the client with TELEPORT_ACK ((Player*)(this))->SetPlayerStatus(TRANSFER_PENDING); WorldPacket * data = new WorldPacket(MSG_MOVE_TELEPORT_ACK, 50); *data << GetNewGUID(); //First 4 bytes = no idea what it is *data << uint32(5); // flags *data << uint32(0x010); // mysterious value #1 *data << float(0); *data << v; *data << v.o; *data << uint32(0x0); return data; } bool Object::SetPosition(const LocationVector & v, bool allowPorting /* = false */) { bool updateMap = false, result = true; if (m_position.x != v.x || m_position.y != v.y) updateMap = true; m_position = const_cast(v); if (!allowPorting && v.z < -500) { m_position.z = 500; sLog.outError( "setPosition: fell through map; height ported" ); result = false; } if (IsInWorld() && updateMap) { m_mapMgr->ChangeObjectLocation(this); } return result; } bool Object::SetPosition( float newX, float newY, float newZ, float newOrientation, bool allowPorting ) { bool updateMap = false, result = true; if (m_position.x != newX || m_position.y != newY) updateMap = true; m_position.ChangeCoords(newX, newY, newZ, newOrientation); if (!allowPorting && newZ < -500) { m_position.z = 500; sLog.outError( "setPosition: fell through map; height ported" ); result = false; } if (IsInWorld() && updateMap) { m_mapMgr->ChangeObjectLocation(this); if(m_objectTypeId == TYPEID_PLAYER && ((Player*)this)->GetGroup() && ((Player*)this)->m_last_group_position.Distance2DSq(m_position) > 25.0f) // distance of 5.0 { ((Player*)this)->GetGroup()->HandlePartialChange(PARTY_UPDATE_FLAG_POSITION, ((Player*)this)); } } return result; } void Object::SetRotation( uint64 guid ) { WorldPacket data(SMSG_AI_REACTION, 12); data << guid; data << uint32(2); SendMessageToSet(&data, false); } void Object::OutPacketToSet(uint16 Opcode, uint16 Len, const void * Data, bool self) { if(self && m_objectTypeId == TYPEID_PLAYER) static_cast(this)->GetSession()->OutPacket(Opcode, Len, Data); if(!IsInWorld()) return; std::set::iterator itr = m_inRangePlayers.begin(); std::set::iterator it_end = m_inRangePlayers.end(); int gm = (m_objectTypeId == TYPEID_PLAYER ? ((Player*)this)->m_isGmInvisible : 0); for(; itr != it_end; ++itr) { ASSERT((*itr)->GetSession()); if(gm) { if((*itr)->GetSession()->GetPermissionCount() > 0) (*itr)->GetSession()->OutPacket(Opcode, Len, Data); } else { (*itr)->GetSession()->OutPacket(Opcode, Len, Data); } } } inline void Object::SendMessageToSet(WorldPacket *data, bool bToSelf,bool myteam_only) { if(bToSelf && m_objectTypeId == TYPEID_PLAYER) { static_cast(this)->GetSession()->SendPacket(data); } if(!IsInWorld()) return; std::set::iterator itr = m_inRangePlayers.begin(); std::set::iterator it_end = m_inRangePlayers.end(); bool gminvis = (m_objectTypeId == TYPEID_PLAYER ? ((Player*)this)->m_isGmInvisible : false); //Zehamster: Splitting into if/else allows us to avoid testing "gminvis==true" at each loop... // saving cpu cycles. Chat messages will be sent to everybody even if player is invisible. if(myteam_only) { uint32 myteam=static_cast(this)->GetTeam(); if(gminvis && data->GetOpcode()!=SMSG_MESSAGECHAT) { for(; itr != it_end; ++itr) { ASSERT((*itr)->GetSession()); if((*itr)->GetSession()->GetPermissionCount() > 0 && (*itr)->GetTeam()==myteam) (*itr)->GetSession()->SendPacket(data); } } else { for(; itr != it_end; ++itr) { ASSERT((*itr)->GetSession()); if((*itr)->GetTeam()==myteam) (*itr)->GetSession()->SendPacket(data); } } } else { if(gminvis && data->GetOpcode()!=SMSG_MESSAGECHAT) { for(; itr != it_end; ++itr) { ASSERT((*itr)->GetSession()); if((*itr)->GetSession()->GetPermissionCount() > 0) (*itr)->GetSession()->SendPacket(data); } } else { for(; itr != it_end; ++itr) { ASSERT((*itr)->GetSession()); (*itr)->GetSession()->SendPacket(data); } } } } //////////////////////////////////////////////////////////////////////////// /// Fill the object's Update Values from a space deliminated list of values. void Object::LoadValues(const char* data) { // thread-safe ;) strtok is not. std::string ndata = data; std::string::size_type last_pos = 0, pos = 0; uint32 index = 0; uint32 val; do { // prevent overflow if(index >= m_valuesCount) { break; } pos = ndata.find(" ", last_pos); val = atol(ndata.substr(last_pos, (pos-last_pos)).c_str()); if(m_uint32Values[index] == 0) m_uint32Values[index] = val; last_pos = pos+1; ++index; } while(pos != std::string::npos); } void Object::_SetUpdateBits(UpdateMask *updateMask, Player *target) const { *updateMask = m_updateMask; } void Object::_SetCreateBits(UpdateMask *updateMask, Player *target) const { /*for( uint16 index = 0; index < m_valuesCount; index++ ) { if(GetUInt32Value(index) != 0) updateMask->SetBit(index); }*/ for(uint32 i = 0; i < m_valuesCount; ++i) if(m_uint32Values[i] != 0) updateMask->SetBit(i); } void Object::AddToWorld() { MapMgr *mapMgr = sWorldCreator.GetInstance(m_mapId, this); if(!mapMgr) return; //instance add failed m_mapMgr = mapMgr; m_inQueue = true; mapMgr->AddObject(this); // correct incorrect instance id's m_instanceId = m_mapMgr->GetInstanceID(); mSemaphoreTeleport = false; } //Unlike addtoworld it pushes it directly ignoring add pool //this can only be called from the thread of mapmgr!!! void Object::PushToWorld(MapMgr*mgr) { if(!mgr) return; //instance add failed m_mapId=mgr->GetMapId(); m_instanceId = mgr->GetInstanceID(); m_mapMgr = mgr; mgr->PushObject(this); // correct incorrect instance id's mSemaphoreTeleport = false; m_inQueue = false; event_Relocate(); // call virtual function to handle stuff.. :P OnPushToWorld(); } void Object::RemoveFromWorld() { ASSERT(m_mapMgr); MapMgr * m = m_mapMgr; m_mapMgr = 0; mSemaphoreTeleport = true; m->RemoveObject(this); // update our event holder event_Relocate(); } //! Set uint32 property void Object::SetUInt32Value( const uint32 index, const uint32 value ) { ASSERT( index < m_valuesCount ); // save updating when val isn't changing. if(m_uint32Values[index] == value) return; m_uint32Values[ index ] = value; if(IsInWorld()) { m_updateMask.SetBit( index ); if(!m_objectUpdated) { m_mapMgr->ObjectUpdated(this); m_objectUpdated = true; } } // Group update handling if(m_objectTypeId == TYPEID_PLAYER) { Group * pGroup = ((Player*)this)->GetGroup(); if(pGroup) pGroup->HandleUpdateFieldChange(index, ((Player*)this)); } } /* //must be in % void Object::ModPUInt32Value(const uint32 index, const int32 value, bool apply ) { ASSERT( index < m_valuesCount ); int32 basevalue = (int32)m_uint32Values[ index ]; if(apply) m_uint32Values[ index ] += ((basevalue*value)/100); else m_uint32Values[ index ] = (basevalue*100)/(100+value); if(IsInWorld()) { m_updateMask.SetBit( index ); if(!m_objectUpdated ) { m_mapMgr->ObjectUpdated(this); m_objectUpdated = true; } } } */ uint32 Object::GetModPUInt32Value(const uint32 index, const int32 value) { ASSERT( index < m_valuesCount ); int32 basevalue = (int32)m_uint32Values[ index ]; return ((basevalue*value)/100); } void Object::ModUInt32Value(uint32 index, int32 value ) { ASSERT( index < m_valuesCount ); if(value == 0) return; m_uint32Values[ index ] += value; if(IsInWorld()) { m_updateMask.SetBit( index ); if(!m_objectUpdated) { m_mapMgr->ObjectUpdated(this); m_objectUpdated = true; } } } void Object::ModPFloatValue(const uint32 index, const float value, bool apply) { ASSERT( index < m_valuesCount ); float basevalue = m_floatValues[ index ]; if(apply) m_floatValues[ index ] += ((basevalue*value)/100); else m_floatValues[ index ] = (basevalue*100)/(100+value); if(IsInWorld()) { m_updateMask.SetBit( index ); if(!m_objectUpdated ) { m_mapMgr->ObjectUpdated(this); m_objectUpdated = true; } } } void Object::ModFloatValue(const uint32 index, const float value ) { ASSERT( index < m_valuesCount ); m_floatValues[ index ] += value; if(IsInWorld()) { m_updateMask.SetBit( index ); if(!m_objectUpdated) { m_mapMgr->ObjectUpdated(this); m_objectUpdated = true; } } } //! Set uint64 property void Object::SetUInt64Value( const uint32 index, const uint64 value ) { assert( index + 1 < m_valuesCount ); #ifndef USING_BIG_ENDIAN if(m_uint32Values[index] == GUID_LOPART(value) && m_uint32Values[index+1] == GUID_HIPART(value)) return; m_uint32Values[ index ] = *((uint32*)&value); m_uint32Values[ index + 1 ] = *(((uint32*)&value) + 1); #else m_uint32Values[index] = value & 0xffffffff; m_uint32Values[index+1] = (value >> 32) & 0xffffffff; #endif if(IsInWorld()) { m_updateMask.SetBit( index ); m_updateMask.SetBit( index + 1 ); if(!m_objectUpdated) { m_mapMgr->ObjectUpdated(this); m_objectUpdated = true; } } } //! Set float property void Object::SetFloatValue( const uint32 index, const float value ) { ASSERT( index < m_valuesCount ); if(m_floatValues[index] == value) return; m_floatValues[ index ] = value; if(IsInWorld()) { m_updateMask.SetBit( index ); if(!m_objectUpdated) { m_mapMgr->ObjectUpdated(this); m_objectUpdated = true; } } } void Object::SetFlag( const uint32 index, uint32 newFlag ) { ASSERT( index < m_valuesCount ); //no change -> no update if((m_uint32Values[ index ] & newFlag)==newFlag) return; m_uint32Values[ index ] |= newFlag; if(IsInWorld()) { m_updateMask.SetBit( index ); if(!m_objectUpdated) { m_mapMgr->ObjectUpdated(this); m_objectUpdated = true; } } } void Object::RemoveFlag( const uint32 index, uint32 oldFlag ) { ASSERT( index < m_valuesCount ); //no change -> no update if((m_uint32Values[ index ] & oldFlag)==0) return; m_uint32Values[ index ] &= ~oldFlag; if(IsInWorld()) { m_updateMask.SetBit( index ); if(!m_objectUpdated) { m_mapMgr->ObjectUpdated(this); m_objectUpdated = true; } } } //////////////////////////////////////////////////////////// float Object::CalcDistance(Object *Ob) { return CalcDistance(this->GetPositionX(), this->GetPositionY(), this->GetPositionZ(), Ob->GetPositionX(), Ob->GetPositionY(), Ob->GetPositionZ()); } float Object::CalcDistance(float ObX, float ObY, float ObZ) { return CalcDistance(this->GetPositionX(), this->GetPositionY(), this->GetPositionZ(), ObX, ObY, ObZ); } float Object::CalcDistance(Object *Oa, Object *Ob) { return CalcDistance(Oa->GetPositionX(), Oa->GetPositionY(), Oa->GetPositionZ(), Ob->GetPositionX(), Ob->GetPositionY(), Ob->GetPositionZ()); } float Object::CalcDistance(Object *Oa, float ObX, float ObY, float ObZ) { return CalcDistance(Oa->GetPositionX(), Oa->GetPositionY(), Oa->GetPositionZ(), ObX, ObY, ObZ); } float Object::CalcDistance(float OaX, float OaY, float OaZ, float ObX, float ObY, float ObZ) { float xdest = OaX - ObX; float ydest = OaY - ObY; float zdest = OaZ - ObZ; return sqrtf(zdest*zdest + ydest*ydest + xdest*xdest); } float Object::calcAngle( float Position1X, float Position1Y, float Position2X, float Position2Y ) { float dx = Position2X-Position1X; float dy = Position2Y-Position1Y; float angle=0.0f; // Calculate angle if (dx == 0.0) { if (dy == 0.0) angle = 0.0; else if (dy > 0.0) angle = M_PI * 0.5 /* / 2 */; else angle = M_PI * 3.0 * 0.5/* / 2 */; } else if (dy == 0.0) { if (dx > 0.0) angle = 0.0; else angle = M_PI; } else { if (dx < 0.0) angle = atanf(dy/dx) + M_PI; else if (dy < 0.0) angle = atanf(dy/dx) + (2*M_PI); else angle = atanf(dy/dx); } // Convert to degrees angle = angle * float(180 / M_PI); // Return return angle; } float Object::calcRadAngle( float Position1X, float Position1Y, float Position2X, float Position2Y ) { float dx = Position2X-Position1X; float dy = Position2Y-Position1Y; float angle=0.0f; // Calculate angle if (dx == 0.0) { if (dy == 0.0) angle = 0.0; else if (dy > 0.0) angle = M_PI * 0.5/*/ 2.0*/; else angle = M_PI * 3.0 * 0.5/*/ 2.0*/; } else if (dy == 0.0) { if (dx > 0.0) angle = 0.0; else angle = M_PI; } else { if (dx < 0.0) angle = atanf(dy/dx) + M_PI; else if (dy < 0.0) angle = atanf(dy/dx) + (2*M_PI); else angle = atanf(dy/dx); } // Return return angle; } float Object::getEasyAngle( float angle ) { while ( angle < 0 ) { angle = angle + 360; } while ( angle >= 360 ) { angle = angle - 360; } return angle; } bool Object::inArc(float Position1X, float Position1Y, float FOV, float Orientation, float Position2X, float Position2Y ) { float angle = calcAngle( Position1X, Position1Y, Position2X, Position2Y ); float lborder = getEasyAngle( ( Orientation - (FOV*0.5/*/2*/) ) ); float rborder = getEasyAngle( ( Orientation + (FOV*0.5/*/2*/) ) ); //sLog.outDebug("Orientation: %f Angle: %f LeftBorder: %f RightBorder %f",Orientation,angle,lborder,rborder); if(((angle >= lborder) && (angle <= rborder)) || ((lborder > rborder) && ((angle < rborder) || (angle > lborder)))) { return true; } else { return false; } } bool Object::isInFront(Object* target) { float dx=target->GetPositionX()-GetPositionX(); float dy=target->GetPositionY()-GetPositionY(); float d = m_position.o; while(d < 0) d+=2*M_PI; while(d > 2*M_PI) d-=2*M_PI; m_position.o = d; if(dy>=0.0) { d-=atan(dy/dx); if(dx<0.0) d-=M_PI; } else { d-=3*M_PI/2 - atan(dx/dy); } if(d < -M_PI/2 || d > M_PI/2) return false; return true; } bool Object::isInBack(Object* target) { float dx=target->GetPositionX()-GetPositionX(); float dy=target->GetPositionY()-GetPositionY(); float d=target->GetOrientation(); while(d < 0) d+=2*M_PI; while(d > 2*M_PI) d-=2*M_PI; if(dy>=0.0) { d-=atan(dy/dx); if(dx<0.0) d-=M_PI; } else { d-=3*M_PI/2 - atan(dx/dy); } if(d < -M_PI/4 || d > M_PI/4) return false; return true; } bool Object::isInRange(Object* target, float range) { float dist = CalcDistance(target); return (dist <= range); } bool Object::IsPet() { if(this->GetTypeId()!=TYPEID_UNIT) return false; if(((Unit*)this)->m_isPet && m_uint32Values[UNIT_FIELD_CREATEDBY] != 0 && m_uint32Values[UNIT_FIELD_SUMMONEDBY] != 0) return true; return false; } void Object::_setFaction() { FactionTemplateDBC* factT = NULL; if(GetTypeId() == TYPEID_UNIT || GetTypeId() == TYPEID_PLAYER) { factT = sFactionTmpStore.LookupEntry(GetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE)); } else if(GetTypeId() == TYPEID_GAMEOBJECT) { factT = sFactionTmpStore.LookupEntry(GetUInt32Value(GAMEOBJECT_FACTION)); } if(!factT) { return; } m_faction = factT; m_factionDBC = sFactionStore.LookupEntry(factT->Faction); } void Object::UpdateOppFactionSet() { m_oppFactsInRange.clear(); for(Object::InRangeSet::iterator i = GetInRangeSetBegin(); i != GetInRangeSetEnd(); ++i) { if (((*i)->GetTypeId() == TYPEID_UNIT) || ((*i)->GetTypeId() == TYPEID_PLAYER) || ((*i)->GetTypeId() == TYPEID_GAMEOBJECT)) { if (isHostile(this, (*i))) { if(!(*i)->IsInRangeOppFactSet(this)) (*i)->m_oppFactsInRange.insert(this); if (!IsInRangeOppFactSet((*i))) m_oppFactsInRange.insert((*i)); } else { if((*i)->IsInRangeOppFactSet(this)) (*i)->m_oppFactsInRange.erase(this); if (IsInRangeOppFactSet((*i))) m_oppFactsInRange.erase((*i)); } } } } void Object::DealDamage(Unit *pVictim, uint32 damage, uint32 targetEvent, uint32 unitEvent, uint32 spellId, bool no_remove_auras) { Player * plr = 0; if(!pVictim || !pVictim->isAlive() || !pVictim->IsInWorld() || !IsInWorld()) return; if(pVictim->GetTypeId() == TYPEID_PLAYER && static_cast(pVictim)->GodModeCheat == true) return; if(pVictim->IsSpiritHealer()) return; if( pVictim->GetStandState())//not standing-> standup { pVictim->SetStandState(STANDSTATE_STAND);//probably mobs also must standup } // This one is easy. If we're attacking a hostile target, and we're not flagged, flag us. // Also, you WONT get flagged if you are dueling that person - FiShBaIt if(pVictim->IsPlayer() && IsPlayer() && static_cast(pVictim)->DuelingWith != static_cast(this)) { if( isHostile( this, pVictim ) ) ((Player*)this)->SetPvPFlag(); } if(!no_remove_auras) { //zack 2007 04 24 : root should not remove self (and also other unknown spells) if(spellId) { pVictim->RemoveAurasByInterruptFlagButSkip(AURA_INTERRUPT_ON_ANY_DAMAGE_TAKEN,spellId); if(Rand(35.0f)) pVictim->RemoveAurasByInterruptFlagButSkip(AURA_INTERRUPT_ON_UNUSED2,spellId); } else { pVictim->RemoveAurasByInterruptFlag(AURA_INTERRUPT_ON_ANY_DAMAGE_TAKEN); if(Rand(35.0f)) pVictim->RemoveAurasByInterruptFlag(AURA_INTERRUPT_ON_UNUSED2); } } if(this->IsUnit()) { if(!pVictim->isInCombat() && pVictim->IsPlayer()) sHookInterface.OnEnterCombat((Player*)pVictim, ((Unit*)this)); if(IsPlayer() && !((Player*)this)->isInCombat()) sHookInterface.OnEnterCombat(((Player*)this), ((Player*)this)); plr = 0; if(IsPet()) plr = static_cast(this)->GetPetOwner(); else if(IsPlayer()) plr = static_cast(this); if(pVictim->GetTypeId()==TYPEID_UNIT && plr && plr->GetTypeId() == TYPEID_PLAYER) // Units can't tag.. { // Tagging Creature *victim = static_cast(pVictim); bool taggable; if(victim->GetCreatureName() && victim->GetCreatureName()->Type == CRITTER) taggable = false; else taggable = true; if(!victim->Tagged && taggable) { victim->Tagged = true; victim->TaggerGuid = plr->GetGUID(); // For new players who get a create object uint32 Flags = pVictim->m_uint32Values[UNIT_DYNAMIC_FLAGS]; Flags |= U_DYN_FLAG_TAPPED_BY_PLAYER; pVictim->m_uint32Values[UNIT_DYNAMIC_FLAGS] |= U_DYN_FLAG_TAGGED_BY_OTHER; // Update existing players. ByteBuffer buf(500); ByteBuffer buf1(500); pVictim->BuildFieldUpdatePacket(&buf1, UNIT_DYNAMIC_FLAGS, Flags); pVictim->BuildFieldUpdatePacket(&buf, UNIT_DYNAMIC_FLAGS, pVictim->m_uint32Values[UNIT_DYNAMIC_FLAGS]); // Loop inrange set, append to their update data. for(std::set::iterator itr = m_inRangePlayers.begin(); itr != m_inRangePlayers.end(); ++itr) { if (static_cast(plr)->InGroup()) { if (static_cast(*itr)->GetGroup() && static_cast(plr)->GetGroup()->GetID() == static_cast(*itr)->GetGroup()->GetID()) { (*itr)->PushUpdateData(&buf1, 1); } else { (*itr)->PushUpdateData(&buf, 1); } } else { (*itr)->PushUpdateData(&buf, 1); } } // Update ourselves plr->PushUpdateData(&buf1, 1); } } if(pVictim->IsUnit()) { // Set our attack target to the victim. static_cast(this)->setAttackTarget(pVictim); } } // Awake sleeping Creatures. if( damage && pVictim->m_sleep) { pVictim->RemoveAura(pVictim->m_sleep); } // break entangling roots /* if( damage && pVictim->m_rooted) { if(Rand(35.0f)) // 35% chance to break the roots pVictim->RemoveAura(pVictim->m_rooted); } */ ///Rage uint32 val; if(pVictim->GetPowerType() == POWER_TYPE_RAGE && !spellId && pVictim != this) { if(pVictim->IsPlayer() && pVictim->isInCombat()) { val = pVictim->GetUInt32Value(UNIT_FIELD_POWER2)+(damage*20)/(pVictim->getLevel()*3); pVictim->SetUInt32Value(UNIT_FIELD_POWER2, val>=1000?1000:val); } } // if(pVictim->IsPlayer()) { Player *pThis = static_cast(pVictim); if(pThis->cannibalize) { sEventMgr.RemoveEvents(pVictim, EVENT_CANNIBALIZE); pThis->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); pThis->cannibalize = false; } } //* BATTLEGROUND DAMAGE COUNTER *// if(pVictim != this) { if(IsPlayer()) plr = ((Player*)this); else if(IsPet()) plr = ((Pet*)this)->GetPetOwner(); if(plr && plr->m_bg) { plr->m_bgScore.DamageDone += damage; plr->m_bg->UpdatePvPData(); } } uint32 health = pVictim->GetUInt32Value(UNIT_FIELD_HEALTH ); /*------------------------------------ DUEL HANDLERS --------------------------*/ if((pVictim->IsPlayer()) && (this->IsPlayer()) && static_cast(pVictim)->DuelingWith == static_cast(this) ) //Both Players { if((health <= damage) && static_cast(this)->DuelingWith != NULL) { //Player in Duel and Player Victim has lost uint32 NewHP = pVictim->GetUInt32Value(UNIT_FIELD_MAXHEALTH)/100; if(NewHP < 5) NewHP = 5; //Set there health to 1% or 5 if 1% is lower then 5 pVictim->SetUInt32Value(UNIT_FIELD_HEALTH, NewHP); //End Duel static_cast(this)->EndDuel(DUEL_WINNER_KNOCKOUT); // surrender emote pVictim->Emote(EMOTE_ONESHOT_BEG); // Animation return; } } if((pVictim->IsPlayer()) && (IsPet())) { if((health <= damage) && static_cast(pVictim)->DuelingWith == static_cast(this)->GetPetOwner()) { Player *petOwner = static_cast(this)->GetPetOwner(); if(petOwner) { //Player in Duel and Player Victim has lost uint32 NewHP = pVictim->GetUInt32Value(UNIT_FIELD_MAXHEALTH)/100; if(NewHP < 5) NewHP = 5; //Set there health to 1% or 5 if 1% is lower then 5 pVictim->SetUInt32Value(UNIT_FIELD_HEALTH, NewHP); //End Duel petOwner->EndDuel(DUEL_WINNER_KNOCKOUT); return; } } } /*------------------------------------ DUEL HANDLERS END--------------------------*/ bool isCritter = false; if(pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->GetCreatureName()) { if(((Creature*)pVictim)->GetCreatureName()->Type == CRITTER) isCritter = true; } /* -------------------------- HIT THAT CAUSES VICTIM TO DIE ---------------------------*/ if ((isCritter || health <= damage) ) { //warlock - seed of corruption pVictim->HandleProc(PROC_ON_DIE,pVictim,NULL); pVictim->m_procCounter = 0; /* victim died! */ if(pVictim->IsPlayer()) ((Player*)pVictim)->KillPlayer(); else pVictim->setDeathState(JUST_DIED); if(pVictim->IsPlayer() && (!IsPlayer() || pVictim == this)) { ((Player*)pVictim)->DeathDurabilityLoss(0.10); } /* Zone Under Attack */ MapInfo * pMapInfo = WorldMapInfoStorage.LookupEntry(GetMapId()); if(pMapInfo && pMapInfo->type == INSTANCE_NULL && !pVictim->IsPlayer() && !pVictim->IsPet() && (IsPlayer() || IsPet())) { // Only NPCs that bear the PvP flag can be truly representing their faction. if(((Creature*)pVictim)->HasFlag(UNIT_FIELD_FLAGS, U_FIELD_FLAG_PVP)) { Player * pAttacker = NULL; if(IsPet() && GetGUIDHigh() == HIGHGUID_PET) pAttacker = ((Pet*)this)->GetPetOwner(); else if(IsPlayer()) pAttacker = ((Player*)this); if(pAttacker) { uint8 teamId = (uint8)pAttacker->GetTeam(); if(teamId == 0) // Swap it. teamId = 1; else teamId = 0; uint32 AreaID = pVictim->GetMapMgr()->GetAreaID(pVictim->GetPositionX(), pVictim->GetPositionY()); if(!AreaID) AreaID = pAttacker->GetZoneId(); // Failsafe for a shitty TerrainMgr if(AreaID) { WorldPacket data(SMSG_ZONE_UNDER_ATTACK, 4); data << AreaID; sWorld.SendFactionMessage(&data, teamId); } } } } if(pVictim->GetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT) > 0) { if(pVictim->GetCurrentSpell()) { Spell *spl = pVictim->GetCurrentSpell(); for(int i = 0; i < 3; i++) { if(spl->m_spellInfo->Effect[i] == SPELL_EFFECT_PERSISTENT_AREA_AURA) { DynamicObject *dObj = GetMapMgr()->GetDynamicObject(pVictim->GetUInt32Value(UNIT_FIELD_CHANNEL_OBJECT)); if(!dObj) return; WorldPacket data(SMSG_GAMEOBJECT_DESPAWN_ANIM, 8); data << dObj->GetGUID(); dObj->SendMessageToSet(&data, false); dObj->RemoveFromWorld(); delete dObj; } } if(spl->m_spellInfo->ChannelInterruptFlags == 48140) spl->cancel(); } } /* Remove all Auras */ pVictim->DropAurasOnDeath(); /* Stop victim from attacking */ if(this->IsUnit()) pVictim->smsg_AttackStop(((Unit*)this)); if(pVictim->GetTypeId() == TYPEID_PLAYER) ((Player*)pVictim)->EventAttackStop(); /* Set victim health to 0 */ pVictim->SetUInt32Value(UNIT_FIELD_HEALTH, 0); if(pVictim->IsPlayer()) { uint32 self_res_spell = ((Player*)pVictim)->SoulStone; ((Player*)pVictim)->SoulStone = ((Player*)pVictim)->SoulStoneReciever = 0; if(!self_res_spell && ((Player*)pVictim)->bReincarnation) { SpellEntry *m_reincarnSpellInfo = sSpellStore.LookupEntry(20608); if(((Player*)pVictim)->CanCastDueToCooldown(m_reincarnSpellInfo)) { uint32 ankh_count = ((Player*)pVictim)->GetItemInterface()->GetItemCount(17030); if(ankh_count) self_res_spell = 21169; } } pVictim->SetUInt32Value(PLAYER_SELF_RES_SPELL, self_res_spell); pVictim->SetUInt32Value( UNIT_FIELD_MOUNTDISPLAYID , 0); pVictim->RemoveFlag( UNIT_FIELD_FLAGS , U_FIELD_FLAG_MOUNT_SIT ); } // Wipe our attacker set on death pVictim->clearAttackers(true); // sent to set. don't send it to the party, becuase if they're out of // range they won't know this guid exists -> possible 132. /*if (this->IsPlayer()) if (((Player*)this)->InGroup()) ((Player*)this)->GetGroup()->SendPartyKillLog(this,pVictim);*/ /* Stop Unit from attacking */ if(this->IsPlayer()) ((Player*)this)->EventAttackStop(); if(this->IsUnit()) { CALL_SCRIPT_EVENT(this, OnTargetDied)(pVictim); ScriptSystem->OnCreatureEvent(((Creature*)this), pVictim, CREATURE_EVENT_ON_KILLED_TARGET); ((Unit*)this)->smsg_AttackStop(pVictim); /* Tell Unit that it's target has Died */ ((Unit*)this)->addStateFlag(UF_TARGET_DIED); // We will no longer be attacking this target, as it's dead. static_cast(this)->setAttackTarget(NULL); } //so now we are completely dead //lets see if we have spirit of redemption if(pVictim->IsPlayer()) { if(((Player*)pVictim)->HasSpell(20711)) //check for spirit of Redemption { SpellEntry * sorInfo = sSpellStore.LookupEntry(27827); if(sorInfo) { Spell *sor = new Spell(pVictim, sorInfo, true, NULL); SpellCastTargets targets; targets.m_unitTarget = pVictim->GetGUID(); sor->prepare(&targets); } } } /* -------------------------------- HONOR + BATTLEGROUND CHECKS ------------------------ */ plr = 0; if(IsPlayer()) plr = ((Player*)this); else if(IsPet()) plr = ((Pet*)this)->GetPetOwner(); if(plr) { if(plr->m_bg != 0) plr->m_bg->HookOnPlayerKill(plr, pVictim); if(pVictim->IsPlayer()) { sHookInterface.OnKillPlayer(plr, ((Player*)pVictim)); if(plr->getLevel() > pVictim->getLevel()) { unsigned int diff = plr->getLevel() - pVictim->getLevel(); if(diff <= 8) { HonorHandler::OnPlayerKilledUnit(plr, pVictim); SetFlag(UNIT_FIELD_AURASTATE,AURASTATE_FLAG_LASTKILLWITHHONOR); } else RemoveFlag(UNIT_FIELD_AURASTATE,AURASTATE_FLAG_LASTKILLWITHHONOR); } else { HonorHandler::OnPlayerKilledUnit(plr, pVictim); SetFlag(UNIT_FIELD_AURASTATE,AURASTATE_FLAG_LASTKILLWITHHONOR); } } else { // REPUTATION plr->Reputation_OnKilledUnit(pVictim, false); RemoveFlag(UNIT_FIELD_AURASTATE,AURASTATE_FLAG_LASTKILLWITHHONOR); } } /* -------------------------------- HONOR + BATTLEGROUND CHECKS END------------------------ */ uint64 victimGuid = pVictim->GetGUID(); if(pVictim->GetTypeId() == TYPEID_UNIT) { pVictim->GetAIInterface()->OnDeath(this); if(GetTypeId() == TYPEID_PLAYER) { WorldPacket data(SMSG_PARTYKILLLOG, 16); data << GetGUID() << pVictim->GetGUID(); SendMessageToSet(&data, true); } // it Seems that pets some how dont get a name and cause a crash here //bool isCritter = (pVictim->GetCreatureName() != NULL)? pVictim->GetCreatureName()->Type : 0; //-----------------------------------LOOOT-------------------------------------------- if ((!pVictim->IsPet())&& ( !isCritter )) { Creature * victim = static_cast(pVictim); // fill loot vector. victim->generateLoot(); Player *owner = 0; if(victim->TaggerGuid) owner = GetMapMgr()->GetPlayer(victim->TaggerGuid); if(owner == 0) // no owner { // donno why this would happen, but anyway.. anyone can loot ;p // no owner no loot //victim->SetFlag(UNIT_DYNAMIC_FLAGS, U_DYN_FLAG_LOOTABLE); } else { // Build the actual update. ByteBuffer buf(500); uint32 Flags = victim->m_uint32Values[UNIT_DYNAMIC_FLAGS]; Flags |= U_DYN_FLAG_LOOTABLE; victim->BuildFieldUpdatePacket(&buf, UNIT_DYNAMIC_FLAGS, Flags); // Check for owner's group. Group * pGroup = owner->GetGroup(); if(pGroup != 0) { // Owner was in a party. // Check loot method. switch(pGroup->GetMethod()) { case PARTY_LOOT_RR: /* //this commented code is not used because it was never tested and finished ! { //get new tagger for creature Player *tp = pGroup->GetnextRRlooter(); if(tp) { //we force on creature a new tagger victim->TaggerGuid = tp->GetGUID(); victim->Tagged = true; if(tp->IsVisible(victim)) // Save updates for non-existant creatures tp->PushUpdateData(&buf, 1); } }break;*/ case PARTY_LOOT_FFA: case PARTY_LOOT_GROUP: case PARTY_LOOT_NBG: { // Loop party players and push update data. GroupMembersSet::iterator itr; SubGroup * sGrp; pGroup->Lock(); for(uint32 Index = 0; Index < pGroup->GetSubGroupCount(); ++Index) { sGrp = pGroup->GetSubGroup(Index); itr = sGrp->GetGroupMembersBegin(); for(; itr != sGrp->GetGroupMembersEnd(); ++itr) { if(itr->player && itr->player->IsVisible(victim)) // Save updates for non-existant creatures itr->player->PushUpdateData(&buf, 1); } } pGroup->Unlock(); }break; case PARTY_LOOT_MASTER: { // Master loot: only the loot master gets the update. Player * pLooter = pGroup->GetLooter(); if(pLooter == 0) pLooter = pGroup->GetLeader(); if(pLooter->IsVisible(victim)) // Save updates for non-existant creatures pLooter->PushUpdateData(&buf, 1); }break; } } else { // Owner killed the mob solo. if(owner->IsVisible(victim)) owner->PushUpdateData(&buf, 1); } } } //---------------------------------looot----------------------------------------- if (GetTypeId() == TYPEID_PLAYER && pVictim->GetUInt64Value(UNIT_FIELD_CREATEDBY) == 0 && pVictim->GetUInt64Value(OBJECT_FIELD_CREATED_BY) == 0 && !pVictim->IsPet()) { // Is this player part of a group if(((Player*)this)->InGroup()) { //Calc Group XP ((Player*)this)->GiveGroupXP(pVictim,(Player*)this); } else { uint32 xp = CalculateXpToGive(pVictim, (Unit*)this); if(xp) ((Player*)this)->GiveXP(xp, victimGuid, true); } if (pVictim->GetTypeId() != TYPEID_PLAYER) sQuestMgr.OnPlayerKill((Player*)this, (Creature*)pVictim); } else /* is Creature or GameObject*/ { /* ----------------------------- PET XP HANDLING -------------- */ if(IsPet() && !pVictim->IsPet()) { Player *petOwner = static_cast(this)->GetPetOwner(); if(petOwner && petOwner->GetTypeId() == TYPEID_PLAYER) { if(petOwner->InGroup()) { //Calc Group XP ((Unit*)this)->GiveGroupXP(pVictim,petOwner); } else { uint32 xp = CalculateXpToGive(pVictim, petOwner); petOwner->GiveXP(xp, victimGuid, true); } } if (pVictim->GetTypeId() != TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_UNIT) sQuestMgr.OnPlayerKill(petOwner, (Creature*)pVictim); } /* ----------------------------- PET XP HANDLING END-------------- */ /* ----------------------------- PET DEATH HANDLING -------------- */ if(pVictim->IsPet()) { static_cast(pVictim)->DelayedRemove(false, true); } /* ----------------------------- PET DEATH HANDLING END -------------- */ } } else if (pVictim->GetTypeId() == TYPEID_PLAYER) { /* -------------------- RESET BREATH STATE ON DEATH -------------- */ ((Player*)pVictim)->m_UnderwaterTime = 0; ((Player*)pVictim)->m_UnderwaterState = 0; ((Player*)pVictim)->m_BreathDamageTimer = 0; ((Player*)pVictim)->m_SwimmingTime = 0; /* -------------------- KILL PET WHEN PLAYER DIES ---------------*/ if(static_cast(pVictim)->GetSummon() != NULL) { if(pVictim->GetUInt32Value(UNIT_CREATED_BY_SPELL) > 0) static_cast(pVictim)->GetSummon()->Dismiss(true); else static_cast(pVictim)->GetSummon()->Remove(true, true, true); } /* -------------------- KILL PET WHEN PLAYER DIES END---------------*/ } else sLog.outError("DealDamage for Unknown Object."); } else /* ---------- NOT DEAD YET --------- */ { if(pVictim != this /* && updateskill */) { // Send AI Reaction UNIT vs UNIT if (GetTypeId() ==TYPEID_UNIT) { ((Unit*)this)->GetAIInterface()->AttackReaction(pVictim, damage, spellId); } // Send AI Victim Reaction if(this->IsPlayer() || this->GetTypeId()==TYPEID_UNIT) if (pVictim->GetTypeId() != TYPEID_PLAYER) { ((Creature*)pVictim)->GetAIInterface()->AttackReaction((Unit*)this, damage, spellId); } } // TODO: Mark victim as a HK /*if(((Player*)pVictim)->GetCurrentBattleground() != NULL && ((Player*)this)->GetCurrentBattleground() != NULL) { }*/ pVictim->SetUInt32Value(UNIT_FIELD_HEALTH,health - damage); } } void Object::SpellNonMeleeDamageLog(Unit *pVictim, uint32 spellID, uint32 damage, bool allowProc,bool no_remove_auras) { //========================================================================================== //==============================Unacceptable Cases Processing=============================== //========================================================================================== if(!pVictim || !pVictim->isAlive()) return; SpellEntry *spellInfo = sSpellStore.LookupEntry( spellID ); if(!spellInfo) return; //========================================================================================== //==============================Variables Initialization==================================== //========================================================================================== uint32 school = spellInfo->School; float res = float(damage); uint32 aproc = PROC_ON_ANY_HOSTILE_ACTION; uint32 vproc = PROC_ON_ANY_HOSTILE_ACTION | PROC_ON_ANY_DAMAGE_VICTIM | PROC_ON_SPELL_HIT_VICTIM; bool critical = false; //========================================================================================== //==============================+Spell Damage Bonus Calculations============================ //========================================================================================== //------------------------------by stats---------------------------------------------------- if(this->IsUnit()) { Unit* caster = (Unit*)(this); caster->RemoveAurasByInterruptFlag(AURA_INTERRUPT_ON_START_ATTACK); int32 plus_damage = 0; if(caster->IsPlayer()) { plus_damage += static_cast(caster)->SpellDmgDoneByInt[school] * caster->GetUInt32Value(UNIT_FIELD_STAT3); plus_damage += static_cast(caster)->SpellDmgDoneBySpr[school] * caster->GetUInt32Value(UNIT_FIELD_STAT4); } //------------------------------by school--------------------------------------------------- plus_damage += caster->GetDamageDoneMod(school); plus_damage += pVictim->DamageTakenMod[school]; //------------------------------by victim type---------------------------------------------- if(((Creature*)pVictim)->GetCreatureName() && caster->IsPlayer()&& !pVictim->IsPlayer()) plus_damage += static_cast(caster)->IncreaseDamageByType[((Creature*)pVictim)->GetCreatureName()->Type]; //========================================================================================== //==============================+Spell Damage Bonus Modifications=========================== //========================================================================================== //------------------------------by cast duration-------------------------------------------- SpellCastTime *sd = sCastTime.LookupEntry(spellInfo->CastingTimeIndex); float castaff = GetCastTime(sd); if(castaff < 1500) castaff = 1500; else if(castaff > 7000) castaff = 7000; if (spellInfo->NameHash == 0xddaf1ac7) //Mage: Ice Lance should take only 1/7 +spell damage bonus castaff = 500; float dmgdoneaffectperc = castaff / 3500; //========================================================================================== //==============================Bonus Adding To Main Damage================================= //========================================================================================== int32 bonus_damage = float2int32(plus_damage * dmgdoneaffectperc); bonus_damage +=pVictim->DamageTakenMod[school]; if(spellInfo->SpellGroupType) { SM_FIValue(caster->SM_FPenalty, &bonus_damage, spellInfo->SpellGroupType); res += bonus_damage; int32 ures = (int32)res; SM_FIValue(caster->SM_FDamageBonus, &ures, spellInfo->SpellGroupType); SM_PIValue(caster->SM_PDamageBonus, &ures, spellInfo->SpellGroupType); res = (float)ures; } else { res += bonus_damage; } //========================================================================================== //==============================Post +SpellDamage Bonus Modifications======================= //========================================================================================== if(res < 0) res = 0; else { //------------------------------by school---------------------------------------------- float summaryPCTmod = caster->GetDamageDonePctMod(school); summaryPCTmod += pVictim->DamageTakenPctMod[school]-1; if (caster->DamageDoneModPCT[school]) summaryPCTmod += caster->DamageDoneModPCT[school]; res *= summaryPCTmod; //------------------------------critical strike chance-------------------------------------- float CritChance = caster->spellcritperc + caster->SpellCritChanceSchool[school] + pVictim->AttackerSpellCritChanceMod[school]; if (caster->IsPlayer()&&(pVictim->m_rooted-pVictim->m_stunned)) CritChance += static_cast(caster)->m_RootedCritChanceBonus; if(spellInfo->SpellGroupType) SM_FFValue(caster->SM_CriticalChance, &CritChance, spellInfo->SpellGroupType); if (pVictim->IsPlayer()) CritChance -=static_cast(pVictim)->CalcRating(14); if (CritChance<0) CritChance = 0; critical = Rand(CritChance); //========================================================================================== //==============================Spell Critical Hit========================================== //========================================================================================== if (critical) { float b = res/2; if(spellInfo->SpellGroupType) SM_PFValue(caster->SM_PCriticalDamage, &b, spellInfo->SpellGroupType); res += b; if (pVictim->IsPlayer()) { // res = res*(1.0f-2.0f*static_cast(pVictim)->CalcRating(14)); //Resilience is a special new rating which was created to reduce the effects of critical hits against your character. //It has two components; it reduces the chance you will be critically hit by x%, //and it reduces the damage dealt to you by critical hits by 2x%. x is the percentage resilience granted by a given resilience rating. //It is believed that resilience also functions against spell crits, //though it's worth noting that NPC mobs cannot get critical hits with spells. float dmg_reduction_pct=2*static_cast(pVictim)->CalcRating(14)/100; if(dmg_reduction_pct>1.0f) dmg_reduction_pct = 1.0f; //we cannot resist more then he is criticalling us, there is no point of the critical then :P res=float2int32(res - res*dmg_reduction_pct); } pVictim->Emote(EMOTE_ONESHOT_WOUNDCRITICAL); aproc |= PROC_ON_SPELL_CRIT_HIT; vproc |= PROC_ON_SPELL_CRIT_HIT_VICTIM; } } } //========================================================================================== //==============================Post Roll Calculations====================================== //========================================================================================== //dirty fix for Ice Lance if ((pVictim->m_rooted -pVictim->m_stunned)>0 && spellInfo->NameHash == 0xddaf1ac7) //Ice Lance deals 3x damage if target is frozen { res *=3; } //------------------------------absorption-------------------------------------------------- uint32 ress=(uint32)res; uint32 abs_dmg = pVictim->AbsorbDamage(school, &ress); uint32 ms_abs_dmg= pVictim->ManaShieldAbsorb(ress); if (ms_abs_dmg) { ress-=ms_abs_dmg; abs_dmg += ms_abs_dmg; } res=(float)ress; dealdamage dmg; dmg.damage_type = school; dmg.full_damage = ress; dmg.resisted_damage = 0; if(res <= 0) dmg.resisted_damage = dmg.full_damage = 1; //------------------------------resistance reducing----------------------------------------- if(this->IsUnit()) { static_cast(this)->CalculateResistanceReduction(pVictim,&dmg); res = dmg.full_damage - dmg.resisted_damage; } //------------------------------special states---------------------------------------------- if(pVictim->GetTypeId() == TYPEID_PLAYER && static_cast(pVictim)->GodModeCheat == true) { res = dmg.full_damage; dmg.resisted_damage = dmg.full_damage; } //DK:FIXME->SplitDamage //========================================================================================== //==============================Data Sending ProcHandling=================================== //========================================================================================== SendSpellNonMeleeDamageLog(this, pVictim, spellID, float2int32(res), school, abs_dmg, dmg.resisted_damage, false, 0, critical, IsPlayer()); if(this->IsUnit() && allowProc && spellInfo->Id != 25501) { static_cast(this)->HandleProc(aproc,pVictim,spellInfo, float2int32(res)); static_cast(this)->m_procCounter = 0; pVictim->HandleProc(vproc,(Unit*)this,spellInfo, float2int32(res)); pVictim->m_procCounter = 0; } DealDamage(pVictim, float2int32(res), 2, 0, spellID); if (pVictim->GetCurrentSpell()) pVictim->GetCurrentSpell()->AddTime(school); //========================================================================================== //==============================Post Damage Processing====================================== //========================================================================================== if (dmg.resisted_damage==dmg.full_damage && !abs_dmg) { //Magic Absorption if (pVictim->IsPlayer()) { if (static_cast(pVictim)->m_RegenManaOnSpellResist) { Player* pl = (Player*)pVictim; uint32 curmana = pl->GetUInt32Value(UNIT_FIELD_POWER1); uint32 maxmana = pl->GetUInt32Value(UNIT_FIELD_MAXPOWER1); curmana+=uint32(float( float(maxmana)*pl->m_RegenManaOnSpellResist)); static_cast(pVictim)->SetUInt32Value(UNIT_FIELD_POWER1,(curmana >= maxmana) ? maxmana : curmana); WorldPacket datamr(SMSG_HEALMANASPELL_ON_PLAYER, 30); datamr << pVictim->GetNewGUID(); datamr << pVictim->GetNewGUID(); datamr << uint32(29442); datamr << uint32(0); datamr << uint32(float( float(maxmana)*pl->m_RegenManaOnSpellResist)); ((Player*)pVictim)->GetSession()->SendPacket(&datamr); } } } if(school == SHADOW_DAMAGE) { //VampiricEmbrace if(pVictim->VampEmbCaster.find(this->GetGUID()) != pVictim->VampEmbCaster.end() && IsUnit()) { if(static_cast(this)->isAlive()) static_cast(this)->VampiricEmbrace(float2int32(res), pVictim); } //VampiricTouch if(this->GetGUID() == pVictim->VampTchCaster && IsUnit()) { if(static_cast(this)->isAlive()) static_cast(this)->VampiricTouch(float2int32(res), pVictim); } if (pVictim->isAlive()&&this->IsUnit()) { //Shadow Word:Death if (spellID==32379||spellID==32996) { uint32 damage = (uint32)res; uint32 absorbed = static_cast(this)->AbsorbDamage(school,&damage); DealDamage((Unit*)this,damage,2,0,spellID); SendSpellNonMeleeDamageLog(this,this,spellID,damage,school,absorbed,0,false,0,false,this->IsPlayer()); } } } } //***************************************************************************************** //* SpellLog packets just to keep the code cleaner and better to read //***************************************************************************************** void Object::SendSpellLog(Object *Caster, Object *Target,uint32 Ability, uint8 SpellLogType) { if ((!Caster || !Target) && Ability) return; WorldPacket data(SMSG_SPELLLOGMISS,28); data << Ability; // spellid data << Caster->GetGUID(); // caster / player data << (uint8)1; // unknown but I think they are const data << (uint32)1; // unknown but I think they are const data << Target->GetGUID(); // target data << SpellLogType; // spelllogtype Caster->SendMessageToSet(&data, true); } void Object::SendSpellNonMeleeDamageLog(Object * Caster, Object * Target,uint32 SpellID,uint32 Damage, uint8 Damage_Type,uint32 AbsorbedDamage, uint32 ResistedDamage,bool PhysicalDamage, uint32 BlockedDamage, bool CriticalHit, bool bToset) { if ((!Caster || !Target) && SpellID) return; WorldPacket data(SMSG_SPELLNONMELEEDAMAGELOG,40); data << Target->GetNewGUID(); data << Caster->GetNewGUID(); data << SpellID; // Spell / ability ID data << Damage; // All damage data << Damage_Type; // Damage type data << AbsorbedDamage; // Absorbed Damage data << ResistedDamage; // Resisted Damage data << BlockedDamage; // Blocked Damage data << uint8(PhysicalDamage); // Physical Damage true / false data << uint8(0); // unknown or it binds with PhysicalDamage data << uint8(CriticalHit ? 2 : 0); // If critical hit this field is == 2 data << uint8(5); // unknown const data << uint32(0); Caster->SendMessageToSet( &data, bToset ); } int32 Object::event_GetInstanceID() { // return -1 for non-inworld.. so we get our shit moved to the right thread if(!IsInWorld()) return -1; else return m_instanceId; } void Object::EventSpellDamage(uint64 Victim, uint32 SpellID, uint32 Damage) { if(!IsInWorld()) return; Unit * pUnit = GetMapMgr()->GetUnit(Victim); if(pUnit == 0) return; SpellNonMeleeDamageLog(pUnit, SpellID, Damage, true); } bool Object::CanActivate() { switch(m_objectTypeId) { case TYPEID_UNIT: { if(UINT32_LOPART(GetGUIDHigh()) != HIGHGUID_PET) return true; }break; case TYPEID_GAMEOBJECT: { if(static_cast(this)->HasAI() && GetUInt32Value(GAMEOBJECT_TYPE_ID) != GAMEOBJECT_TYPE_TRAP) return true; }break; } return false; } void Object::Activate(MapMgr * mgr) { assert(!Active); switch(m_objectTypeId) { case TYPEID_UNIT: mgr->activeCreatures.insert((Creature*)this); break; case TYPEID_GAMEOBJECT: mgr->activeGameObjects.insert((GameObject*)this); break; } Active = true; } void Object::Deactivate(MapMgr * mgr) { assert(Active); switch(m_objectTypeId) { case TYPEID_UNIT: mgr->activeCreatures.erase((Creature*)this); break; case TYPEID_GAMEOBJECT: mgr->activeGameObjects.erase((GameObject*)this); break; } Active = false; } void Object::SetByte(uint32 index, uint32 index1,uint8 value) { ASSERT( index < m_valuesCount ); // save updating when val isn't changing. uint8 * v =&((uint8*)m_uint32Values)[index*4+index1]; if(*v == value) return; *v = value; if(IsInWorld()) { m_updateMask.SetBit( index ); if(!m_objectUpdated) { m_mapMgr->ObjectUpdated(this); m_objectUpdated = true; } } } void Object::SetZoneId(uint32 newZone) { m_zoneId = newZone; if(m_objectTypeId == TYPEID_PLAYER && ((Player*)this)->GetGroup()) ((Player*)this)->GetGroup()->HandlePartialChange(PARTY_UPDATE_FLAG_ZONEID, ((Player*)this)); }