/*
* Ascent MMORPG Server
* Copyright (C) 2005-2007 Ascent Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
#include "StdAfx.h"
GameObject::GameObject(uint32 high, uint32 low)
{
m_objectTypeId = TYPEID_GAMEOBJECT;
m_valuesCount = GAMEOBJECT_END;
m_uint32Values = _fields;
memset(m_uint32Values, 0,(GAMEOBJECT_END)*sizeof(uint32));
m_updateMask.SetCount(GAMEOBJECT_END);
SetUInt32Value( OBJECT_FIELD_TYPE,TYPE_GAMEOBJECT|TYPE_OBJECT);
SetUInt32Value( OBJECT_FIELD_GUID,low);
SetUInt32Value( OBJECT_FIELD_GUID+1,high);
m_wowGuid.Init(GetGUID());
SetFloatValue( OBJECT_FIELD_SCALE_X, 1);//info->Size );
counter=0;//not needed at all but to prevent errors that var was not inited, can be removed in release
pcbannerAura = NULL;
bannerslot = -1;
m_summonedGo = false;
invisible = false;
invisibilityFlag = INVISIBILTY_FLAG_NONE;
spell = 0;
m_summoner = NULL;
charges = -1;
m_ritualcaster = 0;
m_ritualtarget = 0;
m_ritualmembers = NULL;
m_ritualspell = 0;
m_quests = NULL;
pInfo = NULL;
myScript = NULL;
spawnid = 0;
m_spawn = 0;
loot.gold = 0;
m_deleted = false;
mines_remaining = 1;
m_respawnCell=NULL;
}
GameObject::~GameObject()
{
sEventMgr.RemoveEvents(this);
if(m_ritualmembers)
delete[] m_ritualmembers;
uint64 guid = GetUInt64Value(OBJECT_FIELD_CREATED_BY);
if(guid)
{
Player *plr = objmgr.GetPlayer(guid);
if(plr && plr->GetSummonedObject() == this)
plr->SetSummonedObject(NULL);
if(plr == m_summoner)
m_summoner = 0;
}
if(m_respawnCell!=NULL)
m_respawnCell->_respawnObjects.erase(this);
if (m_summonedGo && m_summoner)
for(int i = 0; i < 4; i++)
if (m_summoner->m_ObjectSlots[i] == GetGUIDLow())
m_summoner->m_ObjectSlots[i] = 0;
}
bool GameObject::CreateFromProto(uint32 entry,uint32 mapid, float x, float y, float z, float ang)
{
pInfo= GameObjectNameStorage.LookupEntry(entry);
if(!pInfo)return false;
Object::_Create( mapid, x, y, z, ang );
SetUInt32Value( OBJECT_FIELD_ENTRY, entry );
SetFloatValue( GAMEOBJECT_POS_X, x );
SetFloatValue( GAMEOBJECT_POS_Y, y );
SetFloatValue( GAMEOBJECT_POS_Z, z );
SetFloatValue( GAMEOBJECT_FACING, ang );
//SetUInt32Value( GAMEOBJECT_TIMESTAMP, (uint32)time(NULL));
// SetUInt32Value( GAMEOBJECT_ARTKIT, 0 ); //these must be from wdb somewhere i guess
// SetUInt32Value( GAMEOBJECT_ANIMPROGRESS, 0 );
SetUInt32Value( GAMEOBJECT_STATE, 1 );
SetUInt32Value( GAMEOBJECT_DISPLAYID, pInfo->DisplayID );
SetUInt32Value( GAMEOBJECT_TYPE_ID, pInfo->Type );
InitAI();
return true;
/*
original_flags = m_uint32Values[GAMEOBJECT_FLAGS];
original_state = m_uint32Values[GAMEOBJECT_STATE];
*/
}
/*
void GameObject::Create(uint32 mapid, float x, float y, float z, float ang)
{
Object::_Create( mapid, x, y, z, ang);
SetFloatValue( GAMEOBJECT_POS_X, x);
SetFloatValue( GAMEOBJECT_POS_Y, y );
SetFloatValue( GAMEOBJECT_POS_Z, z );
SetFloatValue( GAMEOBJECT_FACING, ang );
//SetUInt32Value( GAMEOBJECT_TIMESTAMP, (uint32)time(NULL));
}
void GameObject::Create( uint32 guidlow, uint32 guidhigh,uint32 displayid, uint8 state, uint32 entryid, float scale,uint32 typeId, uint32 type,uint32 flags, uint32 mapid, float x, float y, float z, float ang )
{
Object::_Create( mapid, x, y, z, ang);
SetUInt32Value( OBJECT_FIELD_ENTRY, entryid );
SetFloatValue( OBJECT_FIELD_SCALE_X, scale );
SetUInt32Value( GAMEOBJECT_DISPLAYID, displayid );
SetUInt32Value( GAMEOBJECT_STATE, state );
SetUInt32Value( GAMEOBJECT_TYPE_ID, typeId );
SetUInt32Value( GAMEOBJECT_FLAGS, flags );
}*/
void GameObject::TrapSearchTarget()
{
Update(100);
}
void GameObject::Update(uint32 p_time)
{
if(m_event_Instanceid != m_instanceId)
{
event_Relocate();
return;
}
if(!IsInWorld())
return;
if(m_deleted)
return;
if(spell && (GetUInt32Value(GAMEOBJECT_STATE) == 1))
{
if(checkrate > 1)
{
if(counter++%checkrate)
return;
}
ObjectSet::iterator itr = GetInRangeSetBegin();
ObjectSet::iterator it2 = itr;
ObjectSet::iterator iend = GetInRangeSetEnd();
Unit * pUnit;
float dist;
for(; it2 != iend;)
{
itr = it2;
++it2;
dist = GetDistanceSq((*itr));
if( (*itr) != m_summoner && (*itr)->IsUnit() && dist <= range)
{
pUnit = static_cast(*itr);
if(m_summonedGo)
{
if(!m_summoner)
{
ExpireAndDelete();
return;
}
if(!isAttackable(m_summoner,pUnit))continue;
}
Spell * sp=new Spell((Object*)this,spell,true,NULL);
SpellCastTargets tgt((*itr)->GetGUID());
tgt.m_destX = GetPositionX();
tgt.m_destY = GetPositionY();
tgt.m_destZ = GetPositionZ();
sp->prepare(&tgt);
if(m_summonedGo)
{
ExpireAndDelete();
return;
}
if(spell->EffectImplicitTargetA[0] == 16 ||
spell->EffectImplicitTargetB[0] == 16)
return; // on area dont continue.
}
}
}
}
void GameObject::Spawn(MapMgr * m)
{
PushToWorld(m);
CALL_GO_SCRIPT_EVENT(this, OnSpawn)();
}
void GameObject::Despawn(uint32 time)
{
if(!IsInWorld())
return;
loot.items.clear();
//This is for go get deleted while looting
if(m_spawn)
{
SetUInt32Value(GAMEOBJECT_STATE, m_spawn->state);
SetUInt32Value(GAMEOBJECT_FLAGS, m_spawn->flags);
}
WorldPacket data(SMSG_GAMEOBJECT_DESPAWN_ANIM, 8);
data << GetGUID();
SendMessageToSet(&data,true);
CALL_GO_SCRIPT_EVENT(this, OnDespawn)();
if(time)
{
/* Get our originiating mapcell */
MapCell * pCell = m_mapCell;
ASSERT(pCell);
pCell->_respawnObjects.insert( ((Object*)this) );
sEventMgr.RemoveEvents(this);
sEventMgr.AddEvent(m_mapMgr, &MapMgr::EventRespawnGameObject, this, pCell, EVENT_GAMEOBJECT_ITEM_SPAWN, time, 1, 0);
Object::RemoveFromWorld();
m_respawnCell=pCell;
}
else
{
Object::RemoveFromWorld();
ExpireAndDelete();
}
}
void GameObject::SaveToDB()
{
std::stringstream ss;
ss << "INSERT INTO gameobject_spawns VALUES("
<< spawnid << ","
<< GetEntry() << ","
<< GetMapId() << ","
<< GetPositionX() << ","
<< GetPositionY() << ","
<< GetPositionZ() << ","
<< GetOrientation() << ","
<< GetFloatValue(GAMEOBJECT_ROTATION) << ","
<< GetFloatValue(GAMEOBJECT_ROTATION_01) << ","
<< GetFloatValue(GAMEOBJECT_ROTATION_02) << ","
<< GetFloatValue(GAMEOBJECT_ROTATION_03) << ","
<< GetUInt32Value(GAMEOBJECT_STATE) << ","
<< GetUInt32Value(GAMEOBJECT_FLAGS) << ","
<< GetUInt32Value(GAMEOBJECT_FACTION) << ","
<< GetFloatValue(OBJECT_FIELD_SCALE_X) << ","
<< "0)";
WorldDatabase.Execute(ss.str().c_str());
/* std::stringstream ss;
if (!m_sqlid)
m_sqlid = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT);
ss << "DELETE FROM gameobjects WHERE id=" << m_sqlid;
sDatabase.Execute(ss.str().c_str());
ss.rdbuf()->str("");
ss << "INSERT INTO gameobjects VALUES ( "
<< m_sqlid << ", "
<< m_position.x << ", "
<< m_position.y << ", "
<< m_position.z << ", "
<< m_position.o << ", "
<< GetZoneId() << ", "
<< GetMapId() << ", '";
for( uint32 index = 0; index < m_valuesCount; index ++ )
ss << GetUInt32Value(index) << " ";
ss << "', ";
ss << GetEntry() << ", 0, 0)";
sDatabase.Execute( ss.str( ).c_str( ) );*/
}
void GameObject::SaveToFile(std::stringstream & name)
{
/* std::stringstream ss;
if (!m_sqlid)
m_sqlid = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT);
ss.rdbuf()->str("");
ss << "INSERT INTO gameobjects VALUES ( "
<< m_sqlid << ", "
<< m_position.x << ", "
<< m_position.y << ", "
<< m_position.z << ", "
<< m_position.o << ", "
<< GetZoneId() << ", "
<< GetMapId() << ", '";
for( uint32 index = 0; index < m_valuesCount; index ++ )
ss << GetUInt32Value(index) << " ";
ss << "', ";
ss << GetEntry() << ", 0, 0)";
FILE * OutFile;
OutFile = fopen(name.str().c_str(), "wb");
if (!OutFile) return;
fwrite(ss.str().c_str(),1,ss.str().size(),OutFile);
fclose(OutFile);
*/
}
void GameObject::InitAI()
{
if(!pInfo)
return;
// this fixes those fuckers in booty bay
if(pInfo->SpellFocus == 0 &&
pInfo->sound1 == 0 &&
pInfo->sound2 == 0 &&
pInfo->sound3 != 0 &&
pInfo->sound5 != 3 &&
pInfo->sound9 == 1)
return;
if(pInfo->DisplayID == 1027)//Shaman Shrine
{
if(pInfo->ID != 177964 || pInfo->ID != 153556)
{
//Deactivate
//SetUInt32Value(GAMEOBJECT_DYN_FLAGS, 0);
}
}
uint32 spellid = 0;
if(pInfo->Type==GAMEOBJECT_TYPE_TRAP)
{
spellid = pInfo->sound3;
}
else if(pInfo->Type == GAMEOBJECT_TYPE_SPELL_FOCUS)//redirect to properties of another go
{
uint32 new_entry = pInfo->sound2;
if(!new_entry)
return;
pInfo = GameObjectNameStorage.LookupEntry( new_entry );
if(!pInfo)
return;
spellid = pInfo->sound3;
}
else if(pInfo->Type == GAMEOBJECT_TYPE_RITUAL)
{
m_ritualmembers = new uint32[pInfo->SpellFocus];
memset(m_ritualmembers,0,sizeof(uint32)*pInfo->SpellFocus);
}
else if(pInfo->Type == GAMEOBJECT_TYPE_CHEST)
{
Lock *pLock = sLockStore.LookupEntry(GetInfo()->SpellFocus);
if(pLock)
{
for(uint32 i=0; i < 5; i++)
{
if(pLock->locktype[i])
{
if(pLock->locktype[i] == 2) //locktype;
{
//herbalism and mining;
if(pLock->lockmisc[i] == LOCKTYPE_MINING || pLock->lockmisc[i] == LOCKTYPE_HERBALISM)
{
mines_remaining = GetInfo()->sound4 + sRand.randInt(GetInfo()->sound5 - GetInfo()->sound4);
}
}
}
}
}
}
myScript = sScriptMgr.CreateAIScriptClassForGameObject(GetEntry(), this);
// hackfix for bad spell in BWL
if(!spellid || spellid == 22247)
return;
SpellEntry *sp= sSpellStore.LookupEntry(spellid);
if(!sp)
{
spell = NULL;
return;
}
else
{
spell = sp;
}
//ok got valid spell that will be casted on target when it comes close enough
//get the range for that
float r = 0;
for(uint32 i=0;i<3;i++)
{
if(sp->Effect[i])
{
float t = GetRadius(sSpellRadius.LookupEntry(sp->EffectRadiusIndex[i]));
if(t > r)
r = t;
}
}
if(r < 0.1)//no range
r = GetMaxRange(sSpellRange.LookupEntry(sp->rangeIndex));
range = r*r;//square to make code faster
checkrate = 20;//once in 2 seconds
}
bool GameObject::Load(GOSpawn *spawn)
{
if(!CreateFromProto(spawn->entry,0,spawn->x,spawn->y,spawn->z,spawn->facing))
return false;
spawnid = spawn->id;
m_spawn = spawn;
SetFloatValue(GAMEOBJECT_ROTATION,spawn->o);
SetFloatValue(GAMEOBJECT_ROTATION_01 ,spawn->o1);
SetFloatValue(GAMEOBJECT_ROTATION_02 ,spawn->o2);
SetFloatValue(GAMEOBJECT_ROTATION_03 ,spawn->o3);
SetUInt32Value(GAMEOBJECT_FLAGS,spawn->flags);
// SetUInt32Value(GAMEOBJECT_LEVEL,spawn->level);
SetUInt32Value(GAMEOBJECT_STATE,spawn->state);
if(spawn->faction)
{
SetUInt32Value(GAMEOBJECT_FACTION,spawn->faction);
m_faction = sFactionTmpStore.LookupEntry(spawn->faction);
if(m_faction)
m_factionDBC = sFactionStore.LookupEntry(m_faction->Faction);
}
SetFloatValue(OBJECT_FIELD_SCALE_X,spawn->scale);
_LoadQuests();
CALL_GO_SCRIPT_EVENT(this, OnCreate)();
CALL_GO_SCRIPT_EVENT(this, OnSpawn)();
InitAI();
_LoadQuests();
return true;
}
void GameObject::DeleteFromDB()
{
WorldDatabase.Execute("DELETE FROM gameobject_spawns WHERE id=%u", spawnid);
}
void GameObject::EventCloseDoor()
{
SetUInt32Value(GAMEOBJECT_STATE, 0);
}
void GameObject::UseFishingNode(Player *player)
{
sEventMgr.RemoveEvents(this);
if(GetUInt32Value(GAMEOBJECT_FLAGS) != 32)//click on bobber before somth is hooked
{
player->GetSession()->OutPacket(SMSG_FISH_NOT_HOOKED);
EndFishing(player,true);
return;
}
uint32 zone=/*sAreaStore.LookupEntry(GetMapMgr()->GetAreaID(GetPositionX(),GetPositionY()))->ZoneId*/player->GetZoneId();
FishingZoneEntry *entry = FishingZoneStorage.LookupEntry(zone);
if(entry == NULL)
{
sLog.outError("ERROR: Fishing zone information for zone %d not found!", zone);
EndFishing(player, true);
return;
}
uint32 maxskill=entry->MaxSkill;
// uint32 minskill=entry->MaxSkill;
uint32 minskill=entry->MinSkill;
if(player->_GetSkillLineCurrent(SKILL_FISHING,false)_AdvanceSkillLine(SKILL_FISHING,float2int32( 1.0f * sWorld.getRate(RATE_SKILLRATE)));
//Open loot on success, otherwise FISH_ESCAPED.
if(Rand(((player->_GetSkillLineCurrent(SKILL_FISHING,true)-minskill)*100)/maxskill))
{
lootmgr.FillProfessionLoot(&lootmgr.FishingLoot,&loot,zone);
player->SendLoot(GetGUID(),3);
EndFishing(player, false);
}
else //failed
{
player->GetSession()->OutPacket(SMSG_FISH_ESCAPED);
EndFishing(player,true);
}
}
void GameObject::EndFishing(Player* player, bool abort )
{
Spell * spell = player->GetCurrentSpell();
if(spell)
{
if(abort) // abort becouse of a reason
{
//FIXME: here 'failed' should appear over progress bar
spell->SendChannelUpdate(0);
//spell->cancel();
spell->finish();
}
else // spell ended
{
spell->SendChannelUpdate(0);
spell->finish();
}
}
if(!abort)
sEventMgr.AddEvent(this, &GameObject::ExpireAndDelete, EVENT_GAMEOBJECT_EXPIRE, 10000, 1,EVENT_FLAG_DO_NOT_EXECUTE_IN_WORLD_CONTEXT);
else
ExpireAndDelete();
}
void GameObject::FishHooked(Player * player)
{
WorldPacket data;
data.Initialize(SMSG_GAMEOBJECT_CUSTOM_ANIM);
data << GetGUID();
data << (uint32)(0);
player->GetSession()->SendPacket(&data);
//SetUInt32Value(GAMEOBJECT_STATE, 0);
//BuildFieldUpdatePacket(player, GAMEOBJECT_FLAGS, 32);
SetUInt32Value(GAMEOBJECT_FLAGS, 32);
}
/////////////
/// Quests
void GameObject::AddQuest(QuestRelation *Q)
{
m_quests->push_back(Q);
}
void GameObject::DeleteQuest(QuestRelation *Q)
{
list::iterator it;
for ( it = m_quests->begin(); it != m_quests->end(); ++it )
{
if (((*it)->type == Q->type) && ((*it)->qst == Q->qst ))
{
delete (*it);
m_quests->erase(it);
break;
}
}
}
Quest* GameObject::FindQuest(uint32 quest_id, uint8 quest_relation)
{
list::iterator it;
for (it = m_quests->begin(); it != m_quests->end(); ++it)
{
QuestRelation *ptr = (*it);
if ((ptr->qst->id == quest_id) && (ptr->type & quest_relation))
{
return ptr->qst;
}
}
return NULL;
}
uint16 GameObject::GetQuestRelation(uint32 quest_id)
{
uint16 quest_relation = 0;
list::iterator it;
for (it = m_quests->begin(); it != m_quests->end(); ++it)
{
if ((*it)->qst->id == quest_id)
{
quest_relation |= (*it)->type;
}
}
return quest_relation;
}
uint32 GameObject::NumOfQuests()
{
return m_quests->size();
}
void GameObject::_LoadQuests()
{
sQuestMgr.LoadGOQuests(this);
}
/////////////////
// Summoned Go's
void GameObject::_Expire()
{
sEventMgr.RemoveEvents(this);
if(IsInWorld())
RemoveFromWorld();
//sEventMgr.AddEvent(World::getSingletonPtr(), &World::DeleteObject, ((Object*)this), EVENT_DELETE_TIMER, 1000, 1);
delete this;
}
void GameObject::ExpireAndDelete()
{
if(m_deleted)
return;
m_deleted = true;
/* remove any events */
sEventMgr.RemoveEvents(this);
sEventMgr.AddEvent(this, &GameObject::_Expire, EVENT_GAMEOBJECT_EXPIRE, 1, 1,EVENT_FLAG_DO_NOT_EXECUTE_IN_WORLD_CONTEXT);
}
void GameObject::Deactivate()
{
SetUInt32Value(GAMEOBJECT_DYN_FLAGS, 0);
}
void GameObject::CallScriptUpdate()
{
ASSERT(myScript);
myScript->AIUpdate();
}
void GameObject::OnPushToWorld()
{
Object::OnPushToWorld();
/* script */
ScriptSystem->OnGameObjectEvent(this, 0, GAMEOBJECT_EVENT_ON_SPAWN);
}
void GameObject::OnRemoveInRangeObject(Object* pObj)
{
Object::OnRemoveInRangeObject(pObj);
if(m_summonedGo && m_summoner == pObj)
{
for(int i = 0; i < 4; i++)
if (m_summoner->m_ObjectSlots[i] == GetGUIDLow())
m_summoner->m_ObjectSlots[i] = 0;
m_summoner = 0;
ExpireAndDelete();
}
}
void GameObject::RemoveFromWorld()
{
sEventMgr.RemoveEvents(this, EVENT_GAMEOBJECT_TRAP_SEARCH_TARGET);
Object::RemoveFromWorld();
}
bool GameObject::HasLoot()
{
int count=0;
for(vector<__LootItem>::iterator itr = loot.items.begin(); itr != loot.items.end(); ++itr)
count += (itr)->iItemsCount;
return count;
}