/* * 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" #define M_PI 3.14159265358979323846 Creature::Creature(uint32 high, uint32 low) { proto=0; m_valuesCount = UNIT_END; m_objectTypeId = TYPEID_UNIT; m_uint32Values = _fields; memset(m_uint32Values, 0,(UNIT_END)*sizeof(uint32)); m_updateMask.SetCount(UNIT_END); SetUInt32Value( OBJECT_FIELD_TYPE,TYPE_UNIT|TYPE_OBJECT); SetUInt32Value( OBJECT_FIELD_GUID,low); SetUInt32Value( OBJECT_FIELD_GUID+1,high); m_wowGuid.Init(GetGUID()); m_quests = NULL; proto = NULL; spawnid=0; creature_info=NULL; m_H_regenTimer=0; m_P_regenTimer=0; m_useAI = true; mTaxiNode = 0; Tagged = false; TaggerGuid = 0; Skinned = false; m_enslaveCount = 0; m_enslaveSpell = 0; for(uint32 x=0;x<7;x++) { FlatResistanceMod[x]=0; BaseResistanceModPct[x]=0; ResistanceModPct[x]=0; ModDamageDone[x]=0; ModDamageDonePct[x]=1.0; } for(uint32 x=0;x<5;x++) { TotalStatModPct[x]=0; StatModPct[x]=0; FlatStatMod[x]=0; } m_runSpeed=MONSTER_NORMAL_RUN_SPEED; totemOwner = NULL; totemSlot = -1; m_PickPocketed = false; m_SellItems = NULL; _myScriptClass = NULL; m_TaxiNode = 0; _gossipScript = NULL; myFamily = 0; loot.gold = 0; haslinkupevent = false; original_emotestate = 0; mTrainer = 0; m_spawn = 0; spawnid = 0; auctionHouse = 0; has_waypoint_text = has_combat_text = false; SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER,1); SetFloatValue(UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER,1); m_custom_waypoint_map = 0; m_escorter = 0; m_limbostate = false; m_corpseEvent=false; m_runSpeed=8.0f; m_respawnCell=NULL; } Creature::~Creature() { sEventMgr.RemoveEvents(this); if(IsTotem()) totemOwner->m_TotemSlots[totemSlot] = 0; if(_gossipScript != 0) _gossipScript->Destroy(); if(_myScriptClass != 0) _myScriptClass->Destroy(); if(m_custom_waypoint_map != 0) { for(WayPointMap::iterator itr = m_custom_waypoint_map->begin(); itr != m_custom_waypoint_map->end(); ++itr) delete (*itr); delete m_custom_waypoint_map; } if(m_respawnCell!=NULL) m_respawnCell->_respawnObjects.erase(this); } void Creature::Update( uint32 p_time ) { Unit::Update( p_time ); if(IsTotem() && isDead()) { RemoveFromWorld(false); delete this; return; } if(m_corpseEvent) { sEventMgr.RemoveEvents(this); sEventMgr.AddEvent(this, &Creature::OnRemoveCorpse, EVENT_CREATURE_REMOVE_CORPSE, 180000, 1,EVENT_FLAG_DO_NOT_EXECUTE_IN_WORLD_CONTEXT); m_corpseEvent=false; } } void Creature::SafeDelete() { sEventMgr.RemoveEvents(this); //sEventMgr.AddEvent(World::getSingletonPtr(), &World::DeleteObject, ((Object*)this), EVENT_CREATURE_SAFE_DELETE, 1000, 1); sEventMgr.AddEvent(this, &Creature::DeleteMe, EVENT_CREATURE_SAFE_DELETE, 1000, 1,EVENT_FLAG_DO_NOT_EXECUTE_IN_WORLD_CONTEXT); } void Creature::DeleteMe() { if(IsInWorld()) RemoveFromWorld(false); delete this; } void Creature::OnRemoveCorpse() { // time to respawn! if (IsInWorld() && (int32)m_mapMgr->GetInstanceID() == m_instanceId) { sLog.outDetail("Removing corpse of "I64FMT"...", GetGUID()); if(GetMapMgr()->GetMapInfo() && GetMapMgr()->GetMapInfo()->type == INSTANCE_RAID && this->proto && this->proto->boss) { RemoveFromWorld(false); } else { if(proto && proto->RespawnTime) RemoveFromWorld(true); else RemoveFromWorld(false); } setDeathState(DEAD); m_position = m_spawnLocation; } else { // if we got here it's pretty bad } } void Creature::OnRespawn(MapMgr * m) { sLog.outDetail("Respawning "I64FMT"...", GetGUID()); SetUInt32Value(UNIT_FIELD_HEALTH, GetUInt32Value(UNIT_FIELD_MAXHEALTH)); SetUInt32Value(UNIT_DYNAMIC_FLAGS, 0); // not tagging shiat if(proto && m_spawn) { SetUInt32Value(UNIT_NPC_FLAGS, proto->NPCFLags); SetUInt32Value(UNIT_NPC_EMOTESTATE, m_spawn->emote_state); } RemoveFlag(UNIT_FIELD_FLAGS,U_FIELD_FLAG_SKINNABLE); Skinned = false; Tagged = false; TaggerGuid = 0; /* creature death state */ if(proto && proto->death_state == 1) { uint32 newhealth = m_uint32Values[UNIT_FIELD_HEALTH] / 100; if(!newhealth) newhealth = 1; SetUInt32Value(UNIT_FIELD_HEALTH, 1); m_limbostate = true; bInvincible = true; SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_DEAD); } //empty loot loot.items.clear(); setDeathState(ALIVE); GetAIInterface()->StopMovement(0); // after respawn monster can move m_PickPocketed = false; PushToWorld(m); } void Creature::Create (const char* name, uint32 mapid, float x, float y, float z, float ang) { Object::_Create( mapid, x, y, z, ang ); } void Creature::CreateWayPoint (uint32 WayPointID, uint32 mapid, float x, float y, float z, float ang) { Object::_Create( mapid, x, y, z, ang); } /////////// /// Looting void Creature::generateLoot() { lootmgr.FillCreatureLoot(&loot,GetEntry(), m_mapMgr ? (m_mapMgr->iInstanceMode > 0 ? true : false) : false); loot.gold = proto ? proto->money : 0; //For now let fill according to entry if(!loot.gold) { CreatureInfo *info=GetCreatureName(); if (info && info->Type != BEAST) { if(m_uint32Values[UNIT_FIELD_MAXHEALTH] <= 1667) loot.gold = (uint32)((info->Rank+1)*getLevel()*(rand()%5 + 1)); //generate copper else loot.gold = (uint32)((info->Rank+1)*getLevel()*(rand()%5 + 1)*(this->GetUInt32Value(UNIT_FIELD_MAXHEALTH)*0.0006)); //generate copper } } if(loot.gold) loot.gold = int32(float(loot.gold) * sWorld.getRate(RATE_MONEY)); } void Creature::SaveToDB() { if(!spawnid) spawnid = objmgr.GenerateCreatureSpawnID(); std::stringstream ss; ss << "REPLACE INTO creature_spawns VALUES(" << spawnid << "," << GetEntry() << "," << GetMapId() << "," << m_position.x << "," << m_position.y << "," << m_position.z << "," << m_position.o << "," << m_aiInterface->getMoveType() << "," << m_uint32Values[UNIT_FIELD_DISPLAYID] << "," << m_uint32Values[UNIT_FIELD_FACTIONTEMPLATE] << "," << m_uint32Values[UNIT_FIELD_FLAGS] << "," << m_uint32Values[UNIT_FIELD_BYTES_0] << "," << m_uint32Values[UNIT_FIELD_BYTES_2] << "," << m_uint32Values[UNIT_NPC_EMOTESTATE] << "," << ((this->m_spawn ? m_spawn->respawnNpcLink : uint32(0))) << ")"; WorldDatabase.Execute(ss.str().c_str()); } void Creature::SaveToFile(std::stringstream & name) { /* FILE * OutFile; OutFile = fopen(name.str().c_str(), "wb"); if (!OutFile) return; uint32 creatureEntry = GetUInt32Value(OBJECT_FIELD_ENTRY); if (!m_sqlid) m_sqlid = objmgr.GenerateLowGuid(HIGHGUID_UNIT); std::stringstream ss; ss << "DELETE FROM creatures WHERE id=" << m_sqlid; fwrite(ss.str().c_str(),1,ss.str().size(),OutFile); ss.rdbuf()->str(""); ss << "\nINSERT INTO creatures (id, mapId, zoneId, name_id, positionX, positionY, positionZ, orientation, moverandom, running, data) VALUES ( " << m_sqlid << ", " << GetMapId() << ", " << GetZoneId() << ", " << GetUInt32Value(OBJECT_FIELD_ENTRY) << ", " << m_position.x << ", " << m_position.y << ", " << m_position.z << ", " << m_position.o << ", " << GetAIInterface()->getMoveType() << ", " << GetAIInterface()->getMoveRunFlag() << ", '"; for( uint16 index = 0; index < m_valuesCount; index ++ ) ss << GetUInt32Value(index) << " "; ss << "' )"; fwrite(ss.str().c_str(),1,ss.str().size(),OutFile); fclose(OutFile);*/ } void Creature::LoadScript() { _myScriptClass = sScriptMgr.CreateAIScriptClassForEntry(this); } void Creature::DeleteFromDB() { if(!GetSQL_id())return; WorldDatabase.Execute("DELETE FROM creature_spawns WHERE id=%u", GetSQL_id()); WorldDatabase.Execute("DELETE FROM creature_waypoints WHERE creatureid=%u",GetSQL_id()); } ///////////// /// Quests void Creature::AddQuest(QuestRelation *Q) { m_quests->push_back(Q); } void Creature::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* Creature::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 Creature::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 Creature::NumOfQuests() { return m_quests->size(); } void Creature::_LoadQuests() { sQuestMgr.LoadNPCQuests(this); } void Creature::setDeathState(DeathState s) { if(s == JUST_DIED) { GetAIInterface()->SetUnitToFollow(NULL); m_deathState = CORPSE; m_corpseEvent=true; /*sEventMgr.AddEvent(this, &Creature::OnRemoveCorpse, EVENT_CREATURE_REMOVE_CORPSE, 180000, 1,EVENT_FLAG_DO_NOT_EXECUTE_IN_WORLD_CONTEXT);*/ if(m_enslaveSpell) RemoveEnslave(); }else m_deathState = s; } void Creature::AddToWorld() { // force set faction if(m_faction == 0 || m_factionDBC == 0) _setFaction(); if(creature_info == 0) creature_info = CreatureNameStorage.LookupEntry(GetEntry()); if(creature_info == 0) return; if(m_faction == 0 || m_factionDBC == 0) return; Unit::AddToWorld(); } bool Creature::CanAddToWorld() { if(m_factionDBC == 0 || m_faction == 0) _setFaction(); if(creature_info == 0 || m_faction == 0 || m_factionDBC == 0) return false; return true; } void Creature::RemoveFromWorld(bool addev) { if(GetGUIDHigh() != HIGHGUID_UNIT) /* is a pet */ { if(IsInWorld()) Unit::RemoveFromWorld(); SafeDelete(); return; } if(!IS_INSTANCE(m_mapId)) objmgr.SetCreatureBySqlId(GetSQL_id(), 0); if(IsInWorld()) { RemoveAllAuras(); sEventMgr.RemoveEvents(this); if(addev) { if(proto && proto->RespawnTime > 0) Despawn(0, proto->RespawnTime); else Despawn(0,0); } else Despawn(0,0); } } void Creature::EnslaveExpire() { m_enslaveCount++; Player *caster = objmgr.GetPlayer(GetUInt64Value(UNIT_FIELD_CHARMEDBY)); if(caster) { caster->SetUInt64Value(UNIT_FIELD_CHARM, 0); caster->SetUInt64Value(UNIT_FIELD_SUMMON, 0); WorldPacket data(8); data.Initialize(SMSG_PET_SPELLS); data << uint64(0); caster->GetSession()->SendPacket(&data); } SetUInt64Value(UNIT_FIELD_CHARMEDBY, 0); SetUInt64Value(UNIT_FIELD_SUMMONEDBY, 0); SetIsPet(false); m_walkSpeed = 2.5f; m_runSpeed = 6.0f; switch(GetCreatureName()->Type) { case DEMON: SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, 90); break; default: SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, 954); break; }; _setFaction(); GetAIInterface()->Init(((Unit *)this), AITYPE_AGRO, MOVEMENTTYPE_NONE); // Update InRangeSet UpdateOppFactionSet(); } bool Creature::RemoveEnslave() { return RemoveAura(m_enslaveSpell); } void Creature::AddInRangeObject(Object* pObj) { if(pObj->GetTypeId() == TYPEID_PLAYER && pObj->IsPlayer()) ScriptSystem->OnCreatureEvent(((Creature*)this), (Unit*)pObj, CREATURE_EVENT_PLAYER_ENTERS_RANGE); Unit::AddInRangeObject(pObj); } void Creature::OnRemoveInRangeObject(Object* pObj) { if(totemOwner == pObj) // player gone out of range of the totem { // Expire next loop. event_ModifyTimeLeft(EVENT_TOTEM_EXPIRE, 1); } if(m_escorter == pObj) { // we lost our escorter, return to the spawn. m_aiInterface->StopMovement(10000); DestroyCustomWaypointMap(); Despawn(1000, 1000); } Unit::OnRemoveInRangeObject(pObj); } void Creature::ClearInRangeSet() { Unit::ClearInRangeSet(); } void Creature::CalcResistance(uint32 type) { int32 res = (BaseResistance[type] * (100 + BaseResistanceModPct[type])) / 100; if(res < 0) res = 0; res += FlatResistanceMod[type]; if(res < 0) res = 0; res += (res * ResistanceModPct[type]) / 100; if(type==0)res+=GetUInt32Value(UNIT_FIELD_STAT1)*2;//fix armor from agi SetUInt32Value(UNIT_FIELD_RESISTANCES + type, res > 0 ? res : 0); } void Creature::CalcStat(uint32 type) { int32 res=(BaseStats[type]*(100+StatModPct[type]))/100; res+=FlatStatMod[type]; if(res<0)res=0; res+=(res*(TotalStatModPct[type]))/100; SetUInt32Value(UNIT_FIELD_STAT0+type,res>0?res:0); } void Creature::RegenerateHealth() { if(m_limbostate) return; uint32 cur=GetUInt32Value(UNIT_FIELD_HEALTH); uint32 mh=GetUInt32Value(UNIT_FIELD_MAXHEALTH); if(cur>=mh)return; //though creatures have their stats we use some wierd formula for amt float amt = 0.0f; if(PctRegenModifier == 0.0f) amt = getLevel()*2; else if(PctRegenModifier > 0) amt = (getLevel()*2)*(1+PctRegenModifier); else amt = (getLevel()*2)*(-1+PctRegenModifier); //Apply shit from conf file amt*=sWorld.getRate(RATE_HEALTH); if(amt<=1.0)//this fixes regen like 0.98 cur++; else cur+=(uint32)amt; SetUInt32Value(UNIT_FIELD_HEALTH,(cur>=mh)?mh:cur); } void Creature::RegenerateMana() { float amt; if (m_interruptRegen) return; uint32 cur=GetUInt32Value(UNIT_FIELD_POWER1); uint32 mm=GetUInt32Value(UNIT_FIELD_MAXPOWER1); if(cur>=mm)return; amt=(getLevel()+10)*PctPowerRegenModifier[POWER_TYPE_MANA]; //Apply shit from conf file amt*=sWorld.getRate(RATE_POWER1); if(amt<=1.0)//this fixes regen like 0.98 cur++; else cur+=(uint32)amt; SetUInt32Value(UNIT_FIELD_POWER1,(cur>=mm)?mm:cur); } void Creature::RegenerateFocus() { if (m_interruptRegen) return; uint32 cur=GetUInt32Value(UNIT_FIELD_POWER3); uint32 mm=GetUInt32Value(UNIT_FIELD_MAXPOWER3); if(cur>=mm)return; float amt = 10.0 * PctPowerRegenModifier[POWER_TYPE_FOCUS]; cur+=(uint32)amt; SetUInt32Value(UNIT_FIELD_POWER3,(cur>=mm)?mm:cur); } void Creature::CallScriptUpdate() { ASSERT(_myScriptClass); if(!IsInWorld()) return; _myScriptClass->AIUpdate(); } void Creature::AddVendorItem(uint32 itemid, uint32 amount) { CreatureItem ci; ci.amount = amount; ci.itemid = itemid; if(!m_SellItems) { m_SellItems = new vector; objmgr.SetVendorList(GetEntry(), m_SellItems); } m_SellItems->push_back(ci); } void Creature::TotemExpire() { totemOwner->m_TotemSlots[totemSlot] = 0; totemSlot = -1; totemOwner = 0; RemoveFromWorld(false); SafeDelete(); } void Creature::FormationLinkUp(uint32 SqlId) { if(IS_INSTANCE(m_mapId)) return; Creature * creature = objmgr.GetCreatureBySqlId(SqlId); if(creature != 0) { m_aiInterface->m_formationLinkTarget = creature; haslinkupevent = false; event_RemoveEvents(EVENT_CREATURE_FORMATION_LINKUP); } } void Creature::LoadAIAgents() { /*std::stringstream ss; ss << "SELECT * FROM ai_agents where entryId=" << GetUInt32Value(OBJECT_FIELD_ENTRY); QueryResult *result = sDatabase.Query( ss.str().c_str() ); if( !result ) return; AI_Spell *sp; do { Field *fields = result->Fetch(); sp = new AI_Spell; sp->entryId = fields[0].GetUInt32(); sp->agent = fields[1].GetUInt16(); sp->procChance = fields[3].GetUInt32(); sp->spellId = fields[5].GetUInt32(); sp->spellType = fields[6].GetUInt32();; sp->spelltargetType = fields[7].GetUInt32(); sp->floatMisc1 = fields[9].GetFloat(); sp->Misc2 = fields[10].GetUInt32(); sp->minrange = GetMinRange(sSpellRange.LookupEntry(sSpellStore.LookupEntry(sp->spellId)->rangeIndex)); sp->maxrange = GetMaxRange(sSpellRange.LookupEntry(sSpellStore.LookupEntry(sp->spellId)->rangeIndex)); if(sp->agent == AGENT_RANGED) { GetAIInterface()->m_canRangedAttack = true; } else if(sp->agent == AGENT_FLEE) { GetAIInterface()->m_canFlee = true; if(sp->floatMisc1) { GetAIInterface()->m_FleeHealth = sp->floatMisc1; } else { GetAIInterface()->m_FleeHealth = 0.2f; } if(sp->Misc2) { GetAIInterface()->m_FleeDuration = sp->Misc2; } else { GetAIInterface()->m_FleeDuration = 10000; } } else if(sp->agent == AGENT_CALLFORHELP) { GetAIInterface()->m_canCallForHelp = true; if(sp->floatMisc1) GetAIInterface()->m_CallForHelpHealth = sp->floatMisc1; else GetAIInterface()->m_CallForHelpHealth = 0.2f; } else { GetAIInterface()->addSpellToList(sp); } } while( result->NextRow() ); delete result;*/ } WayPoint * Creature::CreateWaypointStruct() { return new WayPoint(); } bool Creature::Load(CreatureSpawn *spawn, uint32 mode, MapInfo *info) { m_spawn = spawn; proto = CreatureProtoStorage.LookupEntry(spawn->entry); if(!proto) return false; creature_info = CreatureNameStorage.LookupEntry(spawn->entry); if(!creature_info) return false; spawnid = spawn->id; //Set fields SetUInt32Value(OBJECT_FIELD_ENTRY,proto->Id); SetFloatValue(OBJECT_FIELD_SCALE_X,proto->Scale); //SetUInt32Value(UNIT_FIELD_HEALTH, (mode ? long2int32(proto->Health * 1.5) : proto->Health)); //SetUInt32Value(UNIT_FIELD_BASE_HEALTH, (mode ? long2int32(proto->Health * 1.5) : proto->Health)); //SetUInt32Value(UNIT_FIELD_MAXHEALTH, (mode ? long2int32(proto->Health * 1.5) : proto->Health)); uint32 health = proto->MinHealth + sRand.randInt(proto->MaxHealth - proto->MinHealth); if(mode) health = long2int32(double(health) * 1.5); SetUInt32Value(UNIT_FIELD_HEALTH, health); SetUInt32Value(UNIT_FIELD_MAXHEALTH, health); SetUInt32Value(UNIT_FIELD_BASE_HEALTH, health); SetUInt32Value(UNIT_FIELD_POWER1,proto->Mana); SetUInt32Value(UNIT_FIELD_MAXPOWER1,proto->Mana); SetUInt32Value(UNIT_FIELD_BASE_MANA,proto->Mana); SetUInt32Value(UNIT_FIELD_DISPLAYID,spawn->displayid); SetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID,spawn->displayid); SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID,proto->MountedDisplayID); // Determine gender (for voices) if(spawn->displayid != creature_info->DisplayID) setGender(1); // Female //SetUInt32Value(UNIT_FIELD_LEVEL, (mode ? proto->Level + (info ? info->lvl_mod_a : 0) : proto->Level)); SetUInt32Value(UNIT_FIELD_LEVEL, proto->MinLevel + (sRand.randInt(proto->MaxLevel - proto->MinLevel))); if(mode && info) ModUInt32Value(UNIT_FIELD_LEVEL, info->lvl_mod_a); for(uint32 i = 0; i < 7; ++i) SetUInt32Value(UNIT_FIELD_RESISTANCES+i,proto->Resistances[i]); SetUInt32Value(UNIT_FIELD_BASEATTACKTIME,proto->AttackTime); SetFloatValue(UNIT_FIELD_MINDAMAGE, (mode ? proto->MinDamage * 1.5 : proto->MinDamage)); SetFloatValue(UNIT_FIELD_MAXDAMAGE, (mode ? proto->MaxDamage * 1.5 : proto->MaxDamage)); SetUInt32Value(UNIT_FIELD_RANGEDATTACKTIME,proto->RangedAttackTime); SetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE,proto->RangedMinDamage); SetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE,proto->RangedMaxDamage); SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, proto->Item1SlotDisplay); SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY_01, proto->Item2SlotDisplay); SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY_02, proto->Item3SlotDisplay); SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO, proto->Item1Info1); SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO_01, proto->Item1Info2); SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO_02, proto->Item2Info1); SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO_03, proto->Item2Info2); SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO_04, proto->Item3Info1); SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO_05, proto->Item3Info2); SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, spawn->factionid); SetUInt32Value(UNIT_FIELD_FLAGS, spawn->flags); SetUInt32Value(UNIT_NPC_EMOTESTATE, spawn->emote_state); SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, proto->BoundingRadius); SetFloatValue(UNIT_FIELD_COMBATREACH, proto->CombatReach); original_emotestate = spawn->emote_state; // set position m_position.ChangeCoords( spawn->x, spawn->y, spawn->z, spawn->o ); m_spawnLocation.ChangeCoords(spawn->x, spawn->y, spawn->z, spawn->o); m_aiInterface->setMoveType(spawn->movetype); m_aiInterface->m_waypoints = objmgr.GetWayPointMap(spawn->id); m_faction = sFactionTmpStore.LookupEntry(spawn->factionid); if(m_faction) { m_factionDBC = sFactionStore.LookupEntry(m_faction->Faction); } //SETUP NPC FLAGS SetUInt32Value(UNIT_NPC_FLAGS,proto->NPCFLags); if ( HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_VENDOR ) ) m_SellItems = objmgr.GetVendorList(GetEntry()); if ( HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER ) ) _LoadQuests(); if ( HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_TAXIVENDOR) ) m_TaxiNode = sTaxiMgr.GetNearestTaxiNode( m_position.x, m_position.y, m_position.z, GetMapId() ); if ( HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP) && _gossipScript == 0 ) SetGossipScript( sScriptMgr.GetGossipScript( GetEntry() ) ); if ( HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_TRAINER) ) mTrainer = objmgr.GetTrainer(GetEntry()); if ( HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_AUCTIONEER ) ) auctionHouse = sAuctionMgr.GetAuctionHouse(GetEntry()); //NPC FLAGS m_aiInterface->m_waypoints=objmgr.GetWayPointMap(spawn->id); //load resistances for(uint32 x=0;x<7;x++) BaseResistance[x]=GetUInt32Value(UNIT_FIELD_RESISTANCES+x); for(uint32 x=0;x<5;x++) BaseStats[x]=GetUInt32Value(UNIT_FIELD_STAT0+x); BaseDamage[0]=GetFloatValue(UNIT_FIELD_MINDAMAGE); BaseDamage[1]=GetFloatValue(UNIT_FIELD_MAXDAMAGE); BaseOffhandDamage[0]=GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE); BaseOffhandDamage[1]=GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE); BaseRangedDamage[0]=GetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE); BaseRangedDamage[1]=GetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE); SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f); // better set this one SetUInt32Value(UNIT_FIELD_BYTES_0, spawn->bytes); SetUInt32Value(UNIT_FIELD_BYTES_2, spawn->bytes2); ////////////AI // kek for(list::iterator itr = proto->spells.begin(); itr != proto->spells.end(); ++itr) { m_aiInterface->addSpellToList(*itr); } m_aiInterface->m_canCallForHelp = proto->m_canCallForHelp; m_aiInterface->m_CallForHelpHealth = proto->m_callForHelpHealth; m_aiInterface->m_canFlee = proto->m_canFlee; m_aiInterface->m_FleeHealth = proto->m_fleeHealth; m_aiInterface->m_FleeDuration = proto->m_fleeDuration; //these fields are always 0 in db GetAIInterface()->setMoveType(0); GetAIInterface()->setMoveRunFlag(0); // load formation data if(spawn->form) { m_aiInterface->m_formationLinkSqlId = spawn->form->fol; m_aiInterface->m_formationFollowDistance = spawn->form->dist; m_aiInterface->m_formationFollowAngle = spawn->form->ang; // add event sEventMgr.AddEvent(this, &Creature::FormationLinkUp, m_aiInterface->m_formationLinkSqlId, EVENT_CREATURE_FORMATION_LINKUP, 1000, 0,EVENT_FLAG_DO_NOT_EXECUTE_IN_WORLD_CONTEXT); haslinkupevent = true; } else { m_aiInterface->m_formationLinkSqlId = 0; m_aiInterface->m_formationFollowDistance = 0; m_aiInterface->m_formationFollowAngle = 0; } //////////////AI myFamily = sCreatureFamilyStore.LookupEntry(creature_info->Family); // PLACE FOR DIRTY FIX BASTARDS // HACK! set call for help on civ health @ 100% if(creature_info->Civilian >= 1) m_aiInterface->m_CallForHelpHealth = 100; //HACK! if(m_uint32Values[UNIT_FIELD_DISPLAYID] == 17743 || m_uint32Values[UNIT_FIELD_DISPLAYID] == 20242 || m_uint32Values[UNIT_FIELD_DISPLAYID] == 15435 || (creature_info->Family == UNIT_TYPE_MISC)) { m_useAI = false; } /* more hacks! */ if(proto->Mana != 0) SetPowerType(POWER_TYPE_MANA); else SetPowerType(0); has_combat_text = objmgr.HasMonsterSay(GetEntry(), MONSTER_SAY_EVENT_ENTER_COMBAT); has_waypoint_text = objmgr.HasMonsterSay(GetEntry(), MONSTER_SAY_EVENT_RANDOM_WAYPOINT); m_aiInterface->m_hasWaypointEvents = ScriptSystem->HasEventType(GetEntry(), CREATURE_EVENT_ON_REACH_WP); m_aiInterface->m_isGuard = isGuard(GetEntry()); /* creature death state */ if(proto->death_state == 1) { uint32 newhealth = m_uint32Values[UNIT_FIELD_HEALTH] / 100; if(!newhealth) newhealth = 1; SetUInt32Value(UNIT_FIELD_HEALTH, 1); m_limbostate = true; bInvincible = true; SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_DEAD); } m_invisibityFlag = proto->invisibility_type; return true; } void Creature::Load(CreatureProto * proto_, float x, float y, float z) { proto = proto_; creature_info = CreatureNameStorage.LookupEntry(proto->Id); if(!creature_info) return; //Set fields SetUInt32Value(OBJECT_FIELD_ENTRY,proto->Id); SetFloatValue(OBJECT_FIELD_SCALE_X,proto->Scale); //SetUInt32Value(UNIT_FIELD_HEALTH, (mode ? long2int32(proto->Health * 1.5) : proto->Health)); //SetUInt32Value(UNIT_FIELD_BASE_HEALTH, (mode ? long2int32(proto->Health * 1.5) : proto->Health)); //SetUInt32Value(UNIT_FIELD_MAXHEALTH, (mode ? long2int32(proto->Health * 1.5) : proto->Health)); uint32 health = proto->MinHealth + sRand.randInt(proto->MaxHealth - proto->MinHealth); SetUInt32Value(UNIT_FIELD_HEALTH, health); SetUInt32Value(UNIT_FIELD_MAXHEALTH, health); SetUInt32Value(UNIT_FIELD_BASE_HEALTH, health); SetUInt32Value(UNIT_FIELD_POWER1,proto->Mana); SetUInt32Value(UNIT_FIELD_MAXPOWER1,proto->Mana); SetUInt32Value(UNIT_FIELD_BASE_MANA,proto->Mana); SetUInt32Value(UNIT_FIELD_DISPLAYID,creature_info->DisplayID); SetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID,creature_info->DisplayID); SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID,proto->MountedDisplayID); //SetUInt32Value(UNIT_FIELD_LEVEL, (mode ? proto->Level + (info ? info->lvl_mod_a : 0) : proto->Level)); SetUInt32Value(UNIT_FIELD_LEVEL, proto->MinLevel + (sRand.randInt(proto->MaxLevel - proto->MinLevel))); for(uint32 i = 0; i < 7; ++i) SetUInt32Value(UNIT_FIELD_RESISTANCES+i,proto->Resistances[i]); SetUInt32Value(UNIT_FIELD_BASEATTACKTIME,proto->AttackTime); SetFloatValue(UNIT_FIELD_MINDAMAGE, proto->MinDamage); SetFloatValue(UNIT_FIELD_MAXDAMAGE, proto->MaxDamage); SetUInt32Value(UNIT_FIELD_RANGEDATTACKTIME,proto->RangedAttackTime); SetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE,proto->RangedMinDamage); SetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE,proto->RangedMaxDamage); SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, proto->Item1SlotDisplay); SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY_01, proto->Item2SlotDisplay); SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY_02, proto->Item3SlotDisplay); SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO, proto->Item1Info1); SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO_01, proto->Item1Info2); SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO_02, proto->Item2Info1); SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO_03, proto->Item2Info2); SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO_04, proto->Item3Info1); SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO_05, proto->Item3Info2); SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, proto->Faction); SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, proto->BoundingRadius); SetFloatValue(UNIT_FIELD_COMBATREACH, proto->CombatReach); original_emotestate = 0; // set position m_position.ChangeCoords( x, y, z, 0 ); m_spawnLocation.ChangeCoords(x, y, z, 0); m_faction = sFactionTmpStore.LookupEntry(proto->Faction); if(m_faction) { m_factionDBC = sFactionStore.LookupEntry(m_faction->Faction); } //SETUP NPC FLAGS SetUInt32Value(UNIT_NPC_FLAGS,proto->NPCFLags); if ( HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_VENDOR ) ) m_SellItems = objmgr.GetVendorList(GetEntry()); if ( HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER ) ) _LoadQuests(); if ( HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_TAXIVENDOR) ) m_TaxiNode = sTaxiMgr.GetNearestTaxiNode( m_position.x, m_position.y, m_position.z, GetMapId() ); if ( HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP) && _gossipScript == 0 ) SetGossipScript( sScriptMgr.GetGossipScript( GetEntry() ) ); if ( HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_TRAINER) ) mTrainer = objmgr.GetTrainer(GetEntry()); if ( HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_AUCTIONEER ) ) auctionHouse = sAuctionMgr.GetAuctionHouse(GetEntry()); //load resistances for(uint32 x=0;x<7;x++) BaseResistance[x]=GetUInt32Value(UNIT_FIELD_RESISTANCES+x); for(uint32 x=0;x<5;x++) BaseStats[x]=GetUInt32Value(UNIT_FIELD_STAT0+x); BaseDamage[0]=GetFloatValue(UNIT_FIELD_MINDAMAGE); BaseDamage[1]=GetFloatValue(UNIT_FIELD_MAXDAMAGE); BaseOffhandDamage[0]=GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE); BaseOffhandDamage[1]=GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE); BaseRangedDamage[0]=GetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE); BaseRangedDamage[1]=GetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE); SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f); // better set this one ////////////AI // kek for(list::iterator itr = proto->spells.begin(); itr != proto->spells.end(); ++itr) { m_aiInterface->addSpellToList(*itr); } m_aiInterface->m_canCallForHelp = proto->m_canCallForHelp; m_aiInterface->m_CallForHelpHealth = proto->m_callForHelpHealth; m_aiInterface->m_canFlee = proto->m_canFlee; m_aiInterface->m_FleeHealth = proto->m_fleeHealth; m_aiInterface->m_FleeDuration = proto->m_fleeDuration; //these fields are always 0 in db GetAIInterface()->setMoveType(0); GetAIInterface()->setMoveRunFlag(0); // load formation data m_aiInterface->m_formationLinkSqlId = 0; m_aiInterface->m_formationFollowDistance = 0; m_aiInterface->m_formationFollowAngle = 0; //////////////AI myFamily = sCreatureFamilyStore.LookupEntry(creature_info->Family); // PLACE FOR DIRTY FIX BASTARDS // HACK! set call for help on civ health @ 100% if(creature_info->Civilian >= 1) m_aiInterface->m_CallForHelpHealth = 100; //HACK! if(m_uint32Values[UNIT_FIELD_DISPLAYID] == 17743 || m_uint32Values[UNIT_FIELD_DISPLAYID] == 20242 || m_uint32Values[UNIT_FIELD_DISPLAYID] == 15435 || (creature_info->Family == UNIT_TYPE_MISC)) { m_useAI = false; } /* more hacks! */ if(proto->Mana != 0) SetPowerType(POWER_TYPE_MANA); else SetPowerType(0); has_combat_text = objmgr.HasMonsterSay(GetEntry(), MONSTER_SAY_EVENT_ENTER_COMBAT); has_waypoint_text = objmgr.HasMonsterSay(GetEntry(), MONSTER_SAY_EVENT_RANDOM_WAYPOINT); m_aiInterface->m_hasWaypointEvents = ScriptSystem->HasEventType(GetEntry(), CREATURE_EVENT_ON_REACH_WP); m_aiInterface->m_isGuard = isGuard(GetEntry()); /* creature death state */ if(proto->death_state == 1) { uint32 newhealth = m_uint32Values[UNIT_FIELD_HEALTH] / 100; if(!newhealth) newhealth = 1; SetUInt32Value(UNIT_FIELD_HEALTH, 1); m_limbostate = true; bInvincible = true; SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_DEAD); } m_invisibityFlag = proto->invisibility_type; } void Creature::OnPushToWorld() { if(proto) { set::iterator itr = proto->start_auras.begin(); SpellEntry * sp; for(; itr != proto->start_auras.end(); ++itr) { sp = sSpellStore.LookupEntry((*itr)); if(sp == 0) continue; CastSpell(this, sp, 0); } } LoadScript(); Unit::OnPushToWorld(); /* script */ ScriptSystem->OnCreatureEvent(this, 0, CREATURE_EVENT_ON_SPAWN); if(_myScriptClass) _myScriptClass->OnLoad(); if(IS_INSTANCE(m_mapMgr->GetMapId())) m_aiInterface->setOutOfCombatRange(0); else objmgr.SetCreatureBySqlId(GetSQL_id(), this); } // this is used for guardians. They are non respawnable creatures linked to a player void Creature::SummonExpire() { RemoveFromWorld(false); SafeDelete();//delete creature totaly. } void Creature::Despawn(uint32 delay, uint32 respawntime) { if(delay) { sEventMgr.AddEvent(this, &Creature::Despawn, (uint32)0, respawntime, EVENT_CREATURE_RESPAWN, delay, 1,0); return; } if(!IsInWorld()) return; if(respawntime) { /* get the cell with our SPAWN location. if we've moved cell this might break :P */ MapCell * pCell = m_mapMgr->GetCellByCoords(m_spawnLocation.x, m_spawnLocation.y); if(!pCell) pCell = m_mapCell; ASSERT(pCell); pCell->_respawnObjects.insert(((Object*)this)); sEventMgr.RemoveEvents(this); sEventMgr.AddEvent(m_mapMgr, &MapMgr::EventRespawnCreature, this, pCell, EVENT_CREATURE_RESPAWN, respawntime, 1, 0); Unit::RemoveFromWorld(); m_position = m_spawnLocation; m_respawnCell=pCell; } else { Unit::RemoveFromWorld(); SafeDelete(); } } void Creature::TriggerScriptEvent(void * func) { ScriptSystem->OnCreatureEvent(this, (gmFunctionObject*)func); } void Creature::DestroyCustomWaypointMap() { if(m_custom_waypoint_map) { for(WayPointMap::iterator itr = m_custom_waypoint_map->begin(); itr != m_custom_waypoint_map->end(); ++itr) { delete (*itr); } delete m_custom_waypoint_map; m_custom_waypoint_map = 0; m_aiInterface->SetWaypointMap(0); } } void Creature::RemoveLimboState(Unit * healer) { if(!m_limbostate != true) return; m_limbostate = false; SetUInt32Value(UNIT_NPC_EMOTESTATE, m_spawn ? m_spawn->emote_state : 0); SetUInt32Value(UNIT_FIELD_HEALTH, GetUInt32Value(UNIT_FIELD_MAXHEALTH)); ScriptSystem->OnCreatureEvent(this, healer ? healer : this, CREATURE_EVENT_ON_LEAVE_LIMBO); bInvincible = false; } // Generates 3 random waypoints around the NPC void Creature::SetGuardWaypoints() { if(!GetMapMgr()) return; if(!GetCreatureName()) return; GetAIInterface()->setMoveType(1); for(int i = 1; i <= 4; i++) { float ang = rand()/100.0; float ran = (rand()%(100))/10.0; while(ran < 1) ran = (rand()%(100))/10.0; WayPoint * wp = new WayPoint; wp->id = i; wp->flags = 0; wp->waittime = 800; /* these guards are antsy :P */ wp->x = GetSpawnX()+ran*sin(ang); wp->y = GetSpawnY()+ran*cos(ang); wp->z = GetMapMgr()->GetLandHeight(wp->x, wp->y); wp->o = 0; wp->backwardemoteid = 0; wp->backwardemoteoneshot = 0; wp->forwardemoteid = 0; wp->forwardemoteoneshot = 0; wp->backwardskinid = GetCreatureName()->DisplayID; wp->forwardskinid = GetCreatureName()->DisplayID; GetAIInterface()->addWayPoint(wp); } }