[8483] Implement glyph 43361.
[getmangos.git] / src / game / GameObject.cpp
blob794e11ca1134a7210972c70ba27bc06acd439ff1
1 /*
2 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "Common.h"
20 #include "QuestDef.h"
21 #include "GameObject.h"
22 #include "ObjectMgr.h"
23 #include "PoolHandler.h"
24 #include "SpellMgr.h"
25 #include "Spell.h"
26 #include "UpdateMask.h"
27 #include "Opcodes.h"
28 #include "WorldPacket.h"
29 #include "World.h"
30 #include "Database/DatabaseEnv.h"
31 #include "LootMgr.h"
32 #include "GridNotifiers.h"
33 #include "GridNotifiersImpl.h"
34 #include "CellImpl.h"
35 #include "InstanceData.h"
36 #include "BattleGround.h"
37 #include "Util.h"
39 GameObject::GameObject() : WorldObject()
41 m_objectType |= TYPEMASK_GAMEOBJECT;
42 m_objectTypeId = TYPEID_GAMEOBJECT;
44 m_updateFlag = (UPDATEFLAG_HIGHGUID | UPDATEFLAG_HAS_POSITION | UPDATEFLAG_POSITION | UPDATEFLAG_ROTATION);
46 m_valuesCount = GAMEOBJECT_END;
47 m_respawnTime = 0;
48 m_respawnDelayTime = 25;
49 m_lootState = GO_NOT_READY;
50 m_spawnedByDefault = true;
51 m_usetimes = 0;
52 m_spellId = 0;
53 m_cooldownTime = 0;
54 m_goInfo = NULL;
56 m_DBTableGuid = 0;
57 m_rotation = 0;
60 GameObject::~GameObject()
62 CleanupsBeforeDelete();
65 void GameObject::CleanupsBeforeDelete()
67 if(m_uint32Values) // field array can be not exist if GameOBject not loaded
69 // Possible crash at access to deleted GO in Unit::m_gameobj
70 if(uint64 owner_guid = GetOwnerGUID())
72 Unit* owner = NULL;
73 if(IS_PLAYER_GUID(owner_guid))
74 owner = ObjectAccessor::GetObjectInWorld(owner_guid, (Player*)NULL);
75 else
76 owner = ObjectAccessor::GetUnit(*this,owner_guid);
78 if(owner)
79 owner->RemoveGameObject(this,false);
80 else
82 const char * ownerType = "creature";
83 if(IS_PLAYER_GUID(owner_guid))
84 ownerType = "player";
85 else if(IS_PET_GUID(owner_guid))
86 ownerType = "pet";
88 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.",
89 GetGUIDLow(), GetGOInfo()->id, m_spellId, GetGOInfo()->GetLinkedGameObjectEntry(), GUID_LOPART(owner_guid), ownerType);
95 void GameObject::AddToWorld()
97 ///- Register the gameobject for guid lookup
98 if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this);
99 Object::AddToWorld();
102 void GameObject::RemoveFromWorld()
104 ///- Remove the gameobject from the accessor
105 if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this);
106 Object::RemoveFromWorld();
109 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)
111 ASSERT(map);
112 Relocate(x,y,z,ang);
113 SetMap(map);
114 SetPhaseMask(phaseMask,false);
116 if(!IsPositionValid())
118 sLog.outError("Gameobject (GUID: %u Entry: %u ) not created. Suggested coordinates isn't valid (X: %f Y: %f)",guidlow,name_id,x,y);
119 return false;
122 GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(name_id);
123 if (!goinfo)
125 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);
126 return false;
129 Object::_Create(guidlow, goinfo->id, HIGHGUID_GAMEOBJECT);
131 m_goInfo = goinfo;
133 if (goinfo->type >= MAX_GAMEOBJECT_TYPE)
135 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);
136 return false;
139 SetFloatValue(GAMEOBJECT_PARENTROTATION+0, rotation0);
140 SetFloatValue(GAMEOBJECT_PARENTROTATION+1, rotation1);
142 UpdateRotationFields(rotation2,rotation3); // GAMEOBJECT_FACING, GAMEOBJECT_ROTATION, GAMEOBJECT_PARENTROTATION+2/3
144 SetFloatValue(OBJECT_FIELD_SCALE_X, goinfo->size);
146 SetUInt32Value(GAMEOBJECT_FACTION, goinfo->faction);
147 SetUInt32Value(GAMEOBJECT_FLAGS, goinfo->flags);
149 SetEntry(goinfo->id);
151 SetUInt32Value(GAMEOBJECT_DISPLAYID, goinfo->displayId);
153 // GAMEOBJECT_BYTES_1, index at 0, 1, 2 and 3
154 SetGoState(go_state);
155 SetGoType(GameobjectTypes(goinfo->type));
156 SetGoArtKit(0); // unknown what this is
157 SetGoAnimProgress(animprogress);
159 //Notify the map's instance data.
160 //Only works if you create the object in it, not if it is moves to that map.
161 //Normally non-players do not teleport to other maps.
162 if(map->IsDungeon() && ((InstanceMap*)map)->GetInstanceData())
164 ((InstanceMap*)map)->GetInstanceData()->OnObjectCreate(this);
167 return true;
170 void GameObject::Update(uint32 /*p_time*/)
172 if (IS_MO_TRANSPORT(GetGUID()))
174 //((Transport*)this)->Update(p_time);
175 return;
178 switch (m_lootState)
180 case GO_NOT_READY:
182 switch(GetGoType())
184 case GAMEOBJECT_TYPE_TRAP:
186 // Arming Time for GAMEOBJECT_TYPE_TRAP (6)
187 Unit* owner = GetOwner();
188 if (owner && ((Player*)owner)->isInCombat())
189 m_cooldownTime = time(NULL) + GetGOInfo()->trap.startDelay;
190 m_lootState = GO_READY;
191 break;
193 case GAMEOBJECT_TYPE_FISHINGNODE:
195 // fishing code (bobber ready)
196 if( time(NULL) > m_respawnTime - FISHING_BOBBER_READY_TIME )
198 // splash bobber (bobber ready now)
199 Unit* caster = GetOwner();
200 if(caster && caster->GetTypeId()==TYPEID_PLAYER)
202 SetGoState(GO_STATE_ACTIVE);
203 SetUInt32Value(GAMEOBJECT_FLAGS, GO_FLAG_NODESPAWN);
205 UpdateData udata;
206 WorldPacket packet;
207 BuildValuesUpdateBlockForPlayer(&udata,((Player*)caster));
208 udata.BuildPacket(&packet);
209 ((Player*)caster)->GetSession()->SendPacket(&packet);
211 WorldPacket data(SMSG_GAMEOBJECT_CUSTOM_ANIM,8+4);
212 data << GetGUID();
213 data << (uint32)(0);
214 ((Player*)caster)->SendMessageToSet(&data,true);
217 m_lootState = GO_READY; // can be successfully open with some chance
219 return;
221 default:
222 m_lootState = GO_READY; // for other GOis same switched without delay to GO_READY
223 break;
225 // NO BREAK for switch (m_lootState)
227 case GO_READY:
229 if (m_respawnTime > 0) // timer on
231 if (m_respawnTime <= time(NULL)) // timer expired
233 m_respawnTime = 0;
234 m_SkillupList.clear();
235 m_usetimes = 0;
237 switch (GetGoType())
239 case GAMEOBJECT_TYPE_FISHINGNODE: // can't fish now
241 Unit* caster = GetOwner();
242 if(caster && caster->GetTypeId()==TYPEID_PLAYER)
244 caster->FinishSpell(CURRENT_CHANNELED_SPELL);
246 WorldPacket data(SMSG_FISH_NOT_HOOKED,0);
247 ((Player*)caster)->GetSession()->SendPacket(&data);
249 // can be delete
250 m_lootState = GO_JUST_DEACTIVATED;
251 return;
253 case GAMEOBJECT_TYPE_DOOR:
254 case GAMEOBJECT_TYPE_BUTTON:
255 //we need to open doors if they are closed (add there another condition if this code breaks some usage, but it need to be here for battlegrounds)
256 if (GetGoState() != GO_STATE_READY)
257 ResetDoorOrButton();
258 //flags in AB are type_button and we need to add them here so no break!
259 default:
260 if (!m_spawnedByDefault) // despawn timer
262 // can be despawned or destroyed
263 SetLootState(GO_JUST_DEACTIVATED);
264 return;
266 // respawn timer
267 uint16 poolid = poolhandler.IsPartOfAPool(GetGUIDLow(), TYPEID_GAMEOBJECT);
268 if (poolid)
269 poolhandler.UpdatePool(poolid, GetGUIDLow(), TYPEID_GAMEOBJECT);
270 else
271 GetMap()->Add(this);
272 break;
277 if(isSpawned())
279 // traps can have time and can not have
280 GameObjectInfo const* goInfo = GetGOInfo();
281 if(goInfo->type == GAMEOBJECT_TYPE_TRAP)
283 if(m_cooldownTime >= time(NULL))
284 return;
286 // traps
287 Unit* owner = GetOwner();
288 Unit* ok = NULL; // pointer to appropriate target if found any
290 bool IsBattleGroundTrap = false;
291 //FIXME: this is activation radius (in different casting radius that must be selected from spell data)
292 //TODO: move activated state code (cast itself) to GO_ACTIVATED, in this place only check activating and set state
293 float radius = goInfo->trap.radius;
294 if(!radius)
296 if(goInfo->trap.cooldown != 3) // cast in other case (at some triggering/linked go/etc explicit call)
297 return;
298 else
300 if(m_respawnTime > 0)
301 break;
303 radius = goInfo->trap.cooldown; // battlegrounds gameobjects has data2 == 0 && data5 == 3
304 IsBattleGroundTrap = true;
308 CellPair p(MaNGOS::ComputeCellPair(GetPositionX(),GetPositionY()));
309 Cell cell(p);
310 cell.data.Part.reserved = ALL_DISTRICT;
312 // Note: this hack with search required until GO casting not implemented
313 // search unfriendly creature
314 if(owner && goInfo->trap.charges > 0) // hunter trap
316 MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck u_check(this, owner, radius);
317 MaNGOS::UnitSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck> checker(this,ok, u_check);
319 CellLock<GridReadGuard> cell_lock(cell, p);
321 TypeContainerVisitor<MaNGOS::UnitSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck>, GridTypeMapContainer > grid_object_checker(checker);
322 cell_lock->Visit(cell_lock, grid_object_checker, *GetMap());
324 // or unfriendly player/pet
325 if(!ok)
327 TypeContainerVisitor<MaNGOS::UnitSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck>, WorldTypeMapContainer > world_object_checker(checker);
328 cell_lock->Visit(cell_lock, world_object_checker, *GetMap());
331 else // environmental trap
333 // environmental damage spells already have around enemies targeting but this not help in case not existed GO casting support
335 // affect only players
336 Player* p_ok = NULL;
337 MaNGOS::AnyPlayerInObjectRangeCheck p_check(this, radius);
338 MaNGOS::PlayerSearcher<MaNGOS::AnyPlayerInObjectRangeCheck> checker(this,p_ok, p_check);
340 CellLock<GridReadGuard> cell_lock(cell, p);
342 TypeContainerVisitor<MaNGOS::PlayerSearcher<MaNGOS::AnyPlayerInObjectRangeCheck>, WorldTypeMapContainer > world_object_checker(checker);
343 cell_lock->Visit(cell_lock, world_object_checker, *GetMap());
344 ok = p_ok;
347 if (ok)
349 Unit *caster = owner ? owner : ok;
351 caster->CastSpell(ok, goInfo->trap.spellId, true, 0, 0, GetGUID());
352 m_cooldownTime = time(NULL) + 4; // 4 seconds
354 // count charges
355 if(goInfo->trap.charges > 0)
356 AddUse();
358 if(IsBattleGroundTrap && ok->GetTypeId() == TYPEID_PLAYER)
360 //BattleGround gameobjects case
361 if(((Player*)ok)->InBattleGround())
362 if(BattleGround *bg = ((Player*)ok)->GetBattleGround())
363 bg->HandleTriggerBuff(GetGUID());
368 if(uint32 max_charges = goInfo->GetCharges())
370 if (m_usetimes >= max_charges)
372 m_usetimes = 0;
373 SetLootState(GO_JUST_DEACTIVATED); // can be despawned or destroyed
378 break;
380 case GO_ACTIVATED:
382 switch(GetGoType())
384 case GAMEOBJECT_TYPE_DOOR:
385 case GAMEOBJECT_TYPE_BUTTON:
386 if (GetGOInfo()->GetAutoCloseTime() && (m_cooldownTime < time(NULL)))
387 ResetDoorOrButton();
388 break;
389 default: break;
391 break;
393 case GO_JUST_DEACTIVATED:
395 //if Gameobject should cast spell, then this, but some GOs (type = 10) should be destroyed
396 if (GetGoType() == GAMEOBJECT_TYPE_GOOBER)
398 uint32 spellId = GetGOInfo()->goober.spellId;
400 if(spellId)
402 std::set<uint32>::const_iterator it = m_unique_users.begin();
403 std::set<uint32>::const_iterator end = m_unique_users.end();
404 for (; it != end; it++)
406 Unit* owner = Unit::GetUnit(*this, uint64(*it));
407 if (owner) owner->CastSpell(owner, spellId, false, 0, 0, GetGUID());
410 m_unique_users.clear();
411 m_usetimes = 0;
413 //any return here in case battleground traps
416 if(GetOwnerGUID())
418 if(Unit* owner = GetOwner())
419 owner->RemoveGameObject(this, false);
421 SetRespawnTime(0);
422 Delete();
423 return;
426 //burning flags in some battlegrounds, if you find better condition, just add it
427 if (GetGOInfo()->IsDespawnAtAction() || GetGoAnimProgress() > 0)
429 SendObjectDeSpawnAnim(GetGUID());
430 //reset flags
431 SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags);
434 loot.clear();
435 SetLootState(GO_READY);
437 if(!m_respawnDelayTime)
438 return;
440 if(!m_spawnedByDefault)
442 m_respawnTime = 0;
443 return;
446 m_respawnTime = time(NULL) + m_respawnDelayTime;
448 // if option not set then object will be saved at grid unload
449 if(sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY))
450 SaveRespawnTime();
452 ObjectAccessor::UpdateObjectVisibility(this);
454 break;
459 void GameObject::Refresh()
461 // not refresh despawned not casted GO (despawned casted GO destroyed in all cases anyway)
462 if(m_respawnTime > 0 && m_spawnedByDefault)
463 return;
465 if(isSpawned())
466 GetMap()->Add(this);
469 void GameObject::AddUniqueUse(Player* player)
471 AddUse();
472 m_unique_users.insert(player->GetGUIDLow());
475 void GameObject::Delete()
477 SendObjectDeSpawnAnim(GetGUID());
479 SetGoState(GO_STATE_READY);
480 SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags);
482 uint16 poolid = poolhandler.IsPartOfAPool(GetGUIDLow(), TYPEID_GAMEOBJECT);
483 if (poolid)
484 poolhandler.UpdatePool(poolid, GetGUIDLow(), TYPEID_GAMEOBJECT);
485 else
486 AddObjectToRemoveList();
489 void GameObject::getFishLoot(Loot *fishloot, Player* loot_owner)
491 fishloot->clear();
493 uint32 zone, subzone;
494 GetZoneAndAreaId(zone,subzone);
496 // if subzone loot exist use it
497 if(LootTemplates_Fishing.HaveLootFor(subzone))
498 fishloot->FillLoot(subzone, LootTemplates_Fishing, loot_owner,true);
499 // else use zone loot
500 else
501 fishloot->FillLoot(zone, LootTemplates_Fishing, loot_owner,true);
504 void GameObject::SaveToDB()
506 // this should only be used when the gameobject has already been loaded
507 // preferably after adding to map, because mapid may not be valid otherwise
508 GameObjectData const *data = objmgr.GetGOData(m_DBTableGuid);
509 if(!data)
511 sLog.outError("GameObject::SaveToDB failed, cannot get gameobject data!");
512 return;
515 SaveToDB(GetMapId(), data->spawnMask, data->phaseMask);
518 void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
520 const GameObjectInfo *goI = GetGOInfo();
522 if (!goI)
523 return;
525 if (!m_DBTableGuid)
526 m_DBTableGuid = GetGUIDLow();
527 // update in loaded data (changing data only in this place)
528 GameObjectData& data = objmgr.NewGOData(m_DBTableGuid);
530 // data->guid = guid don't must be update at save
531 data.id = GetEntry();
532 data.mapid = mapid;
533 data.phaseMask = phaseMask;
534 data.posX = GetPositionX();
535 data.posY = GetPositionY();
536 data.posZ = GetPositionZ();
537 data.orientation = GetOrientation();
538 data.rotation0 = GetFloatValue(GAMEOBJECT_PARENTROTATION+0);
539 data.rotation1 = GetFloatValue(GAMEOBJECT_PARENTROTATION+1);
540 data.rotation2 = GetFloatValue(GAMEOBJECT_PARENTROTATION+2);
541 data.rotation3 = GetFloatValue(GAMEOBJECT_PARENTROTATION+3);
542 data.spawntimesecs = m_spawnedByDefault ? m_respawnDelayTime : -(int32)m_respawnDelayTime;
543 data.animprogress = GetGoAnimProgress();
544 data.go_state = GetGoState();
545 data.spawnMask = spawnMask;
547 // updated in DB
548 std::ostringstream ss;
549 ss << "INSERT INTO gameobject VALUES ( "
550 << m_DBTableGuid << ", "
551 << GetEntry() << ", "
552 << mapid << ", "
553 << uint32(spawnMask) << "," // cast to prevent save as symbol
554 << uint16(GetPhaseMask()) << "," // prevent out of range error
555 << GetPositionX() << ", "
556 << GetPositionY() << ", "
557 << GetPositionZ() << ", "
558 << GetOrientation() << ", "
559 << GetFloatValue(GAMEOBJECT_PARENTROTATION) << ", "
560 << GetFloatValue(GAMEOBJECT_PARENTROTATION+1) << ", "
561 << GetFloatValue(GAMEOBJECT_PARENTROTATION+2) << ", "
562 << GetFloatValue(GAMEOBJECT_PARENTROTATION+3) << ", "
563 << m_respawnDelayTime << ", "
564 << uint32(GetGoAnimProgress()) << ", "
565 << uint32(GetGoState()) << ")";
567 WorldDatabase.BeginTransaction();
568 WorldDatabase.PExecuteLog("DELETE FROM gameobject WHERE guid = '%u'", m_DBTableGuid);
569 WorldDatabase.PExecuteLog( ss.str( ).c_str( ) );
570 WorldDatabase.CommitTransaction();
573 bool GameObject::LoadFromDB(uint32 guid, Map *map)
575 GameObjectData const* data = objmgr.GetGOData(guid);
577 if( !data )
579 sLog.outErrorDb("Gameobject (GUID: %u) not found in table `gameobject`, can't load. ",guid);
580 return false;
583 uint32 entry = data->id;
584 //uint32 map_id = data->mapid; // already used before call
585 uint32 phaseMask = data->phaseMask;
586 float x = data->posX;
587 float y = data->posY;
588 float z = data->posZ;
589 float ang = data->orientation;
591 float rotation0 = data->rotation0;
592 float rotation1 = data->rotation1;
593 float rotation2 = data->rotation2;
594 float rotation3 = data->rotation3;
596 uint32 animprogress = data->animprogress;
597 GOState go_state = data->go_state;
599 m_DBTableGuid = guid;
600 if (map->GetInstanceId() != 0) guid = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT);
602 if (!Create(guid,entry, map, phaseMask, x, y, z, ang, rotation0, rotation1, rotation2, rotation3, animprogress, go_state) )
603 return false;
605 if(!GetGOInfo()->GetDespawnPossibility() && !GetGOInfo()->IsDespawnAtAction())
607 SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NODESPAWN);
608 m_spawnedByDefault = true;
609 m_respawnDelayTime = 0;
610 m_respawnTime = 0;
612 else
614 if(data->spawntimesecs >= 0)
616 m_spawnedByDefault = true;
617 m_respawnDelayTime = data->spawntimesecs;
618 m_respawnTime = objmgr.GetGORespawnTime(m_DBTableGuid, map->GetInstanceId());
620 // ready to respawn
621 if(m_respawnTime && m_respawnTime <= time(NULL))
623 m_respawnTime = 0;
624 objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0);
627 else
629 m_spawnedByDefault = false;
630 m_respawnDelayTime = -data->spawntimesecs;
631 m_respawnTime = 0;
635 return true;
638 void GameObject::DeleteFromDB()
640 objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0);
641 objmgr.DeleteGOData(m_DBTableGuid);
642 WorldDatabase.PExecuteLog("DELETE FROM gameobject WHERE guid = '%u'", m_DBTableGuid);
643 WorldDatabase.PExecuteLog("DELETE FROM game_event_gameobject WHERE guid = '%u'", m_DBTableGuid);
646 GameObjectInfo const *GameObject::GetGOInfo() const
648 return m_goInfo;
651 /*********************************************************/
652 /*** QUEST SYSTEM ***/
653 /*********************************************************/
654 bool GameObject::hasQuest(uint32 quest_id) const
656 QuestRelations const& qr = objmgr.mGOQuestRelations;
657 for(QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr)
659 if(itr->second==quest_id)
660 return true;
662 return false;
665 bool GameObject::hasInvolvedQuest(uint32 quest_id) const
667 QuestRelations const& qr = objmgr.mGOQuestInvolvedRelations;
668 for(QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr)
670 if(itr->second==quest_id)
671 return true;
673 return false;
676 bool GameObject::IsTransport() const
678 // If something is marked as a transport, don't transmit an out of range packet for it.
679 GameObjectInfo const * gInfo = GetGOInfo();
680 if(!gInfo) return false;
681 return gInfo->type == GAMEOBJECT_TYPE_TRANSPORT || gInfo->type == GAMEOBJECT_TYPE_MO_TRANSPORT;
684 Unit* GameObject::GetOwner() const
686 return ObjectAccessor::GetUnit(*this, GetOwnerGUID());
689 void GameObject::SaveRespawnTime()
691 if(m_respawnTime > time(NULL) && m_spawnedByDefault)
692 objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),m_respawnTime);
695 bool GameObject::isVisibleForInState(Player const* u, WorldObject const* viewPoint, bool inVisibleList) const
697 // Not in world
698 if(!IsInWorld() || !u->IsInWorld())
699 return false;
701 // Transport always visible at this step implementation
702 if(IsTransport() && IsInMap(u))
703 return true;
705 // quick check visibility false cases for non-GM-mode
706 if(!u->isGameMaster())
708 // despawned and then not visible for non-GM in GM-mode
709 if(!isSpawned())
710 return false;
712 // special invisibility cases
713 /* TODO: implement trap stealth, take look at spell 2836
714 if(GetGOInfo()->type == GAMEOBJECT_TYPE_TRAP && GetGOInfo()->trap.stealthed && u->IsHostileTo(GetOwner()))
716 if(check stuff here)
717 return false;
721 // check distance
722 return IsWithinDistInMap(viewPoint,World::GetMaxVisibleDistanceForObject() +
723 (inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f), false);
726 void GameObject::Respawn()
728 if(m_spawnedByDefault && m_respawnTime > 0)
730 m_respawnTime = time(NULL);
731 objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0);
735 bool GameObject::ActivateToQuest( Player *pTarget)const
737 if(!objmgr.IsGameObjectForQuests(GetEntry()))
738 return false;
740 switch(GetGoType())
742 // scan GO chest with loot including quest items
743 case GAMEOBJECT_TYPE_CHEST:
745 if(LootTemplates_Gameobject.HaveQuestLootForPlayer(GetGOInfo()->GetLootId(), pTarget))
746 return true;
747 break;
749 case GAMEOBJECT_TYPE_GOOBER:
751 if(pTarget->GetQuestStatus(GetGOInfo()->goober.questId) == QUEST_STATUS_INCOMPLETE)
752 return true;
753 break;
755 default:
756 break;
759 return false;
762 void GameObject::TriggeringLinkedGameObject( uint32 trapEntry, Unit* target)
764 GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(trapEntry);
765 if(!trapInfo || trapInfo->type!=GAMEOBJECT_TYPE_TRAP)
766 return;
768 SpellEntry const* trapSpell = sSpellStore.LookupEntry(trapInfo->trap.spellId);
769 if(!trapSpell) // checked at load already
770 return;
772 float range = GetSpellMaxRange(sSpellRangeStore.LookupEntry(trapSpell->rangeIndex));
774 // search nearest linked GO
775 GameObject* trapGO = NULL;
777 // using original GO distance
778 CellPair p(MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY()));
779 Cell cell(p);
780 cell.data.Part.reserved = ALL_DISTRICT;
782 MaNGOS::NearestGameObjectEntryInObjectRangeCheck go_check(*target,trapEntry,range);
783 MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck> checker(this, trapGO,go_check);
785 TypeContainerVisitor<MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck>, GridTypeMapContainer > object_checker(checker);
786 CellLock<GridReadGuard> cell_lock(cell, p);
787 cell_lock->Visit(cell_lock, object_checker, *GetMap());
790 // found correct GO
791 // FIXME: when GO casting will be implemented trap must cast spell to target
792 if(trapGO)
793 target->CastSpell(target,trapSpell,true, 0, 0, GetGUID());
796 GameObject* GameObject::LookupFishingHoleAround(float range)
798 GameObject* ok = NULL;
800 CellPair p(MaNGOS::ComputeCellPair(GetPositionX(),GetPositionY()));
801 Cell cell(p);
802 cell.data.Part.reserved = ALL_DISTRICT;
803 MaNGOS::NearestGameObjectFishingHole u_check(*this, range);
804 MaNGOS::GameObjectSearcher<MaNGOS::NearestGameObjectFishingHole> checker(this, ok, u_check);
806 CellLock<GridReadGuard> cell_lock(cell, p);
808 TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::NearestGameObjectFishingHole>, GridTypeMapContainer > grid_object_checker(checker);
809 cell_lock->Visit(cell_lock, grid_object_checker, *GetMap());
811 return ok;
814 void GameObject::ResetDoorOrButton()
816 if (m_lootState == GO_READY || m_lootState == GO_JUST_DEACTIVATED)
817 return;
819 SwitchDoorOrButton(false);
820 SetLootState(GO_JUST_DEACTIVATED);
821 m_cooldownTime = 0;
824 void GameObject::UseDoorOrButton(uint32 time_to_restore, bool alternative /* = false */)
826 if(m_lootState != GO_READY)
827 return;
829 if(!time_to_restore)
830 time_to_restore = GetGOInfo()->GetAutoCloseTime();
832 SwitchDoorOrButton(true,alternative);
833 SetLootState(GO_ACTIVATED);
835 m_cooldownTime = time(NULL) + time_to_restore;
838 void GameObject::SwitchDoorOrButton(bool activate, bool alternative /* = false */)
840 if(activate)
841 SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE);
842 else
843 RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE);
845 if(GetGoState() == GO_STATE_READY) //if closed -> open
846 SetGoState(alternative ? GO_STATE_ACTIVE_ALTERNATIVE : GO_STATE_ACTIVE);
847 else //if open -> close
848 SetGoState(GO_STATE_READY);
851 void GameObject::Use(Unit* user)
853 // by default spell caster is user
854 Unit* spellCaster = user;
855 uint32 spellId = 0;
856 bool triggered = false;
858 switch(GetGoType())
860 case GAMEOBJECT_TYPE_DOOR: //0
861 case GAMEOBJECT_TYPE_BUTTON: //1
862 //doors/buttons never really despawn, only reset to default state/flags
863 UseDoorOrButton();
865 // activate script
866 GetMap()->ScriptsStart(sGameObjectScripts, GetDBTableGUIDLow(), spellCaster, this);
867 return;
869 case GAMEOBJECT_TYPE_QUESTGIVER: //2
871 if(user->GetTypeId()!=TYPEID_PLAYER)
872 return;
874 Player* player = (Player*)user;
876 player->PrepareQuestMenu( GetGUID() );
877 player->SendPreparedQuest( GetGUID() );
878 return;
880 //Sitting: Wooden bench, chairs enzz
881 case GAMEOBJECT_TYPE_CHAIR: //7
883 GameObjectInfo const* info = GetGOInfo();
884 if(!info)
885 return;
887 if(user->GetTypeId()!=TYPEID_PLAYER)
888 return;
890 Player* player = (Player*)user;
892 // a chair may have n slots. we have to calculate their positions and teleport the player to the nearest one
894 // check if the db is sane
895 if(info->chair.slots > 0)
897 float lowestDist = DEFAULT_VISIBILITY_DISTANCE;
899 float x_lowest = GetPositionX();
900 float y_lowest = GetPositionY();
902 // the object orientation + 1/2 pi
903 // every slot will be on that straight line
904 float orthogonalOrientation = GetOrientation()+M_PI*0.5f;
905 // find nearest slot
906 for(uint32 i=0; i<info->chair.slots; ++i)
908 // the distance between this slot and the center of the go - imagine a 1D space
909 float relativeDistance = (info->size*i)-(info->size*(info->chair.slots-1)/2.0f);
911 float x_i = GetPositionX() + relativeDistance * cos(orthogonalOrientation);
912 float y_i = GetPositionY() + relativeDistance * sin(orthogonalOrientation);
914 // calculate the distance between the player and this slot
915 float thisDistance = player->GetDistance2d(x_i, y_i);
917 /* debug code. It will spawn a npc on each slot to visualize them.
918 Creature* helper = player->SummonCreature(14496, x_i, y_i, GetPositionZ(), GetOrientation(), TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 10000);
919 std::ostringstream output;
920 output << i << ": thisDist: " << thisDistance;
921 helper->MonsterSay(output.str().c_str(), LANG_UNIVERSAL, 0);
924 if(thisDistance <= lowestDist)
926 lowestDist = thisDistance;
927 x_lowest = x_i;
928 y_lowest = y_i;
931 player->TeleportTo(GetMapId(), x_lowest, y_lowest, GetPositionZ(), GetOrientation(),TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET);
933 else
935 // fallback, will always work
936 player->TeleportTo(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation(),TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET);
938 player->SetStandState(UNIT_STAND_STATE_SIT_LOW_CHAIR+info->chair.height);
939 return;
941 //big gun, its a spell/aura
942 case GAMEOBJECT_TYPE_GOOBER: //10
944 GameObjectInfo const* info = GetGOInfo();
946 if(user->GetTypeId()==TYPEID_PLAYER)
948 Player* player = (Player*)user;
950 // show page
951 if(info->goober.pageId)
953 WorldPacket data(SMSG_GAMEOBJECT_PAGETEXT, 8);
954 data << GetGUID();
955 player->GetSession()->SendPacket(&data);
958 // possible quest objective for active quests
959 player->CastedCreatureOrGO(info->id, GetGUID(), 0);
961 if (info->goober.eventId)
962 GetMap()->ScriptsStart(sEventScripts, info->goober.eventId, player, this);
965 // cast this spell later if provided
966 spellId = info->goober.spellId;
968 break;
970 case GAMEOBJECT_TYPE_CAMERA: //13
972 GameObjectInfo const* info = GetGOInfo();
973 if(!info)
974 return;
976 if(user->GetTypeId()!=TYPEID_PLAYER)
977 return;
979 Player* player = (Player*)user;
981 if (info->camera.cinematicId)
982 player->SendCinematicStart(info->camera.cinematicId);
984 if (info->camera.eventID)
985 GetMap()->ScriptsStart(sEventScripts, info->camera.eventID, player, this);
987 return;
989 //fishing bobber
990 case GAMEOBJECT_TYPE_FISHINGNODE: //17
992 if(user->GetTypeId()!=TYPEID_PLAYER)
993 return;
995 Player* player = (Player*)user;
997 if(player->GetGUID() != GetOwnerGUID())
998 return;
1000 switch(getLootState())
1002 case GO_READY: // ready for loot
1004 // 1) skill must be >= base_zone_skill
1005 // 2) if skill == base_zone_skill => 5% chance
1006 // 3) chance is linear dependence from (base_zone_skill-skill)
1008 uint32 zone, subzone;
1009 GetZoneAndAreaId(zone,subzone);
1011 int32 zone_skill = objmgr.GetFishingBaseSkillLevel( subzone );
1012 if(!zone_skill)
1013 zone_skill = objmgr.GetFishingBaseSkillLevel( zone );
1015 //provide error, no fishable zone or area should be 0
1016 if(!zone_skill)
1017 sLog.outErrorDb("Fishable areaId %u are not properly defined in `skill_fishing_base_level`.",subzone);
1019 int32 skill = player->GetSkillValue(SKILL_FISHING);
1020 int32 chance = skill - zone_skill + 5;
1021 int32 roll = irand(1,100);
1023 DEBUG_LOG("Fishing check (skill: %i zone min skill: %i chance %i roll: %i",skill,zone_skill,chance,roll);
1025 if(skill >= zone_skill && chance >= roll)
1027 // prevent removing GO at spell cancel
1028 player->RemoveGameObject(this,false);
1029 SetOwnerGUID(player->GetGUID());
1031 //fish catched
1032 player->UpdateFishingSkill();
1034 GameObject* ok = LookupFishingHoleAround(DEFAULT_VISIBILITY_DISTANCE);
1035 if (ok)
1037 player->SendLoot(ok->GetGUID(),LOOT_FISHINGHOLE);
1038 player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT, ok->GetGOInfo()->id);
1039 SetLootState(GO_JUST_DEACTIVATED);
1041 else
1042 player->SendLoot(GetGUID(),LOOT_FISHING);
1044 else
1046 // fish escaped, can be deleted now
1047 SetLootState(GO_JUST_DEACTIVATED);
1049 WorldPacket data(SMSG_FISH_ESCAPED, 0);
1050 player->GetSession()->SendPacket(&data);
1052 break;
1054 case GO_JUST_DEACTIVATED: // nothing to do, will be deleted at next update
1055 break;
1056 default:
1058 SetLootState(GO_JUST_DEACTIVATED);
1060 WorldPacket data(SMSG_FISH_NOT_HOOKED, 0);
1061 player->GetSession()->SendPacket(&data);
1062 break;
1066 player->FinishSpell(CURRENT_CHANNELED_SPELL);
1067 return;
1070 case GAMEOBJECT_TYPE_SUMMONING_RITUAL: //18
1072 if(user->GetTypeId()!=TYPEID_PLAYER)
1073 return;
1075 Player* player = (Player*)user;
1077 Unit* caster = GetOwner();
1079 GameObjectInfo const* info = GetGOInfo();
1081 if( !caster || caster->GetTypeId()!=TYPEID_PLAYER )
1082 return;
1084 // accept only use by player from same group for caster except caster itself
1085 if(((Player*)caster)==player || !((Player*)caster)->IsInSameRaidWith(player))
1086 return;
1088 AddUniqueUse(player);
1090 // full amount unique participants including original summoner
1091 if(GetUniqueUseCount() < info->summoningRitual.reqParticipants)
1092 return;
1094 // in case summoning ritual caster is GO creator
1095 spellCaster = caster;
1097 if(!caster->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
1098 return;
1100 spellId = info->summoningRitual.spellId;
1101 if(spellId==62330) // GO store not existed spell, replace by expected
1103 // spell have reagent and mana cost but it not expected use its
1104 // it triggered spell in fact casted at currently channeled GO
1105 spellId = 61993;
1106 triggered = true;
1109 // finish spell
1110 player->FinishSpell(CURRENT_CHANNELED_SPELL);
1112 // can be deleted now
1113 SetLootState(GO_JUST_DEACTIVATED);
1115 // go to end function to spell casting
1116 break;
1118 case GAMEOBJECT_TYPE_SPELLCASTER: //22
1120 SetUInt32Value(GAMEOBJECT_FLAGS,2);
1122 GameObjectInfo const* info = GetGOInfo();
1123 if(!info)
1124 return;
1126 if(info->spellcaster.partyOnly)
1128 Unit* caster = GetOwner();
1129 if( !caster || caster->GetTypeId()!=TYPEID_PLAYER )
1130 return;
1132 if(user->GetTypeId()!=TYPEID_PLAYER || !((Player*)user)->IsInSameRaidWith((Player*)caster))
1133 return;
1136 spellId = info->spellcaster.spellId;
1138 AddUse();
1139 break;
1141 case GAMEOBJECT_TYPE_MEETINGSTONE: //23
1143 GameObjectInfo const* info = GetGOInfo();
1145 if(user->GetTypeId()!=TYPEID_PLAYER)
1146 return;
1148 Player* player = (Player*)user;
1150 Player* targetPlayer = ObjectAccessor::FindPlayer(player->GetSelection());
1152 // accept only use by player from same group for caster except caster itself
1153 if(!targetPlayer || targetPlayer == player || !targetPlayer->IsInSameGroupWith(player))
1154 return;
1156 //required lvl checks!
1157 uint8 level = player->getLevel();
1158 if (level < info->meetingstone.minLevel || level > info->meetingstone.maxLevel)
1159 return;
1160 level = targetPlayer->getLevel();
1161 if (level < info->meetingstone.minLevel || level > info->meetingstone.maxLevel)
1162 return;
1164 if(info->id==194097)
1165 spellId = 61994; // Ritual of Summoning
1166 else
1167 spellId = 59782; // Summoning Stone Effect
1169 break;
1172 case GAMEOBJECT_TYPE_FLAGSTAND: // 24
1174 if(user->GetTypeId()!=TYPEID_PLAYER)
1175 return;
1177 Player* player = (Player*)user;
1179 if( player->CanUseBattleGroundObject() )
1181 // in battleground check
1182 BattleGround *bg = player->GetBattleGround();
1183 if(!bg)
1184 return;
1185 // BG flag click
1186 // AB:
1187 // 15001
1188 // 15002
1189 // 15003
1190 // 15004
1191 // 15005
1192 bg->EventPlayerClickedOnFlag(player, this);
1193 return; //we don;t need to delete flag ... it is despawned!
1195 break;
1197 case GAMEOBJECT_TYPE_FLAGDROP: // 26
1199 if(user->GetTypeId()!=TYPEID_PLAYER)
1200 return;
1202 Player* player = (Player*)user;
1204 if( player->CanUseBattleGroundObject() )
1206 // in battleground check
1207 BattleGround *bg = player->GetBattleGround();
1208 if(!bg)
1209 return;
1210 // BG flag dropped
1211 // WS:
1212 // 179785 - Silverwing Flag
1213 // 179786 - Warsong Flag
1214 // EotS:
1215 // 184142 - Netherstorm Flag
1216 GameObjectInfo const* info = GetGOInfo();
1217 if(info)
1219 switch(info->id)
1221 case 179785: // Silverwing Flag
1222 // check if it's correct bg
1223 if(bg->GetTypeID() == BATTLEGROUND_WS)
1224 bg->EventPlayerClickedOnFlag(player, this);
1225 break;
1226 case 179786: // Warsong Flag
1227 if(bg->GetTypeID() == BATTLEGROUND_WS)
1228 bg->EventPlayerClickedOnFlag(player, this);
1229 break;
1230 case 184142: // Netherstorm Flag
1231 if(bg->GetTypeID() == BATTLEGROUND_EY)
1232 bg->EventPlayerClickedOnFlag(player, this);
1233 break;
1236 //this cause to call return, all flags must be deleted here!!
1237 spellId = 0;
1238 Delete();
1240 break;
1242 case GAMEOBJECT_TYPE_BARBER_CHAIR: //32
1244 GameObjectInfo const* info = GetGOInfo();
1245 if(!info)
1246 return;
1248 if(user->GetTypeId()!=TYPEID_PLAYER)
1249 return;
1251 Player* player = (Player*)user;
1253 // fallback, will always work
1254 player->TeleportTo(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation(),TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET);
1256 WorldPacket data(SMSG_ENABLE_BARBER_SHOP, 0);
1257 player->GetSession()->SendPacket(&data);
1259 player->SetStandState(UNIT_STAND_STATE_SIT_LOW_CHAIR+info->barberChair.chairheight);
1260 return;
1262 default:
1263 sLog.outDebug("Unknown Object Type %u", GetGoType());
1264 break;
1267 if(!spellId)
1268 return;
1270 SpellEntry const *spellInfo = sSpellStore.LookupEntry( spellId );
1271 if(!spellInfo)
1273 sLog.outError("WORLD: unknown spell id %u at use action for gameobject (Entry: %u GoType: %u )", spellId,GetEntry(),GetGoType());
1274 return;
1277 Spell *spell = new Spell(spellCaster, spellInfo, triggered,GetGUID());
1279 // spell target is user of GO
1280 SpellCastTargets targets;
1281 targets.setUnitTarget( user );
1283 spell->prepare(&targets);
1286 // overwrite WorldObject function for proper name localization
1287 const char* GameObject::GetNameForLocaleIdx(int32 loc_idx) const
1289 if (loc_idx >= 0)
1291 GameObjectLocale const *cl = objmgr.GetGameObjectLocale(GetEntry());
1292 if (cl)
1294 if (cl->Name.size() > loc_idx && !cl->Name[loc_idx].empty())
1295 return cl->Name[loc_idx].c_str();
1299 return GetName();
1302 void GameObject::UpdateRotationFields(float rotation2 /*=0.0f*/, float rotation3 /*=0.0f*/)
1304 static double const atan_pow = atan(pow(2.0f, -20.0f));
1306 double f_rot1 = sin(GetOrientation() / 2.0f);
1307 double f_rot2 = cos(GetOrientation() / 2.0f);
1309 int64 i_rot1 = int64(f_rot1 / atan_pow *(f_rot2 >= 0 ? 1.0f : -1.0f));
1310 int64 rotation = (i_rot1 << 43 >> 43) & 0x00000000001FFFFF;
1312 //float f_rot2 = sin(0.0f / 2.0f);
1313 //int64 i_rot2 = f_rot2 / atan(pow(2.0f, -20.0f));
1314 //rotation |= (((i_rot2 << 22) >> 32) >> 11) & 0x000003FFFFE00000;
1316 //float f_rot3 = sin(0.0f / 2.0f);
1317 //int64 i_rot3 = f_rot3 / atan(pow(2.0f, -21.0f));
1318 //rotation |= (i_rot3 >> 42) & 0x7FFFFC0000000000;
1320 m_rotation = rotation;
1322 if(rotation2==0.0f && rotation3==0.0f)
1324 rotation2 = f_rot1;
1325 rotation3 = f_rot2;
1328 SetFloatValue(GAMEOBJECT_PARENTROTATION+2, rotation2);
1329 SetFloatValue(GAMEOBJECT_PARENTROTATION+3, rotation3);