diff options
Diffstat (limited to 'src/game/GameObject.cpp')
-rw-r--r-- | src/game/GameObject.cpp | 241 |
1 files changed, 240 insertions, 1 deletions
diff --git a/src/game/GameObject.cpp b/src/game/GameObject.cpp index f4cd1fb8af4..d7315f9626b 100644 --- a/src/game/GameObject.cpp +++ b/src/game/GameObject.cpp @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "Common.h" #include "QuestDef.h" #include "GameObject.h" @@ -38,11 +39,14 @@ #include "Util.h" #include "OutdoorPvPMgr.h" #include "BattleGroundAV.h" + GameObject::GameObject() : WorldObject(), m_goValue(new GameObjectValue) { m_objectType |= TYPEMASK_GAMEOBJECT; m_objectTypeId = TYPEID_GAMEOBJECT; + m_updateFlag = (UPDATEFLAG_HIGHGUID | UPDATEFLAG_HAS_POSITION | UPDATEFLAG_POSITION | UPDATEFLAG_ROTATION); + m_valuesCount = GAMEOBJECT_END; m_respawnTime = 0; m_respawnDelayTime = 25; @@ -53,14 +57,17 @@ GameObject::GameObject() : WorldObject(), m_goValue(new GameObjectValue) m_cooldownTime = 0; m_goInfo = NULL; m_goData = NULL; + m_DBTableGuid = 0; m_rotation = 0; } + GameObject::~GameObject() { //if(m_uint32Values) // field array can be not exist if GameOBject not loaded // CleanupsBeforeDelete(); } + void GameObject::CleanupsBeforeDelete() { if(m_uint32Values) // field array can be not exist if GameOBject not loaded @@ -74,6 +81,7 @@ void GameObject::CleanupsBeforeDelete() owner = ObjectAccessor::GetObjectInWorld(owner_guid, (Player*)NULL); else*/ owner = ObjectAccessor::GetUnit(*this,owner_guid); + if(owner) owner->RemoveGameObject(this,false); else @@ -83,12 +91,14 @@ void GameObject::CleanupsBeforeDelete() ownerType = "player"; else if(IS_PET_GUID(owner_guid)) ownerType = "pet"; + sLog.outError("Delete GameObject (GUID: %u Entry: %u SpellId %u LinkedGO %u) that lost references to owner (GUID %u Type '%s') GO list. Crash possible later.", GetGUIDLow(), GetGOInfo()->id, m_spellId, GetGOInfo()->GetLinkedGameObjectEntry(), GUID_LOPART(owner_guid), ownerType); } } } } + void GameObject::AddToWorld() { ///- Register the gameobject for guid lookup @@ -96,10 +106,12 @@ void GameObject::AddToWorld() { if(m_zoneScript) m_zoneScript->OnGameObjectCreate(this, true); + ObjectAccessor::Instance().AddObject(this); WorldObject::AddToWorld(); } } + void GameObject::RemoveFromWorld() { ///- Remove the gameobject from the accessor @@ -107,6 +119,7 @@ void GameObject::RemoveFromWorld() { if(m_zoneScript) m_zoneScript->OnGameObjectCreate(this, false); + // Possible crash at access to deleted GO in Unit::m_gameobj if(uint64 owner_guid = GetOwnerGUID()) { @@ -119,53 +132,73 @@ void GameObject::RemoveFromWorld() ObjectAccessor::Instance().RemoveObject(this); } } + bool GameObject::Create(uint32 guidlow, uint32 name_id, Map *map, uint32 phaseMask, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, GOState go_state, uint32 artKit) { ASSERT(map); SetMap(map); + Relocate(x,y,z,ang); if(!IsPositionValid()) { sLog.outError("Gameobject (GUID: %u Entry: %u ) not created. Suggested coordinates isn't valid (X: %f Y: %f)",guidlow,name_id,x,y); return false; } + SetPhaseMask(phaseMask,false); + GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(name_id); if (!goinfo) { sLog.outErrorDb("Gameobject (GUID: %u Entry: %u) not created: it have not exist entry in `gameobject_template`. Map: %u (X: %f Y: %f Z: %f) ang: %f rotation0: %f rotation1: %f rotation2: %f rotation3: %f",guidlow, name_id, map->GetId(), x, y, z, ang, rotation0, rotation1, rotation2, rotation3); return false; } + Object::_Create(guidlow, goinfo->id, HIGHGUID_GAMEOBJECT); + m_goInfo = goinfo; + if (goinfo->type >= MAX_GAMEOBJECT_TYPE) { sLog.outErrorDb("Gameobject (GUID: %u Entry: %u) not created: it have not exist GO type '%u' in `gameobject_template`. It's will crash client if created.",guidlow,name_id,goinfo->type); return false; } + SetFloatValue(GAMEOBJECT_PARENTROTATION+0, rotation0); SetFloatValue(GAMEOBJECT_PARENTROTATION+1, rotation1); + UpdateRotationFields(rotation2,rotation3); // GAMEOBJECT_FACING, GAMEOBJECT_ROTATION, GAMEOBJECT_PARENTROTATION+2/3 + SetFloatValue(OBJECT_FIELD_SCALE_X, goinfo->size); + SetUInt32Value(GAMEOBJECT_FACTION, goinfo->faction); SetUInt32Value(GAMEOBJECT_FLAGS, goinfo->flags); + SetEntry(goinfo->id); + SetUInt32Value(GAMEOBJECT_DISPLAYID, goinfo->displayId); + // GAMEOBJECT_BYTES_1, index at 0, 1, 2 and 3 SetGoState(go_state); SetGoType(GameobjectTypes(goinfo->type)); + SetGoArtKit(0); // unknown what this is SetGoAnimProgress(animprogress); + SetByteValue(GAMEOBJECT_BYTES_1, 2, artKit); + switch(goinfo->type) { case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING: m_goValue->building.health = goinfo->building.intactNumHits + goinfo->building.damagedNumHits; break; } + SetZoneScript(); + return true; } + void GameObject::Update(uint32 /*p_time*/) { if (IS_MO_TRANSPORT(GetGUID())) @@ -173,6 +206,7 @@ void GameObject::Update(uint32 /*p_time*/) //((Transport*)this)->Update(p_time); return; } + switch (m_lootState) { case GO_NOT_READY: @@ -199,13 +233,16 @@ void GameObject::Update(uint32 /*p_time*/) { SetGoState(GO_STATE_ACTIVE); SetUInt32Value(GAMEOBJECT_FLAGS, GO_FLAG_NODESPAWN); + UpdateData udata; WorldPacket packet; BuildValuesUpdateBlockForPlayer(&udata,((Player*)caster)); udata.BuildPacket(&packet); ((Player*)caster)->GetSession()->SendPacket(&packet); + SendCustomAnim(); } + m_lootState = GO_READY; // can be successfully open with some chance } return; @@ -225,6 +262,7 @@ void GameObject::Update(uint32 /*p_time*/) m_respawnTime = 0; m_SkillupList.clear(); m_usetimes = 0; + switch (GetGoType()) { case GAMEOBJECT_TYPE_FISHINGNODE: // can't fish now @@ -233,6 +271,7 @@ void GameObject::Update(uint32 /*p_time*/) if(caster && caster->GetTypeId()==TYPEID_PLAYER) { caster->FinishSpell(CURRENT_CHANNELED_SPELL); + WorldPacket data(SMSG_FISH_NOT_HOOKED,0); ((Player*)caster)->GetSession()->SendPacket(&data); } @@ -263,6 +302,7 @@ void GameObject::Update(uint32 /*p_time*/) } } } + if(isSpawned()) { // traps can have time and can not have @@ -271,9 +311,11 @@ void GameObject::Update(uint32 /*p_time*/) { if(m_cooldownTime >= time(NULL)) return; + // traps Unit* owner = GetOwner(); Unit* ok = NULL; // pointer to appropriate target if found any + bool IsBattleGroundTrap = false; //FIXME: this is activation radius (in different casting radius that must be selected from spell data) //TODO: move activated state code (cast itself) to GO_ACTIVATED, in this place only check activating and set state @@ -286,12 +328,15 @@ void GameObject::Update(uint32 /*p_time*/) { if(m_respawnTime > 0) break; + radius = goInfo->trap.cooldown; // battlegrounds gameobjects has data2 == 0 && data5 == 3 IsBattleGroundTrap = true; + if(!radius) return; } } + // Note: this hack with search required until GO casting not implemented // search unfriendly creature if(owner) // hunter trap @@ -311,17 +356,22 @@ void GameObject::Update(uint32 /*p_time*/) VisitNearbyWorldObject(radius, searcher); ok = player; } + if (ok) { // some traps do not have spell but should be triggered if(goInfo->trap.spellId) CastSpell(ok, goInfo->trap.spellId); + m_cooldownTime = time(NULL) + 4; // 4 seconds + // count charges //if(goInfo->trap.charges > 0) // AddUse(); + if(owner) SetLootState(GO_JUST_DEACTIVATED); + if(IsBattleGroundTrap && ok->GetTypeId() == TYPEID_PLAYER) { //BattleGround gameobjects case @@ -340,6 +390,7 @@ void GameObject::Update(uint32 /*p_time*/) } } } + break; } case GO_ACTIVATED: @@ -361,6 +412,7 @@ void GameObject::Update(uint32 /*p_time*/) if (GetGoType() == GAMEOBJECT_TYPE_GOOBER) { uint32 spellId = GetGOInfo()->goober.spellId; + if(spellId) { std::set<uint32>::const_iterator it = m_unique_users.begin(); @@ -372,11 +424,13 @@ void GameObject::Update(uint32 /*p_time*/) //if (owner) owner->CastSpell(owner, spellId, false, 0, 0, GetGUID()); if (owner) owner->CastSpell(owner, spellId, false); } + m_unique_users.clear(); m_usetimes = 0; } //any return here in case battleground traps } + if(GetOwnerGUID()) { if(Unit* owner = GetOwner(false)) @@ -387,6 +441,7 @@ void GameObject::Update(uint32 /*p_time*/) } return; } + //burning flags in some battlegrounds, if you find better condition, just add it if (GetGOInfo()->IsDespawnAtAction() || GetGoAnimProgress() > 0) { @@ -394,59 +449,76 @@ void GameObject::Update(uint32 /*p_time*/) //reset flags SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags); } + loot.clear(); SetLootState(GO_READY); + if(!m_respawnDelayTime) return; + if(!m_spawnedByDefault) { m_respawnTime = 0; ObjectAccessor::UpdateObjectVisibility(this); return; } + m_respawnTime = time(NULL) + m_respawnDelayTime; + // if option not set then object will be saved at grid unload if(sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY)) SaveRespawnTime(); + ObjectAccessor::UpdateObjectVisibility(this); + break; } } } + void GameObject::Refresh() { // not refresh despawned not casted GO (despawned casted GO destroyed in all cases anyway) if(m_respawnTime > 0 && m_spawnedByDefault) return; + if(isSpawned()) GetMap()->Add(this); } + void GameObject::AddUniqueUse(Player* player) { AddUse(); m_unique_users.insert(player->GetGUIDLow()); } + void GameObject::Delete() { SetLootState(GO_NOT_READY); if (GetOwnerGUID()) if (Unit * owner = GetOwner(false)) owner->RemoveGameObject(this, false); + assert (!GetOwnerGUID()); SendObjectDeSpawnAnim(GetGUID()); + SetGoState(GO_STATE_READY); SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags); + uint16 poolid = poolhandler.IsPartOfAPool(GetGUIDLow(), TYPEID_GAMEOBJECT); if (poolid) poolhandler.UpdatePool(poolid, GetGUIDLow(), TYPEID_GAMEOBJECT); else AddObjectToRemoveList(); } + void GameObject::getFishLoot(Loot *fishloot, Player* loot_owner) { fishloot->clear(); + uint32 zone, subzone; GetZoneAndAreaId(zone,subzone); + // if subzone loot exist use it if(LootTemplates_Fishing.HaveLootFor(subzone)) fishloot->FillLoot(subzone, LootTemplates_Fishing, loot_owner,true); @@ -454,6 +526,7 @@ void GameObject::getFishLoot(Loot *fishloot, Player* loot_owner) else fishloot->FillLoot(zone, LootTemplates_Fishing, loot_owner,true); } + void GameObject::SaveToDB() { // this should only be used when the gameobject has already been loaded @@ -464,17 +537,22 @@ void GameObject::SaveToDB() sLog.outError("GameObject::SaveToDB failed, cannot get gameobject data!"); return; } + SaveToDB(GetMapId(), data->spawnMask, data->phaseMask); } + void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) { const GameObjectInfo *goI = GetGOInfo(); + if (!goI) return; + if (!m_DBTableGuid) m_DBTableGuid = GetGUIDLow(); // update in loaded data (changing data only in this place) GameObjectData& data = objmgr.NewGOData(m_DBTableGuid); + // data->guid = guid don't must be update at save data.id = GetEntry(); data.mapid = mapid; @@ -492,6 +570,7 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) data.go_state = GetGoState(); data.spawnMask = spawnMask; data.artKit = GetGoArtKit(); + // updated in DB std::ostringstream ss; ss << "INSERT INTO gameobject VALUES ( " @@ -511,19 +590,23 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) << m_respawnDelayTime << ", " << uint32(GetGoAnimProgress()) << ", " << uint32(GetGoState()) << ")"; + WorldDatabase.BeginTransaction(); WorldDatabase.PExecuteLog("DELETE FROM gameobject WHERE guid = '%u'", m_DBTableGuid); WorldDatabase.PExecuteLog( ss.str( ).c_str( ) ); WorldDatabase.CommitTransaction(); } + bool GameObject::LoadFromDB(uint32 guid, Map *map) { GameObjectData const* data = objmgr.GetGOData(guid); + if( !data ) { sLog.outErrorDb("Gameobject (GUID: %u) not found in table `gameobject`, can't load. ",guid); return false; } + uint32 entry = data->id; //uint32 map_id = data->mapid; // already used before call uint32 phaseMask = data->phaseMask; @@ -531,20 +614,26 @@ bool GameObject::LoadFromDB(uint32 guid, Map *map) float y = data->posY; float z = data->posZ; float ang = data->orientation; + float rotation0 = data->rotation0; float rotation1 = data->rotation1; float rotation2 = data->rotation2; float rotation3 = data->rotation3; + uint32 animprogress = data->animprogress; GOState go_state = data->go_state; uint32 artKit = data->artKit; + m_DBTableGuid = guid; if (map->GetInstanceId() != 0) guid = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT); + if (!Create(guid,entry, map, phaseMask, x, y, z, ang, rotation0, rotation1, rotation2, rotation3, animprogress, go_state, artKit) ) return false; + if(data->spawntimesecs >= 0) { m_spawnedByDefault = true; + if(!GetGOInfo()->GetDespawnPossibility() && !GetGOInfo()->IsDespawnAtAction()) { SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NODESPAWN); @@ -555,6 +644,7 @@ bool GameObject::LoadFromDB(uint32 guid, Map *map) { m_respawnDelayTime = data->spawntimesecs; m_respawnTime = objmgr.GetGORespawnTime(m_DBTableGuid, map->GetInstanceId()); + // ready to respawn if(m_respawnTime && m_respawnTime <= time(NULL)) { @@ -569,9 +659,12 @@ bool GameObject::LoadFromDB(uint32 guid, Map *map) m_respawnDelayTime = -data->spawntimesecs; m_respawnTime = 0; } + m_goData = data; + return true; } + void GameObject::DeleteFromDB() { objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0); @@ -579,10 +672,12 @@ void GameObject::DeleteFromDB() WorldDatabase.PExecuteLog("DELETE FROM gameobject WHERE guid = '%u'", m_DBTableGuid); WorldDatabase.PExecuteLog("DELETE FROM game_event_gameobject WHERE guid = '%u'", m_DBTableGuid); } + GameObject* GameObject::GetGameObject(WorldObject& object, uint64 guid) { return object.GetMap()->GetGameObject(guid); } + /*********************************************************/ /*** QUEST SYSTEM ***/ /*********************************************************/ @@ -596,6 +691,7 @@ bool GameObject::hasQuest(uint32 quest_id) const } return false; } + bool GameObject::hasInvolvedQuest(uint32 quest_id) const { QuestRelations const& qr = objmgr.mGOQuestInvolvedRelations; @@ -606,6 +702,7 @@ bool GameObject::hasInvolvedQuest(uint32 quest_id) const } return false; } + bool GameObject::IsTransport() const { // If something is marked as a transport, don't transmit an out of range packet for it. @@ -613,31 +710,37 @@ bool GameObject::IsTransport() const if(!gInfo) return false; return gInfo->type == GAMEOBJECT_TYPE_TRANSPORT || gInfo->type == GAMEOBJECT_TYPE_MO_TRANSPORT; } + Unit* GameObject::GetOwner(bool inWorld) const { if (inWorld) return ObjectAccessor::GetUnit(*this, GetOwnerGUID()); return ObjectAccessor::GetUnitInOrOutOfWorld(*this, GetOwnerGUID()); } + void GameObject::SaveRespawnTime() { if(m_goData && m_goData->dbData && m_respawnTime > time(NULL) && m_spawnedByDefault) objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),m_respawnTime); } + bool GameObject::isVisibleForInState(Player const* u, bool inVisibleList) const { // Not in world if(!IsInWorld() || !u->IsInWorld()) return false; + // Transport always visible at this step implementation if(IsTransport() && IsInMap(u)) return true; + // quick check visibility false cases for non-GM-mode if(!u->isGameMaster()) { // despawned and then not visible for non-GM in GM-mode if(!isSpawned()) return false; + // special invisibility cases if(GetGOInfo()->type == GAMEOBJECT_TYPE_TRAP && GetGOInfo()->trap.stealthed) { @@ -646,10 +749,12 @@ bool GameObject::isVisibleForInState(Player const* u, bool inVisibleList) const return false; } } + // check distance return IsWithinDistInMap(u->m_seer,World::GetMaxVisibleDistanceForObject() + (inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f), false); } + bool GameObject::canDetectTrap(Player const* u, float distance) const { if(u->hasUnitState(UNIT_STAT_STUNNED)) @@ -660,14 +765,17 @@ bool GameObject::canDetectTrap(Player const* u, float distance) const return false; if(u->HasAuraType(SPELL_AURA_DETECT_STEALTH)) return true; + //Visible distance is modified by -Level Diff (every level diff = 0.25f in visible distance) float visibleDistance = (int32(u->getLevel()) - int32(GetOwner()->getLevel()))* 0.25f; //GetModifier for trap (miscvalue 1) //35y for aura 2836 //WARNING: these values are guessed, may be not blizzlike visibleDistance += u->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DETECT, 1)* 0.5f; + return distance < visibleDistance; } + void GameObject::Respawn() { if(m_spawnedByDefault && m_respawnTime > 0) @@ -676,10 +784,12 @@ void GameObject::Respawn() objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0); } } + bool GameObject::ActivateToQuest( Player *pTarget)const { if(!objmgr.IsGameObjectForQuests(GetEntry())) return false; + switch(GetGoType()) { // scan GO chest with loot including quest items @@ -706,16 +816,20 @@ bool GameObject::ActivateToQuest( Player *pTarget)const default: break; } + return false; } + void GameObject::TriggeringLinkedGameObject( uint32 trapEntry, Unit* target) { GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(trapEntry); if(!trapInfo || trapInfo->type!=GAMEOBJECT_TYPE_TRAP) return; + SpellEntry const* trapSpell = sSpellStore.LookupEntry(trapInfo->trap.spellId); if(!trapSpell) // checked at load already return; + float range; SpellRangeEntry const * srentry = sSpellRangeStore.LookupEntry(trapSpell->rangeIndex); //get owner to check hostility of GameObject @@ -730,6 +844,7 @@ void GameObject::TriggeringLinkedGameObject( uint32 trapEntry, Unit* target) //if no owner assume that object is hostile to target range = GetSpellMaxRangeForHostile(srentry); } + // search nearest linked GO GameObject* trapGO = NULL; { @@ -737,48 +852,63 @@ void GameObject::TriggeringLinkedGameObject( uint32 trapEntry, Unit* target) CellPair p(Trinity::ComputeCellPair(GetPositionX(), GetPositionY())); Cell cell(p); cell.data.Part.reserved = ALL_DISTRICT; + MaNGOS::NearestGameObjectEntryInObjectRangeCheck go_check(*target,trapEntry,range); MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck> checker(this, trapGO,go_check); + TypeContainerVisitor<Trinity::GameObjectLastSearcher<Trinity::NearestGameObjectEntryInObjectRangeCheck>, GridTypeMapContainer > object_checker(checker); CellLock<GridReadGuard> cell_lock(cell, p); cell_lock->Visit(cell_lock, object_checker, *GetMap(), *target, range); } + // found correct GO // FIXME: when GO casting will be implemented trap must cast spell to target if(trapGO) target->CastSpell(target,trapSpell,true, 0, 0, GetGUID()); } + GameObject* GameObject::LookupFishingHoleAround(float range) { GameObject* ok = NULL; + CellPair p(Trinity::ComputeCellPair(GetPositionX(),GetPositionY())); Cell cell(p); cell.data.Part.reserved = ALL_DISTRICT; MaNGOS::NearestGameObjectFishingHole u_check(*this, range); MaNGOS::GameObjectSearcher<MaNGOS::NearestGameObjectFishingHole> checker(this, ok, u_check); + CellLock<GridReadGuard> cell_lock(cell, p); + TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::NearestGameObjectFishingHole>, GridTypeMapContainer > grid_object_checker(checker); cell_lock->Visit(cell_lock, grid_object_checker, *GetMap(), *this, range); + return ok; } + void GameObject::ResetDoorOrButton() { if (m_lootState == GO_READY || m_lootState == GO_JUST_DEACTIVATED) return; + SwitchDoorOrButton(false); SetLootState(GO_JUST_DEACTIVATED); m_cooldownTime = 0; } + void GameObject::UseDoorOrButton(uint32 time_to_restore, bool alternative /* = false */) { if(m_lootState != GO_READY) return; + if(!time_to_restore) time_to_restore = GetGOInfo()->GetAutoCloseTime(); + SwitchDoorOrButton(true,alternative); SetLootState(GO_ACTIVATED); + m_cooldownTime = time(NULL) + time_to_restore; } + void GameObject::SetGoArtKit(uint8 kit) { SetByteValue(GAMEOBJECT_BYTES_1, 2, kit); @@ -786,6 +916,7 @@ void GameObject::SetGoArtKit(uint8 kit) if(data) data->artKit = kit; } + void GameObject::SetGoArtKit(uint8 artkit, GameObject *go, uint32 lowguid) { const GameObjectData *data = NULL; @@ -796,40 +927,49 @@ void GameObject::SetGoArtKit(uint8 artkit, GameObject *go, uint32 lowguid) } else if(lowguid) data = objmgr.GetGOData(lowguid); + if(data) const_cast<GameObjectData*>(data)->artKit = artkit; } + void GameObject::SwitchDoorOrButton(bool activate, bool alternative /* = false */) { if(activate) SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE); else RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE); + if(GetGoState() == GO_STATE_READY) //if closed -> open SetGoState(alternative ? GO_STATE_ACTIVE_ALTERNATIVE : GO_STATE_ACTIVE); else //if open -> close SetGoState(GO_STATE_READY); } + void GameObject::Use(Unit* user) { // by default spell caster is user Unit* spellCaster = user; uint32 spellId = 0; bool triggered = false; + switch(GetGoType()) { case GAMEOBJECT_TYPE_DOOR: //0 case GAMEOBJECT_TYPE_BUTTON: //1 //doors/buttons never really despawn, only reset to default state/flags UseDoorOrButton(); + // activate script GetMap()->ScriptsStart(sGameObjectScripts, GetDBTableGUIDLow(), spellCaster, this); return; + case GAMEOBJECT_TYPE_QUESTGIVER: //2 { if(user->GetTypeId()!=TYPEID_PLAYER) return; + Player* player = (Player*)user; + player->PrepareQuestMenu( GetGUID() ); player->SendPreparedQuest( GetGUID() ); return; @@ -840,16 +980,22 @@ void GameObject::Use(Unit* user) GameObjectInfo const* info = GetGOInfo(); if(!info) return; + if(user->GetTypeId()!=TYPEID_PLAYER) return; + Player* player = (Player*)user; + // a chair may have n slots. we have to calculate their positions and teleport the player to the nearest one + // check if the db is sane if(info->chair.slots > 0) { float lowestDist = DEFAULT_VISIBILITY_DISTANCE; + float x_lowest = GetPositionX(); float y_lowest = GetPositionY(); + // the object orientation + 1/2 pi // every slot will be on that straight line float orthogonalOrientation = GetOrientation()+M_PI*0.5f; @@ -858,16 +1004,20 @@ void GameObject::Use(Unit* user) { // the distance between this slot and the center of the go - imagine a 1D space float relativeDistance = (info->size*i)-(info->size*(info->chair.slots-1)/2.0f); + float x_i = GetPositionX() + relativeDistance * cos(orthogonalOrientation); float y_i = GetPositionY() + relativeDistance * sin(orthogonalOrientation); + // calculate the distance between the player and this slot float thisDistance = player->GetDistance2d(x_i, y_i); + /* debug code. It will spawn a npc on each slot to visualize them. Creature* helper = player->SummonCreature(14496, x_i, y_i, GetPositionZ(), GetOrientation(), TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 10000); std::ostringstream output; output << i << ": thisDist: " << thisDistance; helper->MonsterSay(output.str().c_str(), LANG_UNIVERSAL, 0); */ + if(thisDistance <= lowestDist) { lowestDist = thisDistance; @@ -889,9 +1039,11 @@ void GameObject::Use(Unit* user) case GAMEOBJECT_TYPE_GOOBER: //10 { GameObjectInfo const* info = GetGOInfo(); + if(user->GetTypeId()==TYPEID_PLAYER) { Player* player = (Player*)user; + // show page if(info->goober.pageId) { @@ -899,13 +1051,17 @@ void GameObject::Use(Unit* user) data << GetGUID(); player->GetSession()->SendPacket(&data); } + // possible quest objective for active quests player->CastedCreatureOrGO(info->id, GetGUID(), 0); + if (info->goober.eventId) GetMap()->ScriptsStart(sEventScripts, info->goober.eventId, player, this); } + // cast this spell later if provided spellId = info->goober.spellId; + break; } case GAMEOBJECT_TYPE_CAMERA: //13 @@ -913,13 +1069,18 @@ void GameObject::Use(Unit* user) GameObjectInfo const* info = GetGOInfo(); if(!info) return; + if(user->GetTypeId()!=TYPEID_PLAYER) return; + Player* player = (Player*)user; + if (info->camera.cinematicId) player->SendCinematicStart(info->camera.cinematicId); + if (info->camera.eventID) GetMap()->ScriptsStart(sEventScripts, info->camera.eventID, player, this); + return; } //fishing bobber @@ -927,9 +1088,12 @@ void GameObject::Use(Unit* user) { if(user->GetTypeId()!=TYPEID_PLAYER) return; + Player* player = (Player*)user; + if(player->GetGUID() != GetOwnerGUID()) return; + switch(getLootState()) { case GO_READY: // ready for loot @@ -937,18 +1101,24 @@ void GameObject::Use(Unit* user) // 1) skill must be >= base_zone_skill // 2) if skill == base_zone_skill => 5% chance // 3) chance is linear dependence from (base_zone_skill-skill) + uint32 zone, subzone; GetZoneAndAreaId(zone,subzone); + int32 zone_skill = objmgr.GetFishingBaseSkillLevel( subzone ); if(!zone_skill) zone_skill = objmgr.GetFishingBaseSkillLevel( zone ); + //provide error, no fishable zone or area should be 0 if(!zone_skill) sLog.outErrorDb("Fishable areaId %u are not properly defined in `skill_fishing_base_level`.",subzone); + int32 skill = player->GetSkillValue(SKILL_FISHING); int32 chance = skill - zone_skill + 5; int32 roll = irand(1,100); + DEBUG_LOG("Fishing check (skill: %i zone min skill: %i chance %i roll: %i",skill,zone_skill,chance,roll); + if(chance >= roll) { player->UpdateFishingSkill(); @@ -959,6 +1129,7 @@ void GameObject::Use(Unit* user) // prevent removing GO at spell cancel player->RemoveGameObject(this,false); SetOwnerGUID(player->GetGUID()); + //TODO: find reasonable value for fishing hole search GameObject* ok = LookupFishingHoleAround(20.0f + CONTACT_DISTANCE); if (ok) @@ -978,34 +1149,47 @@ void GameObject::Use(Unit* user) default: { SetLootState(GO_JUST_DEACTIVATED); + WorldPacket data(SMSG_FISH_NOT_HOOKED, 0); player->GetSession()->SendPacket(&data); break; } } + player->FinishSpell(CURRENT_CHANNELED_SPELL); return; } + case GAMEOBJECT_TYPE_SUMMONING_RITUAL: //18 { if(user->GetTypeId()!=TYPEID_PLAYER) return; + Player* player = (Player*)user; + Unit* caster = GetOwner(); + GameObjectInfo const* info = GetGOInfo(); + if( !caster || caster->GetTypeId()!=TYPEID_PLAYER ) return; + // accept only use by player from same group for caster except caster itself if(((Player*)caster)==player || !((Player*)caster)->IsInSameRaidWith(player)) return; + AddUniqueUse(player); + // full amount unique participants including original summoner if(GetUniqueUseCount() < info->summoningRitual.reqParticipants) return; + // in case summoning ritual caster is GO creator spellCaster = caster; + if(!caster->GetCurrentSpell(CURRENT_CHANNELED_SPELL)) return; + spellId = info->summoningRitual.spellId; if(spellId==62330) // GO store not existed spell, replace by expected { @@ -1014,41 +1198,54 @@ void GameObject::Use(Unit* user) spellId = 61993; triggered = true; } + // finish spell player->FinishSpell(CURRENT_CHANNELED_SPELL); + // can be deleted now SetLootState(GO_JUST_DEACTIVATED); + // go to end function to spell casting break; } case GAMEOBJECT_TYPE_SPELLCASTER: //22 { SetUInt32Value(GAMEOBJECT_FLAGS,2); + GameObjectInfo const* info = GetGOInfo(); if(!info) return; + if(info->spellcaster.partyOnly) { Unit* caster = GetOwner(); if( !caster || caster->GetTypeId()!=TYPEID_PLAYER ) return; + if(user->GetTypeId()!=TYPEID_PLAYER || !((Player*)user)->IsInSameRaidWith((Player*)caster)) return; } + spellId = info->spellcaster.spellId; + AddUse(); break; } case GAMEOBJECT_TYPE_MEETINGSTONE: //23 { GameObjectInfo const* info = GetGOInfo(); + if(user->GetTypeId()!=TYPEID_PLAYER) return; + Player* player = (Player*)user; + Player* targetPlayer = ObjectAccessor::FindPlayer(player->GetSelection()); + // accept only use by player from same group for caster except caster itself if(!targetPlayer || targetPlayer == player || !targetPlayer->IsInSameGroupWith(player)) return; + //required lvl checks! uint8 level = player->getLevel(); if (level < info->meetingstone.minLevel || level > info->meetingstone.maxLevel) @@ -1056,17 +1253,22 @@ void GameObject::Use(Unit* user) level = targetPlayer->getLevel(); if (level < info->meetingstone.minLevel || level > info->meetingstone.maxLevel) return; + if(info->id==194097) spellId = 61994; // Ritual of Summoning else spellId = 59782; // Summoning Stone Effect + break; } + case GAMEOBJECT_TYPE_FLAGSTAND: // 24 { if(user->GetTypeId()!=TYPEID_PLAYER) return; + Player* player = (Player*)user; + if( player->CanUseBattleGroundObject() ) { // in battleground check @@ -1089,7 +1291,9 @@ void GameObject::Use(Unit* user) { if(user->GetTypeId()!=TYPEID_PLAYER) return; + Player* player = (Player*)user; + if( player->CanUseBattleGroundObject() ) { // in battleground check @@ -1133,13 +1337,18 @@ void GameObject::Use(Unit* user) GameObjectInfo const* info = GetGOInfo(); if(!info) return; + if(user->GetTypeId()!=TYPEID_PLAYER) return; + Player* player = (Player*)user; + // fallback, will always work player->TeleportTo(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation(),TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET); + WorldPacket data(SMSG_ENABLE_BARBER_SHOP, 0); player->GetSession()->SendPacket(&data); + player->SetStandState(UNIT_STAND_STATE_SIT_LOW_CHAIR+info->barberChair.chairheight); return; } @@ -1147,8 +1356,10 @@ void GameObject::Use(Unit* user) sLog.outDebug("Unknown Object Type %u", GetGoType()); break; } + if(!spellId) return; + SpellEntry const *spellInfo = sSpellStore.LookupEntry( spellId ); if(!spellInfo) { @@ -1158,18 +1369,23 @@ void GameObject::Use(Unit* user) sLog.outDebug("WORLD: %u non-dbc spell was handled by OutdoorPvP", spellId); return; } + Spell *spell = new Spell(spellCaster, spellInfo, triggered); //Spell *spell = new Spell(spellCaster, spellInfo, triggered,GetGUID()); + // spell target is user of GO SpellCastTargets targets; targets.setUnitTarget( user ); + spell->prepare(&targets); } + void GameObject::CastSpell(Unit* target, uint32 spellId) { SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); if(!spellInfo) return; + bool self = false; for(uint8 i = 0; i < 3; ++i) { @@ -1179,15 +1395,18 @@ void GameObject::CastSpell(Unit* target, uint32 spellId) break; } } + if(self) { if(target) target->CastSpell(target, spellInfo, true); return; } + //summon world trigger Creature *trigger = SummonTrigger(GetPositionX(), GetPositionY(), GetPositionZ(), 0, 1); if(!trigger) return; + trigger->SetVisibility(VISIBILITY_OFF); //should this be true? if(Unit *owner = GetOwner()) { @@ -1197,13 +1416,14 @@ void GameObject::CastSpell(Unit* target, uint32 spellId) else { trigger->setFaction(14); - // Set owner guid for target if no owner avalible - needed by trigger auras + // Set owner guid for target if no owner avalible - needed by trigger auras // - trigger gets despawned and there's no caster avalible (see AuraEffect::TriggerSpell()) trigger->CastSpell(target, spellInfo, true, 0, 0, target ? target->GetGUID() : 0); } //trigger->setDeathState(JUST_DIED); //trigger->RemoveCorpse(); } + void GameObject::SendCustomAnim() { WorldPacket data(SMSG_GAMEOBJECT_CUSTOM_ANIM,8+4); @@ -1211,11 +1431,13 @@ void GameObject::SendCustomAnim() data << (uint32)0; SendMessageToSet(&data, true); } + bool GameObject::IsInRange(float x, float y, float z, float radius) const { GameObjectDisplayInfoEntry const * info = sGameObjectDisplayInfoStore.LookupEntry(GetUInt32Value(GAMEOBJECT_DISPLAYID)); if(!info) return IsWithinDist3d(x, y, z, radius); + float sinA = sin(GetOrientation()); float cosA = cos(GetOrientation()); float dx = x - GetPositionX(); @@ -1230,19 +1452,23 @@ bool GameObject::IsInRange(float x, float y, float z, float radius) const && dy < info->maxY + radius && dy > info->minY - radius && dz < info->maxZ + radius && dz > info->minZ - radius; } + void GameObject::TakenDamage(uint32 damage) { if(!m_goValue->building.health) return; + if(m_goValue->building.health > damage) m_goValue->building.health -= damage; else m_goValue->building.health = 0; + if(HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_DAMAGED)) // from damaged to destroyed { if(!m_goValue->building.health) { RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_DAMAGED); + SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_DESTROYED); SetUInt32Value(GAMEOBJECT_DISPLAYID, m_goInfo->building.destroyedDisplayId); EventInform(m_goInfo->building.destroyedEvent); @@ -1256,12 +1482,14 @@ void GameObject::TakenDamage(uint32 damage) m_goValue->building.health = 0; else if(!m_goValue->building.health) m_goValue->building.health = 1; + SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_DAMAGED); SetUInt32Value(GAMEOBJECT_DISPLAYID, m_goInfo->building.damagedDisplayId); EventInform(m_goInfo->building.damagedEvent); } } } + void GameObject::Rebuild() { RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_DAMAGED + GO_FLAG_DESTROYED); @@ -1269,11 +1497,13 @@ void GameObject::Rebuild() m_goValue->building.health = m_goInfo->building.intactNumHits + m_goInfo->building.damagedNumHits; EventInform(m_goInfo->building.rebuildingEvent); } + void GameObject::EventInform(uint32 eventId) { if(eventId && m_zoneScript) m_zoneScript->ProcessEvent(this, eventId); } + // overwrite WorldObject function for proper name localization const char* GameObject::GetNameForLocaleIdx(int32 loc_idx) const { @@ -1286,27 +1516,36 @@ const char* GameObject::GetNameForLocaleIdx(int32 loc_idx) const return cl->Name[loc_idx].c_str(); } } + return GetName(); } + void GameObject::UpdateRotationFields(float rotation2 /*=0.0f*/, float rotation3 /*=0.0f*/) { static double const atan_pow = atan(pow(2.0f, -20.0f)); + double f_rot1 = sin(GetOrientation() / 2.0f); double f_rot2 = cos(GetOrientation() / 2.0f); + int64 i_rot1 = int64(f_rot1 / atan_pow *(f_rot2 >= 0 ? 1.0f : -1.0f)); int64 rotation = (i_rot1 << 43 >> 43) & 0x00000000001FFFFF; + //float f_rot2 = sin(0.0f / 2.0f); //int64 i_rot2 = f_rot2 / atan(pow(2.0f, -20.0f)); //rotation |= (((i_rot2 << 22) >> 32) >> 11) & 0x000003FFFFE00000; + //float f_rot3 = sin(0.0f / 2.0f); //int64 i_rot3 = f_rot3 / atan(pow(2.0f, -21.0f)); //rotation |= (i_rot3 >> 42) & 0x7FFFFC0000000000; + m_rotation = rotation; + if(rotation2==0.0f && rotation3==0.0f) { rotation2 = f_rot1; rotation3 = f_rot2; } + SetFloatValue(GAMEOBJECT_PARENTROTATION+2, rotation2); SetFloatValue(GAMEOBJECT_PARENTROTATION+3, rotation3); } |