root/trunk/src/game/GameObject.cpp @ 120

Revision 120, 44.6 kB (checked in by yumileroy, 17 years ago)

[svn] * Allow WorldObjects? to keep the grid active, and prevent it from being unloaded. This can be done through calling WorldObject::setActive(bool) from the scripting library. Note that entire instances are still unloaded if no player is present on that map to save resources. This behavior can be changed if the need arises.

Original author: w12x
Date: 2008-10-27 08:41:55-05:00

Line 
1/*
2 * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
3 *
4 * Copyright (C) 2008 Trinity <http://www.trinitycore.org/>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include "Common.h"
22#include "QuestDef.h"
23#include "GameObject.h"
24#include "ObjectMgr.h"
25#include "SpellMgr.h"
26#include "Spell.h"
27#include "UpdateMask.h"
28#include "Opcodes.h"
29#include "WorldPacket.h"
30#include "WorldSession.h"
31#include "World.h"
32#include "Database/DatabaseEnv.h"
33#include "MapManager.h"
34#include "LootMgr.h"
35#include "GridNotifiers.h"
36#include "GridNotifiersImpl.h"
37#include "CellImpl.h"
38#include "InstanceData.h"
39#include "BattleGround.h"
40#include "OutdoorPvPMgr.h"
41#include "Util.h"
42
43GameObject::GameObject() : WorldObject()
44{
45    m_objectType |= TYPEMASK_GAMEOBJECT;
46    m_objectTypeId = TYPEID_GAMEOBJECT;
47                                                            // 2.3.2 - 0x58
48    m_updateFlag = (UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HASPOSITION);
49
50    m_valuesCount = GAMEOBJECT_END;
51    m_respawnTime = 0;
52    m_respawnDelayTime = 25;
53    m_lootState = GO_NOT_READY;
54    m_spawnedByDefault = true;
55    m_usetimes = 0;
56    m_spellId = 0;
57    m_charges = 5;
58    m_cooldownTime = 0;
59    m_goInfo = NULL;
60
61    m_DBTableGuid = 0;
62}
63
64GameObject::~GameObject()
65{
66    if(m_uint32Values)                                      // field array can be not exist if GameOBject not loaded
67    {
68        // crash possable at access to deleted GO in Unit::m_gameobj
69        uint64 owner_guid = GetOwnerGUID();
70        if(owner_guid)
71        {
72            Unit* owner = ObjectAccessor::GetUnit(*this,owner_guid);
73            if(owner)
74                owner->RemoveGameObject(this,false);
75            else if(!IS_PLAYER_GUID(owner_guid))
76                sLog.outError("Delete GameObject (GUID: %u Entry: %u ) that have references in not found creature %u GO list. Crash possable later.",GetGUIDLow(),GetGOInfo()->id,GUID_LOPART(owner_guid));
77        }
78    }
79}
80
81void GameObject::AddToWorld()
82{
83    ///- Register the gameobject for guid lookup
84    if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this);
85    WorldObject::AddToWorld();
86}
87
88void GameObject::RemoveFromWorld()
89{
90    ///- Remove the gameobject from the accessor
91    if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this);
92    WorldObject::RemoveFromWorld();
93}
94
95bool GameObject::Create(uint32 guidlow, uint32 name_id, Map *map, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, uint32 go_state, uint32 ArtKit)
96{
97    Relocate(x,y,z,ang);
98    SetMapId(map->GetId());
99    SetInstanceId(map->GetInstanceId());
100
101    if(!IsPositionValid())
102    {
103        sLog.outError("ERROR: Gameobject (GUID: %u Entry: %u ) not created. Suggested coordinates isn't valid (X: %f Y: %f)",guidlow,name_id,x,y);
104        return false;
105    }
106
107    GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(name_id);
108    if (!goinfo)
109    {
110        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);
111        return false;
112    }
113
114    Object::_Create(guidlow, goinfo->id, HIGHGUID_GAMEOBJECT);
115
116    m_goInfo = goinfo;
117
118    if (goinfo->type >= MAX_GAMEOBJECT_TYPE)
119    {
120        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);
121        return false;
122    }
123
124    SetFloatValue(GAMEOBJECT_POS_X, x);
125    SetFloatValue(GAMEOBJECT_POS_Y, y);
126    SetFloatValue(GAMEOBJECT_POS_Z, z);
127    SetFloatValue(GAMEOBJECT_FACING, ang);                  //this is not facing angle
128
129    SetFloatValue (GAMEOBJECT_ROTATION, rotation0);
130    SetFloatValue (GAMEOBJECT_ROTATION+1, rotation1);
131    SetFloatValue (GAMEOBJECT_ROTATION+2, rotation2);
132    SetFloatValue (GAMEOBJECT_ROTATION+3, rotation3);
133
134    SetFloatValue(OBJECT_FIELD_SCALE_X, goinfo->size);
135
136    SetUInt32Value(GAMEOBJECT_FACTION, goinfo->faction);
137    SetUInt32Value(GAMEOBJECT_FLAGS, goinfo->flags);
138
139    SetUInt32Value(OBJECT_FIELD_ENTRY, goinfo->id);
140
141    SetUInt32Value(GAMEOBJECT_DISPLAYID, goinfo->displayId);
142
143    SetGoState(go_state);
144    SetGoType(GameobjectTypes(goinfo->type));
145
146    SetGoAnimProgress(animprogress);
147
148    SetUInt32Value (GAMEOBJECT_ARTKIT, ArtKit);
149
150    // Spell charges for GAMEOBJECT_TYPE_SPELLCASTER (22)
151    if (goinfo->type == GAMEOBJECT_TYPE_SPELLCASTER)
152        m_charges = goinfo->spellcaster.charges;
153
154    //Notify the map's instance data.
155    //Only works if you create the object in it, not if it is moves to that map.
156    //Normally non-players do not teleport to other maps.
157    if(map->IsDungeon() && ((InstanceMap*)map)->GetInstanceData())
158    {
159        ((InstanceMap*)map)->GetInstanceData()->OnObjectCreate(this);
160    }
161
162    return true;
163}
164
165void GameObject::Update(uint32 /*p_time*/)
166{
167    if (IS_MO_TRANSPORT(GetGUID()))
168    {
169        //((Transport*)this)->Update(p_time);
170        return;
171    }
172
173    switch (m_lootState)
174    {
175        case GO_NOT_READY:
176        {
177            switch(GetGoType())
178            {
179                case GAMEOBJECT_TYPE_TRAP:
180                {
181                    // Arming Time for GAMEOBJECT_TYPE_TRAP (6)
182                    Unit* owner = GetOwner();
183                    if (owner && ((Player*)owner)->isInCombat())
184                        m_cooldownTime = time(NULL) + GetGOInfo()->trap.startDelay;
185                    m_lootState = GO_READY;
186                    break;
187                }
188                case GAMEOBJECT_TYPE_FISHINGNODE:
189                {
190                    // fishing code (bobber ready)
191                    if( time(NULL) > m_respawnTime - FISHING_BOBBER_READY_TIME )
192                    {
193                        // splash bobber (bobber ready now)
194                        Unit* caster = GetOwner();
195                        if(caster && caster->GetTypeId()==TYPEID_PLAYER)
196                        {
197                            SetGoState(0);
198                            SetUInt32Value(GAMEOBJECT_FLAGS, 32);
199
200                            UpdateData udata;
201                            WorldPacket packet;
202                            BuildValuesUpdateBlockForPlayer(&udata,((Player*)caster));
203                            udata.BuildPacket(&packet);
204                            ((Player*)caster)->GetSession()->SendPacket(&packet);
205
206                            WorldPacket data(SMSG_GAMEOBJECT_CUSTOM_ANIM,8+4);
207                            data << GetGUID();
208                            data << (uint32)(0);
209                            ((Player*)caster)->SendMessageToSet(&data,true);
210                        }
211
212                        m_lootState = GO_READY;                 // can be succesfully open with some chance
213                    }
214                    return;
215                }
216                default:
217                    m_lootState = GO_READY;                         // for other GOis same switched without delay to GO_READY
218                    break;
219            }
220            // NO BREAK for switch (m_lootState)
221        }
222        case GO_READY:
223        {
224            if (m_respawnTime > 0)                          // timer on
225            {
226                if (m_respawnTime <= time(NULL))            // timer expired
227                {
228                    m_respawnTime = 0;
229                    m_SkillupList.clear();
230                    m_usetimes = 0;
231
232                    switch (GetGoType())
233                    {
234                        case GAMEOBJECT_TYPE_FISHINGNODE:   //  can't fish now
235                        {
236                            Unit* caster = GetOwner();
237                            if(caster && caster->GetTypeId()==TYPEID_PLAYER)
238                            {
239                                if(caster->m_currentSpells[CURRENT_CHANNELED_SPELL])
240                                {
241                                    caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0);
242                                    caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish(false);
243                                }
244
245                                WorldPacket data(SMSG_FISH_NOT_HOOKED,0);
246                                ((Player*)caster)->GetSession()->SendPacket(&data);
247                            }
248                            // can be delete
249                            m_lootState = GO_JUST_DEACTIVATED;
250                            return;
251                        }
252                        case GAMEOBJECT_TYPE_DOOR:
253                        case GAMEOBJECT_TYPE_BUTTON:
254                            //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)
255                            if( !GetGoState() )
256                                SwitchDoorOrButton(false);
257                            //flags in AB are type_button and we need to add them here so no break!
258                        default:
259                            if(!m_spawnedByDefault)         // despawn timer
260                            {
261                                                            // can be despawned or destroyed
262                                SetLootState(GO_JUST_DEACTIVATED);
263                                return;
264                            }
265                                                            // respawn timer
266                            MapManager::Instance().GetMap(GetMapId(), this)->Add(this);
267                            break;
268                    }
269                }
270            }
271
272            // traps can have time and can not have
273            GameObjectInfo const* goInfo = GetGOInfo();
274            if(goInfo->type == GAMEOBJECT_TYPE_TRAP)
275            {
276                // traps
277                Unit* owner = GetOwner();
278                Unit* ok = NULL;                            // pointer to appropriate target if found any
279
280                if(m_cooldownTime >= time(NULL))
281                    return;
282
283                bool IsBattleGroundTrap = false;
284                //FIXME: this is activation radius (in different casting radius that must be selected from spell data)
285                //TODO: move activated state code (cast itself) to GO_ACTIVATED, in this place only check activating and set state
286                float radius = goInfo->trap.radius;
287                if(!radius)
288                {
289                    if(goInfo->trap.cooldown != 3)            // cast in other case (at some triggring/linked go/etc explicit call)
290                        return;
291                    else
292                    {
293                        if(m_respawnTime > 0)
294                            break;
295
296                        radius = goInfo->trap.cooldown;       // battlegrounds gameobjects has data2 == 0 && data5 == 3
297                        IsBattleGroundTrap = true;
298                    }
299                }
300
301                bool NeedDespawn = (goInfo->trap.charges != 0);
302
303                CellPair p(Trinity::ComputeCellPair(GetPositionX(),GetPositionY()));
304                Cell cell(p);
305                cell.data.Part.reserved = ALL_DISTRICT;
306
307                // Note: this hack with search required until GO casting not implemented
308                // search unfriendly creature
309                if(owner && NeedDespawn)                    // hunter trap
310                {
311                    Trinity::AnyUnfriendlyUnitInObjectRangeCheck u_check(this, owner, radius);
312                    Trinity::UnitSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> checker(ok, u_check);
313
314                    CellLock<GridReadGuard> cell_lock(cell, p);
315
316                    TypeContainerVisitor<Trinity::UnitSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck>, GridTypeMapContainer > grid_object_checker(checker);
317                    cell_lock->Visit(cell_lock, grid_object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
318
319                    // or unfriendly player/pet
320                    if(!ok)
321                    {
322                        TypeContainerVisitor<Trinity::UnitSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck>, WorldTypeMapContainer > world_object_checker(checker);
323                        cell_lock->Visit(cell_lock, world_object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
324                    }
325                }
326                else                                        // environmental trap
327                {
328                    // environmental damage spells already have around enemies targeting but this not help in case not existed GO casting support
329
330                    // affect only players
331                    Player* p_ok = NULL;
332                    Trinity::AnyPlayerInObjectRangeCheck p_check(this, radius);
333                    Trinity::PlayerSearcher<Trinity::AnyPlayerInObjectRangeCheck>  checker(p_ok, p_check);
334
335                    CellLock<GridReadGuard> cell_lock(cell, p);
336
337                    TypeContainerVisitor<Trinity::PlayerSearcher<Trinity::AnyPlayerInObjectRangeCheck>, WorldTypeMapContainer > world_object_checker(checker);
338                    cell_lock->Visit(cell_lock, world_object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
339                    ok = p_ok;
340                }
341
342                if (ok)
343                {
344                    Unit *caster =  owner ? owner : ok;
345
346                    caster->CastSpell(ok, goInfo->trap.spellId, true);
347                    m_cooldownTime = time(NULL) + 4;        // 4 seconds
348
349                    if(NeedDespawn)
350                        SetLootState(GO_JUST_DEACTIVATED);  // can be despawned or destroyed
351
352                    if(IsBattleGroundTrap && ok->GetTypeId() == TYPEID_PLAYER)
353                    {
354                        //BattleGround gameobjects case
355                        if(((Player*)ok)->InBattleGround())
356                            if(BattleGround *bg = ((Player*)ok)->GetBattleGround())
357                                bg->HandleTriggerBuff(GetGUID());
358                    }
359                }
360            }
361
362            if (m_charges && m_usetimes >= m_charges)
363                SetLootState(GO_JUST_DEACTIVATED);          // can be despawned or destroyed
364
365            break;
366        }
367        case GO_ACTIVATED:
368        {
369            switch(GetGoType())
370            {
371                case GAMEOBJECT_TYPE_DOOR:
372                case GAMEOBJECT_TYPE_BUTTON:
373                    if(GetAutoCloseTime() && (m_cooldownTime < time(NULL)))
374                    {
375                        SwitchDoorOrButton(false);
376                        SetLootState(GO_JUST_DEACTIVATED);
377                    }
378                    break;
379            }
380            break;
381        }
382        case GO_JUST_DEACTIVATED:
383        {
384            //if Gameobject should cast spell, then this, but some GOs (type = 10) should be destroyed
385            if (GetGoType() == GAMEOBJECT_TYPE_GOOBER)
386            {
387                uint32 spellId = GetGOInfo()->goober.spellId;
388
389                if(spellId)
390                {
391                    std::set<uint32>::iterator it = m_unique_users.begin();
392                    std::set<uint32>::iterator end = m_unique_users.end();
393                    for (; it != end; it++)
394                    {
395                        Unit* owner = Unit::GetUnit(*this, uint64(*it));
396                        if (owner) owner->CastSpell(owner, spellId, false);
397                    }
398
399                    m_unique_users.clear();
400                    m_usetimes = 0;
401                }
402                //any return here in case battleground traps
403            }
404
405            if(GetOwnerGUID())
406            {
407                m_respawnTime = 0;
408                Delete();
409                return;
410            }
411
412            //burning flags in some battlegrounds, if you find better condition, just add it
413            if (GetGoAnimProgress() > 0)
414            {
415                SendObjectDeSpawnAnim(this->GetGUID());
416                //reset flags
417                SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags);
418            }
419
420            loot.clear();
421            SetLootState(GO_READY);
422
423            if(!m_respawnDelayTime)
424                return;
425
426            if(!m_spawnedByDefault)
427            {
428                m_respawnTime = 0;
429                return;
430            }
431
432            m_respawnTime = time(NULL) + m_respawnDelayTime;
433
434            // if option not set then object will be saved at grid unload
435            if(sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY))
436                SaveRespawnTime();
437
438            ObjectAccessor::UpdateObjectVisibility(this);
439
440            break;
441        }
442    }
443}
444
445void GameObject::Refresh()
446{
447    // not refresh despawned not casted GO (despawned casted GO destroyed in all cases anyway)
448    if(m_respawnTime > 0 && m_spawnedByDefault)
449        return;
450
451    if(isSpawned())
452        MapManager::Instance().GetMap(GetMapId(), this)->Add(this);
453}
454
455void GameObject::AddUniqueUse(Player* player)
456{
457    AddUse();
458    m_unique_users.insert(player->GetGUIDLow());
459}
460
461void GameObject::Delete()
462{
463    SendObjectDeSpawnAnim(GetGUID());
464
465    SetGoState(1);
466    SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags);
467
468    AddObjectToRemoveList();
469}
470
471void GameObject::getFishLoot(Loot *fishloot)
472{
473    fishloot->clear();
474
475    uint32 subzone = GetAreaId();
476
477    // if subzone loot exist use it
478    if(LootTemplates_Fishing.HaveLootFor(subzone))
479        fishloot->FillLoot(subzone, LootTemplates_Fishing, NULL);
480    // else use zone loot
481    else
482        fishloot->FillLoot(GetZoneId(), LootTemplates_Fishing, NULL);
483}
484
485void GameObject::SaveToDB()
486{
487    // this should only be used when the gameobject has already been loaded
488    // perferably after adding to map, because mapid may not be valid otherwise
489    GameObjectData const *data = objmgr.GetGOData(m_DBTableGuid);
490    if(!data)
491    {
492        sLog.outError("GameObject::SaveToDB failed, cannot get gameobject data!");
493        return;
494    }
495
496    SaveToDB(GetMapId(), data->spawnMask);
497}
498
499void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask)
500{
501    const GameObjectInfo *goI = GetGOInfo();
502
503    if (!goI)
504        return;
505   
506    if (!m_DBTableGuid)
507        m_DBTableGuid = GetGUIDLow();
508    // update in loaded data (changing data only in this place)
509    GameObjectData& data = objmgr.NewGOData(m_DBTableGuid);
510
511    // data->guid = guid don't must be update at save
512    data.id = GetEntry();
513    data.mapid = mapid;
514    data.posX = GetFloatValue(GAMEOBJECT_POS_X);
515    data.posY = GetFloatValue(GAMEOBJECT_POS_Y);
516    data.posZ = GetFloatValue(GAMEOBJECT_POS_Z);
517    data.orientation = GetFloatValue(GAMEOBJECT_FACING);
518    data.rotation0 = GetFloatValue(GAMEOBJECT_ROTATION+0);
519    data.rotation1 = GetFloatValue(GAMEOBJECT_ROTATION+1);
520    data.rotation2 = GetFloatValue(GAMEOBJECT_ROTATION+2);
521    data.rotation3 = GetFloatValue(GAMEOBJECT_ROTATION+3);
522    data.spawntimesecs = m_spawnedByDefault ? m_respawnDelayTime : -(int32)m_respawnDelayTime;
523    data.animprogress = GetGoAnimProgress();
524    data.go_state = GetGoState();
525    data.spawnMask = spawnMask;
526    data.ArtKit = GetUInt32Value (GAMEOBJECT_ARTKIT);
527
528    // updated in DB
529    std::ostringstream ss;
530    ss << "INSERT INTO gameobject VALUES ( "
531        << m_DBTableGuid << ", "
532        << GetUInt32Value (OBJECT_FIELD_ENTRY) << ", "
533        << mapid << ", "
534        << (uint32)spawnMask << ", "
535        << GetFloatValue(GAMEOBJECT_POS_X) << ", "
536        << GetFloatValue(GAMEOBJECT_POS_Y) << ", "
537        << GetFloatValue(GAMEOBJECT_POS_Z) << ", "
538        << GetFloatValue(GAMEOBJECT_FACING) << ", "
539        << GetFloatValue(GAMEOBJECT_ROTATION) << ", "
540        << GetFloatValue(GAMEOBJECT_ROTATION+1) << ", "
541        << GetFloatValue(GAMEOBJECT_ROTATION+2) << ", "
542        << GetFloatValue(GAMEOBJECT_ROTATION+3) << ", "
543        << m_respawnDelayTime << ", "
544        << GetGoAnimProgress() << ", "
545        << GetGoState() << ")";
546
547    WorldDatabase.BeginTransaction();
548    WorldDatabase.PExecuteLog("DELETE FROM gameobject WHERE guid = '%u'", m_DBTableGuid);
549    WorldDatabase.PExecuteLog( ss.str( ).c_str( ) );
550    WorldDatabase.CommitTransaction();
551}
552
553bool GameObject::LoadFromDB(uint32 guid, Map *map)
554{
555    GameObjectData const* data = objmgr.GetGOData(guid);
556
557    if( !data )
558    {
559        sLog.outErrorDb("ERROR: Gameobject (GUID: %u) not found in table `gameobject`, can't load. ",guid);
560        return false;
561    }
562
563    uint32 entry = data->id;
564    uint32 map_id = data->mapid;
565    float x = data->posX;
566    float y = data->posY;
567    float z = data->posZ;
568    float ang = data->orientation;
569
570    float rotation0 = data->rotation0;
571    float rotation1 = data->rotation1;
572    float rotation2 = data->rotation2;
573    float rotation3 = data->rotation3;
574
575    uint32 animprogress = data->animprogress;
576    uint32 go_state = data->go_state;
577    uint32 ArtKit = data->ArtKit;
578
579    m_DBTableGuid = guid;
580    if (map->GetInstanceId() != 0) guid = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT);
581
582    if (!Create(guid,entry, map, x, y, z, ang, rotation0, rotation1, rotation2, rotation3, animprogress, go_state, ArtKit) )
583        return false;
584
585    switch(GetGOInfo()->type)
586    {
587        case GAMEOBJECT_TYPE_DOOR:
588        case GAMEOBJECT_TYPE_BUTTON:
589            /* this code (in comment) isn't correct because in battlegrounds we need despawnable doors and buttons, pls remove
590            SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NODESPAWN);
591            m_spawnedByDefault = true;
592            m_respawnDelayTime = 0;
593            m_respawnTime = 0;
594            break;*/
595        default:
596            if(data->spawntimesecs >= 0)
597            {
598                m_spawnedByDefault = true;
599                m_respawnDelayTime = data->spawntimesecs;
600                m_respawnTime = objmgr.GetGORespawnTime(m_DBTableGuid, map->GetInstanceId());
601
602                                                            // ready to respawn
603                if(m_respawnTime && m_respawnTime <= time(NULL))
604                {
605                    m_respawnTime = 0;
606                    objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0);
607                }
608            }
609            else
610            {
611                m_spawnedByDefault = false;
612                m_respawnDelayTime = -data->spawntimesecs;
613                m_respawnTime = 0;
614            }
615            break;
616    }
617
618    return true;
619}
620
621void GameObject::DeleteFromDB()
622{
623    objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0);
624    objmgr.DeleteGOData(m_DBTableGuid);
625    WorldDatabase.PExecuteLog("DELETE FROM gameobject WHERE guid = '%u'", m_DBTableGuid);
626    WorldDatabase.PExecuteLog("DELETE FROM game_event_gameobject WHERE guid = '%u'", m_DBTableGuid);
627}
628
629GameObject* GameObject::GetGameObject(WorldObject& object, uint64 guid)
630{
631    return ObjectAccessor::GetGameObject(object,guid);
632}
633
634GameObjectInfo const *GameObject::GetGOInfo() const
635{
636    return m_goInfo;
637}
638
639uint32 GameObject::GetLootId(GameObjectInfo const* ginfo)
640{
641    if (!ginfo)
642        return 0;
643
644    switch(ginfo->type)
645    {
646        case GAMEOBJECT_TYPE_CHEST:
647            return ginfo->chest.lootId;
648        case GAMEOBJECT_TYPE_FISHINGHOLE:
649            return ginfo->fishinghole.lootId;
650        case GAMEOBJECT_TYPE_FISHINGNODE:
651            return ginfo->fishnode.lootId;
652        default:
653            return 0;
654    }
655}
656
657/*********************************************************/
658/***                    QUEST SYSTEM                   ***/
659/*********************************************************/
660bool GameObject::hasQuest(uint32 quest_id) const
661{
662    QuestRelations const& qr = objmgr.mGOQuestRelations;
663    for(QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr)
664    {
665        if(itr->second==quest_id)
666            return true;
667    }
668    return false;
669}
670
671bool GameObject::hasInvolvedQuest(uint32 quest_id) const
672{
673    QuestRelations const& qr = objmgr.mGOQuestInvolvedRelations;
674    for(QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr)
675    {
676        if(itr->second==quest_id)
677            return true;
678    }
679    return false;
680}
681
682bool GameObject::IsTransport() const
683{
684    // If something is marked as a transport, don't transmit an out of range packet for it.
685    GameObjectInfo const * gInfo = GetGOInfo();
686    if(!gInfo) return false;
687    return gInfo->type == GAMEOBJECT_TYPE_TRANSPORT || gInfo->type == GAMEOBJECT_TYPE_MO_TRANSPORT;
688}
689
690Unit* GameObject::GetOwner() const
691{
692    return ObjectAccessor::GetUnit(*this, GetOwnerGUID());
693}
694
695void GameObject::SaveRespawnTime()
696{
697    if(m_respawnTime > time(NULL) && m_spawnedByDefault)
698        objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),m_respawnTime);
699}
700
701bool GameObject::isVisibleForInState(Player const* u, bool inVisibleList) const
702{
703    // Not in world
704    if(!IsInWorld() || !u->IsInWorld())
705        return false;
706
707    // Transport always visible at this step implementation
708    if(IsTransport() && IsInMap(u))
709        return true;
710
711    // quick check visibility false cases for non-GM-mode
712    if(!u->isGameMaster())
713    {
714        // despawned and then not visible for non-GM in GM-mode
715        if(!isSpawned())
716            return false;
717
718        // special invisibility cases
719        /* TODO: implement trap stealth, take look at spell 2836
720        if(GetGOInfo()->type == GAMEOBJECT_TYPE_TRAP && GetGOInfo()->trap.stealthed && u->IsHostileTo(GetOwner()))
721        {
722            if(check stuff here)
723                return false;
724        }*/
725
726        // Smuggled Mana Cell required 10 invisibility type detection/state
727        if(GetEntry()==187039 && ((u->m_detectInvisibilityMask | u->m_invisibilityMask) & (1<<10))==0)
728            return false;
729    }
730
731    // check distance
732    return IsWithinDistInMap(u,World::GetMaxVisibleDistanceForObject() +
733        (inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f) );
734}
735
736void GameObject::Respawn()
737{
738    if(m_spawnedByDefault && m_respawnTime > 0)
739    {
740        m_respawnTime = time(NULL);
741        objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0);
742    }
743}
744
745bool GameObject::ActivateToQuest( Player *pTarget)const
746{
747    if(!objmgr.IsGameObjectForQuests(GetEntry()))
748        return false;
749
750    switch(GetGoType())
751    {
752        // scan GO chest with loot including quest items
753        case GAMEOBJECT_TYPE_CHEST:
754        {
755            if(LootTemplates_Gameobject.HaveQuestLootForPlayer(GetLootId(), pTarget))
756                return true;
757            break;
758        }
759        case GAMEOBJECT_TYPE_GOOBER:
760        {
761            if(pTarget->GetQuestStatus(GetGOInfo()->goober.questId) == QUEST_STATUS_INCOMPLETE)
762                return true;
763            break;
764        }
765        default:
766            break;
767    }
768
769    return false;
770}
771
772void GameObject::TriggeringLinkedGameObject( uint32 trapEntry, Unit* target)
773{
774    GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(trapEntry);
775    if(!trapInfo || trapInfo->type!=GAMEOBJECT_TYPE_TRAP)
776        return;
777
778    SpellEntry const* trapSpell = sSpellStore.LookupEntry(trapInfo->trap.spellId);
779    if(!trapSpell)                                          // checked at load already
780        return;
781
782    float range = GetSpellMaxRange(sSpellRangeStore.LookupEntry(trapSpell->rangeIndex));
783
784    // search nearest linked GO
785    GameObject* trapGO = NULL;
786    {
787        // using original GO distance
788        CellPair p(Trinity::ComputeCellPair(GetPositionX(), GetPositionY()));
789        Cell cell(p);
790        cell.data.Part.reserved = ALL_DISTRICT;
791
792        Trinity::NearestGameObjectEntryInObjectRangeCheck go_check(*target,trapEntry,range);
793        Trinity::GameObjectLastSearcher<Trinity::NearestGameObjectEntryInObjectRangeCheck> checker(trapGO,go_check);
794
795        TypeContainerVisitor<Trinity::GameObjectLastSearcher<Trinity::NearestGameObjectEntryInObjectRangeCheck>, GridTypeMapContainer > object_checker(checker);
796        CellLock<GridReadGuard> cell_lock(cell, p);
797        cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
798    }
799
800    // found correct GO
801    // FIXME: when GO casting will be implemented trap must cast spell to target
802    if(trapGO)
803        target->CastSpell(target,trapSpell,true);
804}
805
806GameObject* GameObject::LookupFishingHoleAround(float range)
807{
808    GameObject* ok = NULL;
809
810    CellPair p(Trinity::ComputeCellPair(GetPositionX(),GetPositionY()));
811    Cell cell(p);
812    cell.data.Part.reserved = ALL_DISTRICT;
813    Trinity::NearestGameObjectFishingHole u_check(*this, range);
814    Trinity::GameObjectSearcher<Trinity::NearestGameObjectFishingHole> checker(ok, u_check);
815
816    CellLock<GridReadGuard> cell_lock(cell, p);
817
818    TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::NearestGameObjectFishingHole>, GridTypeMapContainer > grid_object_checker(checker);
819    cell_lock->Visit(cell_lock, grid_object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
820
821    return ok;
822}
823
824void GameObject::UseDoorOrButton(uint32 time_to_restore)
825{
826    if(m_lootState != GO_READY)
827        return;
828
829    if(!time_to_restore)
830        time_to_restore = GetAutoCloseTime();
831
832    SwitchDoorOrButton(true);
833    SetLootState(GO_ACTIVATED);
834
835    m_cooldownTime = time(NULL) + time_to_restore;
836
837}
838
839void GameObject::SetGoArtKit(uint32 kit)
840{
841    SetUInt32Value(GAMEOBJECT_ARTKIT, kit);
842    GameObjectData *data = const_cast<GameObjectData*>(objmgr.GetGOData(m_DBTableGuid));
843    if(data)
844        data->ArtKit = kit;
845}
846
847void GameObject::SwitchDoorOrButton(bool activate)
848{
849    if(activate)
850        SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE);
851    else
852        RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE);
853
854    if(GetGoState())                                        //if closed -> open
855        SetGoState(0);
856    else                                                    //if open -> close
857        SetGoState(1);
858}
859
860void GameObject::Use(Unit* user)
861{
862    // by default spell caster is user
863    Unit* spellCaster = user;
864    uint32 spellId = 0;
865
866    switch(GetGoType())
867    {
868        case GAMEOBJECT_TYPE_DOOR:                          //0
869        case GAMEOBJECT_TYPE_BUTTON:                        //1
870            //doors/buttons never really despawn, only reset to default state/flags
871            UseDoorOrButton();
872
873            // activate script
874            sWorld.ScriptsStart(sGameObjectScripts, GetDBTableGUIDLow(), spellCaster, this);
875            return;
876
877        case GAMEOBJECT_TYPE_QUESTGIVER:                    //2
878        {
879            if(user->GetTypeId()!=TYPEID_PLAYER)
880                return;
881
882            Player* player = (Player*)user;
883
884            player->PrepareQuestMenu( GetGUID() );
885            player->SendPreparedQuest( GetGUID() );
886            return;
887        }
888        //Sitting: Wooden bench, chairs enzz
889        case GAMEOBJECT_TYPE_CHAIR:                         //7
890        {
891            GameObjectInfo const* info = GetGOInfo();
892            if(!info)
893                return;
894
895            if(user->GetTypeId()!=TYPEID_PLAYER)
896                return;
897
898            Player* player = (Player*)user;
899
900            // a chair may have n slots. we have to calculate their positions and teleport the player to the nearest one
901
902            // check if the db is sane
903            if(info->chair.slots > 0)
904            {
905                float lowestDist = DEFAULT_VISIBILITY_DISTANCE;
906
907                float x_lowest = GetPositionX();
908                float y_lowest = GetPositionY();
909
910                // the object orientation + 1/2 pi
911                // every slot will be on that straight line
912                float orthogonalOrientation = GetOrientation()+M_PI*0.5f;
913                // find nearest slot
914                for(uint32 i=0; i<info->chair.slots; i++)
915                {
916                    // the distance between this slot and the center of the go - imagine a 1D space
917                    float relativeDistance = (info->size*i)-(info->size*(info->chair.slots-1)/2.0f);
918
919                    float x_i = GetPositionX() + relativeDistance * cos(orthogonalOrientation);
920                    float y_i = GetPositionY() + relativeDistance * sin(orthogonalOrientation);
921
922                    // calculate the distance between the player and this slot
923                    float thisDistance = player->GetDistance2d(x_i, y_i);
924
925                    /* debug code. It will spawn a npc on each slot to visualize them.
926                    Creature* helper = player->SummonCreature(14496, x_i, y_i, GetPositionZ(), GetOrientation(), TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 10000);
927                    std::ostringstream output;
928                    output << i << ": thisDist: " << thisDistance;
929                    helper->MonsterSay(output.str().c_str(), LANG_UNIVERSAL, 0);
930                    */
931
932                    if(thisDistance <= lowestDist)
933                    {
934                        lowestDist = thisDistance;
935                        x_lowest = x_i;
936                        y_lowest = y_i;
937                    }
938                }
939                player->TeleportTo(GetMapId(), x_lowest, y_lowest, GetPositionZ(), GetOrientation(),TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET);
940            }
941            else
942            {
943                // fallback, will always work
944                player->TeleportTo(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation(),TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET);
945            }
946            player->SetStandState(PLAYER_STATE_SIT_LOW_CHAIR+info->chair.height);
947            return;
948        }
949        //big gun, its a spell/aura
950        case GAMEOBJECT_TYPE_GOOBER:                        //10
951        {
952            GameObjectInfo const* info = GetGOInfo();
953
954            if(user->GetTypeId()==TYPEID_PLAYER)
955            {
956                Player* player = (Player*)user;
957
958                // show page
959                if(info->goober.pageId)
960                {
961                    WorldPacket data(SMSG_GAMEOBJECT_PAGETEXT, 8);
962                    data << GetGUID();
963                    player->GetSession()->SendPacket(&data);
964                }
965
966                // possible quest objective for active quests
967                player->CastedCreatureOrGO(info->id, GetGUID(), 0);
968            }
969
970            // cast this spell later if provided
971            spellId = info->goober.spellId;
972
973            break;
974        }
975        case GAMEOBJECT_TYPE_CAMERA:                        //13
976        {
977            GameObjectInfo const* info = GetGOInfo();
978            if(!info)
979                return;
980
981            if(user->GetTypeId()!=TYPEID_PLAYER)
982                return;
983
984            Player* player = (Player*)user;
985
986            if(info->camera.cinematicId)
987            {
988                WorldPacket data(SMSG_TRIGGER_CINEMATIC, 4);
989                data << info->camera.cinematicId;
990                player->GetSession()->SendPacket(&data);
991            }
992            return;
993        }
994        //fishing bobber
995        case GAMEOBJECT_TYPE_FISHINGNODE:                   //17
996        {
997            if(user->GetTypeId()!=TYPEID_PLAYER)
998                return;
999
1000            Player* player = (Player*)user;
1001
1002            if(player->GetGUID() != GetOwnerGUID())
1003                return;
1004
1005            switch(getLootState())
1006            {
1007                case GO_READY:                              // ready for loot
1008                {
1009                    // 1) skill must be >= base_zone_skill
1010                    // 2) if skill == base_zone_skill => 5% chance
1011                    // 3) chance is linear dependence from (base_zone_skill-skill)
1012
1013                    uint32 subzone = GetAreaId();
1014
1015                    int32 zone_skill = objmgr.GetFishingBaseSkillLevel( subzone );
1016                    if(!zone_skill)
1017                        zone_skill = objmgr.GetFishingBaseSkillLevel( GetZoneId() );
1018
1019                    //provide error, no fishable zone or area should be 0
1020                    if(!zone_skill)
1021                        sLog.outErrorDb("Fishable areaId %u are not properly defined in `skill_fishing_base_level`.",subzone);
1022
1023                    int32 skill = player->GetSkillValue(SKILL_FISHING);
1024                    int32 chance = skill - zone_skill + 5;
1025                    int32 roll = irand(1,100);
1026
1027                    DEBUG_LOG("Fishing check (skill: %i zone min skill: %i chance %i roll: %i",skill,zone_skill,chance,roll);
1028
1029                    if(skill >= zone_skill && chance >= roll)
1030                    {
1031                        // prevent removing GO at spell cancel
1032                        player->RemoveGameObject(this,false);
1033                        SetOwnerGUID(player->GetGUID());
1034
1035                        //fish catched
1036                        player->UpdateFishingSkill();
1037
1038                        GameObject* ok = LookupFishingHoleAround(DEFAULT_VISIBILITY_DISTANCE);
1039                        if (ok)
1040                        {
1041                            player->SendLoot(ok->GetGUID(),LOOT_FISHINGHOLE);
1042                            SetLootState(GO_JUST_DEACTIVATED);
1043                        }
1044                        else
1045                            player->SendLoot(GetGUID(),LOOT_FISHING);
1046                    }
1047                    else
1048                    {
1049                        // fish escaped, can be deleted now
1050                        SetLootState(GO_JUST_DEACTIVATED);
1051
1052                        WorldPacket data(SMSG_FISH_ESCAPED, 0);
1053                        player->GetSession()->SendPacket(&data);
1054                    }
1055                    break;
1056                }
1057                case GO_JUST_DEACTIVATED:                   // nothing to do, will be deleted at next update
1058                    break;
1059                default:
1060                {
1061                    SetLootState(GO_JUST_DEACTIVATED);
1062
1063                    WorldPacket data(SMSG_FISH_NOT_HOOKED, 0);
1064                    player->GetSession()->SendPacket(&data);
1065                    break;
1066                }
1067            }
1068
1069            if(player->m_currentSpells[CURRENT_CHANNELED_SPELL])
1070            {
1071                player->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0);
1072                player->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish();
1073            }
1074            return;
1075        }
1076
1077        case GAMEOBJECT_TYPE_SUMMONING_RITUAL:              //18
1078        {
1079            if(user->GetTypeId()!=TYPEID_PLAYER)
1080                return;
1081
1082            Player* player = (Player*)user;
1083
1084            Unit* caster = GetOwner();
1085
1086            GameObjectInfo const* info = GetGOInfo();
1087
1088            if( !caster || caster->GetTypeId()!=TYPEID_PLAYER )
1089                return;
1090
1091            // accept only use by player from same group for caster except caster itself
1092            if(((Player*)caster)==player || !((Player*)caster)->IsInSameRaidWith(player))
1093                return;
1094
1095            AddUniqueUse(player);
1096
1097            // full amount unique participants including original summoner
1098            if(GetUniqueUseCount() < info->summoningRitual.reqParticipants)
1099                return;
1100
1101            // in case summoning ritual caster is GO creator
1102            spellCaster = caster;
1103
1104            if(!caster->m_currentSpells[CURRENT_CHANNELED_SPELL])
1105                return;
1106
1107            spellId = info->summoningRitual.spellId;
1108
1109            // finish spell
1110            caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0);
1111            caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish();
1112
1113            // can be deleted now
1114            SetLootState(GO_JUST_DEACTIVATED);
1115
1116            // go to end function to spell casting
1117            break;
1118        }
1119        case GAMEOBJECT_TYPE_SPELLCASTER:                   //22
1120        {
1121            SetUInt32Value(GAMEOBJECT_FLAGS,2);
1122
1123            GameObjectInfo const* info = GetGOInfo();
1124            if(!info)
1125                return;
1126
1127            if(info->spellcaster.partyOnly)
1128            {
1129                Unit* caster = GetOwner();
1130                if( !caster || caster->GetTypeId()!=TYPEID_PLAYER )
1131                    return;
1132
1133                if(user->GetTypeId()!=TYPEID_PLAYER || !((Player*)user)->IsInSameRaidWith((Player*)caster))
1134                    return;
1135            }
1136
1137            spellId = info->spellcaster.spellId;
1138
1139            AddUse();
1140            break;
1141        }
1142        case GAMEOBJECT_TYPE_MEETINGSTONE:                  //23
1143        {
1144            GameObjectInfo const* info = GetGOInfo();
1145
1146            if(user->GetTypeId()!=TYPEID_PLAYER)
1147                return;
1148
1149            Player* player = (Player*)user;
1150
1151            Player* targetPlayer = ObjectAccessor::FindPlayer(player->GetSelection());
1152
1153            // accept only use by player from same group for caster except caster itself
1154            if(!targetPlayer || targetPlayer == player || !targetPlayer->IsInSameGroupWith(player))
1155                return;
1156
1157            //required lvl checks!
1158            uint8 level = player->getLevel();
1159            if (level < info->meetingstone.minLevel || level > info->meetingstone.maxLevel)
1160                return;
1161            level = targetPlayer->getLevel();
1162            if (level < info->meetingstone.minLevel || level > info->meetingstone.maxLevel)
1163                return;
1164
1165            spellId = 23598;
1166
1167            break;
1168        }
1169
1170        case GAMEOBJECT_TYPE_FLAGSTAND:                     // 24
1171        {
1172            if(user->GetTypeId()!=TYPEID_PLAYER)
1173                return;
1174
1175            Player* player = (Player*)user;
1176
1177            if( player->isAllowUseBattleGroundObject() )
1178            {
1179                // in battleground check
1180                BattleGround *bg = player->GetBattleGround();
1181                if(!bg)
1182                    return;
1183                // BG flag click
1184                // AB:
1185                // 15001
1186                // 15002
1187                // 15003
1188                // 15004
1189                // 15005
1190                bg->EventPlayerClickedOnFlag(player, this);
1191                return;                                     //we don;t need to delete flag ... it is despawned!
1192            }
1193            break;
1194        }
1195        case GAMEOBJECT_TYPE_FLAGDROP:                      // 26
1196        {
1197            if(user->GetTypeId()!=TYPEID_PLAYER)
1198                return;
1199
1200            Player* player = (Player*)user;
1201
1202            if( player->isAllowUseBattleGroundObject() )     
1203            {
1204                // in battleground check
1205                BattleGround *bg = player->GetBattleGround();
1206                if(!bg)
1207                    return;
1208                // BG flag dropped
1209                // WS:
1210                // 179785 - Silverwing Flag
1211                // 179786 - Warsong Flag
1212                // EotS:
1213                // 184142 - Netherstorm Flag
1214                GameObjectInfo const* info = GetGOInfo();
1215                if(info)
1216                {
1217                    switch(info->id)
1218                    {
1219                        case 179785:                        // Silverwing Flag
1220                            // check if it's correct bg
1221                            if(bg->GetTypeID() == BATTLEGROUND_WS)
1222                                bg->EventPlayerClickedOnFlag(player, this);
1223                            break;
1224                        case 179786:                        // Warsong Flag
1225                            if(bg->GetTypeID() == BATTLEGROUND_WS)
1226                                bg->EventPlayerClickedOnFlag(player, this);
1227                            break;
1228                        case 184142:                        // Netherstorm Flag
1229                            if(bg->GetTypeID() == BATTLEGROUND_EY)
1230                                bg->EventPlayerClickedOnFlag(player, this);
1231                            break;
1232                    }
1233                }
1234                //this cause to call return, all flags must be deleted here!!
1235                spellId = 0;
1236                Delete();
1237            }
1238            break;
1239        }
1240        default:
1241            sLog.outDebug("Unknown Object Type %u", GetGoType());
1242            break;
1243    }
1244
1245    if(!spellId)
1246        return;
1247
1248    SpellEntry const *spellInfo = sSpellStore.LookupEntry( spellId );
1249    if(!spellInfo)
1250    {
1251        if(user->GetTypeId()!=TYPEID_PLAYER || !sOutdoorPvPMgr.HandleCustomSpell((Player*)user,spellId,this))
1252            sLog.outError("WORLD: unknown spell id %u at use action for gameobject (Entry: %u GoType: %u )", spellId,GetEntry(),GetGoType());
1253        else
1254            sLog.outDebug("WORLD: %u non-dbc spell was handled by OutdoorPvP", spellId);
1255        return;
1256    }
1257
1258    Spell *spell = new Spell(spellCaster, spellInfo, false);
1259
1260    // spell target is user of GO
1261    SpellCastTargets targets;
1262    targets.setUnitTarget( user );
1263
1264    spell->prepare(&targets);
1265}
Note: See TracBrowser for help on using the browser.