/*
* Copyright (C) 2005,2006 MaNGOS
*
* 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 2 of the License, or
* (at your option) 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "Common.h"
#include "Database/DatabaseEnv.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "World.h"
#include "ObjectMgr.h"
#include "Creature.h"
#include "QuestDef.h"
#include "GossipDef.h"
#include "Player.h"
#include "Opcodes.h"
#include "Log.h"
#include "LootMgr.h"
#include "Chat.h"
#include "MapManager.h"
#include "CreatureAI.h"
#include "CreatureAISelector.h"
#include "Pet.h"
#include "Formulas.h"
// apply implementation of the singletons
#include "Policies/SingletonImp.h"
uint32 CreatureInfo::randomDisplayID() const
{
if(DisplayID_f==0)
return DisplayID_m;
else if(DisplayID_m==0)
return DisplayID_f;
else
return urand(0,1) ? DisplayID_m : DisplayID_f;
}
Creature::Creature() :
Unit(), i_AI(NULL), lootForPickPocketed(false), lootForBody(false), m_lootMoney(0),
m_deathTimer(0), m_respawnTimer(0), m_respawnDelay(25000), m_corpseDelay(60000), m_respawnradius(0.0),
itemcount(0), mTaxiNode(0), m_moveBackward(false), m_moveRandom(false),
m_moveRun(false), m_emoteState(0), m_isPet(false), m_isTotem(false), m_isTamed(false),
m_regenTimer(2000), m_defaultMovementType(IDLE_MOTION_TYPE)
{
m_valuesCount = UNIT_END;
memset(item_list, 0, sizeof(CreatureItem)*MAX_CREATURE_ITEMS);
for(int i =0; i<3; ++i) respawn_cord[i] = 0.0;
m_spells[0] = 0;
m_spells[1] = 0;
m_spells[2] = 0;
m_spells[3] = 0;
m_AlreadyCallAssistence = false;
}
Creature::~Creature()
{
CombatStop();
RemoveAllAuras();
for( SpellsList::iterator i = m_tspells.begin( ); i != m_tspells.end( ); i++ )
delete (*i);
m_tspells.clear();
delete i_AI;
i_AI = NULL;
}
void Creature::CreateTrainerSpells()
{
for(SpellsList::iterator i = m_tspells.begin(); i != m_tspells.end(); ++i)
delete *i;
m_tspells.clear();
TrainerSpell *tspell;
Field *fields;
QueryResult *result = sDatabase.PQuery("SELECT `spell`,`spellcost`,`reqspell`,`reqskill`,`reqskillvalue`,`reqlevel` FROM `npc_trainer` WHERE `entry` = '%u'", GetCreatureInfo()->Entry);
if(!result) return;
SpellEntry * spellinfo;
do
{
fields = result->Fetch();
uint32 spellid = fields[0].GetUInt32();
spellinfo = sSpellStore.LookupEntry(spellid);
if(!spellinfo)
{
sLog.outError("Trainer (GUID: %u ID: %u ) have in list non existed spell %u",GetGUIDLow(),GetEntry(),spellid);
continue;
}
tspell = new TrainerSpell;
tspell->spell = spellinfo;
tspell->spellcost = fields[1].GetUInt32();
tspell->reqspell = fields[2].GetUInt32();
tspell->reqskill = fields[3].GetUInt32();
tspell->reqskillvalue = fields[4].GetUInt32();
tspell->reqlevel = fields[5].GetUInt32();
if(tspell->reqspell && !sSpellStore.LookupEntry(tspell->reqspell))
{
sLog.outError("Trainer (GUID: %u ID: %u ) have for spell %u non-existed required spell %u (requirement ignored)",GetGUIDLow(),GetEntry(),spellid,tspell->reqspell);
tspell->reqspell = 0;
}
m_tspells.push_back(tspell);
} while( result->NextRow() );
delete result;
}
bool Creature::isCanSwimOrFly() const
{
uint32 family = GetCreatureInfo()->family;
// Creature that can swim in water or fly above water or walk by bottom (maybe wrong family filter - please fix)
return (family != 3 && family != 10 && family != 11 && family != 12 && family != 20 && family != 21 && family != 27 );
}
bool Creature::isCanWalkOrFly() const
{
//uint32 family = GetCreatureInfo()->family;
// Creature that can walk by ground or fly above ground (maybe wrong family filter - please fix)
// I not found special family for fish :(
return true;
}
void Creature::AIM_Update(const uint32 &diff)
{
switch( m_deathState )
{
case JUST_DIED:
assert(false); // Dont must be called, see Creature::setDeathState JUST_DIED -> CORPSE promoting.
break;
case DEAD:
{
if( m_respawnTimer <= diff )
{
DEBUG_LOG("Respawning...");
CreatureInfo const *cinfo = objmgr.GetCreatureTemplate(this->GetEntry());
SelectLevel(cinfo);
SetUInt32Value(UNIT_DYNAMIC_FLAGS, 0);
RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
SetUInt32Value(UNIT_NPC_FLAGS, cinfo->npcflag);
SetHealth(GetMaxHealth());
setDeathState( ALIVE );
clearUnitState(UNIT_STAT_ALL_STATE);
i_motionMaster.Clear();
MapManager::Instance().GetMap(GetMapId())->Add(this);
}
else
m_respawnTimer -= diff;
break;
}
case CORPSE:
{
if( m_deathTimer <= diff )
{
m_deathTimer = 0;
DEBUG_LOG("Removing corpse... %u ", GetUInt32Value(OBJECT_FIELD_ENTRY));
ObjectAccessor::Instance().RemoveCreatureCorpseFromPlayerView(this);
lootForPickPocketed = false;
lootForBody = false;
loot.clear();
setDeathState(DEAD);
m_respawnTimer = m_respawnDelay;
if ( isTamed() )
Untamed();
float x,y,z;
GetRespawnCoord(x, y, z);
MapManager::Instance().GetMap(GetMapId())->CreatureRelocation(this,x,y,z,GetOrientation());
}
else
m_deathTimer -= diff;
break;
}
case ALIVE:
{
if(isPet() || isTamed())
{
// unsummon pet that lost owner
Unit* owner = GetOwner();
if(!owner||!IsWithinDistInMap(owner, OWNER_MAX_DISTANCE))
{
if(isPet())
((Pet*)this)->Unsummon();
else
if(isTamed())
Untamed();
return;
}
}
Unit::Update( diff );
i_motionMaster.UpdateMotion(diff);
i_AI->UpdateAI(diff);
if(m_regenTimer > 0)
{
if(diff >= m_regenTimer)
m_regenTimer = 0;
else
m_regenTimer -= diff;
}
if (m_regenTimer != 0)
break;
if (!isInCombat())
{
RegenerateHealth();
RegenerateMana();
}
m_regenTimer = 2000;
break;
}
default:
break;
}
}
void Creature::RegenerateMana()
{
uint32 curValue = GetPower(POWER_MANA);
uint32 maxValue = GetMaxPower(POWER_MANA);
if (curValue >= maxValue) return;
float ManaIncreaseRate = sWorld.getRate(RATE_POWER_MANA);
float Spirit = GetStat(STAT_SPIRIT);
if( ManaIncreaseRate <= 0 ) ManaIncreaseRate = 1;
uint32 addvalue = 0;
if (isInCombat() || isPet())
addvalue = uint32((Spirit/5 + 17) * ManaIncreaseRate);
else
addvalue = maxValue/3;
ModifyPower(POWER_MANA, addvalue);
}
void Creature::RegenerateHealth()
{
uint32 curValue = GetHealth();
uint32 maxValue = GetMaxHealth();
if (curValue >= maxValue) return;
float HealthIncreaseRate = sWorld.getRate(RATE_HEALTH);
float Spirit = GetStat(STAT_SPIRIT);
if( HealthIncreaseRate <= 0 ) HealthIncreaseRate = 1;
uint32 addvalue = 0;
if(isPet())
{
if( GetPower(POWER_MANA) > 0 )
addvalue = uint32(Spirit * 0.25 * HealthIncreaseRate);
else
addvalue = uint32(Spirit * 0.80 * HealthIncreaseRate);
}
else
addvalue = maxValue/3;
ModifyHealth(addvalue);
}
void Creature::AIM_Initialize()
{
i_motionMaster.Initialize(this);
i_AI = FactorySelector::selectAI(this);
}
void Creature::Update( uint32 p_time )
{
AIM_Update( p_time );
}
bool Creature::Create (uint32 guidlow, uint32 mapid, float x, float y, float z, float ang, uint32 Entry)
{
respawn_cord[0] = x;
respawn_cord[1] = y;
respawn_cord[2] = z;
m_mapId =mapid;
m_positionX =x;
m_positionY=y;
m_positionZ=z;
if(!IsPositionValid())
{
sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %d Y: ^%d)",guidlow,Entry,x,y);
return false;
}
m_orientation=ang;
//oX = x; oY = y; dX = x; dY = y; m_moveTime = 0; m_startMove = 0;
return CreateFromProto(guidlow, Entry);
}
uint32 Creature::getDialogStatus(Player *pPlayer, uint32 defstatus)
{
uint32 result = DIALOG_STATUS_NONE;
uint32 status;
uint32 quest_id;
Quest *pQuest;
for( std::list::iterator i = mInvolvedQuests.begin( ); i != mInvolvedQuests.end( ); i++ )
{
quest_id = *i;
pQuest = objmgr.QuestTemplates[quest_id];
status = pPlayer->GetQuestStatus( quest_id );
if ((status == QUEST_STATUS_COMPLETE && !pPlayer->GetQuestRewardStatus(quest_id)) ||
((strlen(pQuest->GetObjectives()) == 0) && pPlayer->CanTakeQuest(pQuest, false)))
{
SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TRACK_UNIT);
if ( pQuest->IsRepeatable() )
return DIALOG_STATUS_REWARD_REP;
else
return DIALOG_STATUS_REWARD;
}
else if ( status == QUEST_STATUS_INCOMPLETE )
result = DIALOG_STATUS_INCOMPLETE;
}
RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TRACK_UNIT);
if ( result == DIALOG_STATUS_INCOMPLETE )
return result;
for( std::list::iterator i = mQuests.begin( ); i != mQuests.end( ); i++ )
{
pQuest = objmgr.QuestTemplates[*i];
if ( !pQuest )
continue;
quest_id = pQuest->GetQuestId();
status = pPlayer->GetQuestStatus( quest_id );
if ( status == QUEST_STATUS_NONE )
{
if ( pPlayer->CanSeeStartQuest( quest_id ) )
{
if ( pPlayer->SatisfyQuestLevel(quest_id, false) )
{
if ( pQuest->IsRepeatable() )
return DIALOG_STATUS_REWARD_REP;
else
return DIALOG_STATUS_AVAILABLE;
}
result = DIALOG_STATUS_UNAVAILABLE;
}
}
}
// can train and help unlearn talentes (2 action -> chat menu)
if( isCanTrainingAndResetTalentsOf(pPlayer) )
return DIALOG_STATUS_CHAT;
if ( result == DIALOG_STATUS_UNAVAILABLE )
return result;
if ( defstatus == DIALOG_STATUS_NONE )
return DIALOG_STATUS_NONE;
else
return DIALOG_STATUS_CHAT;
}
bool Creature::isCanTrainingOf(Player* pPlayer, bool msg) const
{
if(!isTrainer())
return false;
if(m_tspells.empty())
{
sLog.outError("Creature %u (Entry: %u) have UNIT_NPC_FLAG_TRAINER but have empty trainer spell list.",
GetGUIDLow(),GetCreatureInfo()->Entry);
return false;
}
switch(GetCreatureInfo()->trainer_type)
{
case TRAINER_TYPE_CLASS:
if(pPlayer->getClass()!=GetCreatureInfo()->classNum)
{
if(msg)
{
pPlayer->PlayerTalkClass->ClearMenus();
switch(GetCreatureInfo()->classNum)
{
case CLASS_DRUID: pPlayer->PlayerTalkClass->SendGossipMenu( 4913,GetGUID()); break;
case CLASS_HUNTER: pPlayer->PlayerTalkClass->SendGossipMenu(10090,GetGUID()); break;
case CLASS_MAGE: pPlayer->PlayerTalkClass->SendGossipMenu( 328,GetGUID()); break;
case CLASS_PALADIN:pPlayer->PlayerTalkClass->SendGossipMenu( 1635,GetGUID()); break;
case CLASS_PRIEST: pPlayer->PlayerTalkClass->SendGossipMenu( 4436,GetGUID()); break;
case CLASS_ROGUE: pPlayer->PlayerTalkClass->SendGossipMenu( 4797,GetGUID()); break;
case CLASS_SHAMAN: pPlayer->PlayerTalkClass->SendGossipMenu( 5003,GetGUID()); break;
case CLASS_WARLOCK:pPlayer->PlayerTalkClass->SendGossipMenu( 5836,GetGUID()); break;
case CLASS_WARRIOR:pPlayer->PlayerTalkClass->SendGossipMenu( 4985,GetGUID()); break;
}
}
return false;
}
break;
case TRAINER_TYPE_PETS:
if(pPlayer->getClass()!=CLASS_HUNTER)
{
pPlayer->PlayerTalkClass->ClearMenus();
pPlayer->PlayerTalkClass->SendGossipMenu(3620,GetGUID());
return false;
}
break;
case TRAINER_TYPE_MOUNTS:
if(GetCreatureInfo()->race && pPlayer->getRace() != GetCreatureInfo()->race)
{
if(msg)
{
pPlayer->PlayerTalkClass->ClearMenus();
switch(GetCreatureInfo()->classNum)
{
case RACE_DWARF: pPlayer->PlayerTalkClass->SendGossipMenu(5865,GetGUID()); break;
case RACE_GNOME: pPlayer->PlayerTalkClass->SendGossipMenu(4881,GetGUID()); break;
case RACE_HUMAN: pPlayer->PlayerTalkClass->SendGossipMenu(5861,GetGUID()); break;
case RACE_NIGHTELF: pPlayer->PlayerTalkClass->SendGossipMenu(5862,GetGUID()); break;
case RACE_ORC: pPlayer->PlayerTalkClass->SendGossipMenu(5863,GetGUID()); break;
case RACE_TAUREN: pPlayer->PlayerTalkClass->SendGossipMenu(5864,GetGUID()); break;
case RACE_TROLL: pPlayer->PlayerTalkClass->SendGossipMenu(5816,GetGUID()); break;
case RACE_UNDEAD_PLAYER:pPlayer->PlayerTalkClass->SendGossipMenu( 624,GetGUID()); break;
}
}
return false;
}
break;
case TRAINER_TYPE_TRADESKILLS:
if(GetCreatureInfo()->trainer_spell && !pPlayer->HasSpell(GetCreatureInfo()->trainer_spell))
{
if(msg)
{
pPlayer->PlayerTalkClass->ClearMenus();
pPlayer->PlayerTalkClass->SendGossipMenu(11031,GetGUID());
}
return false;
}
break;
default:
sLog.outError("Creature %u (entry: %u) have trainer type %u",GetGUIDLow(),GetCreatureInfo()->Entry,GetCreatureInfo()->trainer_type);
return false;
}
return true;
}
bool Creature::isCanTrainingAndResetTalentsOf(Player* pPlayer) const
{
return pPlayer->getLevel() >= 10
&& GetCreatureInfo()->trainer_type == TRAINER_TYPE_CLASS
&& pPlayer->getClass() == GetCreatureInfo()->classNum;
}
void Creature::prepareGossipMenu( Player *pPlayer,uint32 gossipid )
{
PlayerMenu* pm=pPlayer->PlayerTalkClass;
pm->ClearMenus();
if(!m_goptions.size())
LoadGossipOptions();
GossipOption* gso;
GossipOption* ingso;
for( std::list::iterator i = m_goptions.begin( ); i != m_goptions.end( ); i++ )
{
gso=*i;
if(gso->GossipId == gossipid)
{
bool cantalking=true;
if(gso->Id==1)
{
uint32 textid=GetNpcTextId();
GossipText * gossiptext=objmgr.GetGossipText(textid);
if(!gossiptext)
cantalking=false;
}
else
{
switch (gso->Action)
{
case GOSSIP_OPTION_QUESTGIVER:
pPlayer->PrepareQuestMenu(GetGUID());
//if (pm->GetQuestMenu()->MenuItemCount() == 0)
cantalking=false;
//pm->GetQuestMenu()->ClearMenu();
break;
case GOSSIP_OPTION_ARMORER:
cantalking=false; // added in special mode
break;
case GOSSIP_OPTION_SPIRITHEALER:
if( !pPlayer->isDead() )
cantalking=false;
break;
case GOSSIP_OPTION_VENDOR:
if(!GetItemCount())
{
sLog.outError("Creature %u (Entry: %u) have UNIT_NPC_FLAG_VENDOR but have empty trading item list.",
GetGUIDLow(),GetCreatureInfo()->Entry);
cantalking=false;
}
break;
case GOSSIP_OPTION_TRAINER:
if(!isCanTrainingOf(pPlayer,false))
cantalking=false;
break;
case GOSSIP_OPTION_UNLEARNTALENTS:
if(!isCanTrainingAndResetTalentsOf(pPlayer))
cantalking=false;
break;
case GOSSIP_OPTION_TAXIVENDOR:
case GOSSIP_OPTION_GUARD:
case GOSSIP_OPTION_INNKEEPER:
case GOSSIP_OPTION_BANKER:
case GOSSIP_OPTION_PETITIONER:
case GOSSIP_OPTION_STABLEPET:
case GOSSIP_OPTION_TABARDVENDOR:
case GOSSIP_OPTION_BATTLEFIELD:
case GOSSIP_OPTION_AUCTIONEER:
break; // no checks
default:
sLog.outError("Creature %u (entry: %u) have unknown gossip option %u",GetGUIDLow(),GetCreatureInfo()->Entry,gso->Action);
break;
}
}
if(gso->Option!="" && cantalking )
{
pm->GetGossipMenu()->AddMenuItem((uint8)gso->Icon,gso->Option.c_str(), gossipid,gso->Action,false);
ingso=gso;
}
}
}
if(pm->GetGossipMenu()->MenuItemCount()==0 && HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_TRAINER))
isCanTrainingOf(pPlayer,true);
/*
if(pm->GetGossipMenu()->MenuItemCount()==1 && ingso->Id==8 && GetGossipCount( ingso->GossipId )>0)
{
pm->ClearMenus();
for( std::list::iterator i = m_goptions.begin( ); i != m_goptions.end( ); i++ )
{
gso=*i;
if(gso->GossipId==ingso->Id)
{
if(gso->Option!="")
pm->GetGossipMenu()->AddMenuItem((uint8)gso->Icon,gso->Option.c_str(),ingso->GossipId,gso->Action,false);
}
}
}
*/
}
void Creature::sendPreparedGossip(Player* player)
{
if(!player)
return;
GossipMenu* gossipmenu = player->PlayerTalkClass->GetGossipMenu();
// in case empty gossip menu open quest menu if any
if (gossipmenu->MenuItemCount() == 0)
{
player->SendPreparedQuest(GetGUID());
return;
}
// in case non empty gossip menu (that not included quests list size) show it
// (quest entries from quest menu wiill be included in list)
player->PlayerTalkClass->SendGossipMenu(GetNpcTextId(), GetGUID());
}
void Creature::OnGossipSelect(Player* player, uint32 option)
{
GossipMenu* gossipmenu = player->PlayerTalkClass->GetGossipMenu();
uint32 action=gossipmenu->GetItem(option).m_gAction;
uint32 zoneid=GetZoneId();
uint64 guid=GetGUID();
GossipOption *gossip=GetGossipOption( action );
uint32 textid;
if(!gossip)
{
zoneid=0;
gossip=GetGossipOption( action );
if(!gossip)
return;
}
textid=GetGossipTextId( action, zoneid);
if(textid==0)
textid=GetNpcTextId();
switch (gossip->Action)
{
case GOSSIP_OPTION_GOSSIP:
player->PlayerTalkClass->SendTalking( textid );
break;
case GOSSIP_OPTION_SPIRITHEALER:
if( player->isDead() )
player->GetSession()->SendSpiritResurrect();
break;
case GOSSIP_OPTION_QUESTGIVER:
player->PrepareQuestMenu( guid );
player->SendPreparedQuest( guid );
break;
case GOSSIP_OPTION_VENDOR:
case GOSSIP_OPTION_ARMORER:
player->GetSession()->SendListInventory(guid);
break;
case GOSSIP_OPTION_STABLEPET:
player->GetSession()->SendStablePet(guid);
break;
case GOSSIP_OPTION_TRAINER:
player->GetSession()->SendTrainerList(guid);
break;
case GOSSIP_OPTION_UNLEARNTALENTS:
player->PlayerTalkClass->CloseGossip();
player->SendTalentWipeConfirm(guid);
break;
case GOSSIP_OPTION_TAXIVENDOR:
player->GetSession()->SendTaxiMenu(guid);
break;
case GOSSIP_OPTION_INNKEEPER:
player->PlayerTalkClass->CloseGossip();
player->SetBindPoint( guid );
break;
case GOSSIP_OPTION_BANKER:
player->GetSession()->SendShowBank( guid );
break;
case GOSSIP_OPTION_PETITIONER:
case GOSSIP_OPTION_TABARDVENDOR:
player->GetSession()->SendTabardVendorActivate( guid );
break;
case GOSSIP_OPTION_AUCTIONEER:
player->GetSession()->SendAuctionHello( guid, this );
break;
case GOSSIP_OPTION_GUARD:
case GOSSIP_GUARD_SPELLTRAINER:
case GOSSIP_GUARD_SKILLTRAINER:
prepareGossipMenu( player,gossip->Id );
sendPreparedGossip( player );
break;
default:
OnPoiSelect( player, gossip );
break;
}
}
void Creature::OnPoiSelect(Player* player, GossipOption *gossip)
{
if(gossip->GossipId==GOSSIP_OPTION_GUARD || gossip->GossipId==GOSSIP_GUARD_SPELLTRAINER || gossip->GossipId==GOSSIP_GUARD_SKILLTRAINER)
{
float x,y;
bool findnpc=false;
Poi_Icon icon = ICON_POI_0;
QueryResult *result;
Field *fields;
uint32 mapid=GetMapId();
Map* map=MapManager::Instance().GetMap( mapid );
uint16 areaflag=map->GetAreaFlag(m_positionX,m_positionY);
uint32 zoneid=map->GetZoneId(areaflag);
std::string areaname= gossip->Option;
uint16 pflag;
// use the action relate to creaturetemplate.trainer_type ?
result= sDatabase.PQuery("SELECT `creature`.`position_x`,`creature`.`position_y` FROM `creature`,`creature_template` WHERE `creature`.`map` = '%u' AND `creature`.`id` = `creature_template`.`entry` AND `creature_template`.`trainer_type` = '%u'", mapid, gossip->Action );
if(!result)
return;
do
{
fields = result->Fetch();
x=fields[0].GetFloat();
y=fields[1].GetFloat();
pflag=map->GetAreaFlag(m_positionX,m_positionY);
if(pflag==areaflag)
{
findnpc=true;
break;
}
}while(result->NextRow());
delete result;
if(!findnpc)
{
player->PlayerTalkClass->SendTalking( "$NSorry", "Here no this person.");
return;
}
//need add more case.
switch(gossip->Action)
{
case GOSSIP_GUARD_BANK:
icon=ICON_POI_HOUSE;
break;
case GOSSIP_GUARD_RIDE:
icon=ICON_POI_RWHORSE;
break;
case GOSSIP_GUARD_GUILD:
icon=ICON_POI_BLUETOWER;
break;
default:
icon=ICON_POI_TOWER;
break;
}
uint32 textid=GetGossipTextId( gossip->Action, zoneid );
player->PlayerTalkClass->SendTalking( textid );
player->PlayerTalkClass->SendPointOfInterest( x, y, icon, 2, 15, areaname.c_str() );
}
}
uint32 Creature::GetGossipTextId(uint32 action, uint32 zoneid)
{
QueryResult *result= sDatabase.PQuery("SELECT `textid` FROM `npc_gossip_textid` WHERE `action` = '%u' AND `zoneid` ='%u'", action, zoneid );
if(!result)
return 0;
Field *fields = result->Fetch();
uint32 id = fields[0].GetUInt32();
delete result;
return id;
}
uint32 Creature::GetGossipCount( uint32 gossipid )
{
uint32 count=0;
GossipOption* gso;
for( std::list::iterator i = m_goptions.begin( ); i != m_goptions.end( ); i++ )
{
gso=*i;
if(gso->GossipId == gossipid )
count++;
}
return count;
}
uint32 Creature::GetNpcTextId()
{
QueryResult *result = sDatabase.PQuery("SELECT `textid` FROM `npc_gossip` WHERE `npc_guid`= '%u' LIMIT 1",GetEntry());
if(result)
{
Field *fields = result->Fetch();
uint32 id = fields[0].GetUInt32();
delete result;
return id;
}
return DEFAULT_GOSSIP_MESSAGE;
}
std::string Creature::GetGossipTitle(uint8 type,uint32 id)
{
GossipOption* gso;
for( std::list::iterator i = m_goptions.begin( ); i != m_goptions.end( ); i++ )
{
gso=*i;
if(gso->Id==id && gso->NpcFlag==(uint32)type)
return gso->Option;
}
return NULL;
}
GossipOption* Creature::GetGossipOption( uint32 id )
{
GossipOption* gso;
for( std::list::iterator i = m_goptions.begin( ); i != m_goptions.end( ); i++ )
{
gso=*i;
if(gso->Action==id )
return gso;
}
return NULL;
}
void Creature::LoadGossipOptions()
{
uint32 npcflags=GetUInt32Value(UNIT_NPC_FLAGS);
QueryResult *result = sDatabase.PQuery( "SELECT `id`,`gossip_id`,`npcflag`,`icon`,`action`,`option` FROM `npc_option` WHERE (npcflag & %u)!=0", npcflags );
if(!result)
return;
GossipOption *go;
do
{
Field *fields = result->Fetch();
go=new GossipOption;
go->Id= fields[0].GetUInt32();
go->GossipId = fields[1].GetUInt32();
go->NpcFlag=fields[2].GetUInt32();
go->Icon=fields[3].GetUInt32();
go->Action=fields[4].GetUInt32();
go->Option=fields[5].GetCppString();
addGossipOption(go);
}while( result->NextRow() );
delete result;
}
void Creature::generateMoneyLoot()
{
if (GetCreatureInfo()->maxgold > 0)
{
uint32 diff = GetCreatureInfo()->maxgold - GetCreatureInfo()->mingold + 1;
// rand() result (RAND_MAX) small for large gold loots
if(diff > 10000)
{
uint32 gold_part = rand() % (diff / 10000);
uint32 silver_and_copper_part = rand() % (diff % 10000);
loot.gold = uint32((gold_part * 10000 + silver_and_copper_part + GetCreatureInfo()->mingold) * sWorld.getRate(RATE_DROP_MONEY));
}
else
{
loot.gold = uint32((rand() % diff + GetCreatureInfo()->mingold) * sWorld.getRate(RATE_DROP_MONEY));
}
}
}
void Creature::AI_SendMoveToPacket(float x, float y, float z, uint32 time, bool run, bool WalkBack)
{
/* uint32 timeElap = getMSTime();
if ((timeElap - m_startMove) < m_moveTime)
{
oX = (dX - oX) * ( (timeElap - m_startMove) / m_moveTime );
oY = (dY - oY) * ( (timeElap - m_startMove) / m_moveTime );
}
else
{
oX = dX;
oY = dY;
}
dX = x;
dY = y;
m_orientation = atan2((oY - dY), (oX - dX));
m_startMove = getMSTime();
m_moveTime = time;*/
SendMonsterMove(x,y,z,WalkBack,run,time);
}
Player *Creature::GetLootRecipient() const
{
if (!m_lootRecipient) return NULL;
else return ObjectAccessor::Instance().FindPlayer(m_lootRecipient);
}
void Creature::SetLootRecipient(Player *player)
{
// set the player whose group should receive the right
// to loot the creature after it dies
// should be set to NULL after the loot disappears
if (!player) m_lootRecipient = 0;
else m_lootRecipient = player->GetGUID();
}
void Creature::SaveToDB()
{
sDatabase.PExecute("DELETE FROM `creature` WHERE `guid` = '%u'", GetGUIDLow());
std::ostringstream ss;
ss << "INSERT INTO `creature` VALUES ("
<< GetGUIDLow () << ","
<< GetEntry() << ","
<< m_mapId <<","
<< m_positionX << ","
<< m_positionY << ","
<< m_positionZ << ","
<< m_orientation << ","
<< m_respawnDelay << "," //spawn time min // fix me: store x-y delay but not 1
<< m_respawnDelay << "," //spawn time max
<< (float) 0 << "," //spawn distance (float)
<< (uint32) (0) << "," //currentwaypoint
<< respawn_cord[0] << "," //spawn_position_x
<< respawn_cord[1] << "," //spawn_position_y
<< respawn_cord[2] << "," //spawn_position_z
<< (float)(0) << "," //spawn_orientation
<< GetHealth() << "," //curhealth
<< GetPower(POWER_MANA) << "," //curmana
<< m_respawnTimer << "," //respawntimer
<< (uint32)(m_deathState) << "," // is it really death state or just state?
//or
//<< (uint32)(m_state) << "," // is it really death state or just state?
<< GetDefaultMovementType() << "," // default movement generator type
<< "'')"; // should save auras
sDatabase.Execute( ss.str( ).c_str( ) );
}
void Creature::SelectLevel(const CreatureInfo *cinfo)
{
uint32 minlevel = min(cinfo->maxlevel, cinfo->minlevel);
uint32 maxlevel = max(cinfo->maxlevel, cinfo->minlevel);
uint32 level = minlevel == maxlevel ? minlevel : urand(minlevel, maxlevel);
SetLevel(level);
float rellevel = maxlevel == minlevel ? 0 : (float(level - minlevel))/(maxlevel - minlevel);
uint32 minhealth = min(cinfo->maxhealth, cinfo->minhealth);
uint32 maxhealth = max(cinfo->maxhealth, cinfo->minhealth);
uint32 health = _GetHealthMod(cinfo->rank) * (minhealth + uint32(rellevel*(maxhealth - minhealth)));
SetMaxHealth(health);
SetUInt32Value(UNIT_FIELD_BASE_HEALTH,health);
SetHealth(health);
uint32 minmana = min(cinfo->maxmana, cinfo->minmana);
uint32 maxmana = max(cinfo->maxmana, cinfo->minmana);
uint32 mana = minmana + uint32(rellevel*(maxmana - minmana));
SetMaxPower(POWER_MANA, mana); //MAX Mana
SetUInt32Value(UNIT_FIELD_BASE_MANA, mana);
SetPower(POWER_MANA, mana);
}
float Creature::_GetHealthMod(int32 Rank)
{
switch (Rank) // define rates for each elite rank
{
case CREATURE_ELITE_NORMAL:
return sWorld.getRate(RATE_CREATURE_NORMAL_HP);
case CREATURE_ELITE_ELITE:
return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_HP);
case CREATURE_ELITE_RAREELITE:
return sWorld.getRate(RATE_CREATURE_ELITE_RAREELITE_HP);
case CREATURE_ELITE_WORLDBOSS:
return sWorld.getRate(RATE_CREATURE_ELITE_WORLDBOSS_HP);
case CREATURE_ELITE_RARE:
return sWorld.getRate(RATE_CREATURE_ELITE_RARE_HP);
default:
return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_HP);
}
}
float Creature::_GetDamageMod(int32 Rank)
{
switch (Rank) // define rates for each elite rank
{
case CREATURE_ELITE_NORMAL:
return sWorld.getRate(RATE_CREATURE_NORMAL_DAMAGE);
case CREATURE_ELITE_ELITE:
return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_DAMAGE);
case CREATURE_ELITE_RAREELITE:
return sWorld.getRate(RATE_CREATURE_ELITE_RAREELITE_DAMAGE);
case CREATURE_ELITE_WORLDBOSS:
return sWorld.getRate(RATE_CREATURE_ELITE_WORLDBOSS_DAMAGE);
case CREATURE_ELITE_RARE:
return sWorld.getRate(RATE_CREATURE_ELITE_RARE_DAMAGE);
default:
return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_DAMAGE);
}
}
bool Creature::CreateFromProto(uint32 guidlow,uint32 Entry)
{
Object::_Create(guidlow, HIGHGUID_UNIT);
SetUInt32Value(OBJECT_FIELD_ENTRY,Entry);
CreatureInfo const *cinfo = objmgr.GetCreatureTemplate(Entry);
if(!cinfo)
{
sLog.outString("Error: creature entry %u does not exist.",Entry);
return false;
}
uint32 rank = cinfo->rank;
float damagemod = _GetDamageMod(rank);;
uint32 display_id = cinfo->randomDisplayID();
SetUInt32Value(UNIT_FIELD_DISPLAYID,display_id );
SetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID,display_id );
SelectLevel(cinfo);
SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,cinfo->faction);
SetUInt32Value(UNIT_NPC_FLAGS,cinfo->npcflag);
SetFloatValue(UNIT_FIELD_MINDAMAGE,cinfo->mindmg * damagemod);
SetFloatValue(UNIT_FIELD_MAXDAMAGE,cinfo->maxdmg * damagemod);
SetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE,cinfo->minrangedmg * damagemod);
SetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE,cinfo->maxrangedmg * damagemod);
SetAttackTime(BASE_ATTACK, cinfo->baseattacktime);
SetAttackTime(RANGED_ATTACK,cinfo->rangeattacktime);
SetUInt32Value(UNIT_FIELD_FLAGS,cinfo->Flags);
SetUInt32Value(UNIT_DYNAMIC_FLAGS,cinfo->dynamicflags);
SetArmor(cinfo->armor);
SetResistance(SPELL_SCHOOL_HOLY,cinfo->resistance1);
SetResistance(SPELL_SCHOOL_FIRE,cinfo->resistance2);
SetResistance(SPELL_SCHOOL_NATURE,cinfo->resistance3);
SetResistance(SPELL_SCHOOL_FROST,cinfo->resistance4);
SetResistance(SPELL_SCHOOL_SHADOW,cinfo->resistance5);
SetResistance(SPELL_SCHOOL_ARCANE,cinfo->resistance6);
//this is probably wrong
SetUInt32Value( UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, cinfo->equipmodel[0]);
SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO , cinfo->equipinfo[0]);
SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + 1, cinfo->equipslot[0]);
SetUInt32Value( UNIT_VIRTUAL_ITEM_SLOT_DISPLAY+1, cinfo->equipmodel[1]);
SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + 2, cinfo->equipinfo[1]);
SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + 2 + 1, cinfo->equipslot[1]);
SetUInt32Value( UNIT_VIRTUAL_ITEM_SLOT_DISPLAY+2, cinfo->equipmodel[2]);
SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + 4, cinfo->equipinfo[2]);
SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + 4 + 1, cinfo->equipslot[2]);
SetFloatValue(OBJECT_FIELD_SCALE_X, cinfo->size);
SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS,cinfo->bounding_radius);
SetFloatValue(UNIT_FIELD_COMBATREACH,cinfo->combat_reach );
FactionEntry* factionEntry = sFactionStore.LookupEntry(cinfo->faction);
if (factionEntry->team == ALLIANCE || HORDE)
{
SetPvP(true);
}
if (cinfo->mount != 0)
Mount(cinfo->mount);
m_spells[0] = cinfo->spell1;
m_spells[1] = cinfo->spell2;
m_spells[2] = cinfo->spell3;
m_spells[3] = cinfo->spell4;
SetSpeed( cinfo->speed ) ;
if(cinfo->MovementType < MAX_DB_MOTION_TYPE)
m_defaultMovementType = MovementGeneratorType(cinfo->MovementType);
else
{
m_defaultMovementType = IDLE_MOTION_TYPE;
sLog.outError("Creature template %u have wrong movement generator type value %u, ignore and set to IDLE.",Entry,cinfo->MovementType);
}
return true;
}
bool Creature::LoadFromDB(uint32 guid, QueryResult *result)
{
bool external = (result != NULL);
if (!external)
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
result = sDatabase.PQuery("SELECT `id`,`map`,`position_x`,`position_y`,`position_z`,`orientation`,`spawntimemin`,`spawntimemax`,`spawndist`,`spawn_position_x`,`spawn_position_y`,`spawn_position_z`,`curhealth`,`curmana`,`respawntimer`,`state`,`MovementType`,`auras` FROM `creature` WHERE `guid` = '%u'", guid);
if(!result)
{
sLog.outError("ERROR: Creature (GUID: %u) not found in table `creature`, can't load. ",guid);
return false;
}
Field *fields = result->Fetch();
if(!Create(guid,fields[1].GetUInt32(),fields[2].GetFloat(),fields[3].GetFloat(),
fields[4].GetFloat(),fields[5].GetFloat(),fields[0].GetUInt32()))
{
if (!external) delete result;
return false;
}
SetHealth(fields[12].GetUInt32());
SetPower(POWER_MANA,fields[13].GetUInt32());
m_respawnradius = fields[8].GetFloat();
respawn_cord[0] = fields[9].GetFloat();
respawn_cord[1] = fields[10].GetFloat();
respawn_cord[2] = fields[11].GetFloat();
m_respawnDelay =(fields[6].GetUInt32()+fields[7].GetUInt32())*1000/2;
m_respawnTimer = fields[14].GetUInt32();
m_deathState = (DeathState)fields[15].GetUInt32();
{
uint32 mtg = fields[16].GetUInt32();
if(mtg < MAX_DB_MOTION_TYPE)
m_defaultMovementType = MovementGeneratorType(mtg);
else
{
m_defaultMovementType = IDLE_MOTION_TYPE;
sLog.outError("Creature (GUID: %u ID: %u) have wrong movement generator type value %u, ignore and set to IDLE.",guid,GetEntry(),mtg);
}
}
if(!external) delete result;
if (HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_TRAINER ) )
CreateTrainerSpells();
if ( HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_VENDOR ) )
_LoadGoods();
if ( HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER ) )
_LoadQuests();
AIM_Initialize();
return true;
}
void Creature::_LoadGoods()
{
itemcount = 0;
QueryResult *result = sDatabase.PQuery("SELECT `item`, `maxcount`,`incrtime` FROM `npc_vendor` WHERE `entry` = '%u'", GetEntry());
if(!result) return;
do
{
Field *fields = result->Fetch();
if (GetItemCount() >= MAX_CREATURE_ITEMS)
{
sLog.outError( "Vendor %u has too many items (%u >= %i). Check the DB!", GetUInt32Value(OBJECT_FIELD_ENTRY), GetItemCount(), MAX_CREATURE_ITEMS );
break;
}
AddItem( fields[0].GetUInt32(), fields[1].GetUInt32(), fields[2].GetUInt32());
}
while( result->NextRow() );
delete result;
}
void Creature::_LoadQuests()
{
mQuests.clear();
mInvolvedQuests.clear();
Field *fields;
QueryResult *result = sDatabase.PQuery("SELECT `quest` FROM `creature_questrelation` WHERE `id` = '%u'", GetEntry());
if(result)
{
do
{
fields = result->Fetch();
Quest* qInfo = objmgr.QuestTemplates[fields[0].GetUInt32()];
if (!qInfo) continue;
addQuest(qInfo->GetQuestId());
}
while( result->NextRow() );
delete result;
}
QueryResult *result1 = sDatabase.PQuery("SELECT `quest` FROM `creature_involvedrelation` WHERE `id` = '%u'", GetEntry());
if(!result1) return;
do
{
fields = result1->Fetch();
Quest* qInfo = objmgr.QuestTemplates[fields[0].GetUInt32()];
if (!qInfo) continue;
addInvolvedQuest(qInfo->GetQuestId());
}
while( result1->NextRow() );
delete result1;
}
void Creature::DeleteFromDB()
{
sDatabase.PExecute("DELETE FROM `creature` WHERE `guid` = '%u'", GetGUIDLow());
sDatabase.PExecute("DELETE FROM `creature_involvedrelation` WHERE `id` = '%u'", GetGUIDLow());
}
float Creature::GetAttackDistance(Unit *pl) const
{
int32 playerlevel = pl->getLevel();
int32 creaturelevel = getLevel();
int32 leveldif = playerlevel - creaturelevel;
// "The maximum Aggro Radius has a cap of 25 levels under. Example: A level 30 char has the same Aggro Radius of a level 5 char on a level 60 mob."
if ( leveldif < - 25)
leveldif = -25;
// "The aggro radius of a mob having the same level as the player is roughly 20 yards"
float RetDistance = 20;
// "Aggro Radius varries with level difference at a rate of roughly 1 yard/level"
// radius grow if playlevel < creaturelevel
RetDistance -= (float)leveldif;
// "Minimum Aggro Radius for a mob seems to be combat range (5 yards)"
if(RetDistance < 5 && sWorld.getRate(RATE_CREATURE_AGGRO) != 0)
RetDistance = 5;
return (RetDistance*sWorld.getRate(RATE_CREATURE_AGGRO));
}
CreatureInfo const *Creature::GetCreatureInfo() const
{
return objmgr.GetCreatureTemplate(GetEntry());
}
void Creature::setDeathState(DeathState s)
{
if(s == JUST_DIED)
{
m_deathTimer = m_corpseDelay;
if(!IsStopped()) StopMoving();
}
Unit::setDeathState(s);
if(s == JUST_DIED)
{
SetUInt32Value(UNIT_NPC_FLAGS, 0);
if(GetCreatureInfo()->SkinLootId)
{
LootStore skinStore = LootTemplates_Skinning;
LootStore::iterator tab = skinStore.find(GetCreatureInfo()->SkinLootId);
if ( tab != skinStore.end() )
SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
}
Unit::setDeathState(CORPSE);
}
}
void Creature::Say(char const* message, uint32 language)
{
WorldPacket data;
sChatHandler.FillMessageData( &data, NULL, CHAT_MSG_SAY, language, NULL, 0, message );
SendMessageToSet( &data, false );
}
void Creature::MonsterSay(char const* message, uint32 language, uint64 targetGUID)
{
WorldPacket data;
sChatHandler.FillMessageData( &data, NULL, CHAT_MSG_MONSTER_SAY, language, NULL, targetGUID, message, this );
SendMessageToSet( &data, false );
}
SpellEntry *Creature::reachWithSpellAttack(Unit *pVictim)
{
if(!pVictim)
return NULL;
SpellEntry *spellInfo;
bool bcontinue;
for(uint32 i=0; i < CREATURE_MAX_SPELLS; i++)
{
if(!m_spells[i])
continue;
spellInfo = sSpellStore.LookupEntry(m_spells[i] );
if(!spellInfo)
{
sLog.outError("WORLD: unknown spell id %i\n", m_spells[i]);
continue;
}
bcontinue = true;
for(uint32 j=0;j<3;j++)
{
if( (spellInfo->Effect[j] == SPELL_EFFECT_SCHOOL_DAMAGE ) ||
(spellInfo->Effect[j] == SPELL_EFFECT_INSTAKILL) ||
(spellInfo->Effect[j] == SPELL_EFFECT_ENVIRONMENTAL_DAMAGE) ||
(spellInfo->Effect[j] == SPELL_EFFECT_HEALTH_LEECH )
)
{
bcontinue = false;
break;
}
}
if(bcontinue) continue;
if(spellInfo->manaCost > GetPower(POWER_MANA))
continue;
SpellRangeEntry* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex);
float range = GetMaxRange(srange);
float minrange = GetMinRange(srange);
float dist = GetDistanceSq(pVictim);
//if(!isInFront( pVictim, range ) && spellInfo->AttributesEx )
// continue;
if( dist > range * range || dist < minrange * minrange )
continue;
if(m_silenced)
continue;
return spellInfo;
}
return NULL;
}
SpellEntry *Creature::reachWithSpellCure(Unit *pVictim)
{
if(!pVictim)
return NULL;
SpellEntry *spellInfo;
bool bcontinue;
for(uint32 i=0; i < CREATURE_MAX_SPELLS; i++)
{
if(!m_spells[i])
continue;
spellInfo = sSpellStore.LookupEntry(m_spells[i] );
if(!spellInfo)
{
sLog.outError("WORLD: unknown spell id %i\n", m_spells[i]);
continue;
}
bcontinue = true;
for(uint32 j=0;j<3;j++)
{
if( (spellInfo->Effect[j] == SPELL_EFFECT_HEAL ) )
{
bcontinue = false;
break;
}
}
if(bcontinue) continue;
if(spellInfo->manaCost > GetPower(POWER_MANA))
continue;
SpellRangeEntry* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex);
float range = GetMaxRange(srange);
float minrange = GetMinRange(srange);
float dist = GetDistanceSq(pVictim);
//if(!isInFront( pVictim, range ) && spellInfo->AttributesEx )
// continue;
if( dist > range * range || dist < minrange * minrange )
continue;
if(m_silenced)
continue;
return spellInfo;
}
return NULL;
}
void Creature::Untamed()
{
if(!isTamed())
return;
Unit* owner = GetOwner();
if(owner && owner->GetTypeId()==TYPEID_PLAYER)
((Player*)owner)->UnTamePet(this);
else
{
SetMaxPower(POWER_HAPPINESS,0);
SetPower(POWER_HAPPINESS,0);
SetMaxPower(POWER_FOCUS,0);
SetPower(POWER_FOCUS,0);
SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,GetCreatureInfo()->faction);
SetUInt64Value(UNIT_FIELD_CREATEDBY, 0);
SetUInt32Value(UNIT_FIELD_PETNUMBER,0);
if(owner)
owner->SetPet(0);
SetTamed(false);
AIM_Initialize();
}
}
void Creature::GivePetXP(uint32 xp)
{
if(!isPet() || !GetUInt32Value(UNIT_FIELD_PETNUMBER))
return;
if ( xp < 1 )
return;
uint32 level = getLevel();
// XP to money conversion processed in Player::RewardQuest
if(level >= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
return;
uint32 curXP = GetUInt32Value(UNIT_FIELD_PETEXPERIENCE);
uint32 nextLvlXP = GetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP);
uint32 newXP = curXP + xp;
if(newXP >= nextLvlXP && level+1 > GetOwner()->getLevel())
{
SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, nextLvlXP-1);
return;
}
while( newXP >= nextLvlXP && level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) )
{
newXP -= nextLvlXP;
SetLevel( level + 1 );
SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((MaNGOS::XP::xp_to_level(level+1))/4));
level = getLevel();
nextLvlXP = GetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP);
GivePetLevel(level);
}
SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, newXP);
}
void Creature::GivePetLevel(uint32 level)
{
if(!level)
return;
uint32 loyalty = 1;
CreatureInfo const *cinfo = GetCreatureInfo();
// pet damage will grow up with the pet level,*1.5f for temp
SetFloatValue(UNIT_FIELD_MINDAMAGE, cinfo->mindmg + float(level-cinfo->minlevel)*1.5f);
SetFloatValue(UNIT_FIELD_MAXDAMAGE, cinfo->maxdmg + float(level-cinfo->minlevel)*1.5f);
SetUInt32Value(UNIT_TRAINING_POINTS, (level<<16) + getUsedTrainPoint());
SetUInt32Value(UNIT_FIELD_BYTES_1,(getloyalty()<<8));
SetHealth( 28 + 10 * level);
SetMaxHealth( 28 + 10 * level);
SetStat(STAT_STRENGTH,uint32(20+level*1.55));
SetStat(STAT_AGILITY,uint32(20+level*0.64));
SetStat(STAT_STAMINA,uint32(20+level*1.27));
SetStat(STAT_INTELLECT,uint32(20+level*0.18));
SetStat(STAT_SPIRIT,uint32(20+level*0.36));
SetArmor(level*50);
if(level - cinfo->minlevel >= 21)
loyalty = 7;
else if(level - cinfo->minlevel >= 15)
loyalty = 6;
else if(level - cinfo->minlevel >= 10)
loyalty = 5;
else if(level - cinfo->minlevel >= 6)
loyalty = 4;
else if(level - cinfo->minlevel >= 3)
loyalty = 3;
else if(level - cinfo->minlevel >= 1)
loyalty = 2;
SetUInt32Value(UNIT_FIELD_BYTES_1,(loyalty << 8));
}
void Creature::SaveAsPet()
{
if(!isTamed() && !isPet())
return;
if(!GetEntry())
return;
uint32 loyalty =1;
if(isTamed())
loyalty = 1;
else loyalty = getloyalty();
uint32 owner = GUID_LOPART(GetOwnerGUID());
sDatabase.PExecute("DELETE FROM `character_pet` WHERE `owner` = '%u' AND `entry` = '%u'", owner,GetEntry() );
sDatabase.PExecute("UPDATE `character_pet` SET `current` = 0 WHERE `owner` = '%u' AND `current` = 1", owner );
sDatabase.PExecute("INSERT INTO `character_pet` (`entry`,`owner`,`level`,`exp`,`nextlvlexp`,`spell1`,`spell2`,`spell3`,`spell4`,`action`,`fealty`,`loyalty`,`trainpoint`,`current`) VALUES (%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,1)",
GetEntry(), owner, getLevel(), GetUInt32Value(UNIT_FIELD_PETEXPERIENCE), GetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP),
m_spells[0], m_spells[1], m_spells[2], m_spells[3], STATE_RA_FOLLOW, GetPower(POWER_HAPPINESS),loyalty,getUsedTrainPoint() );
}
bool Creature::IsVisibleInGridForPlayer(Player* pl) const
{
// Live player see live creatures or death creatures with corpse dissappiring time > 0
if(pl->isAlive())
{
return isAlive() || m_deathTimer > 0;
}
// Dead player see live creatures near own corpse
if(pl->getDeathState() == CORPSE)
{
if(isAlive())
{
if(Corpse* corpse = pl->GetCorpse())
{
// 20 - aggro distance for same level, 25 - max additinal distance if player level less that creature level
if(corpse->IsWithinDistInMap(this,(20+25)*sWorld.getRate(RATE_CREATURE_AGGRO)))
return true;
}
}
}
// Dead player see Spirit Healer
if(pl->isDead() && isSpiritHealer())
return true;
// and not see any other
return false;
}
void Creature::CallAssistence()
{
if (!m_AlreadyCallAssistence)
{
CastSpell(this,SPELL_ID_AGGRO, true);
}
}