mirror of
https://github.com/mangosfour/server.git
synced 2025-12-26 16:37:06 +00:00
Initial project location adjustment
This commit is contained in:
parent
11641a8bd7
commit
9d20fe2b32
244 changed files with 0 additions and 0 deletions
163
src/game/Object/AggressorAI.cpp
Normal file
163
src/game/Object/AggressorAI.cpp
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include "AggressorAI.h"
|
||||
#include "Errors.h"
|
||||
#include "Creature.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "VMapFactory.h"
|
||||
#include "World.h"
|
||||
#include "DBCStores.h"
|
||||
#include "Map.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
int
|
||||
AggressorAI::Permissible(const Creature* creature)
|
||||
{
|
||||
// have some hostile factions, it will be selected by IsHostileTo check at MoveInLineOfSight
|
||||
if (!creature->IsCivilian() && !creature->IsNeutralToAll())
|
||||
return PERMIT_BASE_PROACTIVE;
|
||||
|
||||
return PERMIT_BASE_NO;
|
||||
}
|
||||
|
||||
AggressorAI::AggressorAI(Creature* c) : CreatureAI(c), i_state(STATE_NORMAL), i_tracker(TIME_INTERVAL_LOOK)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
AggressorAI::MoveInLineOfSight(Unit* u)
|
||||
{
|
||||
// Ignore Z for flying creatures
|
||||
if (!m_creature->CanFly() && m_creature->GetDistanceZ(u) > CREATURE_Z_ATTACK_RANGE)
|
||||
return;
|
||||
|
||||
if (m_creature->CanInitiateAttack() && u->isTargetableForAttack() &&
|
||||
m_creature->IsHostileTo(u) && u->isInAccessablePlaceFor(m_creature))
|
||||
{
|
||||
float attackRadius = m_creature->GetAttackDistance(u);
|
||||
if (m_creature->IsWithinDistInMap(u, attackRadius) && m_creature->IsWithinLOSInMap(u))
|
||||
{
|
||||
if (!m_creature->getVictim())
|
||||
{
|
||||
AttackStart(u);
|
||||
u->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
|
||||
}
|
||||
else if (sMapStore.LookupEntry(m_creature->GetMapId())->IsDungeon())
|
||||
{
|
||||
m_creature->AddThreat(u);
|
||||
u->SetInCombatWith(m_creature);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AggressorAI::EnterEvadeMode()
|
||||
{
|
||||
if (!m_creature->isAlive())
|
||||
{
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature stopped attacking, he is dead [guid=%u]", m_creature->GetGUIDLow());
|
||||
i_victimGuid.Clear();
|
||||
m_creature->CombatStop(true);
|
||||
m_creature->DeleteThreatList();
|
||||
return;
|
||||
}
|
||||
|
||||
Unit* victim = m_creature->GetMap()->GetUnit(i_victimGuid);
|
||||
|
||||
if (!victim)
|
||||
{
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature stopped attacking, no victim [guid=%u]", m_creature->GetGUIDLow());
|
||||
}
|
||||
else if (!victim->isAlive())
|
||||
{
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature stopped attacking, victim is dead [guid=%u]", m_creature->GetGUIDLow());
|
||||
}
|
||||
else if (victim->HasStealthAura())
|
||||
{
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature stopped attacking, victim is in stealth [guid=%u]", m_creature->GetGUIDLow());
|
||||
}
|
||||
else if (victim->IsTaxiFlying())
|
||||
{
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature stopped attacking, victim is in flight [guid=%u]", m_creature->GetGUIDLow());
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature stopped attacking, victim out run him [guid=%u]", m_creature->GetGUIDLow());
|
||||
// i_state = STATE_LOOK_AT_VICTIM;
|
||||
// i_tracker.Reset(TIME_INTERVAL_LOOK);
|
||||
}
|
||||
|
||||
if (!m_creature->isCharmed())
|
||||
{
|
||||
m_creature->RemoveAllAurasOnEvade();
|
||||
|
||||
// Remove ChaseMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead
|
||||
if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
|
||||
m_creature->GetMotionMaster()->MoveTargetedHome();
|
||||
}
|
||||
|
||||
m_creature->DeleteThreatList();
|
||||
i_victimGuid.Clear();
|
||||
m_creature->CombatStop(true);
|
||||
m_creature->SetLootRecipient(NULL);
|
||||
}
|
||||
|
||||
void
|
||||
AggressorAI::UpdateAI(const uint32 /*diff*/)
|
||||
{
|
||||
// update i_victimGuid if m_creature->getVictim() !=0 and changed
|
||||
if (!m_creature->SelectHostileTarget() || !m_creature->getVictim())
|
||||
return;
|
||||
|
||||
i_victimGuid = m_creature->getVictim()->GetObjectGuid();
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
|
||||
bool
|
||||
AggressorAI::IsVisible(Unit* pl) const
|
||||
{
|
||||
return m_creature->IsWithinDist(pl, sWorld.getConfig(CONFIG_FLOAT_SIGHT_MONSTER))
|
||||
&& pl->isVisibleForOrDetect(m_creature, m_creature, true);
|
||||
}
|
||||
|
||||
void
|
||||
AggressorAI::AttackStart(Unit* u)
|
||||
{
|
||||
if (!u)
|
||||
return;
|
||||
|
||||
if (m_creature->Attack(u, true))
|
||||
{
|
||||
i_victimGuid = u->GetObjectGuid();
|
||||
|
||||
m_creature->AddThreat(u);
|
||||
m_creature->SetInCombatWith(u);
|
||||
u->SetInCombatWith(m_creature);
|
||||
|
||||
HandleMovementOnAttackStart(u);
|
||||
}
|
||||
}
|
||||
60
src/game/Object/AggressorAI.h
Normal file
60
src/game/Object/AggressorAI.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOS_AGGRESSORAI_H
|
||||
#define MANGOS_AGGRESSORAI_H
|
||||
|
||||
#include "CreatureAI.h"
|
||||
#include "Timer.h"
|
||||
#include "ObjectGuid.h"
|
||||
|
||||
class Creature;
|
||||
|
||||
class MANGOS_DLL_DECL AggressorAI : public CreatureAI
|
||||
{
|
||||
enum AggressorState
|
||||
{
|
||||
STATE_NORMAL = 1,
|
||||
STATE_LOOK_AT_VICTIM = 2
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
explicit AggressorAI(Creature* c);
|
||||
|
||||
void MoveInLineOfSight(Unit*) override;
|
||||
void AttackStart(Unit*) override;
|
||||
void EnterEvadeMode() override;
|
||||
bool IsVisible(Unit*) const override;
|
||||
|
||||
void UpdateAI(const uint32) override;
|
||||
static int Permissible(const Creature*);
|
||||
|
||||
private:
|
||||
ObjectGuid i_victimGuid;
|
||||
AggressorState i_state;
|
||||
TimeTracker i_tracker;
|
||||
};
|
||||
|
||||
#endif
|
||||
761
src/game/Object/ArenaTeam.cpp
Normal file
761
src/game/Object/ArenaTeam.cpp
Normal file
|
|
@ -0,0 +1,761 @@
|
|||
/*
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include "WorldPacket.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "ArenaTeam.h"
|
||||
#include "World.h"
|
||||
#include "Player.h"
|
||||
|
||||
void ArenaTeamMember::ModifyPersonalRating(Player* plr, int32 mod, uint32 slot)
|
||||
{
|
||||
if (int32(personal_rating) + mod < 0)
|
||||
personal_rating = 0;
|
||||
else
|
||||
personal_rating += mod;
|
||||
if (plr)
|
||||
plr->SetArenaTeamInfoField(slot, ARENA_TEAM_PERSONAL_RATING, personal_rating);
|
||||
}
|
||||
|
||||
ArenaTeam::ArenaTeam()
|
||||
{
|
||||
m_TeamId = 0;
|
||||
m_Type = ARENA_TYPE_NONE;
|
||||
m_BackgroundColor = 0; // background
|
||||
m_EmblemStyle = 0; // icon
|
||||
m_EmblemColor = 0; // icon color
|
||||
m_BorderStyle = 0; // border
|
||||
m_BorderColor = 0; // border color
|
||||
m_stats.games_week = 0;
|
||||
m_stats.games_season = 0;
|
||||
m_stats.rank = 0;
|
||||
|
||||
int32 conf_value = sWorld.getConfig(CONFIG_INT32_ARENA_STARTRATING);
|
||||
if (conf_value < 0) // -1 = select by season id
|
||||
{
|
||||
if (sWorld.getConfig(CONFIG_UINT32_ARENA_SEASON_ID) >= 6)
|
||||
m_stats.rating = 0;
|
||||
else
|
||||
m_stats.rating = 1500;
|
||||
}
|
||||
else
|
||||
m_stats.rating = uint32(conf_value);
|
||||
|
||||
m_stats.wins_week = 0;
|
||||
m_stats.wins_season = 0;
|
||||
}
|
||||
|
||||
ArenaTeam::~ArenaTeam()
|
||||
{
|
||||
}
|
||||
|
||||
bool ArenaTeam::Create(ObjectGuid captainGuid, ArenaType type, std::string arenaTeamName)
|
||||
{
|
||||
if (!IsArenaTypeValid(type))
|
||||
return false;
|
||||
if (!sObjectMgr.GetPlayer(captainGuid)) // player not exist
|
||||
return false;
|
||||
if (sObjectMgr.GetArenaTeamByName(arenaTeamName)) // arena team with this name already exist
|
||||
return false;
|
||||
|
||||
DEBUG_LOG("GUILD: creating arena team %s to leader: %s", arenaTeamName.c_str(), captainGuid.GetString().c_str());
|
||||
|
||||
m_CaptainGuid = captainGuid;
|
||||
m_Name = arenaTeamName;
|
||||
m_Type = type;
|
||||
|
||||
m_TeamId = sObjectMgr.GenerateArenaTeamId();
|
||||
|
||||
// ArenaTeamName already assigned to ArenaTeam::name, use it to encode string for DB
|
||||
CharacterDatabase.escape_string(arenaTeamName);
|
||||
|
||||
CharacterDatabase.BeginTransaction();
|
||||
// CharacterDatabase.PExecute("DELETE FROM arena_team WHERE arenateamid='%u'", m_TeamId); - MAX(arenateam)+1 not exist
|
||||
CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid='%u'", m_TeamId);
|
||||
CharacterDatabase.PExecute("INSERT INTO arena_team (arenateamid,name,captainguid,type,BackgroundColor,EmblemStyle,EmblemColor,BorderStyle,BorderColor) "
|
||||
"VALUES('%u','%s','%u','%u','%u','%u','%u','%u','%u')",
|
||||
m_TeamId, arenaTeamName.c_str(), m_CaptainGuid.GetCounter(), m_Type, m_BackgroundColor, m_EmblemStyle, m_EmblemColor, m_BorderStyle, m_BorderColor);
|
||||
CharacterDatabase.PExecute("INSERT INTO arena_team_stats (arenateamid, rating, games_week, wins_week, games_season, wins_season, rank) VALUES "
|
||||
"('%u', '%u', '%u', '%u', '%u', '%u', '%u')", m_TeamId, m_stats.rating, m_stats.games_week, m_stats.wins_week, m_stats.games_season, m_stats.wins_season, m_stats.rank);
|
||||
|
||||
CharacterDatabase.CommitTransaction();
|
||||
|
||||
AddMember(m_CaptainGuid);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ArenaTeam::AddMember(ObjectGuid playerGuid)
|
||||
{
|
||||
std::string plName;
|
||||
uint8 plClass;
|
||||
|
||||
// arena team is full (can't have more than type * 2 players!)
|
||||
if (GetMembersSize() >= GetMaxMembersSize())
|
||||
return false;
|
||||
|
||||
Player* pl = sObjectMgr.GetPlayer(playerGuid);
|
||||
if (pl)
|
||||
{
|
||||
if (pl->GetArenaTeamId(GetSlot()))
|
||||
{
|
||||
sLog.outError("Arena::AddMember() : player already in this sized team");
|
||||
return false;
|
||||
}
|
||||
|
||||
plClass = pl->getClass();
|
||||
plName = pl->GetName();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 0 1
|
||||
QueryResult* result = CharacterDatabase.PQuery("SELECT name, class FROM characters WHERE guid='%u'", playerGuid.GetCounter());
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
plName = (*result)[0].GetCppString();
|
||||
plClass = (*result)[1].GetUInt8();
|
||||
delete result;
|
||||
|
||||
// check if player already in arenateam of that size
|
||||
if (Player::GetArenaTeamIdFromDB(playerGuid, GetType()) != 0)
|
||||
{
|
||||
sLog.outError("Arena::AddMember() : player %s already in this sized team", playerGuid.GetString().c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ArenaTeamMember newmember;
|
||||
newmember.name = plName;
|
||||
newmember.guid = playerGuid;
|
||||
newmember.Class = plClass;
|
||||
newmember.games_season = 0;
|
||||
newmember.games_week = 0;
|
||||
newmember.wins_season = 0;
|
||||
newmember.wins_week = 0;
|
||||
|
||||
int32 conf_value = sWorld.getConfig(CONFIG_INT32_ARENA_STARTPERSONALRATING);
|
||||
if (conf_value < 0) // -1 = select by season id
|
||||
{
|
||||
if (sWorld.getConfig(CONFIG_UINT32_ARENA_SEASON_ID) >= 6)
|
||||
{
|
||||
if (m_stats.rating < 1000)
|
||||
newmember.personal_rating = 0;
|
||||
else
|
||||
newmember.personal_rating = 1000;
|
||||
}
|
||||
else
|
||||
{
|
||||
newmember.personal_rating = 1500;
|
||||
}
|
||||
}
|
||||
else
|
||||
newmember.personal_rating = uint32(conf_value);
|
||||
|
||||
m_members.push_back(newmember);
|
||||
|
||||
CharacterDatabase.PExecute("INSERT INTO arena_team_member (arenateamid, guid, personal_rating) VALUES ('%u', '%u', '%u')", m_TeamId, newmember.guid.GetCounter(), newmember.personal_rating);
|
||||
|
||||
if (pl)
|
||||
{
|
||||
pl->SetInArenaTeam(m_TeamId, GetSlot(), GetType());
|
||||
pl->SetArenaTeamIdInvited(0);
|
||||
pl->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_PERSONAL_RATING, newmember.personal_rating);
|
||||
|
||||
// hide promote/remove buttons
|
||||
if (m_CaptainGuid != playerGuid)
|
||||
pl->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_MEMBER, 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ArenaTeam::LoadArenaTeamFromDB(QueryResult* arenaTeamDataResult)
|
||||
{
|
||||
if (!arenaTeamDataResult)
|
||||
return false;
|
||||
|
||||
Field* fields = arenaTeamDataResult->Fetch();
|
||||
|
||||
m_TeamId = fields[0].GetUInt32();
|
||||
m_Name = fields[1].GetCppString();
|
||||
m_CaptainGuid = ObjectGuid(HIGHGUID_PLAYER, fields[2].GetUInt32());
|
||||
m_Type = ArenaType(fields[3].GetUInt32());
|
||||
|
||||
if (!IsArenaTypeValid(m_Type))
|
||||
return false;
|
||||
|
||||
m_BackgroundColor = fields[4].GetUInt32();
|
||||
m_EmblemStyle = fields[5].GetUInt32();
|
||||
m_EmblemColor = fields[6].GetUInt32();
|
||||
m_BorderStyle = fields[7].GetUInt32();
|
||||
m_BorderColor = fields[8].GetUInt32();
|
||||
// load team stats
|
||||
m_stats.rating = fields[9].GetUInt32();
|
||||
m_stats.games_week = fields[10].GetUInt32();
|
||||
m_stats.wins_week = fields[11].GetUInt32();
|
||||
m_stats.games_season = fields[12].GetUInt32();
|
||||
m_stats.wins_season = fields[13].GetUInt32();
|
||||
m_stats.rank = fields[14].GetUInt32();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ArenaTeam::LoadMembersFromDB(QueryResult* arenaTeamMembersResult)
|
||||
{
|
||||
if (!arenaTeamMembersResult)
|
||||
return false;
|
||||
|
||||
bool captainPresentInTeam = false;
|
||||
|
||||
do
|
||||
{
|
||||
Field* fields = arenaTeamMembersResult->Fetch();
|
||||
// prevent crash if db records are broken, when all members in result are already processed and current team hasn't got any members
|
||||
if (!fields)
|
||||
break;
|
||||
uint32 arenaTeamId = fields[0].GetUInt32();
|
||||
if (arenaTeamId < m_TeamId)
|
||||
{
|
||||
// there is in table arena_team_member record which doesn't have arenateamid in arena_team table, report error
|
||||
sLog.outErrorDb("ArenaTeam %u does not exist but it has record in arena_team_member table, deleting it!", arenaTeamId);
|
||||
CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid = '%u'", arenaTeamId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arenaTeamId > m_TeamId)
|
||||
// we loaded all members for this arena_team already, break cycle
|
||||
break;
|
||||
|
||||
ArenaTeamMember newmember;
|
||||
newmember.guid = ObjectGuid(HIGHGUID_PLAYER, fields[1].GetUInt32());
|
||||
newmember.games_week = fields[2].GetUInt32();
|
||||
newmember.wins_week = fields[3].GetUInt32();
|
||||
newmember.games_season = fields[4].GetUInt32();
|
||||
newmember.wins_season = fields[5].GetUInt32();
|
||||
newmember.personal_rating = fields[6].GetUInt32();
|
||||
newmember.name = fields[7].GetCppString();
|
||||
newmember.Class = fields[8].GetUInt8();
|
||||
|
||||
// check if member exists in characters table
|
||||
if (newmember.name.empty())
|
||||
{
|
||||
sLog.outErrorDb("ArenaTeam %u has member with empty name - probably player %s doesn't exist, deleting him from memberlist!", arenaTeamId, newmember.guid.GetString().c_str());
|
||||
DelMember(newmember.guid);
|
||||
continue;
|
||||
}
|
||||
|
||||
// arena team can't be > 2 * arenatype (2 for 2x2, 3 for 3x3, 5 for 5x5)
|
||||
if (GetMembersSize() >= GetMaxMembersSize())
|
||||
return false;
|
||||
|
||||
if (newmember.guid == GetCaptainGuid())
|
||||
captainPresentInTeam = true;
|
||||
|
||||
m_members.push_back(newmember);
|
||||
}
|
||||
while (arenaTeamMembersResult->NextRow());
|
||||
|
||||
if (Empty() || !captainPresentInTeam)
|
||||
{
|
||||
// arena team is empty or captain is not in team, delete from db
|
||||
sLog.outErrorDb("ArenaTeam %u does not have any members or its captain is not in team, disbanding it...", m_TeamId);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ArenaTeam::SetCaptain(ObjectGuid guid)
|
||||
{
|
||||
// disable remove/promote buttons
|
||||
Player* oldcaptain = sObjectMgr.GetPlayer(GetCaptainGuid());
|
||||
if (oldcaptain)
|
||||
oldcaptain->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_MEMBER, 1);
|
||||
|
||||
// set new captain
|
||||
m_CaptainGuid = guid;
|
||||
|
||||
// update database
|
||||
CharacterDatabase.PExecute("UPDATE arena_team SET captainguid = '%u' WHERE arenateamid = '%u'", guid.GetCounter(), m_TeamId);
|
||||
|
||||
// enable remove/promote buttons
|
||||
if (Player* newcaptain = sObjectMgr.GetPlayer(guid))
|
||||
newcaptain->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_MEMBER, 0);
|
||||
}
|
||||
|
||||
void ArenaTeam::DelMember(ObjectGuid guid)
|
||||
{
|
||||
for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
|
||||
{
|
||||
if (itr->guid == guid)
|
||||
{
|
||||
m_members.erase(itr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Player* player = sObjectMgr.GetPlayer(guid))
|
||||
{
|
||||
player->GetSession()->SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, GetName(), "", 0);
|
||||
// delete all info regarding this team
|
||||
for (int i = 0; i < ARENA_TEAM_END; ++i)
|
||||
player->SetArenaTeamInfoField(GetSlot(), ArenaTeamInfoType(i), 0);
|
||||
}
|
||||
|
||||
CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid = '%u' AND guid = '%u'", GetId(), guid.GetCounter());
|
||||
}
|
||||
|
||||
void ArenaTeam::Disband(WorldSession* session)
|
||||
{
|
||||
// event
|
||||
if (session)
|
||||
{
|
||||
// probably only 1 string required...
|
||||
BroadcastEvent(ERR_ARENA_TEAM_DISBANDED_S, session->GetPlayerName(), GetName().c_str());
|
||||
}
|
||||
|
||||
while (!m_members.empty())
|
||||
{
|
||||
// Removing from members is done in DelMember.
|
||||
DelMember(m_members.front().guid);
|
||||
}
|
||||
|
||||
CharacterDatabase.BeginTransaction();
|
||||
CharacterDatabase.PExecute("DELETE FROM arena_team WHERE arenateamid = '%u'", m_TeamId);
|
||||
CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid = '%u'", m_TeamId); //< this should be already done by calling DelMember(memberGuids[j]); for each member
|
||||
CharacterDatabase.PExecute("DELETE FROM arena_team_stats WHERE arenateamid = '%u'", m_TeamId);
|
||||
CharacterDatabase.CommitTransaction();
|
||||
sObjectMgr.RemoveArenaTeam(m_TeamId);
|
||||
}
|
||||
|
||||
void ArenaTeam::Roster(WorldSession* session)
|
||||
{
|
||||
Player* pl = NULL;
|
||||
|
||||
uint8 unk308 = 0;
|
||||
|
||||
WorldPacket data(SMSG_ARENA_TEAM_ROSTER, 100);
|
||||
data << uint32(GetId()); // team id
|
||||
data << uint8(unk308); // 308 unknown value but affect packet structure
|
||||
data << uint32(GetMembersSize()); // members count
|
||||
data << uint32(GetType()); // arena team type?
|
||||
|
||||
for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
|
||||
{
|
||||
pl = sObjectMgr.GetPlayer(itr->guid);
|
||||
|
||||
data << itr->guid; // guid
|
||||
data << uint8((pl ? 1 : 0)); // online flag
|
||||
data << itr->name; // member name
|
||||
data << uint32((itr->guid == GetCaptainGuid() ? 0 : 1));// captain flag 0 captain 1 member
|
||||
data << uint8((pl ? pl->getLevel() : 0)); // unknown, level?
|
||||
data << uint8(itr->Class); // class
|
||||
data << uint32(itr->games_week); // played this week
|
||||
data << uint32(itr->wins_week); // wins this week
|
||||
data << uint32(itr->games_season); // played this season
|
||||
data << uint32(itr->wins_season); // wins this season
|
||||
data << uint32(itr->personal_rating); // personal rating
|
||||
if (unk308)
|
||||
{
|
||||
data << float(0.0); // 308 unk
|
||||
data << float(0.0); // 308 unk
|
||||
}
|
||||
}
|
||||
|
||||
session->SendPacket(&data);
|
||||
DEBUG_LOG("WORLD: Sent SMSG_ARENA_TEAM_ROSTER");
|
||||
}
|
||||
|
||||
void ArenaTeam::Query(WorldSession* session)
|
||||
{
|
||||
WorldPacket data(SMSG_ARENA_TEAM_QUERY_RESPONSE, 4 * 7 + GetName().size() + 1);
|
||||
data << uint32(GetId()); // team id
|
||||
data << GetName(); // team name
|
||||
data << uint32(GetType()); // arena team type (2=2x2, 3=3x3 or 5=5x5)
|
||||
data << uint32(m_BackgroundColor); // background color
|
||||
data << uint32(m_EmblemStyle); // emblem style
|
||||
data << uint32(m_EmblemColor); // emblem color
|
||||
data << uint32(m_BorderStyle); // border style
|
||||
data << uint32(m_BorderColor); // border color
|
||||
session->SendPacket(&data);
|
||||
DEBUG_LOG("WORLD: Sent SMSG_ARENA_TEAM_QUERY_RESPONSE");
|
||||
}
|
||||
|
||||
void ArenaTeam::Stats(WorldSession* session)
|
||||
{
|
||||
WorldPacket data(SMSG_ARENA_TEAM_STATS, 4 * 7);
|
||||
data << uint32(GetId()); // team id
|
||||
data << uint32(m_stats.rating); // rating
|
||||
data << uint32(m_stats.games_week); // games this week
|
||||
data << uint32(m_stats.wins_week); // wins this week
|
||||
data << uint32(m_stats.games_season); // played this season
|
||||
data << uint32(m_stats.wins_season); // wins this season
|
||||
data << uint32(m_stats.rank); // rank
|
||||
session->SendPacket(&data);
|
||||
}
|
||||
|
||||
void ArenaTeam::NotifyStatsChanged()
|
||||
{
|
||||
// this is called after a rated match ended
|
||||
// updates arena team stats for every member of the team (not only the ones who participated!)
|
||||
for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
|
||||
{
|
||||
Player* plr = sObjectMgr.GetPlayer(itr->guid);
|
||||
if (plr)
|
||||
Stats(plr->GetSession());
|
||||
}
|
||||
}
|
||||
|
||||
void ArenaTeam::InspectStats(WorldSession* session, ObjectGuid guid)
|
||||
{
|
||||
ArenaTeamMember* member = GetMember(guid);
|
||||
if (!member)
|
||||
return;
|
||||
|
||||
WorldPacket data(MSG_INSPECT_ARENA_TEAMS, 8 + 1 + 4 * 6);
|
||||
data << guid; // player guid
|
||||
data << uint8(GetSlot()); // slot (0...2)
|
||||
data << uint32(GetId()); // arena team id
|
||||
data << uint32(m_stats.rating); // rating
|
||||
data << uint32(m_stats.games_season); // season played
|
||||
data << uint32(m_stats.wins_season); // season wins
|
||||
data << uint32(member->games_season); // played (count of all games, that the inspected member participated...)
|
||||
data << uint32(member->personal_rating); // personal rating
|
||||
session->SendPacket(&data);
|
||||
}
|
||||
|
||||
void ArenaTeam::SetEmblem(uint32 backgroundColor, uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor)
|
||||
{
|
||||
m_BackgroundColor = backgroundColor;
|
||||
m_EmblemStyle = emblemStyle;
|
||||
m_EmblemColor = emblemColor;
|
||||
m_BorderStyle = borderStyle;
|
||||
m_BorderColor = borderColor;
|
||||
|
||||
CharacterDatabase.PExecute("UPDATE arena_team SET BackgroundColor='%u', EmblemStyle='%u', EmblemColor='%u', BorderStyle='%u', BorderColor='%u' WHERE arenateamid='%u'", m_BackgroundColor, m_EmblemStyle, m_EmblemColor, m_BorderStyle, m_BorderColor, m_TeamId);
|
||||
}
|
||||
|
||||
void ArenaTeam::SetStats(uint32 stat_type, uint32 value)
|
||||
{
|
||||
switch (stat_type)
|
||||
{
|
||||
case STAT_TYPE_RATING:
|
||||
m_stats.rating = value;
|
||||
CharacterDatabase.PExecute("UPDATE arena_team_stats SET rating = '%u' WHERE arenateamid = '%u'", value, GetId());
|
||||
break;
|
||||
case STAT_TYPE_GAMES_WEEK:
|
||||
m_stats.games_week = value;
|
||||
CharacterDatabase.PExecute("UPDATE arena_team_stats SET games_week = '%u' WHERE arenateamid = '%u'", value, GetId());
|
||||
break;
|
||||
case STAT_TYPE_WINS_WEEK:
|
||||
m_stats.wins_week = value;
|
||||
CharacterDatabase.PExecute("UPDATE arena_team_stats SET wins_week = '%u' WHERE arenateamid = '%u'", value, GetId());
|
||||
break;
|
||||
case STAT_TYPE_GAMES_SEASON:
|
||||
m_stats.games_season = value;
|
||||
CharacterDatabase.PExecute("UPDATE arena_team_stats SET games_season = '%u' WHERE arenateamid = '%u'", value, GetId());
|
||||
break;
|
||||
case STAT_TYPE_WINS_SEASON:
|
||||
m_stats.wins_season = value;
|
||||
CharacterDatabase.PExecute("UPDATE arena_team_stats SET wins_season = '%u' WHERE arenateamid = '%u'", value, GetId());
|
||||
break;
|
||||
case STAT_TYPE_RANK:
|
||||
m_stats.rank = value;
|
||||
CharacterDatabase.PExecute("UPDATE arena_team_stats SET rank = '%u' WHERE arenateamid = '%u'", value, GetId());
|
||||
break;
|
||||
default:
|
||||
DEBUG_LOG("unknown stat type in ArenaTeam::SetStats() %u", stat_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ArenaTeam::BroadcastPacket(WorldPacket* packet)
|
||||
{
|
||||
for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
|
||||
{
|
||||
Player* player = sObjectMgr.GetPlayer(itr->guid);
|
||||
if (player)
|
||||
player->GetSession()->SendPacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
void ArenaTeam::BroadcastEvent(ArenaTeamEvents event, ObjectGuid guid, char const* str1 /*=NULL*/, char const* str2 /*=NULL*/, char const* str3 /*=NULL*/)
|
||||
{
|
||||
uint8 strCount = !str1 ? 0 : (!str2 ? 1 : (!str3 ? 2 : 3));
|
||||
|
||||
WorldPacket data(SMSG_ARENA_TEAM_EVENT, 1 + 1 + 1 * strCount + (!guid ? 0 : 8));
|
||||
data << uint8(event);
|
||||
data << uint8(strCount);
|
||||
|
||||
if (str3)
|
||||
{
|
||||
data << str1;
|
||||
data << str2;
|
||||
data << str3;
|
||||
}
|
||||
else if (str2)
|
||||
{
|
||||
data << str1;
|
||||
data << str2;
|
||||
}
|
||||
else if (str1)
|
||||
data << str1;
|
||||
|
||||
if (guid)
|
||||
data << ObjectGuid(guid);
|
||||
|
||||
BroadcastPacket(&data);
|
||||
|
||||
DEBUG_LOG("WORLD: Sent SMSG_ARENA_TEAM_EVENT");
|
||||
}
|
||||
|
||||
uint8 ArenaTeam::GetSlotByType(ArenaType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ARENA_TYPE_2v2: return 0;
|
||||
case ARENA_TYPE_3v3: return 1;
|
||||
case ARENA_TYPE_5v5: return 2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
sLog.outError("FATAL: Unknown arena team type %u for some arena team", type);
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
ArenaType ArenaTeam::GetTypeBySlot(uint8 slot)
|
||||
{
|
||||
switch (slot)
|
||||
{
|
||||
case 0: return ARENA_TYPE_2v2;
|
||||
case 1: return ARENA_TYPE_3v3;
|
||||
case 2: return ARENA_TYPE_5v5;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
sLog.outError("FATAL: Unknown arena team slot %u for some arena team", slot);
|
||||
return ArenaType(0xFF);
|
||||
}
|
||||
|
||||
bool ArenaTeam::HaveMember(ObjectGuid guid) const
|
||||
{
|
||||
for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
|
||||
if (itr->guid == guid)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
float ArenaTeam::GetChanceAgainst(uint32 own_rating, uint32 enemy_rating)
|
||||
{
|
||||
// returns the chance to win against a team with the given rating, used in the rating adjustment calculation
|
||||
// ELO system
|
||||
|
||||
if (sWorld.getConfig(CONFIG_UINT32_ARENA_SEASON_ID) >= 6)
|
||||
if (enemy_rating < 1000)
|
||||
enemy_rating = 1000;
|
||||
return 1.0f / (1.0f + exp(log(10.0f) * (float)((float)enemy_rating - (float)own_rating) / 400.0f));
|
||||
}
|
||||
|
||||
void ArenaTeam::FinishGame(int32 mod)
|
||||
{
|
||||
if (int32(m_stats.rating) + mod < 0)
|
||||
m_stats.rating = 0;
|
||||
else
|
||||
m_stats.rating += mod;
|
||||
|
||||
m_stats.games_week += 1;
|
||||
m_stats.games_season += 1;
|
||||
// update team's rank
|
||||
m_stats.rank = 1;
|
||||
ObjectMgr::ArenaTeamMap::const_iterator i = sObjectMgr.GetArenaTeamMapBegin();
|
||||
for (; i != sObjectMgr.GetArenaTeamMapEnd(); ++i)
|
||||
{
|
||||
if (i->second->GetType() == this->m_Type && i->second->GetStats().rating > m_stats.rating)
|
||||
++m_stats.rank;
|
||||
}
|
||||
}
|
||||
|
||||
int32 ArenaTeam::WonAgainst(uint32 againstRating)
|
||||
{
|
||||
// called when the team has won
|
||||
// 'chance' calculation - to beat the opponent
|
||||
float chance = GetChanceAgainst(m_stats.rating, againstRating);
|
||||
float K = (m_stats.rating < 1000) ? 48.0f : 32.0f;
|
||||
// calculate the rating modification (ELO system with k=32 or k=48 if rating<1000)
|
||||
int32 mod = (int32)floor(K * (1.0f - chance));
|
||||
// modify the team stats accordingly
|
||||
FinishGame(mod);
|
||||
m_stats.wins_week += 1;
|
||||
m_stats.wins_season += 1;
|
||||
|
||||
// return the rating change, used to display it on the results screen
|
||||
return mod;
|
||||
}
|
||||
|
||||
int32 ArenaTeam::LostAgainst(uint32 againstRating)
|
||||
{
|
||||
// called when the team has lost
|
||||
//'chance' calculation - to loose to the opponent
|
||||
float chance = GetChanceAgainst(m_stats.rating, againstRating);
|
||||
float K = (m_stats.rating < 1000) ? 48.0f : 32.0f;
|
||||
// calculate the rating modification (ELO system with k=32 or k=48 if rating<1000)
|
||||
int32 mod = (int32)ceil(K * (0.0f - chance));
|
||||
// modify the team stats accordingly
|
||||
FinishGame(mod);
|
||||
|
||||
// return the rating change, used to display it on the results screen
|
||||
return mod;
|
||||
}
|
||||
|
||||
void ArenaTeam::MemberLost(Player* plr, uint32 againstRating)
|
||||
{
|
||||
// called for each participant of a match after losing
|
||||
for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
|
||||
{
|
||||
if (itr->guid == plr->GetObjectGuid())
|
||||
{
|
||||
// update personal rating
|
||||
float chance = GetChanceAgainst(itr->personal_rating, againstRating);
|
||||
float K = (itr->personal_rating < 1000) ? 48.0f : 32.0f;
|
||||
// calculate the rating modification (ELO system with k=32 or k=48 if rating<1000)
|
||||
int32 mod = (int32)ceil(K * (0.0f - chance));
|
||||
itr->ModifyPersonalRating(plr, mod, GetSlot());
|
||||
// update personal played stats
|
||||
itr->games_week += 1;
|
||||
itr->games_season += 1;
|
||||
// update the unit fields
|
||||
plr->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_GAMES_WEEK, itr->games_week);
|
||||
plr->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_GAMES_SEASON, itr->games_season);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArenaTeam::OfflineMemberLost(ObjectGuid guid, uint32 againstRating)
|
||||
{
|
||||
// called for offline player after ending rated arena match!
|
||||
for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
|
||||
{
|
||||
if (itr->guid == guid)
|
||||
{
|
||||
// update personal rating
|
||||
float chance = GetChanceAgainst(itr->personal_rating, againstRating);
|
||||
float K = (itr->personal_rating < 1000) ? 48.0f : 32.0f;
|
||||
// calculate the rating modification (ELO system with k=32 or k=48 if rating<1000)
|
||||
int32 mod = (int32)ceil(K * (0.0f - chance));
|
||||
if (int32(itr->personal_rating) + mod < 0)
|
||||
itr->personal_rating = 0;
|
||||
else
|
||||
itr->personal_rating += mod;
|
||||
// update personal played stats
|
||||
itr->games_week += 1;
|
||||
itr->games_season += 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArenaTeam::MemberWon(Player* plr, uint32 againstRating)
|
||||
{
|
||||
// called for each participant after winning a match
|
||||
for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
|
||||
{
|
||||
if (itr->guid == plr->GetObjectGuid())
|
||||
{
|
||||
// update personal rating
|
||||
float chance = GetChanceAgainst(itr->personal_rating, againstRating);
|
||||
float K = (itr->personal_rating < 1000) ? 48.0f : 32.0f;
|
||||
// calculate the rating modification (ELO system with k=32 or k=48 if rating<1000)
|
||||
int32 mod = (int32)floor(K * (1.0f - chance));
|
||||
itr->ModifyPersonalRating(plr, mod, GetSlot());
|
||||
// update personal stats
|
||||
itr->games_week += 1;
|
||||
itr->games_season += 1;
|
||||
itr->wins_season += 1;
|
||||
itr->wins_week += 1;
|
||||
// update unit fields
|
||||
plr->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_GAMES_WEEK, itr->games_week);
|
||||
plr->SetArenaTeamInfoField(GetSlot(), ARENA_TEAM_GAMES_SEASON, itr->games_season);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArenaTeam::SaveToDB()
|
||||
{
|
||||
// save team and member stats to db
|
||||
// called after a match has ended, or when calculating arena_points
|
||||
CharacterDatabase.BeginTransaction();
|
||||
CharacterDatabase.PExecute("UPDATE arena_team_stats SET rating = '%u',games_week = '%u',games_season = '%u',rank = '%u',wins_week = '%u',wins_season = '%u' WHERE arenateamid = '%u'", m_stats.rating, m_stats.games_week, m_stats.games_season, m_stats.rank, m_stats.wins_week, m_stats.wins_season, GetId());
|
||||
for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
|
||||
{
|
||||
CharacterDatabase.PExecute("UPDATE arena_team_member SET played_week = '%u', wons_week = '%u', played_season = '%u', wons_season = '%u', personal_rating = '%u' WHERE arenateamid = '%u' AND guid = '%u'", itr->games_week, itr->wins_week, itr->games_season, itr->wins_season, itr->personal_rating, m_TeamId, itr->guid.GetCounter());
|
||||
}
|
||||
CharacterDatabase.CommitTransaction();
|
||||
}
|
||||
|
||||
void ArenaTeam::FinishWeek()
|
||||
{
|
||||
m_stats.games_week = 0; // played this week
|
||||
m_stats.wins_week = 0; // wins this week
|
||||
for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
|
||||
{
|
||||
itr->games_week = 0;
|
||||
itr->wins_week = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool ArenaTeam::IsFighting() const
|
||||
{
|
||||
for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
|
||||
{
|
||||
if (Player* p = sObjectMgr.GetPlayer(itr->guid))
|
||||
{
|
||||
if (p->GetMap()->IsBattleArena())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// add new arena event to all already connected team members
|
||||
void ArenaTeam::MassInviteToEvent(WorldSession* session)
|
||||
{
|
||||
WorldPacket data(SMSG_CALENDAR_ARENA_TEAM);
|
||||
|
||||
data << uint32(m_members.size());
|
||||
|
||||
for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
|
||||
{
|
||||
uint32 level = Player::GetLevelFromDB(itr->guid);
|
||||
|
||||
if (itr->guid != session->GetPlayer()->GetObjectGuid())
|
||||
{
|
||||
data << itr->guid.WriteAsPacked();
|
||||
data << uint8(level);
|
||||
}
|
||||
}
|
||||
session->SendPacket(&data);
|
||||
}
|
||||
234
src/game/Object/ArenaTeam.h
Normal file
234
src/game/Object/ArenaTeam.h
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOSSERVER_ARENATEAM_H
|
||||
#define MANGOSSERVER_ARENATEAM_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "SharedDefines.h"
|
||||
|
||||
class QueryResult;
|
||||
class WorldPacket;
|
||||
class WorldSession;
|
||||
class Player;
|
||||
|
||||
enum ArenaTeamCommandTypes
|
||||
{
|
||||
ERR_ARENA_TEAM_CREATE_S = 0x00,
|
||||
ERR_ARENA_TEAM_INVITE_SS = 0x01,
|
||||
ERR_ARENA_TEAM_QUIT_S = 0x03,
|
||||
ERR_ARENA_TEAM_FOUNDER_S = 0x0E
|
||||
};
|
||||
|
||||
enum ArenaTeamCommandErrors
|
||||
{
|
||||
ERR_ARENA_TEAM_INTERNAL = 0x01,
|
||||
ERR_ALREADY_IN_ARENA_TEAM = 0x02,
|
||||
ERR_ALREADY_IN_ARENA_TEAM_S = 0x03,
|
||||
ERR_INVITED_TO_ARENA_TEAM = 0x04,
|
||||
ERR_ALREADY_INVITED_TO_ARENA_TEAM_S = 0x05,
|
||||
ERR_ARENA_TEAM_NAME_INVALID = 0x06,
|
||||
ERR_ARENA_TEAM_NAME_EXISTS_S = 0x07,
|
||||
ERR_ARENA_TEAM_LEADER_LEAVE_S = 0x08,
|
||||
ERR_ARENA_TEAM_PERMISSIONS = 0x08,
|
||||
ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM = 0x09,
|
||||
ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM_SS = 0x0A,
|
||||
ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S = 0x0B,
|
||||
ERR_ARENA_TEAM_NOT_ALLIED = 0x0C,
|
||||
ERR_ARENA_TEAM_IGNORING_YOU_S = 0x13,
|
||||
ERR_ARENA_TEAM_TARGET_TOO_LOW_S = 0x15,
|
||||
ERR_ARENA_TEAM_TARGET_TOO_HIGH_S = 0x16,
|
||||
ERR_ARENA_TEAM_TOO_MANY_MEMBERS_S = 0x17,
|
||||
ERR_ARENA_TEAM_NOT_FOUND = 0x1B,
|
||||
ERR_ARENA_TEAMS_LOCKED = 0x1E,
|
||||
ERR_ARENA_TEAM_TOO_MANY_CREATE = 0x21,
|
||||
};
|
||||
|
||||
enum ArenaTeamEvents
|
||||
{
|
||||
ERR_ARENA_TEAM_JOIN_SS = 4, // player name + arena team name
|
||||
ERR_ARENA_TEAM_LEAVE_SS = 5, // player name + arena team name
|
||||
ERR_ARENA_TEAM_REMOVE_SSS = 6, // player name + arena team name + captain name
|
||||
ERR_ARENA_TEAM_LEADER_IS_SS = 7, // player name + arena team name
|
||||
ERR_ARENA_TEAM_LEADER_CHANGED_SSS = 8, // old captain + new captain + arena team name
|
||||
ERR_ARENA_TEAM_DISBANDED_S = 9, // captain name + arena team name
|
||||
};
|
||||
|
||||
/*
|
||||
need info how to send these ones:
|
||||
ERR_ARENA_TEAM_YOU_JOIN_S - client show it automatically when accept invite
|
||||
ERR_ARENA_TEAM_TARGET_TOO_LOW_S
|
||||
ERR_ARENA_TEAM_TOO_MANY_MEMBERS_S
|
||||
ERR_ARENA_TEAM_LEVEL_TOO_LOW_I
|
||||
*/
|
||||
|
||||
enum ArenaTeamStatTypes
|
||||
{
|
||||
STAT_TYPE_RATING = 0,
|
||||
STAT_TYPE_GAMES_WEEK = 1,
|
||||
STAT_TYPE_WINS_WEEK = 2,
|
||||
STAT_TYPE_GAMES_SEASON = 3,
|
||||
STAT_TYPE_WINS_SEASON = 4,
|
||||
STAT_TYPE_RANK = 5
|
||||
};
|
||||
|
||||
struct ArenaTeamMember
|
||||
{
|
||||
ObjectGuid guid;
|
||||
std::string name;
|
||||
uint8 Class;
|
||||
uint32 games_week;
|
||||
uint32 wins_week;
|
||||
uint32 games_season;
|
||||
uint32 wins_season;
|
||||
uint32 personal_rating;
|
||||
|
||||
void ModifyPersonalRating(Player* plr, int32 mod, uint32 slot);
|
||||
};
|
||||
|
||||
struct ArenaTeamStats
|
||||
{
|
||||
uint32 rating;
|
||||
uint32 games_week;
|
||||
uint32 wins_week;
|
||||
uint32 games_season;
|
||||
uint32 wins_season;
|
||||
uint32 rank;
|
||||
};
|
||||
|
||||
#define MAX_ARENA_SLOT 3 // 0..2 slots
|
||||
|
||||
class ArenaTeam
|
||||
{
|
||||
public:
|
||||
ArenaTeam();
|
||||
~ArenaTeam();
|
||||
|
||||
bool Create(ObjectGuid captainGuid, ArenaType type, std::string arenaTeamName);
|
||||
void Disband(WorldSession* session);
|
||||
|
||||
typedef std::list<ArenaTeamMember> MemberList;
|
||||
|
||||
uint32 GetId() const { return m_TeamId; }
|
||||
ArenaType GetType() const { return m_Type; }
|
||||
uint8 GetSlot() const { return GetSlotByType(GetType()); }
|
||||
static uint8 GetSlotByType(ArenaType type);
|
||||
static ArenaType GetTypeBySlot(uint8 slot);
|
||||
ObjectGuid GetCaptainGuid() const { return m_CaptainGuid; }
|
||||
std::string GetName() const { return m_Name; }
|
||||
const ArenaTeamStats& GetStats() const { return m_stats; }
|
||||
void SetStats(uint32 stat_type, uint32 value);
|
||||
uint32 GetRating() const { return m_stats.rating; }
|
||||
|
||||
uint32 GetEmblemStyle() const { return m_EmblemStyle; }
|
||||
uint32 GetEmblemColor() const { return m_EmblemColor; }
|
||||
uint32 GetBorderStyle() const { return m_BorderStyle; }
|
||||
uint32 GetBorderColor() const { return m_BorderColor; }
|
||||
uint32 GetBackgroundColor() const { return m_BackgroundColor; }
|
||||
|
||||
void SetCaptain(ObjectGuid guid);
|
||||
bool AddMember(ObjectGuid playerGuid);
|
||||
void DelMember(ObjectGuid guid);
|
||||
|
||||
void SetEmblem(uint32 backgroundColor, uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor);
|
||||
|
||||
size_t GetMembersSize() const { return m_members.size(); }
|
||||
size_t GetMaxMembersSize() const { return size_t(GetType() * 2); }
|
||||
bool Empty() const { return m_members.empty(); }
|
||||
MemberList::iterator m_membersBegin() { return m_members.begin(); }
|
||||
MemberList::iterator m_membersEnd() { return m_members.end(); }
|
||||
bool HaveMember(ObjectGuid guid) const;
|
||||
|
||||
ArenaTeamMember* GetMember(ObjectGuid guid)
|
||||
{
|
||||
for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
|
||||
if (itr->guid == guid)
|
||||
return &(*itr);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ArenaTeamMember* GetMember(const std::string& name)
|
||||
{
|
||||
for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
|
||||
if (itr->name == name)
|
||||
return &(*itr);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool IsFighting() const;
|
||||
|
||||
bool LoadArenaTeamFromDB(QueryResult* arenaTeamDataResult);
|
||||
bool LoadMembersFromDB(QueryResult* arenaTeamMembersResult);
|
||||
void LoadStatsFromDB(uint32 ArenaTeamId);
|
||||
|
||||
void SaveToDB();
|
||||
|
||||
void BroadcastPacket(WorldPacket* packet);
|
||||
|
||||
void BroadcastEvent(ArenaTeamEvents event, ObjectGuid guid, char const* str1 = NULL, char const* str2 = NULL, char const* str3 = NULL);
|
||||
void BroadcastEvent(ArenaTeamEvents event, char const* str1 = NULL, char const* str2 = NULL, char const* str3 = NULL)
|
||||
{
|
||||
BroadcastEvent(event, ObjectGuid(), str1, str2, str3);
|
||||
}
|
||||
|
||||
void Roster(WorldSession* session);
|
||||
void Query(WorldSession* session);
|
||||
void Stats(WorldSession* session);
|
||||
void InspectStats(WorldSession* session, ObjectGuid guid);
|
||||
|
||||
float GetChanceAgainst(uint32 own_rating, uint32 enemy_rating);
|
||||
int32 WonAgainst(uint32 againstRating);
|
||||
void MemberWon(Player* plr, uint32 againstRating);
|
||||
int32 LostAgainst(uint32 againstRating);
|
||||
void MemberLost(Player* plr, uint32 againstRating);
|
||||
void OfflineMemberLost(ObjectGuid guid, uint32 againstRating);
|
||||
|
||||
void NotifyStatsChanged();
|
||||
|
||||
void FinishWeek();
|
||||
void FinishGame(int32 mod);
|
||||
|
||||
// Calendar
|
||||
void MassInviteToEvent(WorldSession* session);
|
||||
|
||||
protected:
|
||||
|
||||
uint32 m_TeamId;
|
||||
ArenaType m_Type;
|
||||
std::string m_Name;
|
||||
ObjectGuid m_CaptainGuid;
|
||||
|
||||
uint32 m_BackgroundColor; // ARGB format
|
||||
uint32 m_EmblemStyle; // icon id
|
||||
uint32 m_EmblemColor; // ARGB format
|
||||
uint32 m_BorderStyle; // border image id
|
||||
uint32 m_BorderColor; // ARGB format
|
||||
|
||||
MemberList m_members;
|
||||
ArenaTeamStats m_stats;
|
||||
};
|
||||
#endif
|
||||
1057
src/game/Object/AuctionHouseMgr.cpp
Normal file
1057
src/game/Object/AuctionHouseMgr.cpp
Normal file
File diff suppressed because it is too large
Load diff
212
src/game/Object/AuctionHouseMgr.h
Normal file
212
src/game/Object/AuctionHouseMgr.h
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef _AUCTION_HOUSE_MGR_H
|
||||
#define _AUCTION_HOUSE_MGR_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "Policies/Singleton.h"
|
||||
#include "DBCStructure.h"
|
||||
|
||||
class Item;
|
||||
class Player;
|
||||
class Unit;
|
||||
class WorldPacket;
|
||||
|
||||
#define MIN_AUCTION_TIME (12*HOUR)
|
||||
#define MAX_AUCTION_SORT 12
|
||||
#define AUCTION_SORT_REVERSED 0x10
|
||||
|
||||
enum AuctionError
|
||||
{
|
||||
AUCTION_OK = 0, // depends on enum AuctionAction
|
||||
AUCTION_ERR_INVENTORY = 1, // depends on enum InventoryChangeResult
|
||||
AUCTION_ERR_DATABASE = 2, // ERR_AUCTION_DATABASE_ERROR (default)
|
||||
AUCTION_ERR_NOT_ENOUGH_MONEY = 3, // ERR_NOT_ENOUGH_MONEY
|
||||
AUCTION_ERR_ITEM_NOT_FOUND = 4, // ERR_ITEM_NOT_FOUND
|
||||
AUCTION_ERR_HIGHER_BID = 5, // ERR_AUCTION_HIGHER_BID
|
||||
AUCTION_ERR_BID_INCREMENT = 7, // ERR_AUCTION_BID_INCREMENT
|
||||
AUCTION_ERR_BID_OWN = 10, // ERR_AUCTION_BID_OWN
|
||||
AUCTION_ERR_RESTRICTED_ACCOUNT = 13 // ERR_RESTRICTED_ACCOUNT
|
||||
};
|
||||
|
||||
enum AuctionAction
|
||||
{
|
||||
AUCTION_STARTED = 0, // ERR_AUCTION_STARTED
|
||||
AUCTION_REMOVED = 1, // ERR_AUCTION_REMOVED
|
||||
AUCTION_BID_PLACED = 2 // ERR_AUCTION_BID_PLACED
|
||||
};
|
||||
|
||||
struct AuctionEntry
|
||||
{
|
||||
uint32 Id;
|
||||
uint32 itemGuidLow; // can be 0 after send won mail with item
|
||||
uint32 itemTemplate;
|
||||
uint32 itemCount;
|
||||
int32 itemRandomPropertyId;
|
||||
uint32 owner; // player low guid, can be 0 for server generated auction
|
||||
std::wstring ownerName; // cache name for sorting
|
||||
uint64 startbid; // start minimal bid value
|
||||
uint64 bid; // current bid, =0 meaning no bids
|
||||
uint64 buyout;
|
||||
time_t expireTime;
|
||||
time_t moneyDeliveryTime;
|
||||
uint32 bidder; // current bidder player lowguid, can be 0 if bid generated by server, use 'bid'!=0 for check bid existance
|
||||
uint64 deposit; // deposit can be calculated only when creating auction
|
||||
AuctionHouseEntry const* auctionHouseEntry; // in AuctionHouse.dbc
|
||||
|
||||
// helpers
|
||||
uint32 GetHouseId() const { return auctionHouseEntry->houseId; }
|
||||
uint32 GetHouseFaction() const { return auctionHouseEntry->faction; }
|
||||
uint64 GetAuctionCut() const;
|
||||
uint64 GetAuctionOutBid() const;
|
||||
bool BuildAuctionInfo(WorldPacket& data) const;
|
||||
void DeleteFromDB() const;
|
||||
void SaveToDB() const;
|
||||
void AuctionBidWinning(Player* bidder = NULL);
|
||||
|
||||
// -1,0,+1 order result
|
||||
int CompareAuctionEntry(uint32 column, const AuctionEntry* auc, Player* viewPlayer) const;
|
||||
|
||||
bool UpdateBid(uint64 newbid, Player* newbidder = NULL);// true if normal bid, false if buyout, bidder==NULL for generated bid
|
||||
};
|
||||
|
||||
// this class is used as auctionhouse instance
|
||||
class AuctionHouseObject
|
||||
{
|
||||
public:
|
||||
AuctionHouseObject() {}
|
||||
~AuctionHouseObject()
|
||||
{
|
||||
for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr)
|
||||
delete itr->second;
|
||||
}
|
||||
|
||||
typedef std::map<uint32, AuctionEntry*> AuctionEntryMap;
|
||||
typedef std::pair<AuctionEntryMap::const_iterator, AuctionEntryMap::const_iterator> AuctionEntryMapBounds;
|
||||
|
||||
uint32 GetCount() { return AuctionsMap.size(); }
|
||||
|
||||
AuctionEntryMap const& GetAuctions() const { return AuctionsMap; }
|
||||
AuctionEntryMapBounds GetAuctionsBounds() const {return AuctionEntryMapBounds(AuctionsMap.begin(), AuctionsMap.end()); }
|
||||
|
||||
void AddAuction(AuctionEntry* ah)
|
||||
{
|
||||
MANGOS_ASSERT(ah);
|
||||
AuctionsMap[ah->Id] = ah;
|
||||
}
|
||||
|
||||
AuctionEntry* GetAuction(uint32 id) const
|
||||
{
|
||||
AuctionEntryMap::const_iterator itr = AuctionsMap.find(id);
|
||||
return itr != AuctionsMap.end() ? itr->second : NULL;
|
||||
}
|
||||
|
||||
bool RemoveAuction(uint32 id)
|
||||
{
|
||||
return AuctionsMap.erase(id);
|
||||
}
|
||||
|
||||
void Update();
|
||||
|
||||
void BuildListBidderItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount);
|
||||
void BuildListOwnerItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount);
|
||||
void BuildListPendingSales(WorldPacket& data, Player* player, uint32& count);
|
||||
|
||||
AuctionEntry* AddAuction(AuctionHouseEntry const* auctionHouseEntry, Item* newItem, uint32 etime, uint64 bid, uint64 buyout = 0, uint64 deposit = 0, Player* pl = NULL);
|
||||
private:
|
||||
AuctionEntryMap AuctionsMap;
|
||||
};
|
||||
|
||||
class AuctionSorter
|
||||
{
|
||||
public:
|
||||
AuctionSorter(AuctionSorter const& sorter) : m_sort(sorter.m_sort), m_viewPlayer(sorter.m_viewPlayer) {}
|
||||
AuctionSorter(uint8* sort, Player* viewPlayer) : m_sort(sort), m_viewPlayer(viewPlayer) {}
|
||||
bool operator()(const AuctionEntry* auc1, const AuctionEntry* auc2) const;
|
||||
|
||||
private:
|
||||
uint8* m_sort;
|
||||
Player* m_viewPlayer;
|
||||
};
|
||||
|
||||
enum AuctionHouseType
|
||||
{
|
||||
AUCTION_HOUSE_ALLIANCE = 0,
|
||||
AUCTION_HOUSE_HORDE = 1,
|
||||
AUCTION_HOUSE_NEUTRAL = 2
|
||||
};
|
||||
|
||||
#define MAX_AUCTION_HOUSE_TYPE 3
|
||||
|
||||
class AuctionHouseMgr
|
||||
{
|
||||
public:
|
||||
AuctionHouseMgr();
|
||||
~AuctionHouseMgr();
|
||||
|
||||
typedef UNORDERED_MAP<uint32, Item*> ItemMap;
|
||||
|
||||
AuctionHouseObject* GetAuctionsMap(AuctionHouseType houseType) { return &mAuctions[houseType]; }
|
||||
AuctionHouseObject* GetAuctionsMap(AuctionHouseEntry const* house);
|
||||
|
||||
Item* GetAItem(uint32 id)
|
||||
{
|
||||
ItemMap::const_iterator itr = mAitems.find(id);
|
||||
if (itr != mAitems.end())
|
||||
{
|
||||
return itr->second;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// auction messages
|
||||
void SendAuctionWonMail(AuctionEntry* auction);
|
||||
void SendAuctionSuccessfulMail(AuctionEntry* auction);
|
||||
void SendAuctionExpiredMail(AuctionEntry* auction);
|
||||
static uint64 GetAuctionDeposit(AuctionHouseEntry const* entry, uint32 time, Item* pItem);
|
||||
|
||||
static uint32 GetAuctionHouseTeam(AuctionHouseEntry const* house);
|
||||
static AuctionHouseEntry const* GetAuctionHouseEntry(Unit* unit);
|
||||
|
||||
public:
|
||||
// load first auction items, because of check if item exists, when loading
|
||||
void LoadAuctionItems();
|
||||
void LoadAuctions();
|
||||
|
||||
void AddAItem(Item* it);
|
||||
bool RemoveAItem(uint32 id);
|
||||
|
||||
void Update();
|
||||
|
||||
private:
|
||||
AuctionHouseObject mAuctions[MAX_AUCTION_HOUSE_TYPE];
|
||||
|
||||
ItemMap mAitems;
|
||||
};
|
||||
|
||||
#define sAuctionMgr MaNGOS::Singleton<AuctionHouseMgr>::Instance()
|
||||
|
||||
#endif
|
||||
247
src/game/Object/Bag.cpp
Normal file
247
src/game/Object/Bag.cpp
Normal file
|
|
@ -0,0 +1,247 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include "Bag.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Database/DatabaseEnv.h"
|
||||
#include "Log.h"
|
||||
#include "UpdateData.h"
|
||||
|
||||
Bag::Bag(): Item()
|
||||
{
|
||||
m_objectType |= (TYPEMASK_ITEM | TYPEMASK_CONTAINER);
|
||||
m_objectTypeId = TYPEID_CONTAINER;
|
||||
|
||||
m_valuesCount = CONTAINER_END;
|
||||
|
||||
memset(m_bagslot, 0, sizeof(Item*) * MAX_BAG_SIZE);
|
||||
}
|
||||
|
||||
Bag::~Bag()
|
||||
{
|
||||
for (int i = 0; i < MAX_BAG_SIZE; ++i)
|
||||
delete m_bagslot[i];
|
||||
}
|
||||
|
||||
void Bag::AddToWorld()
|
||||
{
|
||||
Item::AddToWorld();
|
||||
|
||||
for (uint32 i = 0; i < GetBagSize(); ++i)
|
||||
if (m_bagslot[i])
|
||||
m_bagslot[i]->AddToWorld();
|
||||
}
|
||||
|
||||
void Bag::RemoveFromWorld()
|
||||
{
|
||||
for (uint32 i = 0; i < GetBagSize(); ++i)
|
||||
if (m_bagslot[i])
|
||||
m_bagslot[i]->RemoveFromWorld();
|
||||
|
||||
Item::RemoveFromWorld();
|
||||
}
|
||||
|
||||
bool Bag::Create(uint32 guidlow, uint32 itemid, Player const* owner)
|
||||
{
|
||||
ItemPrototype const* itemProto = ObjectMgr::GetItemPrototype(itemid);
|
||||
|
||||
if (!itemProto || itemProto->ContainerSlots > MAX_BAG_SIZE)
|
||||
return false;
|
||||
|
||||
Object::_Create(guidlow, 0, HIGHGUID_CONTAINER);
|
||||
|
||||
SetEntry(itemid);
|
||||
SetObjectScale(DEFAULT_OBJECT_SCALE);
|
||||
|
||||
SetGuidValue(ITEM_FIELD_OWNER, owner ? owner->GetObjectGuid() : ObjectGuid());
|
||||
SetGuidValue(ITEM_FIELD_CONTAINED, owner ? owner->GetObjectGuid() : ObjectGuid());
|
||||
|
||||
SetUInt32Value(ITEM_FIELD_MAXDURABILITY, itemProto->MaxDurability);
|
||||
SetUInt32Value(ITEM_FIELD_DURABILITY, itemProto->MaxDurability);
|
||||
SetUInt32Value(ITEM_FIELD_STACK_COUNT, 1);
|
||||
|
||||
// Setting the number of Slots the Container has
|
||||
SetUInt32Value(CONTAINER_FIELD_NUM_SLOTS, itemProto->ContainerSlots);
|
||||
|
||||
// Cleaning 20 slots
|
||||
for (uint8 i = 0; i < MAX_BAG_SIZE; ++i)
|
||||
{
|
||||
SetGuidValue(CONTAINER_FIELD_SLOT_1 + (i * 2), ObjectGuid());
|
||||
m_bagslot[i] = NULL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Bag::SaveToDB()
|
||||
{
|
||||
Item::SaveToDB();
|
||||
}
|
||||
|
||||
bool Bag::LoadFromDB(uint32 guidLow, Field* fields, ObjectGuid ownerGuid)
|
||||
{
|
||||
if (!Item::LoadFromDB(guidLow, fields, ownerGuid))
|
||||
return false;
|
||||
|
||||
// cleanup bag content related item value fields (its will be filled correctly from `character_inventory`)
|
||||
for (int i = 0; i < MAX_BAG_SIZE; ++i)
|
||||
{
|
||||
SetGuidValue(CONTAINER_FIELD_SLOT_1 + (i * 2), ObjectGuid());
|
||||
|
||||
delete m_bagslot[i];
|
||||
m_bagslot[i] = NULL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Bag::DeleteFromDB()
|
||||
{
|
||||
for (int i = 0; i < MAX_BAG_SIZE; ++i)
|
||||
if (m_bagslot[i])
|
||||
m_bagslot[i]->DeleteFromDB();
|
||||
|
||||
Item::DeleteFromDB();
|
||||
}
|
||||
|
||||
uint32 Bag::GetFreeSlots() const
|
||||
{
|
||||
uint32 slots = 0;
|
||||
for (uint32 i = 0; i < GetBagSize(); ++i)
|
||||
if (!m_bagslot[i])
|
||||
++slots;
|
||||
|
||||
return slots;
|
||||
}
|
||||
|
||||
void Bag::RemoveItem(uint8 slot, bool /*update*/)
|
||||
{
|
||||
MANGOS_ASSERT(slot < MAX_BAG_SIZE);
|
||||
|
||||
if (m_bagslot[slot])
|
||||
m_bagslot[slot]->SetContainer(NULL);
|
||||
|
||||
m_bagslot[slot] = NULL;
|
||||
SetGuidValue(CONTAINER_FIELD_SLOT_1 + (slot * 2), ObjectGuid());
|
||||
}
|
||||
|
||||
void Bag::StoreItem(uint8 slot, Item* pItem, bool /*update*/)
|
||||
{
|
||||
MANGOS_ASSERT(slot < MAX_BAG_SIZE);
|
||||
|
||||
if (pItem)
|
||||
{
|
||||
m_bagslot[slot] = pItem;
|
||||
SetGuidValue(CONTAINER_FIELD_SLOT_1 + (slot * 2), pItem->GetObjectGuid());
|
||||
pItem->SetGuidValue(ITEM_FIELD_CONTAINED, GetObjectGuid());
|
||||
pItem->SetGuidValue(ITEM_FIELD_OWNER, GetOwnerGuid());
|
||||
pItem->SetContainer(this);
|
||||
pItem->SetSlot(slot);
|
||||
}
|
||||
}
|
||||
|
||||
void Bag::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const
|
||||
{
|
||||
Item::BuildCreateUpdateBlockForPlayer(data, target);
|
||||
|
||||
for (uint32 i = 0; i < GetBagSize(); ++i)
|
||||
if (m_bagslot[i])
|
||||
m_bagslot[i]->BuildCreateUpdateBlockForPlayer(data, target);
|
||||
}
|
||||
|
||||
// If the bag is empty returns true
|
||||
bool Bag::IsEmpty() const
|
||||
{
|
||||
for (uint32 i = 0; i < GetBagSize(); ++i)
|
||||
if (m_bagslot[i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Item* Bag::GetItemByEntry(uint32 item) const
|
||||
{
|
||||
for (uint32 i = 0; i < GetBagSize(); ++i)
|
||||
if (m_bagslot[i] && m_bagslot[i]->GetEntry() == item)
|
||||
return m_bagslot[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Item* Bag::GetItemByLimitedCategory(uint32 limitedCategory) const
|
||||
{
|
||||
for (uint32 i = 0; i < GetBagSize(); ++i)
|
||||
if (m_bagslot[i] && m_bagslot[i]->GetProto()->ItemLimitCategory == limitedCategory)
|
||||
return m_bagslot[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32 Bag::GetItemCount(uint32 item, Item* eItem) const
|
||||
{
|
||||
uint32 count = 0;
|
||||
|
||||
for (uint32 i = 0; i < GetBagSize(); ++i)
|
||||
if (m_bagslot[i])
|
||||
if (m_bagslot[i] != eItem && m_bagslot[i]->GetEntry() == item)
|
||||
count += m_bagslot[i]->GetCount();
|
||||
|
||||
if (eItem && eItem->GetProto()->GemProperties)
|
||||
for (uint32 i = 0; i < GetBagSize(); ++i)
|
||||
if (m_bagslot[i])
|
||||
if (m_bagslot[i] != eItem && m_bagslot[i]->GetProto()->Socket[0].Color)
|
||||
count += m_bagslot[i]->GetGemCountWithID(item);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
uint32 Bag::GetItemCountWithLimitCategory(uint32 limitCategory, Item* eItem) const
|
||||
{
|
||||
uint32 count = 0;
|
||||
|
||||
for (uint32 i = 0; i < GetBagSize(); ++i)
|
||||
if (m_bagslot[i])
|
||||
if (m_bagslot[i] != eItem && m_bagslot[i]->GetProto()->ItemLimitCategory == limitCategory)
|
||||
count += m_bagslot[i]->GetCount();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
uint8 Bag::GetSlotByItemGUID(ObjectGuid guid) const
|
||||
{
|
||||
for (uint32 i = 0; i < GetBagSize(); ++i)
|
||||
if (m_bagslot[i] != 0)
|
||||
if (m_bagslot[i]->GetObjectGuid() == guid)
|
||||
return i;
|
||||
|
||||
return NULL_SLOT;
|
||||
}
|
||||
|
||||
Item* Bag::GetItemByPos(uint8 slot) const
|
||||
{
|
||||
if (slot < GetBagSize())
|
||||
return m_bagslot[slot];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
86
src/game/Object/Bag.h
Normal file
86
src/game/Object/Bag.h
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOS_BAG_H
|
||||
#define MANGOS_BAG_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "ItemPrototype.h"
|
||||
#include "Item.h"
|
||||
|
||||
// Maximum 36 Slots ( (CONTAINER_END - CONTAINER_FIELD_SLOT_1)/2
|
||||
#define MAX_BAG_SIZE 36 // 2.0.12
|
||||
|
||||
class Bag : public Item
|
||||
{
|
||||
public:
|
||||
|
||||
Bag();
|
||||
~Bag();
|
||||
|
||||
void AddToWorld() override;
|
||||
void RemoveFromWorld() override;
|
||||
|
||||
bool Create(uint32 guidlow, uint32 itemid, Player const* owner) override;
|
||||
|
||||
void Clear();
|
||||
void StoreItem(uint8 slot, Item* pItem, bool update);
|
||||
void RemoveItem(uint8 slot, bool update);
|
||||
|
||||
Item* GetItemByPos(uint8 slot) const;
|
||||
Item* GetItemByEntry(uint32 item) const;
|
||||
Item* GetItemByLimitedCategory(uint32 limitedCategory) const;
|
||||
uint32 GetItemCount(uint32 item, Item* eItem = NULL) const;
|
||||
uint32 GetItemCountWithLimitCategory(uint32 limitCategory, Item* eItem = NULL) const;
|
||||
|
||||
uint8 GetSlotByItemGUID(ObjectGuid guid) const;
|
||||
bool IsEmpty() const;
|
||||
uint32 GetFreeSlots() const;
|
||||
uint32 GetBagSize() const { return GetUInt32Value(CONTAINER_FIELD_NUM_SLOTS); }
|
||||
|
||||
// DB operations
|
||||
// overwrite virtual Item::SaveToDB
|
||||
void SaveToDB() override;
|
||||
// overwrite virtual Item::LoadFromDB
|
||||
bool LoadFromDB(uint32 guidLow, Field* fields, ObjectGuid ownerGuid = ObjectGuid()) override;
|
||||
// overwrite virtual Item::DeleteFromDB
|
||||
void DeleteFromDB() override;
|
||||
|
||||
void BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const override;
|
||||
|
||||
protected:
|
||||
|
||||
// Bag Storage space
|
||||
Item* m_bagslot[MAX_BAG_SIZE];
|
||||
};
|
||||
|
||||
inline Item* NewItemOrBag(ItemPrototype const* proto)
|
||||
{
|
||||
if (proto->InventoryType == INVTYPE_BAG)
|
||||
return new Bag;
|
||||
|
||||
return new Item;
|
||||
}
|
||||
|
||||
#endif
|
||||
167
src/game/Object/Camera.cpp
Normal file
167
src/game/Object/Camera.cpp
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include "Camera.h"
|
||||
#include "GridNotifiersImpl.h"
|
||||
#include "CellImpl.h"
|
||||
#include "Log.h"
|
||||
#include "Errors.h"
|
||||
#include "Player.h"
|
||||
|
||||
Camera::Camera(Player* pl) : m_owner(*pl), m_source(pl)
|
||||
{
|
||||
m_source->GetViewPoint().Attach(this);
|
||||
}
|
||||
|
||||
Camera::~Camera()
|
||||
{
|
||||
// view of camera should be already reseted to owner (RemoveFromWorld -> Event_RemovedFromWorld -> ResetView)
|
||||
MANGOS_ASSERT(m_source == &m_owner);
|
||||
|
||||
// for symmetry with constructor and way to make viewpoint's list empty
|
||||
m_source->GetViewPoint().Detach(this);
|
||||
}
|
||||
|
||||
void Camera::ReceivePacket(WorldPacket* data)
|
||||
{
|
||||
m_owner.SendDirectMessage(data);
|
||||
}
|
||||
|
||||
void Camera::UpdateForCurrentViewPoint()
|
||||
{
|
||||
m_gridRef.unlink();
|
||||
|
||||
if (GridType* grid = m_source->GetViewPoint().m_grid)
|
||||
grid->AddWorldObject(this);
|
||||
|
||||
UpdateVisibilityForOwner();
|
||||
}
|
||||
|
||||
void Camera::SetView(WorldObject* obj, bool update_far_sight_field /*= true*/)
|
||||
{
|
||||
MANGOS_ASSERT(obj);
|
||||
|
||||
if (m_source == obj)
|
||||
return;
|
||||
|
||||
if (!m_owner.IsInMap(obj))
|
||||
{
|
||||
sLog.outError("Camera::SetView, viewpoint is not in map with camera's owner");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!obj->isType(TypeMask(TYPEMASK_DYNAMICOBJECT | TYPEMASK_UNIT)))
|
||||
{
|
||||
sLog.outError("Camera::SetView, viewpoint type is not available for client");
|
||||
return;
|
||||
}
|
||||
|
||||
// detach and deregister from active objects if there are no more reasons to be active
|
||||
m_source->GetViewPoint().Detach(this);
|
||||
if (!m_source->isActiveObject())
|
||||
m_source->GetMap()->RemoveFromActive(m_source);
|
||||
|
||||
m_source = obj;
|
||||
|
||||
if (!m_source->isActiveObject())
|
||||
m_source->GetMap()->AddToActive(m_source);
|
||||
|
||||
m_source->GetViewPoint().Attach(this);
|
||||
|
||||
if (update_far_sight_field)
|
||||
m_owner.SetGuidValue(PLAYER_FARSIGHT, (m_source == &m_owner ? ObjectGuid() : m_source->GetObjectGuid()));
|
||||
|
||||
UpdateForCurrentViewPoint();
|
||||
}
|
||||
|
||||
void Camera::Event_ViewPointVisibilityChanged()
|
||||
{
|
||||
if (!m_owner.HaveAtClient(m_source))
|
||||
ResetView();
|
||||
}
|
||||
|
||||
void Camera::ResetView(bool update_far_sight_field /*= true*/)
|
||||
{
|
||||
SetView(&m_owner, update_far_sight_field);
|
||||
}
|
||||
|
||||
void Camera::Event_AddedToWorld()
|
||||
{
|
||||
GridType* grid = m_source->GetViewPoint().m_grid;
|
||||
MANGOS_ASSERT(grid);
|
||||
grid->AddWorldObject(this);
|
||||
|
||||
UpdateVisibilityForOwner();
|
||||
}
|
||||
|
||||
void Camera::Event_RemovedFromWorld()
|
||||
{
|
||||
if (m_source == &m_owner)
|
||||
{
|
||||
m_gridRef.unlink();
|
||||
return;
|
||||
}
|
||||
|
||||
ResetView();
|
||||
}
|
||||
|
||||
void Camera::Event_Moved()
|
||||
{
|
||||
m_gridRef.unlink();
|
||||
m_source->GetViewPoint().m_grid->AddWorldObject(this);
|
||||
}
|
||||
|
||||
void Camera::UpdateVisibilityOf(WorldObject* target)
|
||||
{
|
||||
m_owner.UpdateVisibilityOf(m_source, target);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void Camera::UpdateVisibilityOf(T* target, UpdateData& data, std::set<WorldObject*>& vis)
|
||||
{
|
||||
m_owner.template UpdateVisibilityOf<T>(m_source, target, data, vis);
|
||||
}
|
||||
|
||||
template void Camera::UpdateVisibilityOf(Player* , UpdateData& , std::set<WorldObject*>&);
|
||||
template void Camera::UpdateVisibilityOf(Creature* , UpdateData& , std::set<WorldObject*>&);
|
||||
template void Camera::UpdateVisibilityOf(Corpse* , UpdateData& , std::set<WorldObject*>&);
|
||||
template void Camera::UpdateVisibilityOf(GameObject* , UpdateData& , std::set<WorldObject*>&);
|
||||
template void Camera::UpdateVisibilityOf(DynamicObject* , UpdateData& , std::set<WorldObject*>&);
|
||||
|
||||
void Camera::UpdateVisibilityForOwner()
|
||||
{
|
||||
MaNGOS::VisibleNotifier notifier(*this);
|
||||
Cell::VisitAllObjects(m_source, notifier, m_source->GetMap()->GetVisibilityDistance(), false);
|
||||
notifier.Notify();
|
||||
}
|
||||
|
||||
//////////////////
|
||||
|
||||
ViewPoint::~ViewPoint()
|
||||
{
|
||||
if (!m_cameras.empty())
|
||||
{
|
||||
sLog.outError("ViewPoint destructor called, but some cameras referenced to it");
|
||||
}
|
||||
}
|
||||
147
src/game/Object/Camera.h
Normal file
147
src/game/Object/Camera.h
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOSSERVER_CAMERA_H
|
||||
#define MANGOSSERVER_CAMERA_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "GridDefines.h"
|
||||
|
||||
class ViewPoint;
|
||||
class WorldObject;
|
||||
class UpdateData;
|
||||
class WorldPacket;
|
||||
class Player;
|
||||
|
||||
/// Camera - object-receiver. Receives broadcast packets from nearby worldobjects, object visibility changes and sends them to client
|
||||
class MANGOS_DLL_SPEC Camera
|
||||
{
|
||||
friend class ViewPoint;
|
||||
public:
|
||||
|
||||
explicit Camera(Player* pl);
|
||||
~Camera();
|
||||
|
||||
WorldObject* GetBody() { return m_source;}
|
||||
Player* GetOwner() { return &m_owner;}
|
||||
|
||||
// set camera's view to any worldobject
|
||||
// Note: this worldobject must be in same map, in same phase with camera's owner(player)
|
||||
// client supports only unit and dynamic objects as farsight objects
|
||||
void SetView(WorldObject* obj, bool update_far_sight_field = true);
|
||||
|
||||
// set view to camera's owner
|
||||
void ResetView(bool update_far_sight_field = true);
|
||||
|
||||
template<class T>
|
||||
void UpdateVisibilityOf(T* obj, UpdateData& d, std::set<WorldObject*>& vis);
|
||||
void UpdateVisibilityOf(WorldObject* obj);
|
||||
|
||||
void ReceivePacket(WorldPacket* data);
|
||||
|
||||
// updates visibility of worldobjects around viewpoint for camera's owner
|
||||
void UpdateVisibilityForOwner();
|
||||
|
||||
private:
|
||||
// called when viewpoint changes visibility state
|
||||
void Event_AddedToWorld();
|
||||
void Event_RemovedFromWorld();
|
||||
void Event_Moved();
|
||||
void Event_ViewPointVisibilityChanged();
|
||||
|
||||
Player& m_owner;
|
||||
WorldObject* m_source;
|
||||
|
||||
void UpdateForCurrentViewPoint();
|
||||
|
||||
public:
|
||||
GridReference<Camera>& GetGridRef() { return m_gridRef; }
|
||||
bool isActiveObject() const { return false; }
|
||||
private:
|
||||
GridReference<Camera> m_gridRef;
|
||||
};
|
||||
|
||||
/// Object-observer, notifies farsight object state to cameras that attached to it
|
||||
class MANGOS_DLL_SPEC ViewPoint
|
||||
{
|
||||
friend class Camera;
|
||||
|
||||
typedef std::list<Camera*> CameraList;
|
||||
|
||||
CameraList m_cameras;
|
||||
GridType* m_grid;
|
||||
|
||||
void Attach(Camera* c) { m_cameras.push_back(c); }
|
||||
void Detach(Camera* c) { m_cameras.remove(c); }
|
||||
|
||||
void CameraCall(void (Camera::*handler)())
|
||||
{
|
||||
if (!m_cameras.empty())
|
||||
{
|
||||
for (CameraList::iterator itr = m_cameras.begin(); itr != m_cameras.end();)
|
||||
{
|
||||
Camera* c = *(itr++);
|
||||
(c->*handler)();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
ViewPoint() : m_grid(0) {}
|
||||
~ViewPoint();
|
||||
|
||||
bool hasViewers() const { return !m_cameras.empty(); }
|
||||
|
||||
// these events are called when viewpoint changes visibility state
|
||||
void Event_AddedToWorld(GridType* grid)
|
||||
{
|
||||
m_grid = grid;
|
||||
CameraCall(&Camera::Event_AddedToWorld);
|
||||
}
|
||||
|
||||
void Event_RemovedFromWorld()
|
||||
{
|
||||
m_grid = NULL;
|
||||
CameraCall(&Camera::Event_RemovedFromWorld);
|
||||
}
|
||||
|
||||
void Event_GridChanged(GridType* grid)
|
||||
{
|
||||
m_grid = grid;
|
||||
CameraCall(&Camera::Event_Moved);
|
||||
}
|
||||
|
||||
void Event_ViewPointVisibilityChanged()
|
||||
{
|
||||
CameraCall(&Camera::Event_ViewPointVisibilityChanged);
|
||||
}
|
||||
|
||||
void Call_UpdateVisibilityForOwner()
|
||||
{
|
||||
CameraCall(&Camera::UpdateVisibilityForOwner);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
290
src/game/Object/Corpse.cpp
Normal file
290
src/game/Object/Corpse.cpp
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include "Corpse.h"
|
||||
#include "Player.h"
|
||||
#include "UpdateMask.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Database/DatabaseEnv.h"
|
||||
#include "Opcodes.h"
|
||||
#include "GossipDef.h"
|
||||
#include "World.h"
|
||||
#include "ObjectMgr.h"
|
||||
|
||||
Corpse::Corpse(CorpseType type) : WorldObject(),
|
||||
loot(this),
|
||||
lootRecipient(NULL),
|
||||
lootForBody(false)
|
||||
{
|
||||
m_objectType |= TYPEMASK_CORPSE;
|
||||
m_objectTypeId = TYPEID_CORPSE;
|
||||
|
||||
m_updateFlag = UPDATEFLAG_HAS_POSITION;
|
||||
|
||||
m_valuesCount = CORPSE_END;
|
||||
|
||||
m_type = type;
|
||||
|
||||
m_time = time(NULL);
|
||||
}
|
||||
|
||||
Corpse::~Corpse()
|
||||
{
|
||||
}
|
||||
|
||||
void Corpse::AddToWorld()
|
||||
{
|
||||
///- Register the corpse for guid lookup
|
||||
if (!IsInWorld())
|
||||
sObjectAccessor.AddObject(this);
|
||||
|
||||
Object::AddToWorld();
|
||||
}
|
||||
|
||||
void Corpse::RemoveFromWorld()
|
||||
{
|
||||
///- Remove the corpse from the accessor
|
||||
if (IsInWorld())
|
||||
sObjectAccessor.RemoveObject(this);
|
||||
|
||||
Object::RemoveFromWorld();
|
||||
}
|
||||
|
||||
bool Corpse::Create(uint32 guidlow)
|
||||
{
|
||||
Object::_Create(guidlow, 0, HIGHGUID_CORPSE);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Corpse::Create(uint32 guidlow, Player* owner)
|
||||
{
|
||||
MANGOS_ASSERT(owner);
|
||||
|
||||
WorldObject::_Create(guidlow, HIGHGUID_CORPSE, owner->GetPhaseMask());
|
||||
Relocate(owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ(), owner->GetOrientation());
|
||||
|
||||
// we need to assign owner's map for corpse
|
||||
// in other way we will get a crash in Corpse::SaveToDB()
|
||||
SetMap(owner->GetMap());
|
||||
|
||||
if (!IsPositionValid())
|
||||
{
|
||||
sLog.outError("Corpse (guidlow %d, owner %s) not created. Suggested coordinates isn't valid (X: %f Y: %f)",
|
||||
guidlow, owner->GetName(), owner->GetPositionX(), owner->GetPositionY());
|
||||
return false;
|
||||
}
|
||||
|
||||
SetObjectScale(DEFAULT_OBJECT_SCALE);
|
||||
SetGuidValue(CORPSE_FIELD_OWNER, owner->GetObjectGuid());
|
||||
|
||||
m_grid = MaNGOS::ComputeGridPair(GetPositionX(), GetPositionY());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Corpse::SaveToDB()
|
||||
{
|
||||
// bones should not be saved to DB (would be deleted on startup anyway)
|
||||
MANGOS_ASSERT(GetType() != CORPSE_BONES);
|
||||
|
||||
// prevent DB data inconsistence problems and duplicates
|
||||
CharacterDatabase.BeginTransaction();
|
||||
DeleteFromDB();
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << "INSERT INTO corpse (guid,player,position_x,position_y,position_z,orientation,map,time,corpse_type,instance,phaseMask) VALUES ("
|
||||
<< GetGUIDLow() << ", "
|
||||
<< GetOwnerGuid().GetCounter() << ", "
|
||||
<< GetPositionX() << ", "
|
||||
<< GetPositionY() << ", "
|
||||
<< GetPositionZ() << ", "
|
||||
<< GetOrientation() << ", "
|
||||
<< GetMapId() << ", "
|
||||
<< uint64(m_time) << ", "
|
||||
<< uint32(GetType()) << ", "
|
||||
<< int(GetInstanceId()) << ", "
|
||||
<< uint32(GetPhaseMask()) << ")"; // prevent out of range error
|
||||
CharacterDatabase.Execute(ss.str().c_str());
|
||||
CharacterDatabase.CommitTransaction();
|
||||
}
|
||||
|
||||
void Corpse::DeleteBonesFromWorld()
|
||||
{
|
||||
MANGOS_ASSERT(GetType() == CORPSE_BONES);
|
||||
Corpse* corpse = GetMap()->GetCorpse(GetObjectGuid());
|
||||
|
||||
if (!corpse)
|
||||
{
|
||||
sLog.outError("Bones %u not found in world.", GetGUIDLow());
|
||||
return;
|
||||
}
|
||||
|
||||
AddObjectToRemoveList();
|
||||
}
|
||||
|
||||
void Corpse::DeleteFromDB()
|
||||
{
|
||||
// bones should not be saved to DB (would be deleted on startup anyway)
|
||||
MANGOS_ASSERT(GetType() != CORPSE_BONES);
|
||||
|
||||
// all corpses (not bones)
|
||||
static SqlStatementID id;
|
||||
|
||||
SqlStatement stmt = CharacterDatabase.CreateStatement(id, "DELETE FROM corpse WHERE player = ? AND corpse_type <> '0'");
|
||||
stmt.PExecute(GetOwnerGuid().GetCounter());
|
||||
}
|
||||
|
||||
bool Corpse::LoadFromDB(uint32 lowguid, Field* fields)
|
||||
{
|
||||
//// 0 1 2 3 4 5 6
|
||||
// QueryResult *result = CharacterDatabase.Query("SELECT corpse.guid, player, corpse.position_x, corpse.position_y, corpse.position_z, corpse.orientation, corpse.map,"
|
||||
//// 7 8 9 10 11 12 13 14 15 16 17 18
|
||||
// "time, corpse_type, instance, phaseMask, gender, race, class, playerBytes, playerBytes2, equipmentCache, guildId, playerFlags FROM corpse"
|
||||
uint32 playerLowGuid = fields[1].GetUInt32();
|
||||
float positionX = fields[2].GetFloat();
|
||||
float positionY = fields[3].GetFloat();
|
||||
float positionZ = fields[4].GetFloat();
|
||||
float orientation = fields[5].GetFloat();
|
||||
uint32 mapid = fields[6].GetUInt32();
|
||||
|
||||
Object::_Create(lowguid, 0, HIGHGUID_CORPSE);
|
||||
|
||||
m_time = time_t(fields[7].GetUInt64());
|
||||
m_type = CorpseType(fields[8].GetUInt32());
|
||||
|
||||
if (m_type >= MAX_CORPSE_TYPE)
|
||||
{
|
||||
sLog.outError("%s Owner %s have wrong corpse type (%i), not load.", GetGuidStr().c_str(), GetOwnerGuid().GetString().c_str(), m_type);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 instanceid = fields[9].GetUInt32();
|
||||
uint32 phaseMask = fields[10].GetUInt32();
|
||||
uint8 gender = fields[11].GetUInt8();
|
||||
uint8 race = fields[12].GetUInt8();
|
||||
uint8 _class = fields[13].GetUInt8();
|
||||
uint32 playerBytes = fields[14].GetUInt32();
|
||||
uint32 playerBytes2 = fields[15].GetUInt32();
|
||||
uint32 guildId = fields[17].GetUInt32();
|
||||
uint32 playerFlags = fields[18].GetUInt32();
|
||||
|
||||
ObjectGuid guid = ObjectGuid(HIGHGUID_CORPSE, lowguid);
|
||||
ObjectGuid playerGuid = ObjectGuid(HIGHGUID_PLAYER, playerLowGuid);
|
||||
|
||||
// overwrite possible wrong/corrupted guid
|
||||
SetGuidValue(OBJECT_FIELD_GUID, guid);
|
||||
SetGuidValue(CORPSE_FIELD_OWNER, playerGuid);
|
||||
|
||||
SetObjectScale(DEFAULT_OBJECT_SCALE);
|
||||
|
||||
PlayerInfo const* info = sObjectMgr.GetPlayerInfo(race, _class);
|
||||
if (!info)
|
||||
{
|
||||
sLog.outError("Player %u has incorrect race/class pair.", GetGUIDLow());
|
||||
return false;
|
||||
}
|
||||
SetUInt32Value(CORPSE_FIELD_DISPLAY_ID, gender == GENDER_FEMALE ? info->displayId_f : info->displayId_m);
|
||||
|
||||
// Load equipment
|
||||
Tokens data = StrSplit(fields[16].GetCppString(), " ");
|
||||
for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot)
|
||||
{
|
||||
uint32 visualbase = slot * 2;
|
||||
uint32 item_id = GetUInt32ValueFromArray(data, visualbase);
|
||||
const ItemPrototype* proto = ObjectMgr::GetItemPrototype(item_id);
|
||||
if (!proto)
|
||||
{
|
||||
SetUInt32Value(CORPSE_FIELD_ITEM + slot, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
SetUInt32Value(CORPSE_FIELD_ITEM + slot, proto->DisplayInfoID | (proto->InventoryType << 24));
|
||||
}
|
||||
|
||||
uint8 skin = (uint8)(playerBytes);
|
||||
uint8 face = (uint8)(playerBytes >> 8);
|
||||
uint8 hairstyle = (uint8)(playerBytes >> 16);
|
||||
uint8 haircolor = (uint8)(playerBytes >> 24);
|
||||
uint8 facialhair = (uint8)(playerBytes2);
|
||||
SetUInt32Value(CORPSE_FIELD_BYTES_1, ((0x00) | (race << 8) | (gender << 16) | (skin << 24)));
|
||||
SetUInt32Value(CORPSE_FIELD_BYTES_2, ((face) | (hairstyle << 8) | (haircolor << 16) | (facialhair << 24)));
|
||||
|
||||
//SetUInt32Value(CORPSE_FIELD_GUILD, guildId);
|
||||
|
||||
uint32 flags = CORPSE_FLAG_UNK2;
|
||||
if (playerFlags & PLAYER_FLAGS_HIDE_HELM)
|
||||
flags |= CORPSE_FLAG_HIDE_HELM;
|
||||
if (playerFlags & PLAYER_FLAGS_HIDE_CLOAK)
|
||||
flags |= CORPSE_FLAG_HIDE_CLOAK;
|
||||
SetUInt32Value(CORPSE_FIELD_FLAGS, flags);
|
||||
|
||||
// no need to mark corpse as lootable, because corpses are not saved in battle grounds
|
||||
|
||||
// place
|
||||
SetLocationInstanceId(instanceid);
|
||||
SetLocationMapId(mapid);
|
||||
SetPhaseMask(phaseMask, false);
|
||||
Relocate(positionX, positionY, positionZ, orientation);
|
||||
|
||||
if (!IsPositionValid())
|
||||
{
|
||||
sLog.outError("%s Owner %s not created. Suggested coordinates isn't valid (X: %f Y: %f)",
|
||||
GetGuidStr().c_str(), GetOwnerGuid().GetString().c_str(), GetPositionX(), GetPositionY());
|
||||
return false;
|
||||
}
|
||||
|
||||
m_grid = MaNGOS::ComputeGridPair(GetPositionX(), GetPositionY());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Corpse::isVisibleForInState(Player const* u, WorldObject const* viewPoint, bool inVisibleList) const
|
||||
{
|
||||
return IsInWorld() && u->IsInWorld() && IsWithinDistInMap(viewPoint, GetMap()->GetVisibilityDistance() + (inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f), false);
|
||||
}
|
||||
|
||||
bool Corpse::IsHostileTo(Unit const* unit) const
|
||||
{
|
||||
if (Player* owner = sObjectMgr.GetPlayer(GetOwnerGuid()))
|
||||
return owner->IsHostileTo(unit);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Corpse::IsFriendlyTo(Unit const* unit) const
|
||||
{
|
||||
if (Player* owner = sObjectMgr.GetPlayer(GetOwnerGuid()))
|
||||
return owner->IsFriendlyTo(unit);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Corpse::IsExpired(time_t t) const
|
||||
{
|
||||
if (m_type == CORPSE_BONES)
|
||||
return m_time < t - 60 * MINUTE;
|
||||
else
|
||||
return m_time < t - 3 * DAY;
|
||||
}
|
||||
102
src/game/Object/Corpse.h
Normal file
102
src/game/Object/Corpse.h
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOSSERVER_CORPSE_H
|
||||
#define MANGOSSERVER_CORPSE_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "Object.h"
|
||||
#include "Database/DatabaseEnv.h"
|
||||
#include "GridDefines.h"
|
||||
#include "LootMgr.h"
|
||||
|
||||
enum CorpseType
|
||||
{
|
||||
CORPSE_BONES = 0,
|
||||
CORPSE_RESURRECTABLE_PVE = 1,
|
||||
CORPSE_RESURRECTABLE_PVP = 2
|
||||
};
|
||||
#define MAX_CORPSE_TYPE 3
|
||||
|
||||
// Value equal client resurrection dialog show radius.
|
||||
#define CORPSE_RECLAIM_RADIUS 39
|
||||
|
||||
enum CorpseFlags
|
||||
{
|
||||
CORPSE_FLAG_NONE = 0x00,
|
||||
CORPSE_FLAG_BONES = 0x01,
|
||||
CORPSE_FLAG_UNK1 = 0x02,
|
||||
CORPSE_FLAG_UNK2 = 0x04,
|
||||
CORPSE_FLAG_HIDE_HELM = 0x08,
|
||||
CORPSE_FLAG_HIDE_CLOAK = 0x10,
|
||||
CORPSE_FLAG_LOOTABLE = 0x20
|
||||
};
|
||||
|
||||
class Corpse : public WorldObject
|
||||
{
|
||||
public:
|
||||
explicit Corpse(CorpseType type = CORPSE_BONES);
|
||||
~Corpse();
|
||||
|
||||
void AddToWorld() override;
|
||||
void RemoveFromWorld() override;
|
||||
|
||||
bool Create(uint32 guidlow);
|
||||
bool Create(uint32 guidlow, Player* owner);
|
||||
|
||||
void SaveToDB();
|
||||
bool LoadFromDB(uint32 guid, Field* fields);
|
||||
|
||||
void DeleteBonesFromWorld();
|
||||
void DeleteFromDB();
|
||||
|
||||
ObjectGuid const& GetOwnerGuid() const { return GetGuidValue(CORPSE_FIELD_OWNER); }
|
||||
|
||||
time_t const& GetGhostTime() const { return m_time; }
|
||||
void ResetGhostTime() { m_time = time(NULL); }
|
||||
CorpseType GetType() const { return m_type; }
|
||||
|
||||
bool IsHostileTo(Unit const* unit) const override;
|
||||
bool IsFriendlyTo(Unit const* unit) const override;
|
||||
|
||||
GridPair const& GetGrid() const { return m_grid; }
|
||||
void SetGrid(GridPair const& grid) { m_grid = grid; }
|
||||
|
||||
bool isVisibleForInState(Player const* u, WorldObject const* viewPoint, bool inVisibleList) const override;
|
||||
|
||||
Loot loot; // remove insignia ONLY at BG
|
||||
Player* lootRecipient;
|
||||
bool lootForBody;
|
||||
|
||||
GridReference<Corpse> &GetGridRef() { return m_gridRef; }
|
||||
|
||||
bool IsExpired(time_t t) const;
|
||||
private:
|
||||
GridReference<Corpse> m_gridRef;
|
||||
|
||||
CorpseType m_type;
|
||||
time_t m_time;
|
||||
GridPair m_grid; // gride for corpse position for fast search
|
||||
};
|
||||
#endif
|
||||
2634
src/game/Object/Creature.cpp
Normal file
2634
src/game/Object/Creature.cpp
Normal file
File diff suppressed because it is too large
Load diff
799
src/game/Object/Creature.h
Normal file
799
src/game/Object/Creature.h
Normal file
|
|
@ -0,0 +1,799 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOSSERVER_CREATURE_H
|
||||
#define MANGOSSERVER_CREATURE_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "Unit.h"
|
||||
#include "UpdateMask.h"
|
||||
#include "ItemPrototype.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "LootMgr.h"
|
||||
#include "DBCEnums.h"
|
||||
#include "Database/DatabaseEnv.h"
|
||||
#include "Cell.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
struct SpellEntry;
|
||||
|
||||
class CreatureAI;
|
||||
class Group;
|
||||
class Quest;
|
||||
class Player;
|
||||
class WorldSession;
|
||||
|
||||
struct GameEventCreatureData;
|
||||
|
||||
enum CreatureFlagsExtra
|
||||
{
|
||||
CREATURE_FLAG_EXTRA_INSTANCE_BIND = 0x00000001, // creature kill bind instance with killer and killer's group
|
||||
CREATURE_FLAG_EXTRA_CIVILIAN = 0x00000002, // not aggro (ignore faction/reputation hostility)
|
||||
CREATURE_FLAG_EXTRA_NO_PARRY = 0x00000004, // creature can't parry
|
||||
CREATURE_FLAG_EXTRA_NO_PARRY_HASTEN = 0x00000008, // creature can't counter-attack at parry
|
||||
CREATURE_FLAG_EXTRA_NO_BLOCK = 0x00000010, // creature can't block
|
||||
CREATURE_FLAG_EXTRA_NO_CRUSH = 0x00000020, // creature can't do crush attacks
|
||||
CREATURE_FLAG_EXTRA_NO_XP_AT_KILL = 0x00000040, // creature kill not provide XP
|
||||
CREATURE_FLAG_EXTRA_INVISIBLE = 0x00000080, // creature is always invisible for player (mostly trigger creatures)
|
||||
CREATURE_FLAG_EXTRA_NOT_TAUNTABLE = 0x00000100, // creature is immune to taunt auras and effect attack me
|
||||
CREATURE_FLAG_EXTRA_AGGRO_ZONE = 0x00000200, // creature sets itself in combat with zone on aggro
|
||||
CREATURE_FLAG_EXTRA_GUARD = 0x00000400, // creature is a guard
|
||||
};
|
||||
|
||||
// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform
|
||||
#if defined( __GNUC__ )
|
||||
#pragma pack(1)
|
||||
#else
|
||||
#pragma pack(push,1)
|
||||
#endif
|
||||
|
||||
#define MAX_KILL_CREDIT 2
|
||||
#define MAX_CREATURE_MODEL 4
|
||||
|
||||
// from `creature_template` table
|
||||
struct CreatureInfo
|
||||
{
|
||||
uint32 Entry;
|
||||
uint32 DifficultyEntry[MAX_DIFFICULTY - 1];
|
||||
uint32 KillCredit[MAX_KILL_CREDIT];
|
||||
uint32 ModelId[MAX_CREATURE_MODEL];
|
||||
char* Name;
|
||||
char* SubName;
|
||||
char* IconName;
|
||||
uint32 GossipMenuId;
|
||||
uint32 minlevel;
|
||||
uint32 maxlevel;
|
||||
uint32 minhealth;
|
||||
uint32 maxhealth;
|
||||
uint32 minmana;
|
||||
uint32 maxmana;
|
||||
uint32 armor;
|
||||
uint32 faction_A;
|
||||
uint32 faction_H;
|
||||
uint32 npcflag;
|
||||
float speed_walk;
|
||||
float speed_run;
|
||||
float scale;
|
||||
uint32 rank;
|
||||
float mindmg;
|
||||
float maxdmg;
|
||||
uint32 dmgschool;
|
||||
uint32 attackpower;
|
||||
float dmg_multiplier;
|
||||
uint32 baseattacktime;
|
||||
uint32 rangeattacktime;
|
||||
uint32 unit_class; // enum Classes. Note only 4 classes are known for creatures.
|
||||
uint32 unit_flags; // enum UnitFlags mask values
|
||||
uint32 unit_flags2; // enum UnitFlags2 mask values
|
||||
uint32 dynamicflags;
|
||||
uint32 family; // enum CreatureFamily values (optional)
|
||||
uint32 trainer_type;
|
||||
uint32 trainer_spell;
|
||||
uint32 trainer_class;
|
||||
uint32 trainer_race;
|
||||
float minrangedmg;
|
||||
float maxrangedmg;
|
||||
uint32 rangedattackpower;
|
||||
uint32 type; // enum CreatureType values
|
||||
uint32 type_flags; // enum CreatureTypeFlags mask values
|
||||
uint32 lootid;
|
||||
uint32 pickpocketLootId;
|
||||
uint32 SkinLootId;
|
||||
int32 resistance1;
|
||||
int32 resistance2;
|
||||
int32 resistance3;
|
||||
int32 resistance4;
|
||||
int32 resistance5;
|
||||
int32 resistance6;
|
||||
uint32 PetSpellDataId;
|
||||
uint32 mingold;
|
||||
uint32 maxgold;
|
||||
char const* AIName;
|
||||
uint32 MovementType;
|
||||
uint32 InhabitType;
|
||||
float healthModifier;
|
||||
float powerModifier;
|
||||
bool RacialLeader;
|
||||
uint32 questItems[6];
|
||||
uint32 movementId;
|
||||
bool RegenHealth;
|
||||
uint32 vehicleId;
|
||||
uint32 equipmentId;
|
||||
uint32 trainerId;
|
||||
uint32 vendorId;
|
||||
uint32 MechanicImmuneMask;
|
||||
uint32 flags_extra;
|
||||
uint32 ScriptID;
|
||||
|
||||
// helpers
|
||||
HighGuid GetHighGuid() const
|
||||
{
|
||||
return vehicleId ? HIGHGUID_VEHICLE : HIGHGUID_UNIT;
|
||||
}
|
||||
|
||||
ObjectGuid GetObjectGuid(uint32 lowguid) const { return ObjectGuid(GetHighGuid(), Entry, lowguid); }
|
||||
|
||||
SkillType GetRequiredLootSkill() const
|
||||
{
|
||||
if (type_flags & CREATURE_TYPEFLAGS_HERBLOOT)
|
||||
return SKILL_HERBALISM;
|
||||
else if (type_flags & CREATURE_TYPEFLAGS_MININGLOOT)
|
||||
return SKILL_MINING;
|
||||
else if (type_flags & CREATURE_TYPEFLAGS_ENGINEERLOOT)
|
||||
return SKILL_ENGINEERING;
|
||||
else
|
||||
return SKILL_SKINNING; // normal case
|
||||
}
|
||||
|
||||
bool IsExotic() const
|
||||
{
|
||||
return (type_flags & CREATURE_TYPEFLAGS_EXOTIC);
|
||||
}
|
||||
|
||||
bool isTameable(bool exotic) const
|
||||
{
|
||||
if (type != CREATURE_TYPE_BEAST || family == 0 || (type_flags & CREATURE_TYPEFLAGS_TAMEABLE) == 0)
|
||||
return false;
|
||||
|
||||
// if can tame exotic then can tame any temable
|
||||
return exotic || !IsExotic();
|
||||
}
|
||||
};
|
||||
|
||||
struct CreatureTemplateSpells
|
||||
{
|
||||
uint32 entry;
|
||||
uint32 spells[CREATURE_MAX_SPELLS];
|
||||
};
|
||||
|
||||
struct EquipmentInfo
|
||||
{
|
||||
uint32 entry;
|
||||
uint32 equipentry[3];
|
||||
};
|
||||
|
||||
// from `creature` table
|
||||
struct CreatureData
|
||||
{
|
||||
uint32 id; // entry in creature_template
|
||||
uint16 mapid;
|
||||
uint32 phaseMask;
|
||||
uint32 modelid_override; // overrides any model defined in creature_template
|
||||
int32 equipmentId;
|
||||
float posX;
|
||||
float posY;
|
||||
float posZ;
|
||||
float orientation;
|
||||
uint32 spawntimesecs;
|
||||
float spawndist;
|
||||
uint32 currentwaypoint;
|
||||
uint32 curhealth;
|
||||
uint32 curmana;
|
||||
bool is_dead;
|
||||
uint8 movementType;
|
||||
uint8 spawnMask;
|
||||
|
||||
// helper function
|
||||
ObjectGuid GetObjectGuid(uint32 lowguid) const;
|
||||
};
|
||||
|
||||
enum SplineFlags
|
||||
{
|
||||
SPLINEFLAG_WALKMODE = 0x00001000,
|
||||
SPLINEFLAG_FLYING = 0x00002000,
|
||||
};
|
||||
|
||||
// from `creature_addon` and `creature_template_addon`tables
|
||||
struct CreatureDataAddon
|
||||
{
|
||||
uint32 guidOrEntry;
|
||||
uint32 mount;
|
||||
uint32 bytes1;
|
||||
uint8 sheath_state; // SheathState
|
||||
uint8 pvp_state; // UnitPVPStateFlags
|
||||
uint32 emote;
|
||||
uint32 splineFlags;
|
||||
uint32 const* auras; // loaded as char* "spell1 spell2 ... "
|
||||
};
|
||||
|
||||
struct CreatureModelInfo
|
||||
{
|
||||
uint32 modelid;
|
||||
float bounding_radius;
|
||||
float combat_reach;
|
||||
uint8 gender;
|
||||
uint32 modelid_other_gender; // The oposite gender for this modelid (male/female)
|
||||
uint32 modelid_alternative; // An alternative model. Generally same gender(2)
|
||||
};
|
||||
|
||||
struct CreatureModelRace
|
||||
{
|
||||
uint32 modelid; // Native model/base model the selection is for
|
||||
uint32 racemask; // Races it applies to (and then a player source must exist for selection)
|
||||
uint32 creature_entry; // Modelid from creature_template.entry will be selected
|
||||
uint32 modelid_racial; // Explicit modelid. Used if creature_template entry is not defined
|
||||
};
|
||||
|
||||
// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform
|
||||
#if defined( __GNUC__ )
|
||||
#pragma pack()
|
||||
#else
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
struct CreatureLocale
|
||||
{
|
||||
std::vector<std::string> Name;
|
||||
std::vector<std::string> SubName;
|
||||
};
|
||||
|
||||
struct GossipMenuItemsLocale
|
||||
{
|
||||
std::vector<std::string> OptionText;
|
||||
std::vector<std::string> BoxText;
|
||||
};
|
||||
|
||||
struct PointOfInterestLocale
|
||||
{
|
||||
std::vector<std::string> IconName;
|
||||
};
|
||||
|
||||
enum InhabitTypeValues
|
||||
{
|
||||
INHABIT_GROUND = 1,
|
||||
INHABIT_WATER = 2,
|
||||
INHABIT_AIR = 4,
|
||||
INHABIT_ANYWHERE = INHABIT_GROUND | INHABIT_WATER | INHABIT_AIR
|
||||
};
|
||||
|
||||
// Enums used by StringTextData::Type (CreatureEventAI)
|
||||
enum ChatType
|
||||
{
|
||||
CHAT_TYPE_SAY = 0,
|
||||
CHAT_TYPE_YELL = 1,
|
||||
CHAT_TYPE_TEXT_EMOTE = 2,
|
||||
CHAT_TYPE_BOSS_EMOTE = 3,
|
||||
CHAT_TYPE_WHISPER = 4,
|
||||
CHAT_TYPE_BOSS_WHISPER = 5,
|
||||
CHAT_TYPE_ZONE_YELL = 6
|
||||
};
|
||||
|
||||
// Selection method used by SelectAttackingTarget
|
||||
enum AttackingTarget
|
||||
{
|
||||
ATTACKING_TARGET_RANDOM = 0, // Just selects a random target
|
||||
ATTACKING_TARGET_TOPAGGRO, // Selects targes from top aggro to bottom
|
||||
ATTACKING_TARGET_BOTTOMAGGRO, // Selects targets from bottom aggro to top
|
||||
};
|
||||
|
||||
enum SelectFlags
|
||||
{
|
||||
SELECT_FLAG_IN_LOS = 0x001, // Default Selection Requirement for Spell-targets
|
||||
SELECT_FLAG_PLAYER = 0x002,
|
||||
SELECT_FLAG_POWER_MANA = 0x004, // For Energy based spells, like manaburn
|
||||
SELECT_FLAG_POWER_RAGE = 0x008,
|
||||
SELECT_FLAG_POWER_ENERGY = 0x010,
|
||||
SELECT_FLAG_POWER_RUNIC = 0x020,
|
||||
SELECT_FLAG_IN_MELEE_RANGE = 0x040,
|
||||
SELECT_FLAG_NOT_IN_MELEE_RANGE = 0x080,
|
||||
};
|
||||
|
||||
// Vendors
|
||||
|
||||
enum
|
||||
{
|
||||
VENDOR_ITEM_TYPE_NONE = 0,
|
||||
VENDOR_ITEM_TYPE_ITEM = 1,
|
||||
VENDOR_ITEM_TYPE_CURRENCY = 2,
|
||||
VENDOR_ITEM_TYPE_MAX = 3,
|
||||
};
|
||||
|
||||
struct VendorItem
|
||||
{
|
||||
VendorItem(uint32 _item, uint8 _type, uint32 _maxcount, uint32 _incrtime, uint32 _ExtendedCost, uint16 _conditionId)
|
||||
: item(_item), type(_type), maxcount(_maxcount), incrtime(_incrtime), ExtendedCost(_ExtendedCost), conditionId(_conditionId) {}
|
||||
|
||||
uint32 item;
|
||||
uint8 type;
|
||||
uint32 maxcount; // 0 for infinity item amount, for type = VENDOR_ITEM_TYPE_CURRENCY, maxcount = currency count
|
||||
uint32 incrtime; // time for restore items amount if maxcount != 0
|
||||
uint32 ExtendedCost; // index in ItemExtendedCost.dbc
|
||||
uint16 conditionId; // condition to check for this item
|
||||
bool IsCurrency() const { return type == VENDOR_ITEM_TYPE_CURRENCY; }
|
||||
};
|
||||
typedef std::vector<VendorItem*> VendorItemList;
|
||||
|
||||
struct VendorItemData
|
||||
{
|
||||
VendorItemList m_items;
|
||||
|
||||
VendorItem* GetItem(uint32 slot) const
|
||||
{
|
||||
if (slot >= m_items.size()) return NULL;
|
||||
return m_items[slot];
|
||||
}
|
||||
bool Empty() const { return m_items.empty(); }
|
||||
uint8 GetItemCount() const { return m_items.size(); }
|
||||
void AddItem(uint32 item, uint8 type, uint32 maxcount, uint32 ptime, uint32 ExtendedCost, uint16 conditonId)
|
||||
{
|
||||
m_items.push_back(new VendorItem(item, type, maxcount, ptime, ExtendedCost, conditonId));
|
||||
}
|
||||
bool RemoveItem(uint32 item_id, uint8 type);
|
||||
VendorItem const* FindItemCostPair(uint32 item_id, uint8 type, uint32 extendedCost) const;
|
||||
|
||||
void Clear()
|
||||
{
|
||||
for (VendorItemList::const_iterator itr = m_items.begin(); itr != m_items.end(); ++itr)
|
||||
delete(*itr);
|
||||
m_items.clear();
|
||||
}
|
||||
};
|
||||
|
||||
struct VendorItemCount
|
||||
{
|
||||
explicit VendorItemCount(uint32 _item, uint32 _count)
|
||||
: itemId(_item), count(_count), lastIncrementTime(time(NULL)) {}
|
||||
|
||||
uint32 itemId;
|
||||
uint32 count;
|
||||
time_t lastIncrementTime;
|
||||
};
|
||||
|
||||
typedef std::list<VendorItemCount> VendorItemCounts;
|
||||
|
||||
struct TrainerSpell
|
||||
{
|
||||
TrainerSpell() : spell(0), spellCost(0), reqSkill(0), reqSkillValue(0), reqLevel(0), learnedSpell(0), isProvidedReqLevel(false) {}
|
||||
|
||||
TrainerSpell(uint32 _spell, uint32 _spellCost, uint32 _reqSkill, uint32 _reqSkillValue, uint32 _reqLevel, uint32 _learnedspell, bool _isProvidedReqLevel)
|
||||
: spell(_spell), spellCost(_spellCost), reqSkill(_reqSkill), reqSkillValue(_reqSkillValue), reqLevel(_reqLevel), learnedSpell(_learnedspell), isProvidedReqLevel(_isProvidedReqLevel)
|
||||
{}
|
||||
|
||||
uint32 spell;
|
||||
uint32 spellCost;
|
||||
uint32 reqSkill;
|
||||
uint32 reqSkillValue;
|
||||
uint32 reqLevel;
|
||||
uint32 learnedSpell;
|
||||
bool isProvidedReqLevel;
|
||||
|
||||
// helpers
|
||||
bool IsCastable() const { return learnedSpell != spell; }
|
||||
};
|
||||
|
||||
typedef UNORDERED_MAP < uint32 /*spellid*/, TrainerSpell > TrainerSpellMap;
|
||||
|
||||
struct TrainerSpellData
|
||||
{
|
||||
TrainerSpellData() : trainerType(0) {}
|
||||
|
||||
TrainerSpellMap spellList;
|
||||
uint32 trainerType; // trainer type based at trainer spells, can be different from creature_template value.
|
||||
// req. for correct show non-prof. trainers like weaponmaster, allowed values 0 and 2.
|
||||
TrainerSpell const* Find(uint32 spell_id) const;
|
||||
void Clear() { spellList.clear(); }
|
||||
};
|
||||
|
||||
typedef std::map<uint32, time_t> CreatureSpellCooldowns;
|
||||
|
||||
// max different by z coordinate for creature aggro reaction
|
||||
#define CREATURE_Z_ATTACK_RANGE 3
|
||||
|
||||
#define MAX_VENDOR_ITEMS 300 // Limitation in 4.x.x item count in SMSG_LIST_INVENTORY
|
||||
|
||||
enum VirtualItemSlot
|
||||
{
|
||||
VIRTUAL_ITEM_SLOT_0 = 0,
|
||||
VIRTUAL_ITEM_SLOT_1 = 1,
|
||||
VIRTUAL_ITEM_SLOT_2 = 2,
|
||||
};
|
||||
|
||||
#define MAX_VIRTUAL_ITEM_SLOT 3
|
||||
|
||||
struct CreatureCreatePos
|
||||
{
|
||||
public:
|
||||
// exactly coordinates used
|
||||
CreatureCreatePos(Map* map, float x, float y, float z, float o, uint32 phaseMask)
|
||||
: m_map(map), m_phaseMask(phaseMask), m_closeObject(NULL), m_angle(0.0f), m_dist(0.0f) { m_pos.x = x; m_pos.y = y; m_pos.z = z; m_pos.o = NormalizeOrientation(o); }
|
||||
// if dist == 0.0f -> exactly object coordinates used, in other case close point to object (CONTACT_DIST can be used as minimal distances)
|
||||
CreatureCreatePos(WorldObject* closeObject, float ori, float dist = 0.0f, float angle = 0.0f)
|
||||
: m_map(closeObject->GetMap()), m_phaseMask(closeObject->GetPhaseMask()),
|
||||
m_closeObject(closeObject), m_angle(angle), m_dist(dist) { m_pos.o = NormalizeOrientation(ori); }
|
||||
public:
|
||||
Map* GetMap() const { return m_map; }
|
||||
uint32 GetPhaseMask() const { return m_phaseMask; }
|
||||
void SelectFinalPoint(Creature* cr);
|
||||
bool Relocate(Creature* cr) const;
|
||||
|
||||
// read only after SelectFinalPoint
|
||||
Position m_pos;
|
||||
private:
|
||||
Map* m_map;
|
||||
uint32 m_phaseMask;
|
||||
WorldObject* m_closeObject;
|
||||
float m_angle;
|
||||
float m_dist;
|
||||
};
|
||||
|
||||
enum CreatureSubtype
|
||||
{
|
||||
CREATURE_SUBTYPE_GENERIC, // new Creature
|
||||
CREATURE_SUBTYPE_PET, // new Pet
|
||||
CREATURE_SUBTYPE_TOTEM, // new Totem
|
||||
CREATURE_SUBTYPE_TEMPORARY_SUMMON, // new TemporarySummon
|
||||
};
|
||||
|
||||
enum TemporaryFactionFlags // Used at real faction changes
|
||||
{
|
||||
TEMPFACTION_NONE = 0x00, // When no flag is used in temporary faction change, faction will be persistent. It will then require manual change back to default/another faction when changed once
|
||||
TEMPFACTION_RESTORE_RESPAWN = 0x01, // Default faction will be restored at respawn
|
||||
TEMPFACTION_RESTORE_COMBAT_STOP = 0x02, // ... at CombatStop() (happens at creature death, at evade or custom scripte among others)
|
||||
TEMPFACTION_RESTORE_REACH_HOME = 0x04, // ... at reaching home in home movement (evade), if not already done at CombatStop()
|
||||
TEMPFACTION_TOGGLE_NON_ATTACKABLE = 0x08, // Remove UNIT_FLAG_NON_ATTACKABLE(0x02) when faction is changed (reapply when temp-faction is removed)
|
||||
TEMPFACTION_TOGGLE_OOC_NOT_ATTACK = 0x10, // Remove UNIT_FLAG_OOC_NOT_ATTACKABLE(0x100) when faction is changed (reapply when temp-faction is removed)
|
||||
TEMPFACTION_TOGGLE_PASSIVE = 0x20, // Remove UNIT_FLAG_PASSIVE(0x200) when faction is changed (reapply when temp-faction is removed)
|
||||
TEMPFACTION_ALL,
|
||||
};
|
||||
|
||||
class MANGOS_DLL_SPEC Creature : public Unit
|
||||
{
|
||||
CreatureAI* i_AI;
|
||||
|
||||
public:
|
||||
|
||||
explicit Creature(CreatureSubtype subtype = CREATURE_SUBTYPE_GENERIC);
|
||||
virtual ~Creature();
|
||||
|
||||
void AddToWorld() override;
|
||||
void RemoveFromWorld() override;
|
||||
|
||||
bool Create(uint32 guidlow, CreatureCreatePos& cPos, CreatureInfo const* cinfo, Team team = TEAM_NONE, const CreatureData* data = NULL, GameEventCreatureData const* eventData = NULL);
|
||||
bool LoadCreatureAddon(bool reload);
|
||||
void SelectLevel(const CreatureInfo* cinfo, float percentHealth = 100.0f, float percentMana = 100.0f);
|
||||
void LoadEquipment(uint32 equip_entry, bool force = false);
|
||||
|
||||
bool HasStaticDBSpawnData() const; // listed in `creature` table and have fixed in DB guid
|
||||
|
||||
char const* GetSubName() const { return GetCreatureInfo()->SubName; }
|
||||
|
||||
void Update(uint32 update_diff, uint32 time) override; // overwrite Unit::Update
|
||||
|
||||
virtual void RegenerateAll(uint32 update_diff);
|
||||
uint32 GetEquipmentId() const { return m_equipmentId; }
|
||||
|
||||
CreatureSubtype GetSubtype() const { return m_subtype; }
|
||||
bool IsPet() const { return m_subtype == CREATURE_SUBTYPE_PET; }
|
||||
bool IsTotem() const { return m_subtype == CREATURE_SUBTYPE_TOTEM; }
|
||||
bool IsTemporarySummon() const { return m_subtype == CREATURE_SUBTYPE_TEMPORARY_SUMMON; }
|
||||
|
||||
bool IsCorpse() const { return getDeathState() == CORPSE; }
|
||||
bool IsDespawned() const { return getDeathState() == DEAD; }
|
||||
void SetCorpseDelay(uint32 delay) { m_corpseDelay = delay; }
|
||||
bool IsRacialLeader() const { return GetCreatureInfo()->RacialLeader; }
|
||||
bool IsCivilian() const { return GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_CIVILIAN; }
|
||||
bool IsGuard() const { return GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_GUARD; }
|
||||
|
||||
bool CanWalk() const { return GetCreatureInfo()->InhabitType & INHABIT_GROUND; }
|
||||
virtual bool CanSwim() const { return GetCreatureInfo()->InhabitType & INHABIT_WATER; }
|
||||
bool CanFly() const { return (GetCreatureInfo()->InhabitType & INHABIT_AIR) || (GetByteValue(UNIT_FIELD_BYTES_1, 3) & UNIT_BYTE1_FLAG_UNK_2) || HasAuraType(SPELL_AURA_FLY); }
|
||||
|
||||
bool IsTrainerOf(Player* player, bool msg) const;
|
||||
bool CanInteractWithBattleMaster(Player* player, bool msg) const;
|
||||
bool CanTrainAndResetTalentsOf(Player* pPlayer) const;
|
||||
|
||||
bool IsOutOfThreatArea(Unit* pVictim) const;
|
||||
void FillGuidsListFromThreatList(GuidVector& guids, uint32 maxamount = 0);
|
||||
|
||||
bool IsImmuneToSpell(SpellEntry const* spellInfo, bool castOnSelf) override;
|
||||
bool IsImmuneToSpellEffect(SpellEntry const* spellInfo, SpellEffectIndex index, bool castOnSelf) const override;
|
||||
|
||||
bool IsElite() const
|
||||
{
|
||||
if (IsPet())
|
||||
return false;
|
||||
|
||||
uint32 rank = GetCreatureInfo()->rank;
|
||||
return rank != CREATURE_ELITE_NORMAL && rank != CREATURE_ELITE_RARE;
|
||||
}
|
||||
|
||||
bool IsWorldBoss() const
|
||||
{
|
||||
if (IsPet())
|
||||
return false;
|
||||
|
||||
return GetCreatureInfo()->rank == CREATURE_ELITE_WORLDBOSS;
|
||||
}
|
||||
|
||||
uint32 GetLevelForTarget(Unit const* target) const override; // overwrite Unit::GetLevelForTarget for boss level support
|
||||
|
||||
uint8 getRace() const override;
|
||||
|
||||
bool IsInEvadeMode() const;
|
||||
|
||||
bool AIM_Initialize();
|
||||
|
||||
CreatureAI* AI() { return i_AI; }
|
||||
|
||||
void SetWalk(bool enable, bool asDefault = true);
|
||||
void SetLevitate(bool enable);
|
||||
void SetRoot(bool enable) override;
|
||||
void SetWaterWalk(bool enable) override;
|
||||
|
||||
uint32 GetShieldBlockDamageValue() const override // dunno mob block value
|
||||
{
|
||||
return uint32(BASE_BLOCK_DAMAGE_PERCENT);
|
||||
}
|
||||
|
||||
SpellSchoolMask GetMeleeDamageSchoolMask() const override { return m_meleeDamageSchoolMask; }
|
||||
void SetMeleeDamageSchool(SpellSchools school) { m_meleeDamageSchoolMask = SpellSchoolMask(1 << school); }
|
||||
|
||||
void _AddCreatureSpellCooldown(uint32 spell_id, time_t end_time);
|
||||
void _AddCreatureCategoryCooldown(uint32 category, time_t apply_time);
|
||||
void AddCreatureSpellCooldown(uint32 spellid);
|
||||
bool HasSpellCooldown(uint32 spell_id) const;
|
||||
bool HasCategoryCooldown(uint32 spell_id) const;
|
||||
|
||||
bool HasSpell(uint32 spellID) const override;
|
||||
|
||||
bool UpdateEntry(uint32 entry, Team team = ALLIANCE, const CreatureData* data = NULL, GameEventCreatureData const* eventData = NULL, bool preserveHPAndPower = true);
|
||||
|
||||
void ApplyGameEventSpells(GameEventCreatureData const* eventData, bool activated);
|
||||
bool UpdateStats(Stats stat) override;
|
||||
bool UpdateAllStats() override;
|
||||
void UpdateResistances(uint32 school) override;
|
||||
void UpdateArmor() override;
|
||||
void UpdateMaxHealth() override;
|
||||
void UpdateMaxPower(Powers power) override;
|
||||
void UpdateAttackPowerAndDamage(bool ranged = false) override;
|
||||
void UpdateDamagePhysical(WeaponAttackType attType) override;
|
||||
uint32 GetCurrentEquipmentId() { return m_equipmentId; }
|
||||
float GetSpellDamageMod(int32 Rank);
|
||||
|
||||
VendorItemData const* GetVendorItems() const;
|
||||
VendorItemData const* GetVendorTemplateItems() const;
|
||||
uint32 GetVendorItemCurrentCount(VendorItem const* vItem);
|
||||
uint32 UpdateVendorItemCurrentCount(VendorItem const* vItem, uint32 used_count);
|
||||
|
||||
TrainerSpellData const* GetTrainerTemplateSpells() const;
|
||||
TrainerSpellData const* GetTrainerSpells() const;
|
||||
|
||||
CreatureInfo const* GetCreatureInfo() const { return m_creatureInfo; }
|
||||
CreatureDataAddon const* GetCreatureAddon() const;
|
||||
|
||||
static uint32 ChooseDisplayId(const CreatureInfo* cinfo, const CreatureData* data = NULL, GameEventCreatureData const* eventData = NULL);
|
||||
|
||||
std::string GetAIName() const;
|
||||
std::string GetScriptName() const;
|
||||
uint32 GetScriptId() const;
|
||||
|
||||
// overwrite WorldObject function for proper name localization
|
||||
const char* GetNameForLocaleIdx(int32 locale_idx) const override;
|
||||
|
||||
void SetDeathState(DeathState s) override; // overwrite virtual Unit::SetDeathState
|
||||
|
||||
bool LoadFromDB(uint32 guid, Map* map);
|
||||
void SaveToDB();
|
||||
// overwrited in Pet
|
||||
virtual void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask);
|
||||
virtual void DeleteFromDB(); // overwrited in Pet
|
||||
static void DeleteFromDB(uint32 lowguid, CreatureData const* data);
|
||||
|
||||
Loot loot;
|
||||
bool lootForPickPocketed;
|
||||
bool lootForBody;
|
||||
bool lootForSkin;
|
||||
|
||||
void PrepareBodyLootState();
|
||||
ObjectGuid GetLootRecipientGuid() const { return m_lootRecipientGuid; }
|
||||
uint32 GetLootGroupRecipientId() const { return m_lootGroupRecipientId; }
|
||||
Player* GetLootRecipient() const; // use group cases as prefered
|
||||
Group* GetGroupLootRecipient() const;
|
||||
bool HasLootRecipient() const { return m_lootGroupRecipientId || m_lootRecipientGuid; }
|
||||
bool IsGroupLootRecipient() const { return m_lootGroupRecipientId; }
|
||||
void SetLootRecipient(Unit* unit);
|
||||
void AllLootRemovedFromCorpse();
|
||||
Player* GetOriginalLootRecipient() const; // ignore group changes/etc, not for looting
|
||||
|
||||
SpellEntry const* ReachWithSpellAttack(Unit* pVictim);
|
||||
SpellEntry const* ReachWithSpellCure(Unit* pVictim);
|
||||
|
||||
uint32 m_spells[CREATURE_MAX_SPELLS];
|
||||
CreatureSpellCooldowns m_CreatureSpellCooldowns;
|
||||
CreatureSpellCooldowns m_CreatureCategoryCooldowns;
|
||||
|
||||
float GetAttackDistance(Unit const* pl) const;
|
||||
|
||||
void SendAIReaction(AiReaction reactionType);
|
||||
|
||||
void DoFleeToGetAssistance();
|
||||
void CallForHelp(float fRadius);
|
||||
void CallAssistance();
|
||||
void SetNoCallAssistance(bool val) { m_AlreadyCallAssistance = val; }
|
||||
void SetNoSearchAssistance(bool val) { m_AlreadySearchedAssistance = val; }
|
||||
bool HasSearchedAssistance() { return m_AlreadySearchedAssistance; }
|
||||
bool CanAssistTo(const Unit* u, const Unit* enemy, bool checkfaction = true) const;
|
||||
bool CanInitiateAttack();
|
||||
|
||||
MovementGeneratorType GetDefaultMovementType() const { return m_defaultMovementType; }
|
||||
void SetDefaultMovementType(MovementGeneratorType mgt) { m_defaultMovementType = mgt; }
|
||||
|
||||
// for use only in LoadHelper, Map::Add Map::CreatureCellRelocation
|
||||
Cell const& GetCurrentCell() const { return m_currentCell; }
|
||||
void SetCurrentCell(Cell const& cell) { m_currentCell = cell; }
|
||||
|
||||
bool IsVisibleInGridForPlayer(Player* pl) const override;
|
||||
|
||||
void RemoveCorpse();
|
||||
bool IsDeadByDefault() const { return m_isDeadByDefault; };
|
||||
|
||||
void ForcedDespawn(uint32 timeMSToDespawn = 0);
|
||||
|
||||
time_t const& GetRespawnTime() const { return m_respawnTime; }
|
||||
time_t GetRespawnTimeEx() const;
|
||||
void SetRespawnTime(uint32 respawn) { m_respawnTime = respawn ? time(NULL) + respawn : 0; }
|
||||
void Respawn();
|
||||
void SaveRespawnTime() override;
|
||||
|
||||
uint32 GetRespawnDelay() const { return m_respawnDelay; }
|
||||
void SetRespawnDelay(uint32 delay) { m_respawnDelay = delay; }
|
||||
|
||||
float GetRespawnRadius() const { return m_respawnradius; }
|
||||
void SetRespawnRadius(float dist) { m_respawnradius = dist; }
|
||||
|
||||
// Functions spawn/remove creature with DB guid in all loaded map copies (if point grid loaded in map)
|
||||
static void AddToRemoveListInMaps(uint32 db_guid, CreatureData const* data);
|
||||
static void SpawnInMaps(uint32 db_guid, CreatureData const* data);
|
||||
|
||||
void StartGroupLoot(Group* group, uint32 timer) override;
|
||||
|
||||
void SendZoneUnderAttackMessage(Player* attacker);
|
||||
|
||||
void SetInCombatWithZone();
|
||||
|
||||
Unit* SelectAttackingTarget(AttackingTarget target, uint32 position, uint32 uiSpellEntry, uint32 selectFlags = 0) const;
|
||||
Unit* SelectAttackingTarget(AttackingTarget target, uint32 position, SpellEntry const* pSpellInfo = NULL, uint32 selectFlags = 0) const;
|
||||
|
||||
bool HasQuest(uint32 quest_id) const override;
|
||||
bool HasInvolvedQuest(uint32 quest_id) const override;
|
||||
|
||||
GridReference<Creature>& GetGridRef() { return m_gridRef; }
|
||||
bool IsRegeneratingHealth() { return m_regenHealth; }
|
||||
virtual uint8 GetPetAutoSpellSize() const { return CREATURE_MAX_SPELLS; }
|
||||
virtual uint32 GetPetAutoSpellOnPos(uint8 pos) const
|
||||
{
|
||||
if (pos >= CREATURE_MAX_SPELLS || m_charmInfo->GetCharmSpell(pos)->GetType() != ACT_ENABLED)
|
||||
return 0;
|
||||
else
|
||||
return m_charmInfo->GetCharmSpell(pos)->GetAction();
|
||||
}
|
||||
|
||||
void SetCombatStartPosition(float x, float y, float z) { m_combatStartX = x; m_combatStartY = y; m_combatStartZ = z; }
|
||||
void GetCombatStartPosition(float& x, float& y, float& z) { x = m_combatStartX; y = m_combatStartY; z = m_combatStartZ; }
|
||||
|
||||
void SetRespawnCoord(CreatureCreatePos const& pos) { m_respawnPos = pos.m_pos; }
|
||||
void SetRespawnCoord(float x, float y, float z, float ori) { m_respawnPos.x = x; m_respawnPos.y = y; m_respawnPos.z = z; m_respawnPos.o = ori; }
|
||||
void GetRespawnCoord(float& x, float& y, float& z, float* ori = NULL, float* dist = NULL) const;
|
||||
void ResetRespawnCoord();
|
||||
|
||||
void SetDeadByDefault(bool death_state) { m_isDeadByDefault = death_state; }
|
||||
|
||||
void SetFactionTemporary(uint32 factionId, uint32 tempFactionFlags = TEMPFACTION_ALL);
|
||||
void ClearTemporaryFaction();
|
||||
uint32 GetTemporaryFactionFlags() { return m_temporaryFactionFlags; }
|
||||
|
||||
void SendAreaSpiritHealerQueryOpcode(Player* pl);
|
||||
|
||||
void SetVirtualItem(VirtualItemSlot slot, uint32 item_id) { SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + slot, item_id); }
|
||||
|
||||
protected:
|
||||
bool MeetsSelectAttackingRequirement(Unit* pTarget, SpellEntry const* pSpellInfo, uint32 selectFlags) const;
|
||||
|
||||
bool CreateFromProto(uint32 guidlow, CreatureInfo const* cinfo, Team team, const CreatureData* data = NULL, GameEventCreatureData const* eventData = NULL);
|
||||
bool InitEntry(uint32 entry, const CreatureData* data = NULL, GameEventCreatureData const* eventData = NULL);
|
||||
|
||||
uint32 m_groupLootTimer; // (msecs)timer used for group loot
|
||||
uint32 m_groupLootId; // used to find group which is looting corpse
|
||||
void StopGroupLoot() override;
|
||||
|
||||
// vendor items
|
||||
VendorItemCounts m_vendorItemCounts;
|
||||
|
||||
void _RealtimeSetCreatureInfo();
|
||||
|
||||
static float _GetHealthMod(int32 Rank);
|
||||
static float _GetDamageMod(int32 Rank);
|
||||
|
||||
uint32 m_lootMoney;
|
||||
ObjectGuid m_lootRecipientGuid; // player who will have rights for looting if m_lootGroupRecipient==0 or group disbanded
|
||||
uint32 m_lootGroupRecipientId; // group who will have rights for looting if set and exist
|
||||
|
||||
/// Timers
|
||||
uint32 m_corpseDecayTimer; // (msecs)timer for death or corpse disappearance
|
||||
time_t m_respawnTime; // (secs) time of next respawn
|
||||
uint32 m_respawnDelay; // (secs) delay between corpse disappearance and respawning
|
||||
uint32 m_corpseDelay; // (secs) delay between death and corpse disappearance
|
||||
float m_respawnradius;
|
||||
|
||||
CreatureSubtype m_subtype; // set in Creatures subclasses for fast it detect without dynamic_cast use
|
||||
void RegenerateMana();
|
||||
void RegenerateHealth();
|
||||
MovementGeneratorType m_defaultMovementType;
|
||||
Cell m_currentCell; // store current cell where creature listed
|
||||
uint32 m_equipmentId;
|
||||
|
||||
// below fields has potential for optimization
|
||||
bool m_AlreadyCallAssistance;
|
||||
bool m_AlreadySearchedAssistance;
|
||||
bool m_regenHealth;
|
||||
bool m_AI_locked;
|
||||
bool m_isDeadByDefault;
|
||||
uint32 m_temporaryFactionFlags; // used for real faction changes (not auras etc)
|
||||
|
||||
SpellSchoolMask m_meleeDamageSchoolMask;
|
||||
uint32 m_originalEntry;
|
||||
|
||||
float m_combatStartX;
|
||||
float m_combatStartY;
|
||||
float m_combatStartZ;
|
||||
|
||||
Position m_respawnPos;
|
||||
|
||||
private:
|
||||
GridReference<Creature> m_gridRef;
|
||||
CreatureInfo const* m_creatureInfo; // in difficulty mode > 0 can different from ObjMgr::GetCreatureTemplate(GetEntry())
|
||||
};
|
||||
|
||||
class ForcedDespawnDelayEvent : public BasicEvent
|
||||
{
|
||||
public:
|
||||
ForcedDespawnDelayEvent(Creature& owner) : BasicEvent(), m_owner(owner) { }
|
||||
bool Execute(uint64 e_time, uint32 p_time) override;
|
||||
|
||||
private:
|
||||
Creature& m_owner;
|
||||
};
|
||||
|
||||
#endif
|
||||
241
src/game/Object/CreatureAI.cpp
Normal file
241
src/game/Object/CreatureAI.cpp
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include "CreatureAI.h"
|
||||
#include "Creature.h"
|
||||
#include "DBCStores.h"
|
||||
#include "Spell.h"
|
||||
#include "GridNotifiers.h"
|
||||
#include "GridNotifiersImpl.h"
|
||||
#include "CellImpl.h"
|
||||
|
||||
CreatureAI::~CreatureAI()
|
||||
{
|
||||
}
|
||||
|
||||
void CreatureAI::AttackedBy(Unit* attacker)
|
||||
{
|
||||
if (!m_creature->getVictim())
|
||||
AttackStart(attacker);
|
||||
}
|
||||
|
||||
CanCastResult CreatureAI::CanCastSpell(Unit* pTarget, const SpellEntry* pSpell, bool isTriggered)
|
||||
{
|
||||
// If not triggered, we check
|
||||
if (!isTriggered)
|
||||
{
|
||||
// State does not allow
|
||||
if (m_creature->hasUnitState(UNIT_STAT_CAN_NOT_REACT_OR_LOST_CONTROL))
|
||||
return CAST_FAIL_STATE;
|
||||
|
||||
if (pSpell->GetPreventionType() == SPELL_PREVENTION_TYPE_SILENCE && m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
|
||||
return CAST_FAIL_STATE;
|
||||
|
||||
if (pSpell->GetPreventionType() == SPELL_PREVENTION_TYPE_PACIFY && m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED))
|
||||
return CAST_FAIL_STATE;
|
||||
|
||||
// Check for power (also done by Spell::CheckCast())
|
||||
if (m_creature->GetPower((Powers)pSpell->powerType) < Spell::CalculatePowerCost(pSpell, m_creature))
|
||||
return CAST_FAIL_POWER;
|
||||
}
|
||||
|
||||
if (const SpellRangeEntry* pSpellRange = sSpellRangeStore.LookupEntry(pSpell->rangeIndex))
|
||||
{
|
||||
if (pTarget != m_creature)
|
||||
{
|
||||
// pTarget is out of range of this spell (also done by Spell::CheckCast())
|
||||
float fDistance = m_creature->GetCombatDistance(pTarget, pSpell->rangeIndex == SPELL_RANGE_IDX_COMBAT);
|
||||
|
||||
if (fDistance > (m_creature->IsHostileTo(pTarget) ? pSpellRange->maxRange : pSpellRange->maxRangeFriendly))
|
||||
return CAST_FAIL_TOO_FAR;
|
||||
|
||||
float fMinRange = m_creature->IsHostileTo(pTarget) ? pSpellRange->minRange : pSpellRange->minRangeFriendly;
|
||||
|
||||
if (fMinRange && fDistance < fMinRange)
|
||||
return CAST_FAIL_TOO_CLOSE;
|
||||
}
|
||||
|
||||
return CAST_OK;
|
||||
}
|
||||
else
|
||||
return CAST_FAIL_OTHER;
|
||||
}
|
||||
|
||||
CanCastResult CreatureAI::DoCastSpellIfCan(Unit* pTarget, uint32 uiSpell, uint32 uiCastFlags, ObjectGuid uiOriginalCasterGUID)
|
||||
{
|
||||
Unit* pCaster = m_creature;
|
||||
|
||||
if (uiCastFlags & CAST_FORCE_TARGET_SELF)
|
||||
pCaster = pTarget;
|
||||
|
||||
// Allowed to cast only if not casting (unless we interrupt ourself) or if spell is triggered
|
||||
if (!pCaster->IsNonMeleeSpellCasted(false) || (uiCastFlags & (CAST_TRIGGERED | CAST_INTERRUPT_PREVIOUS)))
|
||||
{
|
||||
if (const SpellEntry* pSpell = sSpellStore.LookupEntry(uiSpell))
|
||||
{
|
||||
// If cast flag CAST_AURA_NOT_PRESENT is active, check if target already has aura on them
|
||||
if (uiCastFlags & CAST_AURA_NOT_PRESENT)
|
||||
{
|
||||
if (pTarget->HasAura(uiSpell))
|
||||
return CAST_FAIL_TARGET_AURA;
|
||||
}
|
||||
|
||||
// Check if cannot cast spell
|
||||
if (!(uiCastFlags & (CAST_FORCE_TARGET_SELF | CAST_FORCE_CAST)))
|
||||
{
|
||||
CanCastResult castResult = CanCastSpell(pTarget, pSpell, uiCastFlags & CAST_TRIGGERED);
|
||||
|
||||
if (castResult != CAST_OK)
|
||||
return castResult;
|
||||
}
|
||||
|
||||
// Interrupt any previous spell
|
||||
if (uiCastFlags & CAST_INTERRUPT_PREVIOUS && pCaster->IsNonMeleeSpellCasted(false))
|
||||
pCaster->InterruptNonMeleeSpells(false);
|
||||
|
||||
pCaster->CastSpell(pTarget, pSpell, uiCastFlags & CAST_TRIGGERED, NULL, NULL, uiOriginalCasterGUID);
|
||||
return CAST_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
sLog.outErrorDb("DoCastSpellIfCan by creature entry %u attempt to cast spell %u but spell does not exist.", m_creature->GetEntry(), uiSpell);
|
||||
return CAST_FAIL_OTHER;
|
||||
}
|
||||
}
|
||||
else
|
||||
return CAST_FAIL_IS_CASTING;
|
||||
}
|
||||
|
||||
bool CreatureAI::DoMeleeAttackIfReady()
|
||||
{
|
||||
return m_creature->UpdateMeleeAttackingState();
|
||||
}
|
||||
|
||||
void CreatureAI::SetCombatMovement(bool enable, bool stopOrStartMovement /*=false*/)
|
||||
{
|
||||
m_isCombatMovement = enable;
|
||||
|
||||
if (enable)
|
||||
m_creature->clearUnitState(UNIT_STAT_NO_COMBAT_MOVEMENT);
|
||||
else
|
||||
m_creature->addUnitState(UNIT_STAT_NO_COMBAT_MOVEMENT);
|
||||
|
||||
if (stopOrStartMovement && m_creature->getVictim()) // Only change current movement while in combat
|
||||
{
|
||||
if (enable)
|
||||
m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim(), m_attackDistance, m_attackAngle);
|
||||
else if (!enable && m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
|
||||
m_creature->StopMoving();
|
||||
}
|
||||
}
|
||||
|
||||
void CreatureAI::HandleMovementOnAttackStart(Unit* victim)
|
||||
{
|
||||
if (m_isCombatMovement)
|
||||
m_creature->GetMotionMaster()->MoveChase(victim, m_attackDistance, m_attackAngle);
|
||||
// TODO - adapt this to only stop OOC-MMGens when MotionMaster rewrite is finished
|
||||
else if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE || m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == RANDOM_MOTION_TYPE)
|
||||
{
|
||||
m_creature->GetMotionMaster()->MoveIdle();
|
||||
m_creature->StopMoving();
|
||||
}
|
||||
}
|
||||
|
||||
// ////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Event system
|
||||
// ////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class AiDelayEventAround : public BasicEvent
|
||||
{
|
||||
public:
|
||||
AiDelayEventAround(AIEventType eventType, ObjectGuid invokerGuid, Creature& owner, std::list<Creature*> const& receivers, uint32 miscValue) :
|
||||
BasicEvent(),
|
||||
m_eventType(eventType),
|
||||
m_invokerGuid(invokerGuid),
|
||||
m_owner(owner),
|
||||
m_miscValue(miscValue)
|
||||
{
|
||||
// Pushing guids because in delay can happen some creature gets despawned => invalid pointer
|
||||
m_receiverGuids.reserve(receivers.size());
|
||||
for (std::list<Creature*>::const_iterator itr = receivers.begin(); itr != receivers.end(); ++itr)
|
||||
m_receiverGuids.push_back((*itr)->GetObjectGuid());
|
||||
}
|
||||
|
||||
bool Execute(uint64 /*e_time*/, uint32 /*p_time*/) override
|
||||
{
|
||||
Unit* pInvoker = m_owner.GetMap()->GetUnit(m_invokerGuid);
|
||||
|
||||
for (GuidVector::const_reverse_iterator itr = m_receiverGuids.rbegin(); itr != m_receiverGuids.rend(); ++itr)
|
||||
{
|
||||
if (Creature* pReceiver = m_owner.GetMap()->GetAnyTypeCreature(*itr))
|
||||
{
|
||||
pReceiver->AI()->ReceiveAIEvent(m_eventType, &m_owner, pInvoker, m_miscValue);
|
||||
// Special case for type 0 (call-assistance)
|
||||
if (m_eventType == AI_EVENT_CALL_ASSISTANCE && pInvoker && pReceiver->CanAssistTo(&m_owner, pInvoker))
|
||||
{
|
||||
pReceiver->SetNoCallAssistance(true);
|
||||
pReceiver->AI()->AttackStart(pInvoker);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_receiverGuids.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
AiDelayEventAround();
|
||||
|
||||
ObjectGuid m_invokerGuid;
|
||||
GuidVector m_receiverGuids;
|
||||
Creature& m_owner;
|
||||
|
||||
AIEventType m_eventType;
|
||||
uint32 m_miscValue;
|
||||
};
|
||||
|
||||
void CreatureAI::SendAIEventAround(AIEventType eventType, Unit* pInvoker, uint32 uiDelay, float fRadius, uint32 miscValue /*=0*/) const
|
||||
{
|
||||
if (fRadius > 0)
|
||||
{
|
||||
std::list<Creature*> receiverList;
|
||||
|
||||
// Use this check here to collect only assitable creatures in case of CALL_ASSISTANCE, else be less strict
|
||||
MaNGOS::AnyAssistCreatureInRangeCheck u_check(m_creature, eventType == AI_EVENT_CALL_ASSISTANCE ? pInvoker : NULL, fRadius);
|
||||
MaNGOS::CreatureListSearcher<MaNGOS::AnyAssistCreatureInRangeCheck> searcher(receiverList, u_check);
|
||||
Cell::VisitGridObjects(m_creature, searcher, fRadius);
|
||||
|
||||
if (!receiverList.empty())
|
||||
{
|
||||
AiDelayEventAround* e = new AiDelayEventAround(eventType, pInvoker ? pInvoker->GetObjectGuid() : ObjectGuid(), *m_creature, receiverList, miscValue);
|
||||
m_creature->m_Events.AddEvent(e, m_creature->m_Events.CalculateTime(uiDelay));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CreatureAI::SendAIEvent(AIEventType eventType, Unit* pInvoker, Creature* pReceiver, uint32 miscValue /*=0*/) const
|
||||
{
|
||||
MANGOS_ASSERT(pReceiver);
|
||||
pReceiver->AI()->ReceiveAIEvent(eventType, m_creature, pInvoker, miscValue);
|
||||
}
|
||||
389
src/game/Object/CreatureAI.h
Normal file
389
src/game/Object/CreatureAI.h
Normal file
|
|
@ -0,0 +1,389 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOS_CREATUREAI_H
|
||||
#define MANGOS_CREATUREAI_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "Platform/Define.h"
|
||||
#include "Policies/Singleton.h"
|
||||
#include "Dynamic/ObjectRegistry.h"
|
||||
#include "Dynamic/FactoryHolder.h"
|
||||
#include "ObjectGuid.h"
|
||||
|
||||
class WorldObject;
|
||||
class GameObject;
|
||||
class Unit;
|
||||
class Creature;
|
||||
class Player;
|
||||
struct SpellEntry;
|
||||
class ChatHandler;
|
||||
|
||||
#define TIME_INTERVAL_LOOK 5000
|
||||
#define VISIBILITY_RANGE 10000
|
||||
|
||||
enum CanCastResult
|
||||
{
|
||||
CAST_OK = 0,
|
||||
CAST_FAIL_IS_CASTING = 1,
|
||||
CAST_FAIL_OTHER = 2,
|
||||
CAST_FAIL_TOO_FAR = 3,
|
||||
CAST_FAIL_TOO_CLOSE = 4,
|
||||
CAST_FAIL_POWER = 5,
|
||||
CAST_FAIL_STATE = 6,
|
||||
CAST_FAIL_TARGET_AURA = 7
|
||||
};
|
||||
|
||||
enum CastFlags
|
||||
{
|
||||
CAST_INTERRUPT_PREVIOUS = 0x01, // Interrupt any spell casting
|
||||
CAST_TRIGGERED = 0x02, // Triggered (this makes spell cost zero mana and have no cast time)
|
||||
CAST_FORCE_CAST = 0x04, // Forces cast even if creature is out of mana or out of range
|
||||
CAST_NO_MELEE_IF_OOM = 0x08, // Prevents creature from entering melee if out of mana or out of range
|
||||
CAST_FORCE_TARGET_SELF = 0x10, // Forces the target to cast this spell on itself
|
||||
CAST_AURA_NOT_PRESENT = 0x20, // Only casts the spell if the target does not have an aura from the spell
|
||||
};
|
||||
|
||||
enum AIEventType
|
||||
{
|
||||
// Usable with Event AI
|
||||
AI_EVENT_JUST_DIED = 0, // Sender = Killed Npc, Invoker = Killer
|
||||
AI_EVENT_CRITICAL_HEALTH = 1, // Sender = Hurt Npc, Invoker = DamageDealer - Expected to be sent by 10% health
|
||||
AI_EVENT_LOST_HEALTH = 2, // Sender = Hurt Npc, Invoker = DamageDealer - Expected to be sent by 50% health
|
||||
AI_EVENT_LOST_SOME_HEALTH = 3, // Sender = Hurt Npc, Invoker = DamageDealer - Expected to be sent by 90% health
|
||||
AI_EVENT_GOT_FULL_HEALTH = 4, // Sender = Healed Npc, Invoker = Healer
|
||||
AI_EVENT_CUSTOM_EVENTAI_A = 5, // Sender = Npc that throws custom event, Invoker = TARGET_T_ACTION_INVOKER (if exists)
|
||||
AI_EVENT_CUSTOM_EVENTAI_B = 6, // Sender = Npc that throws custom event, Invoker = TARGET_T_ACTION_INVOKER (if exists)
|
||||
AI_EVENT_GOT_CCED = 7, // Sender = CCed Npc, Invoker = Caster that CCed
|
||||
MAXIMAL_AI_EVENT_EVENTAI = 8,
|
||||
|
||||
// Internal Use
|
||||
AI_EVENT_CALL_ASSISTANCE = 10, // Sender = Attacked Npc, Invoker = Enemy
|
||||
|
||||
// Predefined for SD2
|
||||
AI_EVENT_START_ESCORT = 100, // Invoker = Escorting Player
|
||||
AI_EVENT_START_ESCORT_B = 101, // Invoker = Escorting Player
|
||||
AI_EVENT_START_EVENT = 102, // Invoker = EventStarter
|
||||
AI_EVENT_START_EVENT_A = 103, // Invoker = EventStarter
|
||||
AI_EVENT_START_EVENT_B = 104, // Invoker = EventStarter
|
||||
|
||||
// Some IDs for special cases in SD2
|
||||
AI_EVENT_CUSTOM_A = 1000,
|
||||
AI_EVENT_CUSTOM_B = 1001,
|
||||
AI_EVENT_CUSTOM_C = 1002,
|
||||
AI_EVENT_CUSTOM_D = 1003,
|
||||
AI_EVENT_CUSTOM_E = 1004,
|
||||
AI_EVENT_CUSTOM_F = 1005,
|
||||
};
|
||||
|
||||
class MANGOS_DLL_SPEC CreatureAI
|
||||
{
|
||||
public:
|
||||
explicit CreatureAI(Creature* creature) :
|
||||
m_creature(creature),
|
||||
m_isCombatMovement(true),
|
||||
m_attackDistance(0.0f),
|
||||
m_attackAngle(0.0f)
|
||||
{}
|
||||
|
||||
virtual ~CreatureAI();
|
||||
|
||||
///== Information about AI ========================
|
||||
/**
|
||||
* This funcion is used to display information about the AI.
|
||||
* It is called when the .npc aiinfo command is used.
|
||||
* Use this for on-the-fly debugging
|
||||
* @param reader is a ChatHandler to send messages to.
|
||||
*/
|
||||
virtual void GetAIInformation(ChatHandler& /*reader*/) {}
|
||||
|
||||
///== Reactions At =================================
|
||||
|
||||
/**
|
||||
* Called if IsVisible(Unit* pWho) is true at each (relative) override pWho move, reaction at visibility zone enter
|
||||
* Note: The Unit* pWho can be out of Line of Sight, usually this is only visibiliy (by state) and range dependendend
|
||||
* Note: This function is not called for creatures who are in evade mode
|
||||
* @param pWho Unit* who moved in the visibility range and is visisble
|
||||
*/
|
||||
virtual void MoveInLineOfSight(Unit* /*pWho*/) {}
|
||||
|
||||
/**
|
||||
* Called for reaction at enter to combat if not in combat yet
|
||||
* @param pEnemy Unit* of whom the Creature enters combat with, can be NULL
|
||||
*/
|
||||
virtual void EnterCombat(Unit* /*pEnemy*/) {}
|
||||
|
||||
/**
|
||||
* Called for reaction at stopping attack at no attackers or targets
|
||||
* This is called usually in Unit::SelectHostileTarget, if no more target exists
|
||||
*/
|
||||
virtual void EnterEvadeMode() {}
|
||||
|
||||
/**
|
||||
* Called at reaching home after MoveTargetedHome
|
||||
*/
|
||||
virtual void JustReachedHome() {}
|
||||
|
||||
/**
|
||||
* Called at any Heal received from any Unit
|
||||
* @param pHealer Unit* which deals the heal
|
||||
* @param uiHealedAmount Amount of healing received
|
||||
*/
|
||||
virtual void HealedBy(Unit* /*pHealer*/, uint32& /*uiHealedAmount*/) {}
|
||||
|
||||
/**
|
||||
* Called at any Damage to any victim (before damage apply)
|
||||
* @param pDoneTo Unit* to whom Damage of amount uiDamage will be dealt
|
||||
* @param uiDamage Amount of Damage that will be dealt, can be changed here
|
||||
*/
|
||||
virtual void DamageDeal(Unit* /*pDoneTo*/, uint32& /*uiDamage*/) {}
|
||||
|
||||
/**
|
||||
* Called at any Damage from any attacker (before damage apply)
|
||||
* Note: Use for recalculation damage or special reaction at damage
|
||||
* for attack reaction use AttackedBy called for not DOT damage in Unit::DealDamage also
|
||||
* @param pDealer Unit* who will deal Damage to the creature
|
||||
* @param uiDamage Amount of Damage that will be dealt, can be changed here
|
||||
*/
|
||||
virtual void DamageTaken(Unit* /*pDealer*/, uint32& /*uiDamage*/) {}
|
||||
|
||||
/**
|
||||
* Called when the creature is killed
|
||||
* @param pKiller Unit* who killed the creature
|
||||
*/
|
||||
virtual void JustDied(Unit* /*pKiller*/) {}
|
||||
|
||||
/**
|
||||
* Called when the corpse of this creature gets removed
|
||||
* @param uiRespawnDelay Delay (in seconds). If != 0, then this is the time after which the creature will respawn, if = 0 the default respawn-delay will be used
|
||||
*/
|
||||
virtual void CorpseRemoved(uint32& /*uiRespawnDelay*/) {}
|
||||
|
||||
/**
|
||||
* Called when a summoned creature is killed
|
||||
* @param pSummoned Summoned Creature* that got killed
|
||||
*/
|
||||
virtual void SummonedCreatureJustDied(Creature* /*pSummoned*/) {}
|
||||
|
||||
/**
|
||||
* Called when the creature kills a unit
|
||||
* @param pVictim Victim that got killed
|
||||
*/
|
||||
virtual void KilledUnit(Unit* /*pVictim*/) {}
|
||||
|
||||
/**
|
||||
* Called when owner of m_creature (if m_creature is PROTECTOR_PET) kills a unit
|
||||
* @param pVictim Victim that got killed (by owner of creature)
|
||||
*/
|
||||
virtual void OwnerKilledUnit(Unit* /*pVictim*/) {}
|
||||
|
||||
/**
|
||||
* Called when the creature summon successfully other creature
|
||||
* @param pSummoned Creature that got summoned
|
||||
*/
|
||||
virtual void JustSummoned(Creature* /*pSummoned*/) {}
|
||||
|
||||
/**
|
||||
* Called when the creature summon successfully a gameobject
|
||||
* @param pGo GameObject that was summoned
|
||||
*/
|
||||
virtual void JustSummoned(GameObject* /*pGo*/) {}
|
||||
|
||||
/**
|
||||
* Called when a summoned creature gets TemporarySummon::UnSummon ed
|
||||
* @param pSummoned Summoned creature that despawned
|
||||
*/
|
||||
virtual void SummonedCreatureDespawn(Creature* /*pSummoned*/) {}
|
||||
|
||||
/**
|
||||
* Called when hit by a spell
|
||||
* @param pCaster Caster who casted the spell
|
||||
* @param pSpell The spell that hit the creature
|
||||
*/
|
||||
virtual void SpellHit(Unit* /*pCaster*/, const SpellEntry* /*pSpell*/) {}
|
||||
|
||||
/**
|
||||
* Called when spell hits creature's target
|
||||
* @param pTarget Target that we hit with the spell
|
||||
* @param pSpell Spell with which we hit pTarget
|
||||
*/
|
||||
virtual void SpellHitTarget(Unit* /*pTarget*/, const SpellEntry* /*pSpell*/) {}
|
||||
|
||||
/**
|
||||
* Called when the creature is target of hostile action: swing, hostile spell landed, fear/etc)
|
||||
* @param pAttacker Unit* who attacked the creature
|
||||
*/
|
||||
virtual void AttackedBy(Unit* pAttacker);
|
||||
|
||||
/**
|
||||
* Called when creature is respawned (for reseting variables)
|
||||
*/
|
||||
virtual void JustRespawned() {}
|
||||
|
||||
/**
|
||||
* Called at waypoint reached or point movement finished
|
||||
* @param uiMovementType Type of the movement (enum MovementGeneratorType)
|
||||
* @param uiData Data related to the finished movement (ie point-id)
|
||||
*/
|
||||
virtual void MovementInform(uint32 /*uiMovementType*/, uint32 /*uiData*/) {}
|
||||
|
||||
/**
|
||||
* Called if a temporary summoned of m_creature reach a move point
|
||||
* @param pSummoned Summoned Creature that finished some movement
|
||||
* @param uiMotionType Type of the movement (enum MovementGeneratorType)
|
||||
* @param uiData Data related to the finished movement (ie point-id)
|
||||
*/
|
||||
virtual void SummonedMovementInform(Creature* /*pSummoned*/, uint32 /*uiMotionType*/, uint32 /*uiData*/) {}
|
||||
|
||||
/**
|
||||
* Called at text emote receive from player
|
||||
* @param pPlayer Player* who sent the emote
|
||||
* @param uiEmote ID of the emote the player used with the creature as target
|
||||
*/
|
||||
virtual void ReceiveEmote(Player* /*pPlayer*/, uint32 /*uiEmote*/) {}
|
||||
|
||||
///== Triggered Actions Requested ==================
|
||||
|
||||
/**
|
||||
* Called when creature attack expected (if creature can and no have current victim)
|
||||
* Note: for reaction at hostile action must be called AttackedBy function.
|
||||
* Note: Usually called by MoveInLineOfSight, in Unit::SelectHostileTarget or when the AI is forced to attack an enemy
|
||||
* @param pWho Unit* who is possible target
|
||||
*/
|
||||
virtual void AttackStart(Unit* /*pWho*/) {}
|
||||
|
||||
/**
|
||||
* Called at World update tick, by default every 100ms
|
||||
* This setting is dependend on CONFIG_UINT32_INTERVAL_MAPUPDATE
|
||||
* Note: Use this function to handle Timers, Threat-Management and MeleeAttacking
|
||||
* @param uiDiff Passed time since last call
|
||||
*/
|
||||
virtual void UpdateAI(const uint32 /*uiDiff*/) {}
|
||||
|
||||
///== State checks =================================
|
||||
|
||||
/**
|
||||
* Check if unit is visible for MoveInLineOfSight
|
||||
* Note: This check is by default only the state-depending (visibility, range), NOT LineOfSight
|
||||
* @param pWho Unit* who is checked if it is visisble for the creature
|
||||
*/
|
||||
virtual bool IsVisible(Unit* /*pWho*/) const { return false; }
|
||||
|
||||
// Called when victim entered water and creature can not enter water
|
||||
// TODO: rather unused
|
||||
virtual bool canReachByRangeAttack(Unit*) { return false; }
|
||||
|
||||
///== Helper functions =============================
|
||||
|
||||
/// This function is used to do the actual melee damage (if possible)
|
||||
bool DoMeleeAttackIfReady();
|
||||
|
||||
/// Internal helper function, to check if a spell can be cast
|
||||
CanCastResult CanCastSpell(Unit* pTarget, const SpellEntry* pSpell, bool isTriggered);
|
||||
|
||||
/**
|
||||
* Function to cast a spell if possible
|
||||
* @param pTarget Unit* onto whom the spell should be cast
|
||||
* @param uiSpell ID of the spell that the creature will try to cast
|
||||
* @param uiCastFlags Some flags to define how to cast, see enum CastFlags
|
||||
* @param OriginalCasterGuid the original caster of the spell if required, empty by default
|
||||
*/
|
||||
CanCastResult DoCastSpellIfCan(Unit* pTarget, uint32 uiSpell, uint32 uiCastFlags = 0, ObjectGuid OriginalCasterGuid = ObjectGuid());
|
||||
|
||||
/// Set combat movement (on/off), also sets UNIT_STAT_NO_COMBAT_MOVEMENT
|
||||
void SetCombatMovement(bool enable, bool stopOrStartMovement = false);
|
||||
bool IsCombatMovement() const { return m_isCombatMovement; }
|
||||
|
||||
///== Event Handling ===============================
|
||||
|
||||
/**
|
||||
* Send an AI Event to nearby Creatures around
|
||||
* @param uiType number to specify the event, default cases listed in enum AIEventType
|
||||
* @param pInvoker Unit that triggered this event (like an attacker)
|
||||
* @param uiDelay delay time until the Event will be triggered
|
||||
* @param fRadius range in which for receiver is searched
|
||||
*/
|
||||
void SendAIEventAround(AIEventType eventType, Unit* pInvoker, uint32 uiDelay, float fRadius, uint32 miscValue = 0) const;
|
||||
|
||||
/**
|
||||
* Send an AI Event to a Creature
|
||||
* @param eventType to specify the event, default cases listed in enum AIEventType
|
||||
* @param pInvoker Unit that triggered this event (like an attacker)
|
||||
* @param pReceiver Creature to receive this event
|
||||
*/
|
||||
void SendAIEvent(AIEventType eventType, Unit* pInvoker, Creature* pReceiver, uint32 miscValue = 0) const;
|
||||
|
||||
/**
|
||||
* Called when an AI Event is received
|
||||
* @param eventType to specify the event, default cases listed in enum AIEventType
|
||||
* @param pSender Creature that sent this event
|
||||
* @param pInvoker Unit that triggered this event (like an attacker)
|
||||
*/
|
||||
virtual void ReceiveAIEvent(AIEventType /*eventType*/, Creature* /*pSender*/, Unit* /*pInvoker*/, uint32 /*miscValue*/) {}
|
||||
|
||||
protected:
|
||||
void HandleMovementOnAttackStart(Unit* victim);
|
||||
|
||||
///== Fields =======================================
|
||||
|
||||
/// Pointer to the Creature controlled by this AI
|
||||
Creature* const m_creature;
|
||||
|
||||
/// Combat movement currently enabled
|
||||
bool m_isCombatMovement;
|
||||
/// How should an enemy be chased
|
||||
float m_attackDistance;
|
||||
float m_attackAngle;
|
||||
};
|
||||
|
||||
struct SelectableAI : public FactoryHolder<CreatureAI>, public Permissible<Creature>
|
||||
{
|
||||
SelectableAI(const char* id) : FactoryHolder<CreatureAI>(id) {}
|
||||
};
|
||||
|
||||
template<class REAL_AI>
|
||||
struct CreatureAIFactory : public SelectableAI
|
||||
{
|
||||
CreatureAIFactory(const char* name) : SelectableAI(name) {}
|
||||
|
||||
CreatureAI* Create(void*) const override;
|
||||
|
||||
int Permit(const Creature* c) const { return REAL_AI::Permissible(c); }
|
||||
};
|
||||
|
||||
enum Permitions
|
||||
{
|
||||
PERMIT_BASE_NO = -1,
|
||||
PERMIT_BASE_IDLE = 1,
|
||||
PERMIT_BASE_REACTIVE = 100,
|
||||
PERMIT_BASE_PROACTIVE = 200,
|
||||
PERMIT_BASE_FACTION_SPECIFIC = 400,
|
||||
PERMIT_BASE_SPECIAL = 800
|
||||
};
|
||||
|
||||
typedef FactoryHolder<CreatureAI> CreatureAICreator;
|
||||
typedef FactoryHolder<CreatureAI>::FactoryHolderRegistry CreatureAIRegistry;
|
||||
typedef FactoryHolder<CreatureAI>::FactoryHolderRepository CreatureAIRepository;
|
||||
|
||||
#endif
|
||||
37
src/game/Object/CreatureAIImpl.h
Normal file
37
src/game/Object/CreatureAIImpl.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef CREATUREAIIMPL_H
|
||||
#define CREATUREAIIMPL_H
|
||||
|
||||
#include "CreatureAI.h"
|
||||
|
||||
template<class REAL_AI>
|
||||
inline CreatureAI*
|
||||
CreatureAIFactory<REAL_AI>::Create(void* data) const
|
||||
{
|
||||
Creature* creature = reinterpret_cast<Creature*>(data);
|
||||
return (new REAL_AI(creature));
|
||||
}
|
||||
#endif
|
||||
53
src/game/Object/CreatureAIRegistry.cpp
Normal file
53
src/game/Object/CreatureAIRegistry.cpp
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include "NullCreatureAI.h"
|
||||
#include "ReactorAI.h"
|
||||
#include "AggressorAI.h"
|
||||
#include "GuardAI.h"
|
||||
#include "PetAI.h"
|
||||
#include "TotemAI.h"
|
||||
#include "CreatureEventAI.h"
|
||||
#include "RandomMovementGenerator.h"
|
||||
#include "CreatureAIImpl.h"
|
||||
#include "MovementGeneratorImpl.h"
|
||||
#include "CreatureAIRegistry.h"
|
||||
#include "WaypointMovementGenerator.h"
|
||||
|
||||
namespace AIRegistry
|
||||
{
|
||||
void Initialize()
|
||||
{
|
||||
(new CreatureAIFactory<NullCreatureAI>("NullAI"))->RegisterSelf();
|
||||
(new CreatureAIFactory<AggressorAI>("AggressorAI"))->RegisterSelf();
|
||||
(new CreatureAIFactory<ReactorAI>("ReactorAI"))->RegisterSelf();
|
||||
(new CreatureAIFactory<GuardAI>("GuardAI"))->RegisterSelf();
|
||||
(new CreatureAIFactory<PetAI>("PetAI"))->RegisterSelf();
|
||||
(new CreatureAIFactory<TotemAI>("TotemAI"))->RegisterSelf();
|
||||
(new CreatureAIFactory<CreatureEventAI>("EventAI"))->RegisterSelf();
|
||||
|
||||
(new MovementGeneratorFactory<RandomMovementGenerator<Creature> >(RANDOM_MOTION_TYPE))->RegisterSelf();
|
||||
(new MovementGeneratorFactory<WaypointMovementGenerator<Creature> >(WAYPOINT_MOTION_TYPE))->RegisterSelf();
|
||||
}
|
||||
}
|
||||
33
src/game/Object/CreatureAIRegistry.h
Normal file
33
src/game/Object/CreatureAIRegistry.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOS_CREATUREAIREGISTRY_H
|
||||
#define MANGOS_CREATUREAIREGISTRY_H
|
||||
|
||||
namespace AIRegistry
|
||||
{
|
||||
void Initialize(void);
|
||||
}
|
||||
|
||||
#endif
|
||||
123
src/game/Object/CreatureAISelector.cpp
Normal file
123
src/game/Object/CreatureAISelector.cpp
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include "CreatureAISelector.h"
|
||||
#include "Creature.h"
|
||||
#include "CreatureAIImpl.h"
|
||||
#include "NullCreatureAI.h"
|
||||
#include "Policies/Singleton.h"
|
||||
#include "MovementGenerator.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "Pet.h"
|
||||
|
||||
INSTANTIATE_SINGLETON_1(CreatureAIRegistry);
|
||||
INSTANTIATE_SINGLETON_1(MovementGeneratorRegistry);
|
||||
|
||||
namespace FactorySelector
|
||||
{
|
||||
CreatureAI* selectAI(Creature* creature)
|
||||
{
|
||||
// Allow scripting AI for normal creatures and not controlled pets (guardians and mini-pets)
|
||||
if ((!creature->IsPet() || !((Pet*)creature)->isControlled()) && !creature->isCharmed())
|
||||
if (CreatureAI* scriptedAI = sScriptMgr.GetCreatureAI(creature))
|
||||
return scriptedAI;
|
||||
|
||||
CreatureAIRegistry& ai_registry(CreatureAIRepository::Instance());
|
||||
|
||||
const CreatureAICreator* ai_factory = NULL;
|
||||
|
||||
std::string ainame = creature->GetAIName();
|
||||
|
||||
// select by NPC flags _first_ - otherwise EventAI might be choosen for pets/totems
|
||||
// excplicit check for isControlled() and owner type to allow guardian, mini-pets and pets controlled by NPCs to be scripted by EventAI
|
||||
Unit* owner = NULL;
|
||||
if ((creature->IsPet() && ((Pet*)creature)->isControlled() &&
|
||||
((owner = creature->GetOwner()) && owner->GetTypeId() == TYPEID_PLAYER)) || creature->isCharmed())
|
||||
ai_factory = ai_registry.GetRegistryItem("PetAI");
|
||||
else if (creature->IsTotem())
|
||||
ai_factory = ai_registry.GetRegistryItem("TotemAI");
|
||||
|
||||
// select by script name
|
||||
if (!ai_factory && !ainame.empty())
|
||||
ai_factory = ai_registry.GetRegistryItem(ainame.c_str());
|
||||
|
||||
if (!ai_factory && creature->IsGuard())
|
||||
ai_factory = ai_registry.GetRegistryItem("GuardAI");
|
||||
|
||||
// select by permit check
|
||||
if (!ai_factory)
|
||||
{
|
||||
int best_val = PERMIT_BASE_NO;
|
||||
typedef CreatureAIRegistry::RegistryMapType RMT;
|
||||
RMT const& l = ai_registry.GetRegisteredItems();
|
||||
for (RMT::const_iterator iter = l.begin(); iter != l.end(); ++iter)
|
||||
{
|
||||
const CreatureAICreator* factory = iter->second;
|
||||
const SelectableAI* p = dynamic_cast<const SelectableAI*>(factory);
|
||||
MANGOS_ASSERT(p != NULL);
|
||||
int val = p->Permit(creature);
|
||||
if (val > best_val)
|
||||
{
|
||||
best_val = val;
|
||||
ai_factory = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// select NullCreatureAI if not another cases
|
||||
ainame = (ai_factory == NULL) ? "NullCreatureAI" : ai_factory->key();
|
||||
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature %u used AI is %s.", creature->GetGUIDLow(), ainame.c_str());
|
||||
return (ai_factory == NULL ? new NullCreatureAI(creature) : ai_factory->Create(creature));
|
||||
}
|
||||
|
||||
MovementGenerator* selectMovementGenerator(Creature* creature)
|
||||
{
|
||||
MovementGeneratorRegistry& mv_registry(MovementGeneratorRepository::Instance());
|
||||
MANGOS_ASSERT(creature->GetCreatureInfo() != NULL);
|
||||
MovementGeneratorCreator const* mv_factory = mv_registry.GetRegistryItem(
|
||||
creature->GetOwnerGuid().IsPlayer() ? FOLLOW_MOTION_TYPE : creature->GetDefaultMovementType());
|
||||
|
||||
/* if( mv_factory == NULL )
|
||||
{
|
||||
int best_val = -1;
|
||||
std::vector<std::string> l;
|
||||
mv_registry.GetRegisteredItems(l);
|
||||
for( std::vector<std::string>::iterator iter = l.begin(); iter != l.end(); ++iter)
|
||||
{
|
||||
const MovementGeneratorCreator *factory = mv_registry.GetRegistryItem((*iter).c_str());
|
||||
const SelectableMovement *p = dynamic_cast<const SelectableMovement *>(factory);
|
||||
ASSERT( p != NULL );
|
||||
int val = p->Permit(creature);
|
||||
if( val > best_val )
|
||||
{
|
||||
best_val = val;
|
||||
mv_factory = p;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
return (mv_factory == NULL ? NULL : mv_factory->Create(creature));
|
||||
}
|
||||
}
|
||||
37
src/game/Object/CreatureAISelector.h
Normal file
37
src/game/Object/CreatureAISelector.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOS_CREATUREAISELECTOR_H
|
||||
#define MANGOS_CREATUREAISELECTOR_H
|
||||
|
||||
class CreatureAI;
|
||||
class Creature;
|
||||
class MovementGenerator;
|
||||
|
||||
namespace FactorySelector
|
||||
{
|
||||
CreatureAI* selectAI(Creature*);
|
||||
MovementGenerator* selectMovementGenerator(Creature*);
|
||||
}
|
||||
#endif
|
||||
1582
src/game/Object/CreatureEventAI.cpp
Normal file
1582
src/game/Object/CreatureEventAI.cpp
Normal file
File diff suppressed because it is too large
Load diff
683
src/game/Object/CreatureEventAI.h
Normal file
683
src/game/Object/CreatureEventAI.h
Normal file
|
|
@ -0,0 +1,683 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOS_CREATURE_EAI_H
|
||||
#define MANGOS_CREATURE_EAI_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "Creature.h"
|
||||
#include "CreatureAI.h"
|
||||
#include "Unit.h"
|
||||
|
||||
class Player;
|
||||
class WorldObject;
|
||||
|
||||
#define EVENT_UPDATE_TIME 500
|
||||
#define MAX_ACTIONS 3
|
||||
#define MAX_PHASE 32
|
||||
|
||||
enum EventAI_Type
|
||||
{
|
||||
EVENT_T_TIMER_IN_COMBAT = 0, // InitialMin, InitialMax, RepeatMin, RepeatMax
|
||||
EVENT_T_TIMER_OOC = 1, // InitialMin, InitialMax, RepeatMin, RepeatMax
|
||||
EVENT_T_HP = 2, // HPMax%, HPMin%, RepeatMin, RepeatMax
|
||||
EVENT_T_MANA = 3, // ManaMax%,ManaMin% RepeatMin, RepeatMax
|
||||
EVENT_T_AGGRO = 4, // NONE
|
||||
EVENT_T_KILL = 5, // RepeatMin, RepeatMax
|
||||
EVENT_T_DEATH = 6, // NONE
|
||||
EVENT_T_EVADE = 7, // NONE
|
||||
EVENT_T_SPELLHIT = 8, // SpellID, School, RepeatMin, RepeatMax
|
||||
EVENT_T_RANGE = 9, // MinDist, MaxDist, RepeatMin, RepeatMax
|
||||
EVENT_T_OOC_LOS = 10, // NoHostile, MaxRnage, RepeatMin, RepeatMax
|
||||
EVENT_T_SPAWNED = 11, // Condition, CondValue1
|
||||
EVENT_T_TARGET_HP = 12, // HPMax%, HPMin%, RepeatMin, RepeatMax
|
||||
EVENT_T_TARGET_CASTING = 13, // RepeatMin, RepeatMax
|
||||
EVENT_T_FRIENDLY_HP = 14, // HPDeficit, Radius, RepeatMin, RepeatMax
|
||||
EVENT_T_FRIENDLY_IS_CC = 15, // DispelType, Radius, RepeatMin, RepeatMax
|
||||
EVENT_T_FRIENDLY_MISSING_BUFF = 16, // SpellId, Radius, RepeatMin, RepeatMax
|
||||
EVENT_T_SUMMONED_UNIT = 17, // CreatureId, RepeatMin, RepeatMax
|
||||
EVENT_T_TARGET_MANA = 18, // ManaMax%, ManaMin%, RepeatMin, RepeatMax
|
||||
EVENT_T_QUEST_ACCEPT = 19, // QuestID
|
||||
EVENT_T_QUEST_COMPLETE = 20, //
|
||||
EVENT_T_REACHED_HOME = 21, // NONE
|
||||
EVENT_T_RECEIVE_EMOTE = 22, // EmoteId, Condition, CondValue1, CondValue2
|
||||
EVENT_T_AURA = 23, // Param1 = SpellID, Param2 = Number of time stacked, Param3/4 Repeat Min/Max
|
||||
EVENT_T_TARGET_AURA = 24, // Param1 = SpellID, Param2 = Number of time stacked, Param3/4 Repeat Min/Max
|
||||
EVENT_T_SUMMONED_JUST_DIED = 25, // CreatureId, RepeatMin, RepeatMax
|
||||
EVENT_T_SUMMONED_JUST_DESPAWN = 26, // CreatureId, RepeatMin, RepeatMax
|
||||
EVENT_T_MISSING_AURA = 27, // Param1 = SpellID, Param2 = Number of time stacked expected, Param3/4 Repeat Min/Max
|
||||
EVENT_T_TARGET_MISSING_AURA = 28, // Param1 = SpellID, Param2 = Number of time stacked expected, Param3/4 Repeat Min/Max
|
||||
EVENT_T_TIMER_GENERIC = 29, // InitialMin, InitialMax, RepeatMin, RepeatMax
|
||||
EVENT_T_RECEIVE_AI_EVENT = 30, // AIEventType, Sender-Entry, unused, unused
|
||||
|
||||
EVENT_T_END,
|
||||
};
|
||||
|
||||
enum EventAI_ActionType
|
||||
{
|
||||
ACTION_T_NONE = 0, // No action
|
||||
ACTION_T_TEXT = 1, // TextId1, optionally -TextId2, optionally -TextId3(if -TextId2 exist). If more than just -TextId1 is defined, randomize. Negative values.
|
||||
ACTION_T_SET_FACTION = 2, // FactionId (or 0 for default)
|
||||
ACTION_T_MORPH_TO_ENTRY_OR_MODEL = 3, // Creature_template entry(param1) OR ModelId (param2) (or 0 for both to demorph)
|
||||
ACTION_T_SOUND = 4, // SoundId
|
||||
ACTION_T_EMOTE = 5, // EmoteId
|
||||
ACTION_T_RANDOM_SAY = 6, // UNUSED
|
||||
ACTION_T_RANDOM_YELL = 7, // UNUSED
|
||||
ACTION_T_RANDOM_TEXTEMOTE = 8, // UNUSED
|
||||
ACTION_T_RANDOM_SOUND = 9, // SoundId1, SoundId2, SoundId3 (-1 in any field means no output if randomed that field)
|
||||
ACTION_T_RANDOM_EMOTE = 10, // EmoteId1, EmoteId2, EmoteId3 (-1 in any field means no output if randomed that field)
|
||||
ACTION_T_CAST = 11, // SpellId, Target, CastFlags
|
||||
ACTION_T_SUMMON = 12, // CreatureID, Target, Duration in ms
|
||||
ACTION_T_THREAT_SINGLE_PCT = 13, // Threat%, Target
|
||||
ACTION_T_THREAT_ALL_PCT = 14, // Threat%
|
||||
ACTION_T_QUEST_EVENT = 15, // QuestID, Target
|
||||
ACTION_T_CAST_EVENT = 16, // QuestID, SpellId, Target - must be removed as hack?
|
||||
ACTION_T_SET_UNIT_FIELD = 17, // Field_Number, Value, Target
|
||||
ACTION_T_SET_UNIT_FLAG = 18, // Flags (may be more than one field OR'd together), Target
|
||||
ACTION_T_REMOVE_UNIT_FLAG = 19, // Flags (may be more than one field OR'd together), Target
|
||||
ACTION_T_AUTO_ATTACK = 20, // AllowAttackState (0 = stop attack, anything else means continue attacking)
|
||||
ACTION_T_COMBAT_MOVEMENT = 21, // AllowCombatMovement (0 = stop combat based movement, anything else continue attacking)
|
||||
ACTION_T_SET_PHASE = 22, // Phase
|
||||
ACTION_T_INC_PHASE = 23, // Value (may be negative to decrement phase, should not be 0)
|
||||
ACTION_T_EVADE = 24, // No Params
|
||||
ACTION_T_FLEE_FOR_ASSIST = 25, // No Params
|
||||
ACTION_T_QUEST_EVENT_ALL = 26, // QuestID
|
||||
ACTION_T_CAST_EVENT_ALL = 27, // CreatureId, SpellId
|
||||
ACTION_T_REMOVEAURASFROMSPELL = 28, // Target, Spellid
|
||||
ACTION_T_RANGED_MOVEMENT = 29, // Distance, Angle
|
||||
ACTION_T_RANDOM_PHASE = 30, // PhaseId1, PhaseId2, PhaseId3
|
||||
ACTION_T_RANDOM_PHASE_RANGE = 31, // PhaseMin, PhaseMax
|
||||
ACTION_T_SUMMON_ID = 32, // CreatureId, Target, SpawnId
|
||||
ACTION_T_KILLED_MONSTER = 33, // CreatureId, Target
|
||||
ACTION_T_SET_INST_DATA = 34, // Field, Data
|
||||
ACTION_T_SET_INST_DATA64 = 35, // Field, Target
|
||||
ACTION_T_UPDATE_TEMPLATE = 36, // Entry, Team
|
||||
ACTION_T_DIE = 37, // No Params
|
||||
ACTION_T_ZONE_COMBAT_PULSE = 38, // No Params
|
||||
ACTION_T_CALL_FOR_HELP = 39, // Radius
|
||||
ACTION_T_SET_SHEATH = 40, // Sheath (0-passive,1-melee,2-ranged)
|
||||
ACTION_T_FORCE_DESPAWN = 41, // Delay (0-instant despawn)
|
||||
ACTION_T_SET_INVINCIBILITY_HP_LEVEL = 42, // MinHpValue, format(0-flat,1-percent from max health)
|
||||
ACTION_T_MOUNT_TO_ENTRY_OR_MODEL = 43, // Creature_template entry(param1) OR ModelId (param2) (or 0 for both to unmount)
|
||||
ACTION_T_CHANCED_TEXT = 44, // Chance to display the text, TextId1, optionally TextId2. If more than just -TextId1 is defined, randomize. Negative values.
|
||||
ACTION_T_THROW_AI_EVENT = 45, // EventType, Radius, unused
|
||||
ACTION_T_SET_THROW_MASK = 46, // EventTypeMask, unused, unused
|
||||
ACTION_T_SUMMON_UNIQUE = 47, // CreatureId, Target, SpawnId
|
||||
ACTION_T_EMOTE_TARGET = 48, // EmoteId, TargetGuid
|
||||
ACTION_T_END,
|
||||
};
|
||||
|
||||
enum Target
|
||||
{
|
||||
// Self (m_creature)
|
||||
TARGET_T_SELF = 0, // Self cast
|
||||
|
||||
// Hostile targets (if pet then returns pet owner)
|
||||
TARGET_T_HOSTILE = 1, // Our current target (ie: highest aggro)
|
||||
TARGET_T_HOSTILE_SECOND_AGGRO = 2, // Second highest aggro (generaly used for cleaves and some special attacks)
|
||||
TARGET_T_HOSTILE_LAST_AGGRO = 3, // Dead last on aggro (no idea what this could be used for)
|
||||
TARGET_T_HOSTILE_RANDOM = 4, // Just any random target on our threat list
|
||||
TARGET_T_HOSTILE_RANDOM_NOT_TOP = 5, // Any random target except top threat
|
||||
|
||||
// Invoker targets (if pet then returns pet owner)
|
||||
TARGET_T_ACTION_INVOKER = 6, // Unit who caused this Event to occur (only works for EVENT_T_AGGRO, EVENT_T_KILL, EVENT_T_DEATH, EVENT_T_SPELLHIT, EVENT_T_OOC_LOS, EVENT_T_FRIENDLY_HP, EVENT_T_FRIENDLY_IS_CC, EVENT_T_FRIENDLY_MISSING_BUFF, EVENT_T_RECEIVE_EMOTE, EVENT_T_RECEIVE_AI_EVENT)
|
||||
TARGET_T_ACTION_INVOKER_OWNER = 7, // Unit who is responsible for Event to occur (only works for EVENT_T_AGGRO, EVENT_T_KILL, EVENT_T_DEATH, EVENT_T_SPELLHIT, EVENT_T_OOC_LOS, EVENT_T_FRIENDLY_HP, EVENT_T_FRIENDLY_IS_CC, EVENT_T_FRIENDLY_MISSING_BUFF, EVENT_T_RECEIVE_EMOTE, EVENT_T_RECEIVE_AI_EVENT)
|
||||
TARGET_T_EVENT_SENDER = 10, // Unit who sent an AIEvent that was received with EVENT_T_RECEIVE_AI_EVENT
|
||||
|
||||
// Hostile targets (including pets)
|
||||
TARGET_T_HOSTILE_RANDOM_PLAYER = 8, // Just any random player on our threat list
|
||||
TARGET_T_HOSTILE_RANDOM_NOT_TOP_PLAYER = 9 // Any random player from threat list except top threat
|
||||
};
|
||||
|
||||
enum EventFlags
|
||||
{
|
||||
EFLAG_REPEATABLE = 0x01, // Event repeats
|
||||
EFLAG_DIFFICULTY_0 = 0x02, // Event only occurs in instance difficulty 0
|
||||
EFLAG_DIFFICULTY_1 = 0x04, // Event only occurs in instance difficulty 1
|
||||
EFLAG_DIFFICULTY_2 = 0x08, // Event only occurs in instance difficulty 2
|
||||
EFLAG_DIFFICULTY_3 = 0x10, // Event only occurs in instance difficulty 3
|
||||
EFLAG_RANDOM_ACTION = 0x20, // Event only execute one from existed actions instead each action.
|
||||
EFLAG_RESERVED_6 = 0x40,
|
||||
EFLAG_DEBUG_ONLY = 0x80, // Event only occurs in debug build
|
||||
// no free bits, uint8 field
|
||||
EFLAG_DIFFICULTY_ALL = (EFLAG_DIFFICULTY_0 | EFLAG_DIFFICULTY_1 | EFLAG_DIFFICULTY_2 | EFLAG_DIFFICULTY_3)
|
||||
};
|
||||
|
||||
enum SpawnedEventMode
|
||||
{
|
||||
SPAWNED_EVENT_ALWAY = 0,
|
||||
SPAWNED_EVENT_MAP = 1,
|
||||
SPAWNED_EVENT_ZONE = 2
|
||||
};
|
||||
|
||||
struct CreatureEventAI_Action
|
||||
{
|
||||
EventAI_ActionType type: 16;
|
||||
union
|
||||
{
|
||||
// ACTION_T_TEXT = 1
|
||||
struct
|
||||
{
|
||||
int32 TextId[3];
|
||||
} text;
|
||||
// ACTION_T_SET_FACTION = 2
|
||||
struct
|
||||
{
|
||||
uint32 factionId; // faction id or 0 to restore default faction
|
||||
uint32 factionFlags; // flags will restore default faction at evade and/or respawn
|
||||
} set_faction;
|
||||
// ACTION_T_MORPH_TO_ENTRY_OR_MODEL = 3
|
||||
struct
|
||||
{
|
||||
uint32 creatureId; // set one from fields (or 0 for both to demorph)
|
||||
uint32 modelId;
|
||||
} morph;
|
||||
// ACTION_T_SOUND = 4
|
||||
struct
|
||||
{
|
||||
uint32 soundId;
|
||||
} sound;
|
||||
// ACTION_T_EMOTE = 5
|
||||
struct
|
||||
{
|
||||
uint32 emoteId;
|
||||
} emote;
|
||||
// ACTION_T_RANDOM_SOUND = 9
|
||||
struct
|
||||
{
|
||||
int32 soundId1; // (-1 in any field means no output if randomed that field)
|
||||
int32 soundId2;
|
||||
int32 soundId3;
|
||||
} random_sound;
|
||||
// ACTION_T_RANDOM_EMOTE = 10
|
||||
struct
|
||||
{
|
||||
int32 emoteId1; // (-1 in any field means no output if randomed that field)
|
||||
int32 emoteId2;
|
||||
int32 emoteId3;
|
||||
} random_emote;
|
||||
// ACTION_T_CAST = 11
|
||||
struct
|
||||
{
|
||||
uint32 spellId;
|
||||
uint32 target;
|
||||
uint32 castFlags;
|
||||
} cast;
|
||||
// ACTION_T_SUMMON = 12
|
||||
struct
|
||||
{
|
||||
uint32 creatureId;
|
||||
uint32 target;
|
||||
uint32 duration;
|
||||
} summon;
|
||||
// ACTION_T_THREAT_SINGLE_PCT = 13
|
||||
struct
|
||||
{
|
||||
int32 percent;
|
||||
uint32 target;
|
||||
} threat_single_pct;
|
||||
// ACTION_T_THREAT_ALL_PCT = 14
|
||||
struct
|
||||
{
|
||||
int32 percent;
|
||||
} threat_all_pct;
|
||||
// ACTION_T_QUEST_EVENT = 15
|
||||
struct
|
||||
{
|
||||
uint32 questId;
|
||||
uint32 target;
|
||||
} quest_event;
|
||||
// ACTION_T_CAST_EVENT = 16
|
||||
struct
|
||||
{
|
||||
uint32 creatureId;
|
||||
uint32 spellId;
|
||||
uint32 target;
|
||||
} cast_event;
|
||||
// ACTION_T_SET_UNIT_FIELD = 17
|
||||
struct
|
||||
{
|
||||
uint32 field;
|
||||
uint32 value;
|
||||
uint32 target;
|
||||
} set_unit_field;
|
||||
// ACTION_T_SET_UNIT_FLAG = 18, // value provided mask bits that will be set
|
||||
// ACTION_T_REMOVE_UNIT_FLAG = 19, // value provided mask bits that will be clear
|
||||
struct
|
||||
{
|
||||
uint32 value;
|
||||
uint32 target;
|
||||
} unit_flag;
|
||||
// ACTION_T_AUTO_ATTACK = 20
|
||||
struct
|
||||
{
|
||||
uint32 state; // 0 = stop attack, anything else means continue attacking
|
||||
} auto_attack;
|
||||
// ACTION_T_COMBAT_MOVEMENT = 21
|
||||
struct
|
||||
{
|
||||
uint32 state; // 0 = stop combat based movement, anything else continue attacking
|
||||
uint32 melee; // if set: at stop send melee combat stop if in combat, use for terminate melee fighting state for switch to ranged
|
||||
} combat_movement;
|
||||
// ACTION_T_SET_PHASE = 22
|
||||
struct
|
||||
{
|
||||
uint32 phase;
|
||||
} set_phase;
|
||||
// ACTION_T_INC_PHASE = 23
|
||||
struct
|
||||
{
|
||||
int32 step;
|
||||
} set_inc_phase;
|
||||
// ACTION_T_QUEST_EVENT_ALL = 26
|
||||
struct
|
||||
{
|
||||
uint32 questId;
|
||||
} quest_event_all;
|
||||
// ACTION_T_CAST_EVENT_ALL = 27
|
||||
struct
|
||||
{
|
||||
uint32 creatureId;
|
||||
uint32 spellId;
|
||||
} cast_event_all;
|
||||
// ACTION_T_REMOVEAURASFROMSPELL = 28
|
||||
struct
|
||||
{
|
||||
uint32 target;
|
||||
uint32 spellId;
|
||||
} remove_aura;
|
||||
// ACTION_T_RANGED_MOVEMENT = 29
|
||||
struct
|
||||
{
|
||||
uint32 distance;
|
||||
int32 angle;
|
||||
} ranged_movement;
|
||||
// ACTION_T_RANDOM_PHASE = 30
|
||||
struct
|
||||
{
|
||||
uint32 phase1;
|
||||
uint32 phase2;
|
||||
uint32 phase3;
|
||||
} random_phase;
|
||||
// ACTION_T_RANDOM_PHASE_RANGE = 31
|
||||
struct
|
||||
{
|
||||
uint32 phaseMin;
|
||||
uint32 phaseMax;
|
||||
} random_phase_range;
|
||||
// ACTION_T_SUMMON_ID = 32
|
||||
struct
|
||||
{
|
||||
uint32 creatureId;
|
||||
uint32 target;
|
||||
uint32 spawnId;
|
||||
} summon_id;
|
||||
// ACTION_T_KILLED_MONSTER = 33
|
||||
struct
|
||||
{
|
||||
uint32 creatureId;
|
||||
uint32 target;
|
||||
} killed_monster;
|
||||
// ACTION_T_SET_INST_DATA = 34
|
||||
struct
|
||||
{
|
||||
uint32 field;
|
||||
uint32 value;
|
||||
} set_inst_data;
|
||||
// ACTION_T_SET_INST_DATA64 = 35
|
||||
struct
|
||||
{
|
||||
uint32 field;
|
||||
uint32 target;
|
||||
} set_inst_data64;
|
||||
// ACTION_T_UPDATE_TEMPLATE = 36
|
||||
struct
|
||||
{
|
||||
uint32 creatureId;
|
||||
uint32 team;
|
||||
} update_template;
|
||||
// ACTION_T_CALL_FOR_HELP = 39
|
||||
struct
|
||||
{
|
||||
uint32 radius;
|
||||
} call_for_help;
|
||||
// ACTION_T_SET_SHEATH = 40
|
||||
struct
|
||||
{
|
||||
uint32 sheath;
|
||||
} set_sheath;
|
||||
// ACTION_T_FORCE_DESPAWN = 41
|
||||
struct
|
||||
{
|
||||
uint32 msDelay;
|
||||
} forced_despawn;
|
||||
// ACTION_T_SET_INVINCIBILITY_HP_LEVEL = 42
|
||||
struct
|
||||
{
|
||||
uint32 hp_level;
|
||||
uint32 is_percent;
|
||||
} invincibility_hp_level;
|
||||
// ACTION_T_MOUNT_TO_ENTRY_OR_MODEL = 43
|
||||
struct
|
||||
{
|
||||
uint32 creatureId; // set one from fields (or 0 for both to dismount)
|
||||
uint32 modelId;
|
||||
} mount;
|
||||
|
||||
// ACTION_T_CHANCED_TEXT = 44
|
||||
struct
|
||||
{
|
||||
uint32 chance;
|
||||
int32 TextId[2];
|
||||
} chanced_text;
|
||||
// ACTION_T_THROW_AI_EVENT = 45
|
||||
struct
|
||||
{
|
||||
uint32 eventType;
|
||||
uint32 radius;
|
||||
uint32 unused;
|
||||
} throwEvent;
|
||||
// ACTION_T_SET_THROW_MASK = 46
|
||||
struct
|
||||
{
|
||||
uint32 eventTypeMask;
|
||||
uint32 unused1;
|
||||
uint32 unused2;
|
||||
} setThrowMask;
|
||||
// ACTION_T_SUMMON_UNIQUE = 47
|
||||
struct
|
||||
{
|
||||
uint32 creatureId;
|
||||
uint32 target;
|
||||
uint32 spawnId;
|
||||
} summon_unique;
|
||||
// ACTION_T_EMOTE_TARGET = 48
|
||||
struct
|
||||
{
|
||||
uint32 emoteId;
|
||||
uint32 targetGuid;
|
||||
} emoteTarget;
|
||||
// RAW
|
||||
struct
|
||||
{
|
||||
uint32 param1;
|
||||
uint32 param2;
|
||||
uint32 param3;
|
||||
} raw;
|
||||
};
|
||||
};
|
||||
|
||||
#define AIEVENT_DEFAULT_THROW_RADIUS 30.0f
|
||||
|
||||
struct CreatureEventAI_Event
|
||||
{
|
||||
uint32 event_id;
|
||||
|
||||
uint32 creature_id;
|
||||
|
||||
uint32 event_inverse_phase_mask;
|
||||
|
||||
EventAI_Type event_type : 16;
|
||||
uint8 event_chance : 8;
|
||||
uint8 event_flags : 8;
|
||||
|
||||
union
|
||||
{
|
||||
// EVENT_T_TIMER_IN_COMBAT = 0
|
||||
// EVENT_T_TIMER_OOC = 1
|
||||
// EVENT_T_TIMER_GENERIC = 29
|
||||
struct
|
||||
{
|
||||
uint32 initialMin;
|
||||
uint32 initialMax;
|
||||
uint32 repeatMin;
|
||||
uint32 repeatMax;
|
||||
} timer;
|
||||
// EVENT_T_HP = 2
|
||||
// EVENT_T_MANA = 3
|
||||
// EVENT_T_TARGET_HP = 12
|
||||
// EVENT_T_TARGET_MANA = 18
|
||||
struct
|
||||
{
|
||||
uint32 percentMax;
|
||||
uint32 percentMin;
|
||||
uint32 repeatMin;
|
||||
uint32 repeatMax;
|
||||
} percent_range;
|
||||
// EVENT_T_KILL = 5
|
||||
struct
|
||||
{
|
||||
uint32 repeatMin;
|
||||
uint32 repeatMax;
|
||||
} kill;
|
||||
// EVENT_T_SPELLHIT = 8
|
||||
struct
|
||||
{
|
||||
uint32 spellId;
|
||||
uint32 schoolMask; // -1 (==0xffffffff) is ok value for full mask, or must be more limited mask like (0 < 1) = 1 for normal/physical school
|
||||
uint32 repeatMin;
|
||||
uint32 repeatMax;
|
||||
} spell_hit;
|
||||
// EVENT_T_RANGE = 9
|
||||
struct
|
||||
{
|
||||
uint32 minDist;
|
||||
uint32 maxDist;
|
||||
uint32 repeatMin;
|
||||
uint32 repeatMax;
|
||||
} range;
|
||||
// EVENT_T_OOC_LOS = 10
|
||||
struct
|
||||
{
|
||||
uint32 noHostile;
|
||||
uint32 maxRange;
|
||||
uint32 repeatMin;
|
||||
uint32 repeatMax;
|
||||
} ooc_los;
|
||||
// EVENT_T_SPAWNED = 11
|
||||
struct
|
||||
{
|
||||
uint32 condition;
|
||||
uint32 conditionValue1;
|
||||
} spawned;
|
||||
// EVENT_T_TARGET_CASTING = 13
|
||||
struct
|
||||
{
|
||||
uint32 repeatMin;
|
||||
uint32 repeatMax;
|
||||
} target_casting;
|
||||
// EVENT_T_FRIENDLY_HP = 14
|
||||
struct
|
||||
{
|
||||
uint32 hpDeficit;
|
||||
uint32 radius;
|
||||
uint32 repeatMin;
|
||||
uint32 repeatMax;
|
||||
} friendly_hp;
|
||||
// EVENT_T_FRIENDLY_IS_CC = 15
|
||||
struct
|
||||
{
|
||||
uint32 dispelType; // unused ?
|
||||
uint32 radius;
|
||||
uint32 repeatMin;
|
||||
uint32 repeatMax;
|
||||
} friendly_is_cc;
|
||||
// EVENT_T_FRIENDLY_MISSING_BUFF = 16
|
||||
struct
|
||||
{
|
||||
uint32 spellId;
|
||||
uint32 radius;
|
||||
uint32 repeatMin;
|
||||
uint32 repeatMax;
|
||||
} friendly_buff;
|
||||
// EVENT_T_SUMMONED_UNIT = 17
|
||||
// EVENT_T_SUMMONED_JUST_DIED = 25
|
||||
// EVENT_T_SUMMONED_JUST_DESPAWN = 26
|
||||
struct
|
||||
{
|
||||
uint32 creatureId;
|
||||
uint32 repeatMin;
|
||||
uint32 repeatMax;
|
||||
} summoned;
|
||||
// EVENT_T_QUEST_ACCEPT = 19
|
||||
// EVENT_T_QUEST_COMPLETE = 20
|
||||
struct
|
||||
{
|
||||
uint32 questId;
|
||||
} quest;
|
||||
// EVENT_T_RECEIVE_EMOTE = 22
|
||||
struct
|
||||
{
|
||||
uint32 emoteId;
|
||||
uint32 condition;
|
||||
uint32 conditionValue1;
|
||||
uint32 conditionValue2;
|
||||
} receive_emote;
|
||||
// EVENT_T_AURA = 23
|
||||
// EVENT_T_TARGET_AURA = 24
|
||||
// EVENT_T_MISSING_AURA = 27
|
||||
// EVENT_T_TARGET_MISSING_AURA = 28
|
||||
struct
|
||||
{
|
||||
uint32 spellId;
|
||||
uint32 amount;
|
||||
uint32 repeatMin;
|
||||
uint32 repeatMax;
|
||||
} buffed;
|
||||
// EVENT_T_RECEIVE_AI_EVENT = 30
|
||||
struct
|
||||
{
|
||||
uint32 eventType; // See CreatureAI.h enum AIEventType - Receive only events of this type
|
||||
uint32 senderEntry; // Optional npc from only whom this event can be received
|
||||
uint32 unused1;
|
||||
uint32 unused2;
|
||||
} receiveAIEvent;
|
||||
// RAW
|
||||
struct
|
||||
{
|
||||
uint32 param1;
|
||||
uint32 param2;
|
||||
uint32 param3;
|
||||
uint32 param4;
|
||||
} raw;
|
||||
};
|
||||
|
||||
CreatureEventAI_Action action[MAX_ACTIONS];
|
||||
};
|
||||
// Event_Map
|
||||
typedef std::vector<CreatureEventAI_Event> CreatureEventAI_Event_Vec;
|
||||
typedef UNORDERED_MAP<uint32, CreatureEventAI_Event_Vec > CreatureEventAI_Event_Map;
|
||||
|
||||
struct CreatureEventAI_Summon
|
||||
{
|
||||
uint32 id;
|
||||
|
||||
float position_x;
|
||||
float position_y;
|
||||
float position_z;
|
||||
float orientation;
|
||||
uint32 SpawnTimeSecs;
|
||||
};
|
||||
|
||||
// EventSummon_Map
|
||||
typedef UNORDERED_MAP<uint32, CreatureEventAI_Summon> CreatureEventAI_Summon_Map;
|
||||
|
||||
struct CreatureEventAIHolder
|
||||
{
|
||||
CreatureEventAIHolder(CreatureEventAI_Event p) : Event(p), Time(0), Enabled(true) {}
|
||||
|
||||
CreatureEventAI_Event Event;
|
||||
uint32 Time;
|
||||
bool Enabled;
|
||||
|
||||
// helper
|
||||
bool UpdateRepeatTimer(Creature* creature, uint32 repeatMin, uint32 repeatMax);
|
||||
};
|
||||
|
||||
class MANGOS_DLL_SPEC CreatureEventAI : public CreatureAI
|
||||
{
|
||||
public:
|
||||
explicit CreatureEventAI(Creature* c);
|
||||
~CreatureEventAI()
|
||||
{
|
||||
m_CreatureEventAIList.clear();
|
||||
}
|
||||
|
||||
void GetAIInformation(ChatHandler& reader) override;
|
||||
|
||||
void JustRespawned() override;
|
||||
void Reset();
|
||||
void JustReachedHome() override;
|
||||
void EnterCombat(Unit* enemy) override;
|
||||
void EnterEvadeMode() override;
|
||||
void JustDied(Unit* killer) override;
|
||||
void KilledUnit(Unit* victim) override;
|
||||
void JustSummoned(Creature* pUnit) override;
|
||||
void AttackStart(Unit* who) override;
|
||||
void MoveInLineOfSight(Unit* who) override;
|
||||
void SpellHit(Unit* pUnit, const SpellEntry* pSpell) override;
|
||||
void DamageTaken(Unit* done_by, uint32& damage) override;
|
||||
void HealedBy(Unit* healer, uint32& healedAmount) override;
|
||||
void UpdateAI(const uint32 diff) override;
|
||||
bool IsVisible(Unit*) const override;
|
||||
void ReceiveEmote(Player* pPlayer, uint32 text_emote) override;
|
||||
void SummonedCreatureJustDied(Creature* unit) override;
|
||||
void SummonedCreatureDespawn(Creature* unit) override;
|
||||
void ReceiveAIEvent(AIEventType eventType, Creature* pSender, Unit* pInvoker, uint32 miscValue) override;
|
||||
|
||||
static int Permissible(const Creature*);
|
||||
|
||||
bool ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pActionInvoker = NULL, Creature* pAIEventSender = NULL);
|
||||
void ProcessAction(CreatureEventAI_Action const& action, uint32 rnd, uint32 EventId, Unit* pActionInvoker, Creature* pAIEventSender);
|
||||
inline uint32 GetRandActionParam(uint32 rnd, uint32 param1, uint32 param2, uint32 param3);
|
||||
inline int32 GetRandActionParam(uint32 rnd, int32 param1, int32 param2, int32 param3);
|
||||
/// If the bool& param is true, an error should be reported
|
||||
inline Unit* GetTargetByType(uint32 Target, Unit* pActionInvoker, Creature* pAIEventSender, bool& isError, uint32 forSpellId = 0, uint32 selectFlags = 0);
|
||||
|
||||
bool SpawnedEventConditionsCheck(CreatureEventAI_Event const& event);
|
||||
|
||||
Unit* DoSelectLowestHpFriendly(float range, uint32 MinHPDiff);
|
||||
void DoFindFriendlyMissingBuff(std::list<Creature*>& _list, float range, uint32 spellid);
|
||||
void DoFindFriendlyCC(std::list<Creature*>& _list, float range);
|
||||
|
||||
protected:
|
||||
uint32 m_EventUpdateTime; // Time between event updates
|
||||
uint32 m_EventDiff; // Time between the last event call
|
||||
bool m_bEmptyList;
|
||||
|
||||
// Variables used by Events themselves
|
||||
typedef std::vector<CreatureEventAIHolder> CreatureEventAIList;
|
||||
CreatureEventAIList m_CreatureEventAIList; // Holder for events (stores enabled, time, and eventid)
|
||||
|
||||
uint8 m_Phase; // Current phase, max 32 phases
|
||||
bool m_MeleeEnabled; // If we allow melee auto attack
|
||||
uint32 m_InvinceabilityHpLevel; // Minimal health level allowed at damage apply
|
||||
|
||||
uint32 m_throwAIEventMask; // Automatically throw AIEvents that are encoded into this mask
|
||||
// Note that Step 100 means that AI_EVENT_GOT_FULL_HEALTH was sent
|
||||
// Steps 0..2 correspond to AI_EVENT_LOST_SOME_HEALTH(90%), AI_EVENT_LOST_HEALTH(50%), AI_EVENT_CRITICAL_HEALTH(10%)
|
||||
uint32 m_throwAIEventStep; // Used for damage taken/ received heal
|
||||
};
|
||||
|
||||
#endif
|
||||
918
src/game/Object/CreatureEventAIMgr.cpp
Normal file
918
src/game/Object/CreatureEventAIMgr.cpp
Normal file
|
|
@ -0,0 +1,918 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include "Common.h"
|
||||
#include "Database/DatabaseEnv.h"
|
||||
#include "SQLStorages.h"
|
||||
#include "CreatureEventAI.h"
|
||||
#include "CreatureEventAIMgr.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "ProgressBar.h"
|
||||
#include "Policies/Singleton.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "GridDefines.h"
|
||||
#include "SpellMgr.h"
|
||||
#include "World.h"
|
||||
|
||||
INSTANTIATE_SINGLETON_1(CreatureEventAIMgr);
|
||||
|
||||
// -------------------
|
||||
void CreatureEventAIMgr::LoadCreatureEventAI_Texts(bool check_entry_use)
|
||||
{
|
||||
// Load EventAI Text
|
||||
sObjectMgr.LoadMangosStrings(WorldDatabase, "creature_ai_texts", MIN_CREATURE_AI_TEXT_STRING_ID, MAX_CREATURE_AI_TEXT_STRING_ID, true);
|
||||
if (check_entry_use)
|
||||
CheckUnusedAITexts();
|
||||
}
|
||||
|
||||
void CreatureEventAIMgr::CheckUnusedAITexts()
|
||||
{
|
||||
if (m_usedTextsAmount == sObjectMgr.GetLoadedStringsCount(MIN_CREATURE_AI_TEXT_STRING_ID))
|
||||
return;
|
||||
|
||||
sLog.outString("Checking EventAI for unused texts, this might take a while");
|
||||
|
||||
std::set<int32> idx_set;
|
||||
// check not used strings this is negative range
|
||||
for (int32 i = MAX_CREATURE_AI_TEXT_STRING_ID + 1; i <= MIN_CREATURE_AI_TEXT_STRING_ID; ++i)
|
||||
if (sObjectMgr.GetMangosStringLocale(i))
|
||||
idx_set.insert(i);
|
||||
|
||||
for (CreatureEventAI_Event_Map::const_iterator itr = m_CreatureEventAI_Event_Map.begin(); itr != m_CreatureEventAI_Event_Map.end(); ++itr)
|
||||
{
|
||||
for (size_t i = 0; i < itr->second.size(); ++i)
|
||||
{
|
||||
CreatureEventAI_Event const& event = itr->second[i];
|
||||
|
||||
for (int j = 0; j < MAX_ACTIONS; ++j)
|
||||
{
|
||||
CreatureEventAI_Action const& action = event.action[j];
|
||||
switch (action.type)
|
||||
{
|
||||
case ACTION_T_TEXT:
|
||||
case ACTION_T_CHANCED_TEXT:
|
||||
{
|
||||
// ACTION_T_CHANCED_TEXT contains a chance value in first param
|
||||
int k = action.type == ACTION_T_TEXT ? 0 : 1;
|
||||
for (; k < 3; ++k)
|
||||
if (action.text.TextId[k])
|
||||
idx_set.erase(action.text.TextId[k]);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (std::set<int32>::const_iterator itr = idx_set.begin(); itr != idx_set.end(); ++itr)
|
||||
sLog.outErrorEventAI("Entry %i in table `creature_ai_texts` but not used in EventAI scripts.", *itr);
|
||||
}
|
||||
|
||||
/// Helper function to check if a target-suite is suitable for the event-type
|
||||
bool IsValidTargetType(EventAI_Type eventType, EventAI_ActionType actionType, uint32 targetType, uint32 eventId, uint8 action)
|
||||
{
|
||||
switch (targetType)
|
||||
{
|
||||
case TARGET_T_SELF:
|
||||
if (actionType == ACTION_T_QUEST_EVENT || actionType == ACTION_T_CAST_EVENT || actionType == ACTION_T_QUEST_EVENT_ALL || actionType == ACTION_T_KILLED_MONSTER)
|
||||
{
|
||||
sLog.outErrorEventAI("Event %u Action%u uses incorrect Target type %u for event-type %u (must target player)", eventId, action, targetType, eventType);
|
||||
return false;
|
||||
}
|
||||
return true; // Can always be used
|
||||
case TARGET_T_HOSTILE_RANDOM:
|
||||
case TARGET_T_HOSTILE_RANDOM_NOT_TOP:
|
||||
if (actionType == ACTION_T_QUEST_EVENT || actionType == ACTION_T_CAST_EVENT || actionType == ACTION_T_QUEST_EVENT_ALL || actionType == ACTION_T_KILLED_MONSTER)
|
||||
sLog.outErrorEventAI("Event %u Action%u uses LIKELY bad Target type %u for event-type %u (must target player)", eventId, action, targetType, eventType);
|
||||
// no break, check if valid at all
|
||||
case TARGET_T_HOSTILE:
|
||||
case TARGET_T_HOSTILE_SECOND_AGGRO:
|
||||
case TARGET_T_HOSTILE_LAST_AGGRO:
|
||||
case TARGET_T_HOSTILE_RANDOM_PLAYER:
|
||||
case TARGET_T_HOSTILE_RANDOM_NOT_TOP_PLAYER:
|
||||
switch (eventType)
|
||||
{
|
||||
case EVENT_T_TIMER_OOC:
|
||||
case EVENT_T_OOC_LOS:
|
||||
case EVENT_T_REACHED_HOME:
|
||||
sLog.outErrorEventAI("Event %u Action%u uses incorrect Target type %u for event-type %u (cannot be used OOC)", eventId, action, targetType, eventType);
|
||||
return false;
|
||||
case EVENT_T_TIMER_GENERIC:
|
||||
sLog.outErrorEventAI("Event %u Action%u uses LIKELY incorrect Target type %u for event-type %u (cannot be used OOC)", eventId, action, targetType, eventType);
|
||||
return true; // Does not need to be an error
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
case TARGET_T_ACTION_INVOKER: // Unit who caused this Event to occur (only works for EVENT_T_AGGRO, EVENT_T_KILL, EVENT_T_DEATH, EVENT_T_SPELLHIT, EVENT_T_OOC_LOS, EVENT_T_FRIENDLY_HP, EVENT_T_FRIENDLY_IS_CC, EVENT_T_FRIENDLY_MISSING_BUFF, EVENT_T_RECEIVE_EMOTE, EVENT_T_RECEIVE_AI_EVENT)
|
||||
case TARGET_T_ACTION_INVOKER_OWNER: // Unit who is responsible for Event to occur (only works for EVENT_T_AGGRO, EVENT_T_KILL, EVENT_T_DEATH, EVENT_T_SPELLHIT, EVENT_T_OOC_LOS, EVENT_T_FRIENDLY_HP, EVENT_T_FRIENDLY_IS_CC, EVENT_T_FRIENDLY_MISSING_BUFF, EVENT_T_RECEIVE_EMOTE, EVENT_T_RECEIVE_AI_EVENT)
|
||||
switch (eventType)
|
||||
{
|
||||
case EVENT_T_AGGRO:
|
||||
case EVENT_T_KILL:
|
||||
case EVENT_T_DEATH:
|
||||
case EVENT_T_SPELLHIT:
|
||||
case EVENT_T_OOC_LOS:
|
||||
case EVENT_T_FRIENDLY_HP:
|
||||
case EVENT_T_FRIENDLY_IS_CC:
|
||||
case EVENT_T_FRIENDLY_MISSING_BUFF:
|
||||
case EVENT_T_RECEIVE_EMOTE:
|
||||
case EVENT_T_RECEIVE_AI_EVENT:
|
||||
return true;
|
||||
default:
|
||||
sLog.outErrorEventAI("Event %u Action%u uses incorrect Target type %u for event-type %u", eventId, action, targetType, eventType);
|
||||
return false;
|
||||
}
|
||||
case TARGET_T_EVENT_SENDER: // Unit who sent an AIEvent that was received with EVENT_T_RECEIVE_AI_EVENT
|
||||
if (eventType != EVENT_T_RECEIVE_AI_EVENT)
|
||||
{
|
||||
sLog.outErrorEventAI("Event %u Action%u uses incorrect Target type %u for event-type %u", eventId, action, targetType, eventType);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
sLog.outErrorEventAI("Event %u Action%u uses incorrect Target type", eventId, action);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------
|
||||
void CreatureEventAIMgr::LoadCreatureEventAI_Summons(bool check_entry_use)
|
||||
{
|
||||
// Drop Existing EventSummon Map
|
||||
m_CreatureEventAI_Summon_Map.clear();
|
||||
|
||||
// Gather additional data for EventAI
|
||||
QueryResult* result = WorldDatabase.Query("SELECT id, position_x, position_y, position_z, orientation, spawntimesecs FROM creature_ai_summons");
|
||||
if (result)
|
||||
{
|
||||
BarGoLink bar(result->GetRowCount());
|
||||
uint32 Count = 0;
|
||||
|
||||
do
|
||||
{
|
||||
bar.step();
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
CreatureEventAI_Summon temp;
|
||||
|
||||
temp.id = fields[0].GetUInt32();
|
||||
temp.position_x = fields[1].GetFloat();
|
||||
temp.position_y = fields[2].GetFloat();
|
||||
temp.position_z = fields[3].GetFloat();
|
||||
temp.orientation = fields[4].GetFloat();
|
||||
temp.SpawnTimeSecs = fields[5].GetUInt32();
|
||||
|
||||
if (!MaNGOS::IsValidMapCoord(temp.position_x, temp.position_y, temp.position_z, temp.orientation))
|
||||
{
|
||||
sLog.outErrorEventAI("Summon id %u have wrong coordinates (%f, %f, %f, %f), skipping.", temp.id, temp.position_x, temp.position_y, temp.position_z, temp.orientation);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add to map
|
||||
m_CreatureEventAI_Summon_Map[temp.id] = temp;
|
||||
++Count;
|
||||
}
|
||||
while (result->NextRow());
|
||||
|
||||
delete result;
|
||||
|
||||
if (check_entry_use)
|
||||
CheckUnusedAISummons();
|
||||
|
||||
sLog.outString();
|
||||
sLog.outString(">> Loaded %u CreatureEventAI summon definitions", Count);
|
||||
}
|
||||
else
|
||||
{
|
||||
BarGoLink bar(1);
|
||||
bar.step();
|
||||
sLog.outString();
|
||||
sLog.outString(">> Loaded 0 CreatureEventAI Summon definitions. DB table `creature_ai_summons` is empty.");
|
||||
}
|
||||
}
|
||||
|
||||
void CreatureEventAIMgr::CheckUnusedAISummons()
|
||||
{
|
||||
std::set<int32> idx_set;
|
||||
// check not used strings this is negative range
|
||||
for (CreatureEventAI_Summon_Map::const_iterator itr = m_CreatureEventAI_Summon_Map.begin(); itr != m_CreatureEventAI_Summon_Map.end(); ++itr)
|
||||
idx_set.insert(itr->first);
|
||||
|
||||
for (CreatureEventAI_Event_Map::const_iterator itr = m_CreatureEventAI_Event_Map.begin(); itr != m_CreatureEventAI_Event_Map.end(); ++itr)
|
||||
{
|
||||
for (size_t i = 0; i < itr->second.size(); ++i)
|
||||
{
|
||||
CreatureEventAI_Event const& event = itr->second[i];
|
||||
|
||||
for (int j = 0; j < MAX_ACTIONS; ++j)
|
||||
{
|
||||
CreatureEventAI_Action const& action = event.action[j];
|
||||
switch (action.type)
|
||||
{
|
||||
case ACTION_T_SUMMON_ID:
|
||||
{
|
||||
if (action.summon_id.spawnId)
|
||||
idx_set.erase(action.summon_id.spawnId);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (std::set<int32>::const_iterator itr = idx_set.begin(); itr != idx_set.end(); ++itr)
|
||||
sLog.outErrorEventAI("Entry %i in table `creature_ai_summons` but not used in EventAI scripts.", *itr);
|
||||
}
|
||||
|
||||
// -------------------
|
||||
void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
|
||||
{
|
||||
// Drop Existing EventAI List
|
||||
m_CreatureEventAI_Event_Map.clear();
|
||||
std::set<int32> usedTextIds;
|
||||
|
||||
// Gather event data
|
||||
QueryResult* result = WorldDatabase.Query("SELECT id, creature_id, event_type, event_inverse_phase_mask, event_chance, event_flags, "
|
||||
"event_param1, event_param2, event_param3, event_param4, "
|
||||
"action1_type, action1_param1, action1_param2, action1_param3, "
|
||||
"action2_type, action2_param1, action2_param2, action2_param3, "
|
||||
"action3_type, action3_param1, action3_param2, action3_param3 "
|
||||
"FROM creature_ai_scripts");
|
||||
if (result)
|
||||
{
|
||||
BarGoLink bar(result->GetRowCount());
|
||||
uint32 Count = 0;
|
||||
|
||||
do
|
||||
{
|
||||
bar.step();
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
CreatureEventAI_Event temp;
|
||||
temp.event_id = EventAI_Type(fields[0].GetUInt32());
|
||||
uint32 i = temp.event_id;
|
||||
|
||||
temp.creature_id = fields[1].GetUInt32();
|
||||
uint32 creature_id = temp.creature_id;
|
||||
|
||||
uint32 e_type = fields[2].GetUInt32();
|
||||
// Report any errors in event
|
||||
if (e_type >= EVENT_T_END)
|
||||
{
|
||||
sLog.outErrorEventAI("Event %u have wrong type (%u), skipping.", i, e_type);
|
||||
continue;
|
||||
}
|
||||
temp.event_type = EventAI_Type(e_type);
|
||||
|
||||
temp.event_inverse_phase_mask = fields[3].GetUInt32();
|
||||
temp.event_chance = fields[4].GetUInt8();
|
||||
temp.event_flags = fields[5].GetUInt8();
|
||||
temp.raw.param1 = fields[6].GetUInt32();
|
||||
temp.raw.param2 = fields[7].GetUInt32();
|
||||
temp.raw.param3 = fields[8].GetUInt32();
|
||||
temp.raw.param4 = fields[9].GetUInt32();
|
||||
|
||||
// Creature does not exist in database
|
||||
if (!sCreatureStorage.LookupEntry<CreatureInfo>(temp.creature_id))
|
||||
{
|
||||
sLog.outErrorEventAI("Event %u has script for non-existing creature entry (%u), skipping.", i, temp.creature_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
// No chance of this event occuring
|
||||
if (temp.event_chance == 0)
|
||||
sLog.outErrorEventAI("Event %u has 0 percent chance. Event will never trigger!", i);
|
||||
// Chance above 100, force it to be 100
|
||||
else if (temp.event_chance > 100)
|
||||
{
|
||||
sLog.outErrorEventAI("Creature %u are using event %u with more than 100 percent chance. Adjusting to 100 percent.", temp.creature_id, i);
|
||||
temp.event_chance = 100;
|
||||
}
|
||||
|
||||
// Individual event checks
|
||||
switch (temp.event_type)
|
||||
{
|
||||
case EVENT_T_TIMER_IN_COMBAT:
|
||||
case EVENT_T_TIMER_OOC:
|
||||
case EVENT_T_TIMER_GENERIC:
|
||||
if (temp.timer.initialMax < temp.timer.initialMin)
|
||||
sLog.outErrorEventAI("Creature %u are using timed event(%u) with param2 < param1 (InitialMax < InitialMin). Event will never repeat.", temp.creature_id, i);
|
||||
if (temp.timer.repeatMax < temp.timer.repeatMin)
|
||||
sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
break;
|
||||
case EVENT_T_HP:
|
||||
case EVENT_T_MANA:
|
||||
case EVENT_T_TARGET_HP:
|
||||
case EVENT_T_TARGET_MANA:
|
||||
if (temp.percent_range.percentMax > 100)
|
||||
sLog.outErrorEventAI("Creature %u are using percentage event(%u) with param2 (MinPercent) > 100. Event will never trigger! ", temp.creature_id, i);
|
||||
|
||||
if (temp.percent_range.percentMax <= temp.percent_range.percentMin)
|
||||
sLog.outErrorEventAI("Creature %u are using percentage event(%u) with param1 <= param2 (MaxPercent <= MinPercent). Event will never trigger! ", temp.creature_id, i);
|
||||
|
||||
if (temp.event_flags & EFLAG_REPEATABLE && !temp.percent_range.repeatMin && !temp.percent_range.repeatMax)
|
||||
{
|
||||
sLog.outErrorEventAI("Creature %u has param3 and param4=0 (RepeatMin/RepeatMax) but cannot be repeatable without timers. Removing EFLAG_REPEATABLE for event %u.", temp.creature_id, i);
|
||||
temp.event_flags &= ~EFLAG_REPEATABLE;
|
||||
}
|
||||
break;
|
||||
case EVENT_T_SPELLHIT:
|
||||
if (temp.spell_hit.spellId)
|
||||
{
|
||||
SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.spell_hit.spellId);
|
||||
if (!pSpell)
|
||||
{
|
||||
sLog.outErrorEventAI("Creature %u has nonexistent SpellID(%u) defined in event %u.", temp.creature_id, temp.spell_hit.spellId, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((temp.spell_hit.schoolMask & pSpell->SchoolMask) != pSpell->SchoolMask)
|
||||
sLog.outErrorEventAI("Creature %u has param1(spellId %u) but param2 is not -1 and not equal to spell's school mask. Event %u can never trigger.", temp.creature_id, temp.spell_hit.schoolMask, i);
|
||||
}
|
||||
|
||||
if (!temp.spell_hit.schoolMask)
|
||||
sLog.outErrorEventAI("Creature %u is using invalid SpellSchoolMask(%u) defined in event %u.", temp.creature_id, temp.spell_hit.schoolMask, i);
|
||||
|
||||
if (temp.spell_hit.repeatMax < temp.spell_hit.repeatMin)
|
||||
sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
break;
|
||||
case EVENT_T_RANGE:
|
||||
if (temp.range.maxDist < temp.range.minDist)
|
||||
sLog.outErrorEventAI("Creature %u are using event(%u) with param2 < param1 (MaxDist < MinDist). Event will never repeat.", temp.creature_id, i);
|
||||
if (temp.range.repeatMax < temp.range.repeatMin)
|
||||
sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
break;
|
||||
case EVENT_T_OOC_LOS:
|
||||
if (temp.ooc_los.repeatMax < temp.ooc_los.repeatMin)
|
||||
sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
break;
|
||||
case EVENT_T_SPAWNED:
|
||||
switch (temp.spawned.condition)
|
||||
{
|
||||
case SPAWNED_EVENT_ALWAY:
|
||||
break;
|
||||
case SPAWNED_EVENT_MAP:
|
||||
if (!sMapStore.LookupEntry(temp.spawned.conditionValue1))
|
||||
sLog.outErrorEventAI("Creature %u are using spawned event(%u) with param1 = %u 'map specific' but map (param2: %u) does not exist. Event will never repeat.", temp.creature_id, i, temp.spawned.condition, temp.spawned.conditionValue1);
|
||||
break;
|
||||
case SPAWNED_EVENT_ZONE:
|
||||
if (!GetAreaEntryByAreaID(temp.spawned.conditionValue1))
|
||||
sLog.outErrorEventAI("Creature %u are using spawned event(%u) with param1 = %u 'area specific' but area (param2: %u) does not exist. Event will never repeat.", temp.creature_id, i, temp.spawned.condition, temp.spawned.conditionValue1);
|
||||
break;
|
||||
default:
|
||||
sLog.outErrorEventAI("Creature %u are using invalid spawned event %u mode (%u) in param1", temp.creature_id, i, temp.spawned.condition);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case EVENT_T_FRIENDLY_HP:
|
||||
if (temp.friendly_hp.repeatMax < temp.friendly_hp.repeatMin)
|
||||
sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
break;
|
||||
case EVENT_T_FRIENDLY_IS_CC:
|
||||
if (temp.friendly_is_cc.repeatMax < temp.friendly_is_cc.repeatMin)
|
||||
sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
break;
|
||||
case EVENT_T_FRIENDLY_MISSING_BUFF:
|
||||
{
|
||||
SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.friendly_buff.spellId);
|
||||
if (!pSpell)
|
||||
{
|
||||
sLog.outErrorEventAI("Creature %u has nonexistent SpellID(%u) defined in event %u.", temp.creature_id, temp.friendly_buff.spellId, i);
|
||||
continue;
|
||||
}
|
||||
if (temp.friendly_buff.radius <= 0)
|
||||
{
|
||||
sLog.outErrorEventAI("Creature %u has wrong radius (%u) for EVENT_T_FRIENDLY_MISSING_BUFF defined in event %u.", temp.creature_id, temp.friendly_buff.radius, i);
|
||||
continue;
|
||||
}
|
||||
if (temp.friendly_buff.repeatMax < temp.friendly_buff.repeatMin)
|
||||
sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
break;
|
||||
}
|
||||
case EVENT_T_KILL:
|
||||
if (temp.kill.repeatMax < temp.kill.repeatMin)
|
||||
sLog.outErrorEventAI("Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
break;
|
||||
case EVENT_T_TARGET_CASTING:
|
||||
if (temp.target_casting.repeatMax < temp.target_casting.repeatMin)
|
||||
sLog.outErrorEventAI("Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
break;
|
||||
case EVENT_T_SUMMONED_UNIT:
|
||||
case EVENT_T_SUMMONED_JUST_DIED:
|
||||
case EVENT_T_SUMMONED_JUST_DESPAWN:
|
||||
if (!sCreatureStorage.LookupEntry<CreatureInfo>(temp.summoned.creatureId))
|
||||
sLog.outErrorEventAI("Creature %u are using event(%u) with nonexistent creature template id (%u) in param1, skipped.", temp.creature_id, i, temp.summoned.creatureId);
|
||||
if (temp.summoned.repeatMax < temp.summoned.repeatMin)
|
||||
sLog.outErrorEventAI("Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
break;
|
||||
case EVENT_T_QUEST_ACCEPT:
|
||||
case EVENT_T_QUEST_COMPLETE:
|
||||
if (!sObjectMgr.GetQuestTemplate(temp.quest.questId))
|
||||
sLog.outErrorEventAI("Creature %u are using event(%u) with nonexistent quest id (%u) in param1, skipped.", temp.creature_id, i, temp.quest.questId);
|
||||
sLog.outErrorEventAI("Creature %u using not implemented event (%u) in event %u.", temp.creature_id, temp.event_id, i);
|
||||
continue;
|
||||
|
||||
case EVENT_T_AGGRO:
|
||||
case EVENT_T_DEATH:
|
||||
case EVENT_T_EVADE:
|
||||
case EVENT_T_REACHED_HOME:
|
||||
{
|
||||
if (temp.event_flags & EFLAG_REPEATABLE)
|
||||
{
|
||||
sLog.outErrorEventAI("Creature %u has EFLAG_REPEATABLE set. Event can never be repeatable. Removing flag for event %u.", temp.creature_id, i);
|
||||
temp.event_flags &= ~EFLAG_REPEATABLE;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_T_RECEIVE_EMOTE:
|
||||
{
|
||||
if (!sEmotesTextStore.LookupEntry(temp.receive_emote.emoteId))
|
||||
{
|
||||
sLog.outErrorEventAI("Creature %u using event %u: param1 (EmoteTextId: %u) are not valid.", temp.creature_id, i, temp.receive_emote.emoteId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!PlayerCondition::IsValid(0, ConditionType(temp.receive_emote.condition), temp.receive_emote.conditionValue1, temp.receive_emote.conditionValue2))
|
||||
{
|
||||
sLog.outErrorEventAI("Creature %u using event %u: param2 (Condition: %u) are not valid.", temp.creature_id, i, temp.receive_emote.condition);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(temp.event_flags & EFLAG_REPEATABLE))
|
||||
{
|
||||
sLog.outErrorEventAI("Creature %u using event %u: EFLAG_REPEATABLE not set. Event must always be repeatable. Flag applied.", temp.creature_id, i);
|
||||
temp.event_flags |= EFLAG_REPEATABLE;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_T_AURA:
|
||||
case EVENT_T_TARGET_AURA:
|
||||
case EVENT_T_MISSING_AURA:
|
||||
case EVENT_T_TARGET_MISSING_AURA:
|
||||
{
|
||||
SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.buffed.spellId);
|
||||
if (!pSpell)
|
||||
{
|
||||
sLog.outErrorEventAI("Creature %u has nonexistent SpellID(%u) defined in event %u.", temp.creature_id, temp.buffed.spellId, i);
|
||||
continue;
|
||||
}
|
||||
if (temp.buffed.amount < 1)
|
||||
{
|
||||
sLog.outErrorEventAI("Creature %u has wrong spell stack size (%u) defined in event %u.", temp.creature_id, temp.buffed.amount, i);
|
||||
continue;
|
||||
}
|
||||
if (temp.buffed.repeatMax < temp.buffed.repeatMin)
|
||||
sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
|
||||
break;
|
||||
}
|
||||
case EVENT_T_RECEIVE_AI_EVENT:
|
||||
{
|
||||
// Sender-Creature does not exist in database
|
||||
if (temp.receiveAIEvent.senderEntry && !sCreatureStorage.LookupEntry<CreatureInfo>(temp.receiveAIEvent.senderEntry))
|
||||
{
|
||||
sLog.outErrorEventAI("Event %u has nonexisting creature (%u) defined for event RECEIVE_AI_EVENT, skipping.", i, temp.receiveAIEvent.senderEntry);
|
||||
continue;
|
||||
}
|
||||
// Event-Type is not defined
|
||||
if (temp.receiveAIEvent.eventType >= MAXIMAL_AI_EVENT_EVENTAI)
|
||||
{
|
||||
sLog.outErrorEventAI("Event %u has unfitting event-type (%u) defined for event RECEIVE_AI_EVENT (must be less than %u), skipping.", i, temp.receiveAIEvent.eventType, MAXIMAL_AI_EVENT_EVENTAI);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
sLog.outErrorEventAI("Creature %u using not checked at load event (%u) in event %u. Need check code update?", temp.creature_id, temp.event_id, i);
|
||||
break;
|
||||
}
|
||||
|
||||
for (uint32 j = 0; j < MAX_ACTIONS; ++j)
|
||||
{
|
||||
uint16 action_type = fields[10 + (j * 4)].GetUInt16();
|
||||
if (action_type >= ACTION_T_END)
|
||||
{
|
||||
sLog.outErrorEventAI("Event %u Action %u has incorrect action type (%u), replace by ACTION_T_NONE.", i, j + 1, action_type);
|
||||
temp.action[j].type = ACTION_T_NONE;
|
||||
continue;
|
||||
}
|
||||
|
||||
CreatureEventAI_Action& action = temp.action[j];
|
||||
|
||||
action.type = EventAI_ActionType(action_type);
|
||||
action.raw.param1 = fields[11 + (j * 4)].GetUInt32();
|
||||
action.raw.param2 = fields[12 + (j * 4)].GetUInt32();
|
||||
action.raw.param3 = fields[13 + (j * 4)].GetUInt32();
|
||||
|
||||
// Report any errors in actions
|
||||
switch (action.type)
|
||||
{
|
||||
case ACTION_T_NONE:
|
||||
break;
|
||||
case ACTION_T_CHANCED_TEXT:
|
||||
// Check first param as chance
|
||||
if (!action.chanced_text.chance)
|
||||
sLog.outErrorEventAI("Event %u Action %u has not set chance param1. Text will not be displayed", i, j + 1);
|
||||
else if (action.chanced_text.chance >= 100)
|
||||
sLog.outErrorEventAI("Event %u Action %u has set chance param1 >= 100. Text will always be displayed", i, j + 1);
|
||||
// no break here to check texts
|
||||
case ACTION_T_TEXT:
|
||||
{
|
||||
bool not_set = false;
|
||||
int firstTextParam = action.type == ACTION_T_TEXT ? 0 : 1;
|
||||
for (int k = firstTextParam; k < 3; ++k)
|
||||
{
|
||||
if (action.text.TextId[k])
|
||||
{
|
||||
if (k > firstTextParam && not_set)
|
||||
sLog.outErrorEventAI("Event %u Action %u has param%d, but it follow after not set param. Required for randomized text.", i, j + 1, k + 1);
|
||||
|
||||
if (!action.text.TextId[k])
|
||||
not_set = true;
|
||||
// range negative
|
||||
else if (action.text.TextId[k] > MIN_CREATURE_AI_TEXT_STRING_ID || action.text.TextId[k] <= MAX_CREATURE_AI_TEXT_STRING_ID)
|
||||
{
|
||||
sLog.outErrorEventAI("Event %u Action %u param%d references out-of-range entry (%i) in texts table.", i, j + 1, k + 1, action.text.TextId[k]);
|
||||
action.text.TextId[k] = 0;
|
||||
}
|
||||
else if (!sObjectMgr.GetMangosStringLocale(action.text.TextId[k]))
|
||||
{
|
||||
sLog.outErrorEventAI("Event %u Action %u param%d references non-existing entry (%i) in texts table.", i, j + 1, k + 1, action.text.TextId[k]);
|
||||
action.text.TextId[k] = 0;
|
||||
}
|
||||
else
|
||||
usedTextIds.insert(action.text.TextId[k]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ACTION_T_SET_FACTION:
|
||||
if (action.set_faction.factionId != 0 && !sFactionTemplateStore.LookupEntry(action.set_faction.factionId))
|
||||
{
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent FactionId %u.", i, j + 1, action.set_faction.factionId);
|
||||
action.set_faction.factionId = 0;
|
||||
}
|
||||
break;
|
||||
case ACTION_T_MORPH_TO_ENTRY_OR_MODEL:
|
||||
if (action.morph.creatureId != 0 || action.morph.modelId != 0)
|
||||
{
|
||||
if (action.morph.creatureId && !sCreatureStorage.LookupEntry<CreatureInfo>(action.morph.creatureId))
|
||||
{
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent Creature entry %u.", i, j + 1, action.morph.creatureId);
|
||||
action.morph.creatureId = 0;
|
||||
}
|
||||
|
||||
if (action.morph.modelId)
|
||||
{
|
||||
if (action.morph.creatureId)
|
||||
{
|
||||
sLog.outErrorEventAI("Event %u Action %u have unused ModelId %u with also set creature id %u.", i, j + 1, action.morph.modelId, action.morph.creatureId);
|
||||
action.morph.modelId = 0;
|
||||
}
|
||||
else if (!sCreatureDisplayInfoStore.LookupEntry(action.morph.modelId))
|
||||
{
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent ModelId %u.", i, j + 1, action.morph.modelId);
|
||||
action.morph.modelId = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ACTION_T_SOUND:
|
||||
if (!sSoundEntriesStore.LookupEntry(action.sound.soundId))
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent SoundID %u.", i, j + 1, action.sound.soundId);
|
||||
break;
|
||||
case ACTION_T_EMOTE:
|
||||
if (!sEmotesStore.LookupEntry(action.emote.emoteId))
|
||||
sLog.outErrorEventAI("Event %u Action %u param1 (EmoteId: %u) are not valid.", i, j + 1, action.emote.emoteId);
|
||||
break;
|
||||
case ACTION_T_RANDOM_SOUND:
|
||||
if (!sSoundEntriesStore.LookupEntry(action.random_sound.soundId1))
|
||||
sLog.outErrorEventAI("Event %u Action %u param1 uses nonexistent SoundID %u.", i, j + 1, action.random_sound.soundId1);
|
||||
if (action.random_sound.soundId2 >= 0 && !sSoundEntriesStore.LookupEntry(action.random_sound.soundId2))
|
||||
sLog.outErrorEventAI("Event %u Action %u param2 uses nonexistent SoundID %u.", i, j + 1, action.random_sound.soundId2);
|
||||
if (action.random_sound.soundId3 >= 0 && !sSoundEntriesStore.LookupEntry(action.random_sound.soundId3))
|
||||
sLog.outErrorEventAI("Event %u Action %u param3 uses nonexistent SoundID %u.", i, j + 1, action.random_sound.soundId3);
|
||||
break;
|
||||
case ACTION_T_RANDOM_EMOTE:
|
||||
if (!sEmotesStore.LookupEntry(action.random_emote.emoteId1))
|
||||
sLog.outErrorEventAI("Event %u Action %u param1 (EmoteId: %u) are not valid.", i, j + 1, action.random_emote.emoteId1);
|
||||
if (action.random_emote.emoteId2 >= 0 && !sEmotesStore.LookupEntry(action.random_emote.emoteId2))
|
||||
sLog.outErrorEventAI("Event %u Action %u param2 (EmoteId: %u) are not valid.", i, j + 1, action.random_emote.emoteId2);
|
||||
if (action.random_emote.emoteId3 >= 0 && !sEmotesStore.LookupEntry(action.random_emote.emoteId3))
|
||||
sLog.outErrorEventAI("Event %u Action %u param3 (EmoteId: %u) are not valid.", i, j + 1, action.random_emote.emoteId3);
|
||||
break;
|
||||
case ACTION_T_CAST:
|
||||
{
|
||||
const SpellEntry* spell = sSpellStore.LookupEntry(action.cast.spellId);
|
||||
if (!spell)
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent SpellID %u.", i, j + 1, action.cast.spellId);
|
||||
/* FIXME: temp.raw.param3 not have event tipes with recovery time in it....
|
||||
else
|
||||
{
|
||||
if (spell->RecoveryTime > 0 && temp.event_flags & EFLAG_REPEATABLE)
|
||||
{
|
||||
// output as debug for now, also because there's no general rule all spells have RecoveryTime
|
||||
if (temp.event_param3 < spell->RecoveryTime)
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "CreatureEventAI: Event %u Action %u uses SpellID %u but cooldown is longer(%u) than minumum defined in event param3(%u).", i, j+1,action.cast.spellId, spell->RecoveryTime, temp.event_param3);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Cast is always triggered if target is forced to cast on self
|
||||
if (action.cast.castFlags & CAST_FORCE_TARGET_SELF)
|
||||
action.cast.castFlags |= CAST_TRIGGERED;
|
||||
|
||||
IsValidTargetType(temp.event_type, action.type, action.cast.target, i, j + 1);
|
||||
|
||||
// Some Advanced target type checks - Can have false positives
|
||||
if (!sLog.HasLogFilter(LOG_FILTER_EVENT_AI_DEV) && spell)
|
||||
{
|
||||
// spell must be cast on self, but is not
|
||||
if ((IsOnlySelfTargeting(spell) || spell->rangeIndex == SPELL_RANGE_IDX_SELF_ONLY) && action.cast.target != TARGET_T_SELF && !(action.cast.castFlags & CAST_FORCE_TARGET_SELF))
|
||||
sLog.outErrorEventAI("Event %u Action %u uses SpellID %u that must be self cast (target is %u)", i, j + 1, action.cast.spellId, action.cast.target);
|
||||
|
||||
// TODO: spell must be cast on enemy, but is not
|
||||
|
||||
// used TARGET_T_ACTION_INVOKER, but likely should be _INVOKER_OWNER instead
|
||||
if (action.cast.target == TARGET_T_ACTION_INVOKER &&
|
||||
(IsSpellHaveEffect(spell, SPELL_EFFECT_QUEST_COMPLETE) || IsSpellHaveEffect(spell, SPELL_EFFECT_CREATE_RANDOM_ITEM) || IsSpellHaveEffect(spell, SPELL_EFFECT_DUMMY)
|
||||
|| IsSpellHaveEffect(spell, SPELL_EFFECT_KILL_CREDIT_PERSONAL) || IsSpellHaveEffect(spell, SPELL_EFFECT_KILL_CREDIT_GROUP)))
|
||||
sLog.outErrorEventAI("Event %u Action %u has TARGET_T_ACTION_INVOKER(%u) target type, but should have TARGET_T_ACTION_INVOKER_OWNER(%u).", i, j + 1, TARGET_T_ACTION_INVOKER, TARGET_T_ACTION_INVOKER_OWNER);
|
||||
|
||||
// Spell that should only target players, but could get any
|
||||
if (spell->HasAttribute(SPELL_ATTR_EX3_TARGET_ONLY_PLAYER) &&
|
||||
(action.cast.target == TARGET_T_ACTION_INVOKER || action.cast.target == TARGET_T_HOSTILE_RANDOM || action.cast.target == TARGET_T_HOSTILE_RANDOM_NOT_TOP))
|
||||
sLog.outErrorEventAI("Event %u Action %u uses Target type %u for a spell (%u) that should only target players. This could be wrong.", i, j + 1, action.cast.target, action.cast.spellId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ACTION_T_SUMMON:
|
||||
if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.summon.creatureId))
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.summon.creatureId);
|
||||
|
||||
IsValidTargetType(temp.event_type, action.type, action.summon.target, i, j + 1);
|
||||
break;
|
||||
case ACTION_T_THREAT_SINGLE_PCT:
|
||||
if (std::abs(action.threat_single_pct.percent) > 100)
|
||||
sLog.outErrorEventAI("Event %u Action %u uses invalid percent value %u.", i, j + 1, action.threat_single_pct.percent);
|
||||
IsValidTargetType(temp.event_type, action.type, action.threat_single_pct.target, i, j + 1);
|
||||
break;
|
||||
case ACTION_T_THREAT_ALL_PCT:
|
||||
if (std::abs(action.threat_all_pct.percent) > 100)
|
||||
sLog.outErrorEventAI("Event %u Action %u uses invalid percent value %u.", i, j + 1, action.threat_all_pct.percent);
|
||||
break;
|
||||
case ACTION_T_QUEST_EVENT:
|
||||
if (Quest const* qid = sObjectMgr.GetQuestTemplate(action.quest_event.questId))
|
||||
{
|
||||
if (!qid->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT))
|
||||
sLog.outErrorEventAI("Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i, j + 1, action.quest_event.questId);
|
||||
}
|
||||
else
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent Quest entry %u.", i, j + 1, action.quest_event.questId);
|
||||
|
||||
IsValidTargetType(temp.event_type, action.type, action.quest_event.target, i, j + 1);
|
||||
break;
|
||||
case ACTION_T_CAST_EVENT:
|
||||
if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.cast_event.creatureId))
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.cast_event.creatureId);
|
||||
if (!sSpellStore.LookupEntry(action.cast_event.spellId))
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent SpellID %u.", i, j + 1, action.cast_event.spellId);
|
||||
IsValidTargetType(temp.event_type, action.type, action.cast_event.target, i, j + 1);
|
||||
break;
|
||||
case ACTION_T_SET_UNIT_FIELD:
|
||||
if (action.set_unit_field.field < OBJECT_END || action.set_unit_field.field >= UNIT_END)
|
||||
sLog.outErrorEventAI("Event %u Action %u param1 (UNIT_FIELD*). Index out of range for intended use.", i, j + 1);
|
||||
IsValidTargetType(temp.event_type, action.type, action.set_unit_field.target, i, j + 1);
|
||||
break;
|
||||
case ACTION_T_SET_UNIT_FLAG:
|
||||
case ACTION_T_REMOVE_UNIT_FLAG:
|
||||
IsValidTargetType(temp.event_type, action.type, action.unit_flag.target, i, j + 1);
|
||||
break;
|
||||
case ACTION_T_SET_PHASE:
|
||||
if (action.set_phase.phase >= MAX_PHASE)
|
||||
sLog.outErrorEventAI("Event %u Action %u attempts to set phase >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1);
|
||||
break;
|
||||
case ACTION_T_INC_PHASE:
|
||||
if (action.set_inc_phase.step == 0)
|
||||
sLog.outErrorEventAI("Event %u Action %u is incrementing phase by 0. Was this intended?", i, j + 1);
|
||||
else if (std::abs(action.set_inc_phase.step) > MAX_PHASE - 1)
|
||||
sLog.outErrorEventAI("Event %u Action %u is change phase by too large for any use %i.", i, j + 1, action.set_inc_phase.step);
|
||||
break;
|
||||
case ACTION_T_QUEST_EVENT_ALL:
|
||||
if (Quest const* qid = sObjectMgr.GetQuestTemplate(action.quest_event_all.questId))
|
||||
{
|
||||
if (!qid->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT))
|
||||
sLog.outErrorEventAI("Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i, j + 1, action.quest_event_all.questId);
|
||||
}
|
||||
else
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent Quest entry %u.", i, j + 1, action.quest_event_all.questId);
|
||||
break;
|
||||
case ACTION_T_CAST_EVENT_ALL:
|
||||
if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.cast_event_all.creatureId))
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.cast_event_all.creatureId);
|
||||
if (!sSpellStore.LookupEntry(action.cast_event_all.spellId))
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent SpellID %u.", i, j + 1, action.cast_event_all.spellId);
|
||||
break;
|
||||
case ACTION_T_REMOVEAURASFROMSPELL:
|
||||
if (!sSpellStore.LookupEntry(action.remove_aura.spellId))
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent SpellID %u.", i, j + 1, action.remove_aura.spellId);
|
||||
IsValidTargetType(temp.event_type, action.type, action.remove_aura.target, i, j + 1);
|
||||
break;
|
||||
case ACTION_T_RANDOM_PHASE: // PhaseId1, PhaseId2, PhaseId3
|
||||
if (action.random_phase.phase1 >= MAX_PHASE)
|
||||
sLog.outErrorEventAI("Event %u Action %u attempts to set phase1 >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1);
|
||||
if (action.random_phase.phase2 >= MAX_PHASE)
|
||||
sLog.outErrorEventAI("Event %u Action %u attempts to set phase2 >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1);
|
||||
if (action.random_phase.phase3 >= MAX_PHASE)
|
||||
sLog.outErrorEventAI("Event %u Action %u attempts to set phase3 >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1);
|
||||
break;
|
||||
case ACTION_T_RANDOM_PHASE_RANGE: // PhaseMin, PhaseMax
|
||||
if (action.random_phase_range.phaseMin >= MAX_PHASE)
|
||||
sLog.outErrorEventAI("Event %u Action %u attempts to set phaseMin >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1);
|
||||
if (action.random_phase_range.phaseMin >= MAX_PHASE)
|
||||
sLog.outErrorEventAI("Event %u Action %u attempts to set phaseMax >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1);
|
||||
if (action.random_phase_range.phaseMin >= action.random_phase_range.phaseMax)
|
||||
{
|
||||
sLog.outErrorEventAI("Event %u Action %u attempts to set phaseMax <= phaseMin.", i, j + 1);
|
||||
std::swap(action.random_phase_range.phaseMin, action.random_phase_range.phaseMax);
|
||||
// equal case processed at call
|
||||
}
|
||||
break;
|
||||
case ACTION_T_SUMMON_ID:
|
||||
if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.summon_id.creatureId))
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.summon_id.creatureId);
|
||||
IsValidTargetType(temp.event_type, action.type, action.summon_id.target, i, j + 1);
|
||||
if (m_CreatureEventAI_Summon_Map.find(action.summon_id.spawnId) == m_CreatureEventAI_Summon_Map.end())
|
||||
sLog.outErrorEventAI("Event %u Action %u summons missing CreatureEventAI_Summon %u", i, j + 1, action.summon_id.spawnId);
|
||||
break;
|
||||
case ACTION_T_KILLED_MONSTER:
|
||||
if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.killed_monster.creatureId))
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.killed_monster.creatureId);
|
||||
IsValidTargetType(temp.event_type, action.type, action.killed_monster.target, i, j + 1);
|
||||
break;
|
||||
case ACTION_T_SET_INST_DATA:
|
||||
if (!(temp.event_flags & EFLAG_DIFFICULTY_ALL))
|
||||
sLog.outErrorEventAI("Event %u Action %u. Cannot set instance data without difficulty event flags.", i, j + 1);
|
||||
if (action.set_inst_data.value > 4/*SPECIAL*/)
|
||||
sLog.outErrorEventAI("Event %u Action %u attempts to set instance data above encounter state 4. Custom case?", i, j + 1);
|
||||
break;
|
||||
case ACTION_T_SET_INST_DATA64:
|
||||
if (!(temp.event_flags & EFLAG_DIFFICULTY_ALL))
|
||||
sLog.outErrorEventAI("Event %u Action %u. Cannot set instance data without difficulty event flags.", i, j + 1);
|
||||
IsValidTargetType(temp.event_type, action.type, action.set_inst_data64.target, i, j + 1);
|
||||
break;
|
||||
case ACTION_T_UPDATE_TEMPLATE:
|
||||
if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.update_template.creatureId))
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.update_template.creatureId);
|
||||
break;
|
||||
case ACTION_T_SET_SHEATH:
|
||||
if (action.set_sheath.sheath >= MAX_SHEATH_STATE)
|
||||
{
|
||||
sLog.outErrorEventAI("Event %u Action %u uses wrong sheath state %u.", i, j + 1, action.set_sheath.sheath);
|
||||
action.set_sheath.sheath = SHEATH_STATE_UNARMED;
|
||||
}
|
||||
break;
|
||||
case ACTION_T_SET_INVINCIBILITY_HP_LEVEL:
|
||||
if (action.invincibility_hp_level.is_percent)
|
||||
{
|
||||
if (action.invincibility_hp_level.hp_level > 100)
|
||||
{
|
||||
sLog.outErrorEventAI("Event %u Action %u uses wrong percent value %u.", i, j + 1, action.invincibility_hp_level.hp_level);
|
||||
action.invincibility_hp_level.hp_level = 100;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ACTION_T_MOUNT_TO_ENTRY_OR_MODEL:
|
||||
if (action.mount.creatureId != 0 || action.mount.modelId != 0)
|
||||
{
|
||||
if (action.mount.creatureId && !sCreatureStorage.LookupEntry<CreatureInfo>(action.mount.creatureId))
|
||||
{
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent Creature entry %u.", i, j + 1, action.mount.creatureId);
|
||||
action.morph.creatureId = 0;
|
||||
}
|
||||
|
||||
if (action.mount.modelId)
|
||||
{
|
||||
if (action.mount.creatureId)
|
||||
{
|
||||
sLog.outErrorEventAI("Event %u Action %u have unused ModelId %u with also set creature id %u.", i, j + 1, action.mount.modelId, action.mount.creatureId);
|
||||
action.mount.modelId = 0;
|
||||
}
|
||||
else if (!sCreatureDisplayInfoStore.LookupEntry(action.mount.modelId))
|
||||
{
|
||||
sLog.outErrorEventAI("Event %u Action %u uses nonexistent ModelId %u.", i, j + 1, action.mount.modelId);
|
||||
action.mount.modelId = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ACTION_T_EVADE: // No Params
|
||||
case ACTION_T_FLEE_FOR_ASSIST: // No Params
|
||||
case ACTION_T_DIE: // No Params
|
||||
case ACTION_T_ZONE_COMBAT_PULSE: // No Params
|
||||
case ACTION_T_FORCE_DESPAWN: // Delay
|
||||
case ACTION_T_AUTO_ATTACK: // AllowAttackState (0 = stop attack, anything else means continue attacking)
|
||||
case ACTION_T_COMBAT_MOVEMENT: // AllowCombatMovement (0 = stop combat based movement, anything else continue attacking)
|
||||
case ACTION_T_RANGED_MOVEMENT: // Distance, Angle
|
||||
case ACTION_T_CALL_FOR_HELP: // Distance
|
||||
break;
|
||||
|
||||
case ACTION_T_RANDOM_SAY:
|
||||
case ACTION_T_RANDOM_YELL:
|
||||
case ACTION_T_RANDOM_TEXTEMOTE:
|
||||
sLog.outErrorEventAI("Event %u Action %u currently unused ACTION type. Did you forget to update database?", i, j + 1);
|
||||
break;
|
||||
|
||||
case ACTION_T_THROW_AI_EVENT:
|
||||
if (action.throwEvent.eventType >= MAXIMAL_AI_EVENT_EVENTAI)
|
||||
{
|
||||
sLog.outErrorEventAI("Event %u Action %u uses invalid event type %u (must be less than %u), skipping", i, j + 1, action.throwEvent.eventType, MAXIMAL_AI_EVENT_EVENTAI);
|
||||
continue;
|
||||
}
|
||||
if (action.throwEvent.radius > SIZE_OF_GRIDS)
|
||||
sLog.outErrorEventAI("Event %u Action %u uses unexpectedly huge radius %u (expected to be less than %f)", i, j + 1, action.throwEvent.radius, SIZE_OF_GRIDS);
|
||||
|
||||
if (action.throwEvent.radius == 0)
|
||||
{
|
||||
sLog.outErrorEventAI("Event %u Action %u uses unexpected radius 0 (set to %f of CONFIG_FLOAT_CREATURE_FAMILY_ASSISTANCE_RADIUS)", i, j + 1, sWorld.getConfig(CONFIG_FLOAT_CREATURE_FAMILY_ASSISTANCE_RADIUS));
|
||||
action.throwEvent.radius = uint32(sWorld.getConfig(CONFIG_FLOAT_CREATURE_FAMILY_ASSISTANCE_RADIUS));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ACTION_T_SET_THROW_MASK:
|
||||
if (action.setThrowMask.eventTypeMask & ~((1 << MAXIMAL_AI_EVENT_EVENTAI) - 1))
|
||||
{
|
||||
sLog.outErrorEventAI("Event %u Action %u uses invalid AIEvent-typemask %u (must be smaller than %u)", i, j + 1, action.setThrowMask.eventTypeMask, MAXIMAL_AI_EVENT_EVENTAI << 1);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sLog.outErrorEventAI("Event %u Action %u have currently not checked at load action type (%u). Need check code update?", i, j + 1, temp.action[j].type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add to list
|
||||
m_CreatureEventAI_Event_Map[creature_id].push_back(temp);
|
||||
++Count;
|
||||
}
|
||||
while (result->NextRow());
|
||||
|
||||
delete result;
|
||||
m_usedTextsAmount = usedTextIds.size();
|
||||
|
||||
// post check
|
||||
for (uint32 i = 1; i < sCreatureStorage.GetMaxEntry(); ++i)
|
||||
{
|
||||
if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
|
||||
{
|
||||
bool ainame = strcmp(cInfo->AIName, "EventAI") == 0;
|
||||
bool hasevent = m_CreatureEventAI_Event_Map.find(i) != m_CreatureEventAI_Event_Map.end();
|
||||
if (ainame && !hasevent)
|
||||
sLog.outErrorEventAI("EventAI not has script for creature entry (%u), but AIName = '%s'.", i, cInfo->AIName);
|
||||
else if (!ainame && hasevent)
|
||||
sLog.outErrorEventAI("EventAI has script for creature entry (%u), but AIName = '%s' instead 'EventAI'.", i, cInfo->AIName);
|
||||
}
|
||||
}
|
||||
|
||||
CheckUnusedAITexts();
|
||||
CheckUnusedAISummons();
|
||||
|
||||
sLog.outString();
|
||||
sLog.outString(">> Loaded %u CreatureEventAI scripts", Count);
|
||||
}
|
||||
else
|
||||
{
|
||||
BarGoLink bar(1);
|
||||
bar.step();
|
||||
sLog.outString();
|
||||
sLog.outString(">> Loaded 0 CreatureEventAI scripts. DB table `creature_ai_scripts` is empty.");
|
||||
}
|
||||
}
|
||||
55
src/game/Object/CreatureEventAIMgr.h
Normal file
55
src/game/Object/CreatureEventAIMgr.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOS_CREATURE_EAI_MGR_H
|
||||
#define MANGOS_CREATURE_EAI_MGR_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "CreatureEventAI.h"
|
||||
|
||||
class CreatureEventAIMgr
|
||||
{
|
||||
public:
|
||||
CreatureEventAIMgr() : m_usedTextsAmount(0) {};
|
||||
~CreatureEventAIMgr() {};
|
||||
|
||||
void LoadCreatureEventAI_Texts(bool check_entry_use);
|
||||
void LoadCreatureEventAI_Summons(bool check_entry_use);
|
||||
void LoadCreatureEventAI_Scripts();
|
||||
|
||||
CreatureEventAI_Event_Map const& GetCreatureEventAIMap() const { return m_CreatureEventAI_Event_Map; }
|
||||
CreatureEventAI_Summon_Map const& GetCreatureEventAISummonMap() const { return m_CreatureEventAI_Summon_Map; }
|
||||
|
||||
private:
|
||||
void CheckUnusedAITexts();
|
||||
void CheckUnusedAISummons();
|
||||
|
||||
CreatureEventAI_Event_Map m_CreatureEventAI_Event_Map;
|
||||
CreatureEventAI_Summon_Map m_CreatureEventAI_Summon_Map;
|
||||
|
||||
uint32 m_usedTextsAmount;
|
||||
};
|
||||
|
||||
#define sEventAIMgr MaNGOS::Singleton<CreatureEventAIMgr>::Instance()
|
||||
#endif
|
||||
231
src/game/Object/DynamicObject.cpp
Normal file
231
src/game/Object/DynamicObject.cpp
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include "Common.h"
|
||||
#include "UpdateMask.h"
|
||||
#include "Opcodes.h"
|
||||
#include "World.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "Database/DatabaseEnv.h"
|
||||
#include "GridNotifiers.h"
|
||||
#include "CellImpl.h"
|
||||
#include "GridNotifiersImpl.h"
|
||||
#include "SpellMgr.h"
|
||||
#include "DBCStores.h"
|
||||
|
||||
DynamicObject::DynamicObject() : WorldObject()
|
||||
{
|
||||
m_objectType |= TYPEMASK_DYNAMICOBJECT;
|
||||
m_objectTypeId = TYPEID_DYNAMICOBJECT;
|
||||
|
||||
m_updateFlag = UPDATEFLAG_HAS_POSITION;
|
||||
|
||||
m_valuesCount = DYNAMICOBJECT_END;
|
||||
}
|
||||
|
||||
void DynamicObject::AddToWorld()
|
||||
{
|
||||
///- Register the dynamicObject for guid lookup
|
||||
if (!IsInWorld())
|
||||
GetMap()->GetObjectsStore().insert<DynamicObject>(GetObjectGuid(), (DynamicObject*)this);
|
||||
|
||||
Object::AddToWorld();
|
||||
}
|
||||
|
||||
void DynamicObject::RemoveFromWorld()
|
||||
{
|
||||
///- Remove the dynamicObject from the accessor
|
||||
if (IsInWorld())
|
||||
{
|
||||
GetMap()->GetObjectsStore().erase<DynamicObject>(GetObjectGuid(), (DynamicObject*)NULL);
|
||||
GetViewPoint().Event_RemovedFromWorld();
|
||||
}
|
||||
|
||||
Object::RemoveFromWorld();
|
||||
}
|
||||
|
||||
bool DynamicObject::Create(uint32 guidlow, Unit* caster, uint32 spellId, SpellEffectIndex effIndex, float x, float y, float z, int32 duration, float radius, DynamicObjectType type)
|
||||
{
|
||||
WorldObject::_Create(guidlow, HIGHGUID_DYNAMICOBJECT, caster->GetPhaseMask());
|
||||
SetMap(caster->GetMap());
|
||||
Relocate(x, y, z, 0);
|
||||
|
||||
if (!IsPositionValid())
|
||||
{
|
||||
sLog.outError("DynamicObject (spell %u eff %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)", spellId, effIndex, GetPositionX(), GetPositionY());
|
||||
return false;
|
||||
}
|
||||
|
||||
SpellEntry const* spellProto = sSpellStore.LookupEntry(spellId);
|
||||
if (!spellProto)
|
||||
{
|
||||
sLog.outError("DynamicObject (spell %u) not created. Spell not exist!", spellId);
|
||||
return false;
|
||||
}
|
||||
|
||||
SetEntry(spellId);
|
||||
SetObjectScale(DEFAULT_OBJECT_SCALE);
|
||||
|
||||
SetGuidValue(DYNAMICOBJECT_CASTER, caster->GetObjectGuid());
|
||||
|
||||
/* Bytes field, so it's really 4 bit fields. These flags are unknown, but we do know that 0x00000001 is set for most.
|
||||
Farsight for example, does not have this flag, instead it has 0x80000002.
|
||||
Flags are set dynamically with some conditions, so one spell may have different flags set, depending on those conditions.
|
||||
The size of the visual may be controlled to some degree with these flags.
|
||||
|
||||
uint32 bytes = 0x00000000;
|
||||
bytes |= 0x01;
|
||||
bytes |= 0x00 << 8;
|
||||
bytes |= 0x00 << 16;
|
||||
bytes |= 0x00 << 24;
|
||||
*/
|
||||
SetUInt32Value(DYNAMICOBJECT_BYTES, spellProto->SpellVisual[0] | (type << 28));
|
||||
|
||||
SetUInt32Value(DYNAMICOBJECT_SPELLID, spellId);
|
||||
SetFloatValue(DYNAMICOBJECT_RADIUS, radius);
|
||||
SetUInt32Value(DYNAMICOBJECT_CASTTIME, WorldTimer::getMSTime()); // new 2.4.0
|
||||
|
||||
m_aliveDuration = duration;
|
||||
m_radius = radius;
|
||||
m_effIndex = effIndex;
|
||||
m_spellId = spellId;
|
||||
m_positive = IsPositiveEffect(spellProto, m_effIndex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Unit* DynamicObject::GetCaster() const
|
||||
{
|
||||
// can be not found in some cases
|
||||
return ObjectAccessor::GetUnit(*this, GetCasterGuid());
|
||||
}
|
||||
|
||||
void DynamicObject::Update(uint32 /*update_diff*/, uint32 p_time)
|
||||
{
|
||||
// caster can be not in world at time dynamic object update, but dynamic object not yet deleted in Unit destructor
|
||||
Unit* caster = GetCaster();
|
||||
if (!caster)
|
||||
{
|
||||
Delete();
|
||||
return;
|
||||
}
|
||||
|
||||
bool deleteThis = false;
|
||||
|
||||
if (m_aliveDuration > int32(p_time))
|
||||
m_aliveDuration -= p_time;
|
||||
else
|
||||
deleteThis = true;
|
||||
|
||||
// have radius and work as persistent effect
|
||||
if (m_radius)
|
||||
{
|
||||
// TODO: make a timer and update this in larger intervals
|
||||
MaNGOS::DynamicObjectUpdater notifier(*this, caster, m_positive);
|
||||
Cell::VisitAllObjects(this, notifier, m_radius);
|
||||
}
|
||||
|
||||
if (deleteThis)
|
||||
{
|
||||
caster->RemoveDynObjectWithGUID(GetObjectGuid());
|
||||
Delete();
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicObject::Delete()
|
||||
{
|
||||
SendObjectDeSpawnAnim(GetObjectGuid());
|
||||
AddObjectToRemoveList();
|
||||
}
|
||||
|
||||
void DynamicObject::Delay(int32 delaytime)
|
||||
{
|
||||
m_aliveDuration -= delaytime;
|
||||
for (GuidSet::iterator iter = m_affected.begin(); iter != m_affected.end();)
|
||||
{
|
||||
Unit* target = GetMap()->GetUnit((*iter));
|
||||
if (target)
|
||||
{
|
||||
SpellAuraHolder* holder = target->GetSpellAuraHolder(m_spellId, GetCasterGuid());
|
||||
if (!holder)
|
||||
{
|
||||
++iter;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool foundAura = false;
|
||||
for (int32 i = m_effIndex + 1; i < MAX_EFFECT_INDEX; ++i)
|
||||
{
|
||||
SpellEffectEntry const* effect = holder->GetSpellProto()->GetSpellEffect(SpellEffectIndex(i));
|
||||
if(!effect)
|
||||
continue;
|
||||
if ((effect->Effect == SPELL_EFFECT_PERSISTENT_AREA_AURA || effect->Effect == SPELL_EFFECT_ADD_FARSIGHT) && holder->m_auras[i])
|
||||
{
|
||||
foundAura = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundAura)
|
||||
{
|
||||
++iter;
|
||||
continue;
|
||||
}
|
||||
|
||||
target->DelaySpellAuraHolder(m_spellId, delaytime, GetCasterGuid());
|
||||
++iter;
|
||||
}
|
||||
else
|
||||
m_affected.erase(iter++);
|
||||
}
|
||||
}
|
||||
|
||||
bool DynamicObject::isVisibleForInState(Player const* u, WorldObject const* viewPoint, bool inVisibleList) const
|
||||
{
|
||||
if (!IsInWorld() || !u->IsInWorld())
|
||||
return false;
|
||||
|
||||
// always seen by owner
|
||||
if (GetCasterGuid() == u->GetObjectGuid())
|
||||
return true;
|
||||
|
||||
// normal case
|
||||
return IsWithinDistInMap(viewPoint, GetMap()->GetVisibilityDistance() + (inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f), false);
|
||||
}
|
||||
|
||||
bool DynamicObject::IsHostileTo(Unit const* unit) const
|
||||
{
|
||||
if (Unit* owner = GetCaster())
|
||||
return owner->IsHostileTo(unit);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DynamicObject::IsFriendlyTo(Unit const* unit) const
|
||||
{
|
||||
if (Unit* owner = GetCaster())
|
||||
return owner->IsFriendlyTo(unit);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
87
src/game/Object/DynamicObject.h
Normal file
87
src/game/Object/DynamicObject.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOSSERVER_DYNAMICOBJECT_H
|
||||
#define MANGOSSERVER_DYNAMICOBJECT_H
|
||||
|
||||
#include "Object.h"
|
||||
#include "DBCEnums.h"
|
||||
#include "Unit.h"
|
||||
|
||||
enum DynamicObjectType
|
||||
{
|
||||
DYNAMIC_OBJECT_PORTAL = 0x0, // unused
|
||||
DYNAMIC_OBJECT_AREA_SPELL = 0x1,
|
||||
DYNAMIC_OBJECT_FARSIGHT_FOCUS = 0x2,
|
||||
DYNAMIC_OBJECT_RAID_MARKER = 0x3,
|
||||
};
|
||||
|
||||
struct SpellEntry;
|
||||
|
||||
class DynamicObject : public WorldObject
|
||||
{
|
||||
public:
|
||||
explicit DynamicObject();
|
||||
|
||||
void AddToWorld() override;
|
||||
void RemoveFromWorld() override;
|
||||
|
||||
bool Create(uint32 guidlow, Unit* caster, uint32 spellId, SpellEffectIndex effIndex, float x, float y, float z, int32 duration, float radius, DynamicObjectType type);
|
||||
void Update(uint32 update_diff, uint32 p_time) override;
|
||||
void Delete();
|
||||
uint32 GetSpellId() const { return m_spellId; }
|
||||
SpellEffectIndex GetEffIndex() const { return m_effIndex; }
|
||||
uint32 GetDuration() const { return m_aliveDuration; }
|
||||
ObjectGuid const& GetCasterGuid() const { return GetGuidValue(DYNAMICOBJECT_CASTER); }
|
||||
Unit* GetCaster() const;
|
||||
float GetRadius() const { return m_radius; }
|
||||
DynamicObjectType GetType() const { return (DynamicObjectType)GetByteValue(DYNAMICOBJECT_BYTES, 0); }
|
||||
bool IsAffecting(Unit* unit) const { return m_affected.find(unit->GetObjectGuid()) != m_affected.end(); }
|
||||
void AddAffected(Unit* unit) { m_affected.insert(unit->GetObjectGuid()); }
|
||||
void RemoveAffected(Unit* unit) { m_affected.erase(unit->GetObjectGuid()); }
|
||||
void Delay(int32 delaytime);
|
||||
|
||||
bool IsHostileTo(Unit const* unit) const override;
|
||||
bool IsFriendlyTo(Unit const* unit) const override;
|
||||
|
||||
float GetObjectBoundingRadius() const override // overwrite WorldObject version
|
||||
{
|
||||
return 0.0f; // dynamic object not have real interact size
|
||||
}
|
||||
|
||||
bool isVisibleForInState(Player const* u, WorldObject const* viewPoint, bool inVisibleList) const override;
|
||||
|
||||
GridReference<DynamicObject> &GetGridRef() { return m_gridRef; }
|
||||
|
||||
protected:
|
||||
uint32 m_spellId;
|
||||
SpellEffectIndex m_effIndex;
|
||||
int32 m_aliveDuration;
|
||||
float m_radius; // radius apply persistent effect, 0 = no persistent effect
|
||||
bool m_positive;
|
||||
GuidSet m_affected;
|
||||
private:
|
||||
GridReference<DynamicObject> m_gridRef;
|
||||
};
|
||||
#endif
|
||||
162
src/game/Object/Formulas.h
Normal file
162
src/game/Object/Formulas.h
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOS_FORMULAS_H
|
||||
#define MANGOS_FORMULAS_H
|
||||
|
||||
#include "World.h"
|
||||
|
||||
namespace MaNGOS
|
||||
{
|
||||
namespace Honor
|
||||
{
|
||||
inline float hk_honor_at_level(uint32 level, uint32 count = 1)
|
||||
{
|
||||
return (float)ceil(count * (-0.53177f + 0.59357f * exp((level + 23.54042f) / 26.07859f)));
|
||||
}
|
||||
}
|
||||
namespace XP
|
||||
{
|
||||
enum XPColorChar { RED, ORANGE, YELLOW, GREEN, GRAY };
|
||||
|
||||
inline uint32 GetGrayLevel(uint32 pl_level)
|
||||
{
|
||||
if (pl_level <= 5)
|
||||
return 0;
|
||||
else if (pl_level <= 39)
|
||||
return pl_level - 5 - pl_level / 10;
|
||||
else if (pl_level <= 59)
|
||||
return pl_level - 1 - pl_level / 5;
|
||||
else
|
||||
return pl_level - 9;
|
||||
}
|
||||
|
||||
inline XPColorChar GetColorCode(uint32 pl_level, uint32 mob_level)
|
||||
{
|
||||
if (mob_level >= pl_level + 5)
|
||||
return RED;
|
||||
else if (mob_level >= pl_level + 3)
|
||||
return ORANGE;
|
||||
else if (mob_level >= pl_level - 2)
|
||||
return YELLOW;
|
||||
else if (mob_level > GetGrayLevel(pl_level))
|
||||
return GREEN;
|
||||
else
|
||||
return GRAY;
|
||||
}
|
||||
|
||||
inline uint32 GetZeroDifference(uint32 pl_level)
|
||||
{
|
||||
if (pl_level < 8) return 5;
|
||||
if (pl_level < 10) return 6;
|
||||
if (pl_level < 12) return 7;
|
||||
if (pl_level < 16) return 8;
|
||||
if (pl_level < 20) return 9;
|
||||
if (pl_level < 30) return 11;
|
||||
if (pl_level < 40) return 12;
|
||||
if (pl_level < 45) return 13;
|
||||
if (pl_level < 50) return 14;
|
||||
if (pl_level < 55) return 15;
|
||||
if (pl_level < 60) return 16;
|
||||
return 17;
|
||||
}
|
||||
|
||||
inline uint32 BaseGain(uint32 pl_level, uint32 mob_level, ContentLevels content)
|
||||
{
|
||||
uint32 nBaseExp;
|
||||
switch (content)
|
||||
{
|
||||
case CONTENT_1_60: nBaseExp = 45; break;
|
||||
case CONTENT_61_70: nBaseExp = 235; break;
|
||||
case CONTENT_71_80: nBaseExp = 580; break;
|
||||
case CONTENT_81_85: nBaseExp = 1878; break;
|
||||
default:
|
||||
sLog.outError("BaseGain: Unsupported content level %u", content);
|
||||
nBaseExp = 45; break;
|
||||
}
|
||||
|
||||
if (mob_level >= pl_level)
|
||||
{
|
||||
uint32 nLevelDiff = mob_level - pl_level;
|
||||
if (nLevelDiff > 4)
|
||||
nLevelDiff = 4;
|
||||
return ((pl_level * 5 + nBaseExp) * (20 + nLevelDiff) / 10 + 1) / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32 gray_level = GetGrayLevel(pl_level);
|
||||
if (mob_level > gray_level)
|
||||
{
|
||||
uint32 ZD = GetZeroDifference(pl_level);
|
||||
return (pl_level * 5 + nBaseExp) * (ZD + mob_level - pl_level) / ZD;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline uint32 Gain(Player* pl, Unit* u)
|
||||
{
|
||||
if (u->GetTypeId() == TYPEID_UNIT && (
|
||||
((Creature*)u)->IsTotem() || ((Creature*)u)->IsPet() ||
|
||||
(((Creature*)u)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_XP_AT_KILL)))
|
||||
return 0;
|
||||
|
||||
uint32 xp_gain = BaseGain(pl->getLevel(), u->getLevel(), GetContentLevelsForMapAndZone(pl->GetMapId(), pl->GetZoneId()));
|
||||
if (xp_gain == 0)
|
||||
return 0;
|
||||
|
||||
if (u->GetTypeId() == TYPEID_UNIT && ((Creature*)u)->IsElite())
|
||||
xp_gain *= 2;
|
||||
|
||||
return (uint32)(xp_gain * sWorld.getConfig(CONFIG_FLOAT_RATE_XP_KILL));
|
||||
}
|
||||
|
||||
inline float xp_in_group_rate(uint32 count, bool isRaid)
|
||||
{
|
||||
if (isRaid)
|
||||
{
|
||||
// FIX ME: must apply decrease modifiers dependent from raid size
|
||||
return 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (count)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
return 1.0f;
|
||||
case 3:
|
||||
return 1.166f;
|
||||
case 4:
|
||||
return 1.3f;
|
||||
case 5:
|
||||
default:
|
||||
return 1.4f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
98
src/game/Object/GMTicketMgr.cpp
Normal file
98
src/game/Object/GMTicketMgr.cpp
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include "Common.h"
|
||||
#include "Database/DatabaseEnv.h"
|
||||
#include "SQLStorages.h"
|
||||
#include "GMTicketMgr.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "ProgressBar.h"
|
||||
#include "Policies/Singleton.h"
|
||||
#include "Player.h"
|
||||
|
||||
INSTANTIATE_SINGLETON_1(GMTicketMgr);
|
||||
|
||||
void GMTicketMgr::LoadGMTickets()
|
||||
{
|
||||
m_GMTicketMap.clear(); // For reload case
|
||||
|
||||
QueryResult* result = CharacterDatabase.Query(
|
||||
// 0 1 2 3 4
|
||||
"SELECT guid, ticket_text, response_text, UNIX_TIMESTAMP(ticket_lastchange), ticket_id FROM character_ticket ORDER BY ticket_id ASC");
|
||||
|
||||
if (!result)
|
||||
{
|
||||
BarGoLink bar(1);
|
||||
|
||||
bar.step();
|
||||
|
||||
sLog.outString();
|
||||
sLog.outString(">> Loaded `character_ticket`, table is empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
BarGoLink bar(result->GetRowCount());
|
||||
|
||||
do
|
||||
{
|
||||
bar.step();
|
||||
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
uint32 guidlow = fields[0].GetUInt32();
|
||||
if (!guidlow)
|
||||
continue;
|
||||
|
||||
ObjectGuid guid = ObjectGuid(HIGHGUID_PLAYER, guidlow);
|
||||
|
||||
GMTicket& ticket = m_GMTicketMap[guid];
|
||||
|
||||
if (ticket.GetPlayerGuid()) // already exist
|
||||
{
|
||||
CharacterDatabase.PExecute("DELETE FROM character_ticket WHERE ticket_id = '%u'", fields[4].GetUInt32());
|
||||
continue;
|
||||
}
|
||||
|
||||
ticket.Init(guid, fields[1].GetCppString(), fields[2].GetCppString(), time_t(fields[3].GetUInt64()));
|
||||
m_GMTicketListByCreatingOrder.push_back(&ticket);
|
||||
}
|
||||
while (result->NextRow());
|
||||
delete result;
|
||||
|
||||
sLog.outString();
|
||||
sLog.outString(">> Loaded " SIZEFMTD " GM tickets", GetTicketCount());
|
||||
}
|
||||
|
||||
void GMTicketMgr::DeleteAll()
|
||||
{
|
||||
for (GMTicketMap::const_iterator itr = m_GMTicketMap.begin(); itr != m_GMTicketMap.end(); ++itr)
|
||||
{
|
||||
if (Player* owner = sObjectMgr.GetPlayer(itr->first))
|
||||
owner->GetSession()->SendGMTicketGetTicket(0x0A);
|
||||
}
|
||||
CharacterDatabase.Execute("DELETE FROM character_ticket");
|
||||
m_GMTicketListByCreatingOrder.clear();
|
||||
m_GMTicketMap.clear();
|
||||
}
|
||||
184
src/game/Object/GMTicketMgr.h
Normal file
184
src/game/Object/GMTicketMgr.h
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef _GMTICKETMGR_H
|
||||
#define _GMTICKETMGR_H
|
||||
|
||||
#include "Policies/Singleton.h"
|
||||
#include "Database/DatabaseEnv.h"
|
||||
#include "Util.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include <map>
|
||||
|
||||
class GMTicket
|
||||
{
|
||||
public:
|
||||
explicit GMTicket() : m_lastUpdate(0)
|
||||
{
|
||||
}
|
||||
|
||||
void Init(ObjectGuid guid, const std::string& text, const std::string& responsetext, time_t update)
|
||||
{
|
||||
m_guid = guid;
|
||||
m_text = text;
|
||||
m_responseText = responsetext;
|
||||
m_lastUpdate = update;
|
||||
}
|
||||
|
||||
ObjectGuid const& GetPlayerGuid() const
|
||||
{
|
||||
return m_guid;
|
||||
}
|
||||
|
||||
const char* GetText() const
|
||||
{
|
||||
return m_text.c_str();
|
||||
}
|
||||
|
||||
const char* GetResponse() const
|
||||
{
|
||||
return m_responseText.c_str();
|
||||
}
|
||||
|
||||
uint64 GetLastUpdate() const
|
||||
{
|
||||
return m_lastUpdate;
|
||||
}
|
||||
|
||||
void SetText(const char* text)
|
||||
{
|
||||
m_text = text ? text : "";
|
||||
m_lastUpdate = time(NULL);
|
||||
|
||||
std::string escapedString = m_text;
|
||||
CharacterDatabase.escape_string(escapedString);
|
||||
CharacterDatabase.PExecute("UPDATE character_ticket SET ticket_text = '%s' WHERE guid = '%u'", escapedString.c_str(), m_guid.GetCounter());
|
||||
}
|
||||
|
||||
void SetResponseText(const char* text)
|
||||
{
|
||||
m_responseText = text ? text : "";
|
||||
m_lastUpdate = time(NULL);
|
||||
|
||||
std::string escapedString = m_responseText;
|
||||
CharacterDatabase.escape_string(escapedString);
|
||||
CharacterDatabase.PExecute("UPDATE character_ticket SET response_text = '%s' WHERE guid = '%u'", escapedString.c_str(), m_guid.GetCounter());
|
||||
}
|
||||
|
||||
bool HasResponse() { return !m_responseText.empty(); }
|
||||
|
||||
void DeleteFromDB() const
|
||||
{
|
||||
CharacterDatabase.PExecute("DELETE FROM character_ticket WHERE guid = '%u' LIMIT 1", m_guid.GetCounter());
|
||||
}
|
||||
|
||||
void SaveToDB() const
|
||||
{
|
||||
CharacterDatabase.BeginTransaction();
|
||||
DeleteFromDB();
|
||||
|
||||
std::string escapedString = m_text;
|
||||
CharacterDatabase.escape_string(escapedString);
|
||||
|
||||
std::string escapedString2 = m_responseText;
|
||||
CharacterDatabase.escape_string(escapedString2);
|
||||
|
||||
CharacterDatabase.PExecute("INSERT INTO character_ticket (guid, ticket_text, response_text) VALUES ('%u', '%s', '%s')", m_guid.GetCounter(), escapedString.c_str(), escapedString2.c_str());
|
||||
CharacterDatabase.CommitTransaction();
|
||||
}
|
||||
private:
|
||||
ObjectGuid m_guid;
|
||||
std::string m_text;
|
||||
std::string m_responseText;
|
||||
time_t m_lastUpdate;
|
||||
};
|
||||
typedef std::map<ObjectGuid, GMTicket> GMTicketMap;
|
||||
typedef std::list<GMTicket*> GMTicketList; // for creating order access
|
||||
|
||||
class GMTicketMgr
|
||||
{
|
||||
public:
|
||||
GMTicketMgr() { }
|
||||
~GMTicketMgr() { }
|
||||
|
||||
void LoadGMTickets();
|
||||
|
||||
GMTicket* GetGMTicket(ObjectGuid guid)
|
||||
{
|
||||
GMTicketMap::iterator itr = m_GMTicketMap.find(guid);
|
||||
if (itr == m_GMTicketMap.end())
|
||||
return NULL;
|
||||
return &(itr->second);
|
||||
}
|
||||
|
||||
size_t GetTicketCount() const
|
||||
{
|
||||
return m_GMTicketMap.size();
|
||||
}
|
||||
|
||||
GMTicket* GetGMTicketByOrderPos(uint32 pos)
|
||||
{
|
||||
if (pos >= GetTicketCount())
|
||||
return NULL;
|
||||
|
||||
GMTicketList::iterator itr = m_GMTicketListByCreatingOrder.begin();
|
||||
std::advance(itr, pos);
|
||||
if (itr == m_GMTicketListByCreatingOrder.end())
|
||||
return NULL;
|
||||
return *itr;
|
||||
}
|
||||
|
||||
|
||||
void Delete(ObjectGuid guid)
|
||||
{
|
||||
GMTicketMap::iterator itr = m_GMTicketMap.find(guid);
|
||||
if (itr == m_GMTicketMap.end())
|
||||
return;
|
||||
itr->second.DeleteFromDB();
|
||||
m_GMTicketListByCreatingOrder.remove(&itr->second);
|
||||
m_GMTicketMap.erase(itr);
|
||||
}
|
||||
|
||||
void DeleteAll();
|
||||
|
||||
void Create(ObjectGuid guid, const char* text)
|
||||
{
|
||||
GMTicket& ticket = m_GMTicketMap[guid];
|
||||
if (ticket.GetPlayerGuid()) // overwrite ticket
|
||||
{
|
||||
ticket.DeleteFromDB();
|
||||
m_GMTicketListByCreatingOrder.remove(&ticket);
|
||||
}
|
||||
|
||||
ticket.Init(guid, text, "", time(NULL));
|
||||
ticket.SaveToDB();
|
||||
m_GMTicketListByCreatingOrder.push_back(&ticket);
|
||||
}
|
||||
private:
|
||||
GMTicketMap m_GMTicketMap;
|
||||
GMTicketList m_GMTicketListByCreatingOrder;
|
||||
};
|
||||
|
||||
#define sTicketMgr MaNGOS::Singleton<GMTicketMgr>::Instance()
|
||||
#endif
|
||||
2347
src/game/Object/GameObject.cpp
Normal file
2347
src/game/Object/GameObject.cpp
Normal file
File diff suppressed because it is too large
Load diff
845
src/game/Object/GameObject.h
Normal file
845
src/game/Object/GameObject.h
Normal file
|
|
@ -0,0 +1,845 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOSSERVER_GAMEOBJECT_H
|
||||
#define MANGOSSERVER_GAMEOBJECT_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "Object.h"
|
||||
#include "LootMgr.h"
|
||||
#include "Database/DatabaseEnv.h"
|
||||
|
||||
// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform
|
||||
#if defined( __GNUC__ )
|
||||
#pragma pack(1)
|
||||
#else
|
||||
#pragma pack(push,1)
|
||||
#endif
|
||||
|
||||
// from `gameobject_template`
|
||||
struct GameObjectInfo
|
||||
{
|
||||
uint32 id;
|
||||
uint32 type;
|
||||
uint32 displayId;
|
||||
char* name;
|
||||
char* IconName;
|
||||
char* castBarCaption;
|
||||
char* unk1;
|
||||
uint32 faction;
|
||||
uint32 flags;
|
||||
float size;
|
||||
uint32 questItems[6];
|
||||
union // different GO types have different data field
|
||||
{
|
||||
// 0 GAMEOBJECT_TYPE_DOOR
|
||||
struct
|
||||
{
|
||||
uint32 startOpen; // 0 used client side to determine GO_ACTIVATED means open/closed
|
||||
uint32 lockId; // 1 -> Lock.dbc
|
||||
uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / IN_MILLISECONDS (previous was 0x10000)
|
||||
uint32 noDamageImmune; // 3 break opening whenever you recieve damage?
|
||||
uint32 openTextID; // 4 can be used to replace castBarCaption?
|
||||
uint32 closeTextID; // 5
|
||||
uint32 ignoredByPathing; //6
|
||||
} door;
|
||||
// 1 GAMEOBJECT_TYPE_BUTTON
|
||||
struct
|
||||
{
|
||||
uint32 startOpen; // 0
|
||||
uint32 lockId; // 1 -> Lock.dbc
|
||||
uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / IN_MILLISECONDS (previous was 0x10000)
|
||||
uint32 linkedTrapId; // 3
|
||||
uint32 noDamageImmune; // 4 isBattlegroundObject
|
||||
uint32 large; // 5
|
||||
uint32 openTextID; // 6 can be used to replace castBarCaption?
|
||||
uint32 closeTextID; // 7
|
||||
uint32 losOK; // 8
|
||||
} button;
|
||||
// 2 GAMEOBJECT_TYPE_QUESTGIVER
|
||||
struct
|
||||
{
|
||||
uint32 lockId; // 0 -> Lock.dbc
|
||||
uint32 questList; // 1
|
||||
uint32 pageMaterial; // 2
|
||||
uint32 gossipID; // 3
|
||||
uint32 customAnim; // 4
|
||||
uint32 noDamageImmune; // 5
|
||||
uint32 openTextID; // 6 can be used to replace castBarCaption?
|
||||
uint32 losOK; // 7
|
||||
uint32 allowMounted; // 8
|
||||
uint32 large; // 9
|
||||
} questgiver;
|
||||
// 3 GAMEOBJECT_TYPE_CHEST
|
||||
struct
|
||||
{
|
||||
uint32 lockId; // 0 -> Lock.dbc
|
||||
uint32 lootId; // 1
|
||||
uint32 chestRestockTime; // 2
|
||||
uint32 consumable; // 3
|
||||
uint32 minSuccessOpens; // 4
|
||||
uint32 maxSuccessOpens; // 5
|
||||
uint32 eventId; // 6 lootedEvent
|
||||
uint32 linkedTrapId; // 7
|
||||
uint32 questId; // 8 not used currently but store quest required for GO activation for player
|
||||
uint32 level; // 9
|
||||
uint32 losOK; // 10
|
||||
uint32 leaveLoot; // 11
|
||||
uint32 notInCombat; // 12
|
||||
uint32 logLoot; // 13
|
||||
uint32 openTextID; // 14 can be used to replace castBarCaption?
|
||||
uint32 groupLootRules; // 15
|
||||
uint32 floatingTooltip; //16
|
||||
} chest;
|
||||
// 4 GAMEOBJECT_TYPE_BINDER - empty
|
||||
// 5 GAMEOBJECT_TYPE_GENERIC
|
||||
struct
|
||||
{
|
||||
uint32 floatingTooltip; // 0
|
||||
uint32 highlight; // 1
|
||||
uint32 serverOnly; // 2
|
||||
uint32 large; // 3
|
||||
uint32 floatOnWater; // 4
|
||||
uint32 questID; // 5
|
||||
} _generic;
|
||||
// 6 GAMEOBJECT_TYPE_TRAP
|
||||
struct
|
||||
{
|
||||
uint32 lockId; // 0 -> Lock.dbc
|
||||
uint32 level; // 1
|
||||
uint32 radius; // 2 radius for trap activation
|
||||
uint32 spellId; // 3
|
||||
uint32 charges; // 4 need respawn (if > 0)
|
||||
uint32 cooldown; // 5 time in secs
|
||||
uint32 autoCloseTime; //6 secs till autoclose = autoCloseTime / IN_MILLISECONDS (previous was 0x10000)
|
||||
uint32 startDelay; // 7
|
||||
uint32 serverOnly; // 8
|
||||
uint32 stealthed; // 9
|
||||
uint32 large; // 10
|
||||
uint32 stealthAffected; // 11
|
||||
uint32 openTextID; // 12 can be used to replace castBarCaption?
|
||||
uint32 closeTextID; // 13
|
||||
uint32 ignoreTotems; //14
|
||||
} trap;
|
||||
// 7 GAMEOBJECT_TYPE_CHAIR
|
||||
struct
|
||||
{
|
||||
uint32 slots; // 0
|
||||
uint32 height; // 1
|
||||
uint32 onlyCreatorUse; // 2
|
||||
uint32 triggeredEvent; //3
|
||||
} chair;
|
||||
// 8 GAMEOBJECT_TYPE_SPELL_FOCUS
|
||||
struct
|
||||
{
|
||||
uint32 focusId; // 0
|
||||
uint32 dist; // 1
|
||||
uint32 linkedTrapId; // 2
|
||||
uint32 serverOnly; // 3
|
||||
uint32 questID; // 4
|
||||
uint32 large; // 5
|
||||
uint32 floatingTooltip; //6
|
||||
} spellFocus;
|
||||
// 9 GAMEOBJECT_TYPE_TEXT
|
||||
struct
|
||||
{
|
||||
uint32 pageID; // 0
|
||||
uint32 language; // 1
|
||||
uint32 pageMaterial; // 2
|
||||
uint32 allowMounted; // 3
|
||||
} text;
|
||||
// 10 GAMEOBJECT_TYPE_GOOBER
|
||||
struct
|
||||
{
|
||||
uint32 lockId; // 0 -> Lock.dbc
|
||||
uint32 questId; // 1
|
||||
uint32 eventId; // 2
|
||||
uint32 autoCloseTime; //3 secs till autoclose = autoCloseTime / IN_MILLISECONDS (previous was 0x10000)
|
||||
uint32 customAnim; // 4
|
||||
uint32 consumable; // 5
|
||||
uint32 cooldown; // 6
|
||||
uint32 pageId; // 7
|
||||
uint32 language; // 8
|
||||
uint32 pageMaterial; // 9
|
||||
uint32 spellId; // 10
|
||||
uint32 noDamageImmune; // 11
|
||||
uint32 linkedTrapId; // 12
|
||||
uint32 large; // 13
|
||||
uint32 openTextID; // 14 can be used to replace castBarCaption?
|
||||
uint32 closeTextID; // 15
|
||||
uint32 losOK; // 16 isBattlegroundObject
|
||||
uint32 allowMounted; // 17
|
||||
uint32 floatingTooltip; // 18
|
||||
uint32 gossipID; // 19
|
||||
uint32 WorldStateSetsState; //20
|
||||
} goober;
|
||||
// 11 GAMEOBJECT_TYPE_TRANSPORT
|
||||
struct
|
||||
{
|
||||
uint32 startFrame; //0
|
||||
uint32 startOpen; // 1
|
||||
uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / IN_MILLISECONDS (previous was 0x10000)
|
||||
uint32 pause1EventID; //3
|
||||
uint32 pause2EventID; //4
|
||||
uint32 baseMap; //5
|
||||
uint32 nextFrame1; //6
|
||||
uint32 unk7; //7
|
||||
uint32 nextFrame2; //8
|
||||
uint32 unk9; //9
|
||||
uint32 nextFrame3; //10
|
||||
uint32 unk11; //11
|
||||
uint32 unk12; //12
|
||||
uint32 unk13; //13
|
||||
uint32 unk14; //14
|
||||
uint32 unk15; //15
|
||||
uint32 unk16; //16
|
||||
uint32 unk17; //17
|
||||
uint32 unk18; //18
|
||||
uint32 unk19; //19
|
||||
uint32 unk20; //20
|
||||
uint32 unk21; //21
|
||||
uint32 unk22; //22 ring of valor elevators
|
||||
uint32 unk23; //23 ring of valor elevators
|
||||
} transport;
|
||||
// 12 GAMEOBJECT_TYPE_AREADAMAGE
|
||||
struct
|
||||
{
|
||||
uint32 lockId; // 0
|
||||
uint32 radius; // 1
|
||||
uint32 damageMin; // 2
|
||||
uint32 damageMax; // 3
|
||||
uint32 damageSchool; // 4
|
||||
uint32 autoCloseTime; //5 secs till autoclose = autoCloseTime / IN_MILLISECONDS (previous was 0x10000)
|
||||
uint32 openTextID; // 6
|
||||
uint32 closeTextID; // 7
|
||||
} areadamage;
|
||||
// 13 GAMEOBJECT_TYPE_CAMERA
|
||||
struct
|
||||
{
|
||||
uint32 lockId; // 0 -> Lock.dbc
|
||||
uint32 cinematicId; // 1
|
||||
uint32 eventID; // 2
|
||||
uint32 openTextID; // 3 can be used to replace castBarCaption?
|
||||
} camera;
|
||||
// 14 GAMEOBJECT_TYPE_MAPOBJECT - empty
|
||||
// 15 GAMEOBJECT_TYPE_MO_TRANSPORT
|
||||
struct
|
||||
{
|
||||
uint32 taxiPathId; // 0
|
||||
uint32 moveSpeed; // 1
|
||||
uint32 accelRate; // 2
|
||||
uint32 startEventID; // 3
|
||||
uint32 stopEventID; // 4
|
||||
uint32 transportPhysics; // 5
|
||||
uint32 mapID; // 6
|
||||
uint32 worldState1; //7
|
||||
} moTransport;
|
||||
// 16 GAMEOBJECT_TYPE_DUELFLAG - empty
|
||||
//17 GAMEOBJECT_TYPE_FISHINGNODE - empty
|
||||
// 18 GAMEOBJECT_TYPE_SUMMONING_RITUAL
|
||||
struct
|
||||
{
|
||||
uint32 reqParticipants; // 0
|
||||
uint32 spellId; // 1
|
||||
uint32 animSpell; // 2
|
||||
uint32 ritualPersistent; // 3
|
||||
uint32 casterTargetSpell; // 4
|
||||
uint32 casterTargetSpellTargets; // 5
|
||||
uint32 castersGrouped; // 6
|
||||
uint32 ritualNoTargetCheck; // 7
|
||||
} summoningRitual;
|
||||
// 19 GAMEOBJECT_TYPE_MAILBOX - empty
|
||||
//20 GAMEOBJECT_TYPE_DONOTUSE - empty
|
||||
// 21 GAMEOBJECT_TYPE_GUARDPOST
|
||||
struct
|
||||
{
|
||||
uint32 creatureID; // 0
|
||||
uint32 charges; // 1
|
||||
} guardpost;
|
||||
// 22 GAMEOBJECT_TYPE_SPELLCASTER
|
||||
struct
|
||||
{
|
||||
uint32 spellId; // 0
|
||||
uint32 charges; // 1
|
||||
uint32 partyOnly; // 2
|
||||
uint32 allowMounted; //3
|
||||
uint32 large; //4
|
||||
} spellcaster;
|
||||
// 23 GAMEOBJECT_TYPE_MEETINGSTONE
|
||||
struct
|
||||
{
|
||||
uint32 minLevel; // 0
|
||||
uint32 maxLevel; // 1
|
||||
uint32 areaID; // 2
|
||||
} meetingstone;
|
||||
// 24 GAMEOBJECT_TYPE_FLAGSTAND
|
||||
struct
|
||||
{
|
||||
uint32 lockId; // 0
|
||||
uint32 pickupSpell; // 1
|
||||
uint32 radius; // 2
|
||||
uint32 returnAura; // 3
|
||||
uint32 returnSpell; // 4
|
||||
uint32 noDamageImmune; // 5
|
||||
uint32 openTextID; // 6
|
||||
uint32 losOK; // 7
|
||||
} flagstand;
|
||||
// 25 GAMEOBJECT_TYPE_FISHINGHOLE
|
||||
struct
|
||||
{
|
||||
uint32 radius; // 0 how close bobber must land for sending loot
|
||||
uint32 lootId; // 1
|
||||
uint32 minSuccessOpens; // 2
|
||||
uint32 maxSuccessOpens; // 3
|
||||
uint32 lockId; // 4 -> Lock.dbc; possibly 1628 for all?
|
||||
} fishinghole;
|
||||
// 26 GAMEOBJECT_TYPE_FLAGDROP
|
||||
struct
|
||||
{
|
||||
uint32 lockId; // 0
|
||||
uint32 eventID; // 1
|
||||
uint32 pickupSpell; // 2
|
||||
uint32 noDamageImmune; // 3
|
||||
uint32 openTextID; // 4
|
||||
} flagdrop;
|
||||
// 27 GAMEOBJECT_TYPE_MINI_GAME
|
||||
struct
|
||||
{
|
||||
uint32 gameType; // 0
|
||||
} miniGame;
|
||||
// 29 GAMEOBJECT_TYPE_CAPTURE_POINT
|
||||
struct
|
||||
{
|
||||
uint32 radius; // 0
|
||||
uint32 spell; // 1
|
||||
uint32 worldState1; // 2
|
||||
uint32 worldState2; // 3
|
||||
uint32 winEventID1; // 4
|
||||
uint32 winEventID2; // 5
|
||||
uint32 contestedEventID1; // 6
|
||||
uint32 contestedEventID2; // 7
|
||||
uint32 progressEventID1; // 8
|
||||
uint32 progressEventID2; // 9
|
||||
uint32 neutralEventID1; // 10
|
||||
uint32 neutralEventID2; // 11
|
||||
uint32 neutralPercent; // 12
|
||||
uint32 worldState3; // 13
|
||||
uint32 minSuperiority; // 14
|
||||
uint32 maxSuperiority; // 15
|
||||
uint32 minTime; // 16
|
||||
uint32 maxTime; // 17
|
||||
uint32 large; // 18
|
||||
uint32 highlight; // 19
|
||||
uint32 startingValue; //20
|
||||
uint32 unidirectional; //21
|
||||
} capturePoint;
|
||||
// 30 GAMEOBJECT_TYPE_AURA_GENERATOR
|
||||
struct
|
||||
{
|
||||
uint32 startOpen; // 0
|
||||
uint32 radius; // 1
|
||||
uint32 auraID1; // 2
|
||||
uint32 conditionID1; // 3
|
||||
uint32 auraID2; // 4
|
||||
uint32 conditionID2; // 5
|
||||
uint32 serverOnly; // 6
|
||||
} auraGenerator;
|
||||
//31 GAMEOBJECT_TYPE_DUNGEON_DIFFICULTY
|
||||
struct
|
||||
{
|
||||
uint32 mapID; //0
|
||||
uint32 difficulty; //1
|
||||
} dungeonDifficulty;
|
||||
//32 GAMEOBJECT_TYPE_BARBER_CHAIR
|
||||
struct
|
||||
{
|
||||
uint32 chairheight; //0
|
||||
uint32 heightOffset; //1
|
||||
} barberChair;
|
||||
//33 GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING // Much guesswork
|
||||
struct
|
||||
{
|
||||
uint32 intactNumHits; //0
|
||||
uint32 creditProxyCreature; //1
|
||||
uint32 empty1; //2
|
||||
uint32 intactEvent; //3
|
||||
uint32 damagedDisplayId; //4
|
||||
uint32 damagedNumHits; //5
|
||||
uint32 unk1; //6
|
||||
uint32 unk2; //7
|
||||
uint32 unk3; //8
|
||||
uint32 damagedEvent; //9
|
||||
uint32 destroyedDisplayId; //10
|
||||
uint32 unk4; //11
|
||||
uint32 unk5; //12
|
||||
uint32 unk6; //13
|
||||
uint32 destroyedEvent; //14
|
||||
uint32 empty10; //15
|
||||
uint32 debuildingTimeSecs; //16 // unk, only few with value 300)
|
||||
uint32 empty11; //17
|
||||
uint32 destructibleData; //18 m_ID of DestructibleModelData.DBC
|
||||
uint32 empty12; //19
|
||||
uint32 unk7; //20
|
||||
uint32 empty13; //21
|
||||
uint32 rebuildingEvent; //22
|
||||
uint32 unk8; //23
|
||||
} destructibleBuilding;
|
||||
//34 GAMEOBJECT_TYPE_GUILDBANK - empty
|
||||
//35 GAMEOBJECT_TYPE_TRAPDOOR
|
||||
struct
|
||||
{
|
||||
uint32 whenToPause; // 0
|
||||
uint32 startOpen; // 1
|
||||
uint32 autoClose; // 2
|
||||
} trapDoor;
|
||||
|
||||
// not use for specific field access (only for output with loop by all filed), also this determinate max union size
|
||||
struct
|
||||
{
|
||||
uint32 data[32];
|
||||
} raw;
|
||||
};
|
||||
|
||||
uint32 unk2;
|
||||
uint32 MinMoneyLoot;
|
||||
uint32 MaxMoneyLoot;
|
||||
uint32 ScriptId;
|
||||
|
||||
// helpers
|
||||
bool IsDespawnAtAction() const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GAMEOBJECT_TYPE_CHEST: return chest.consumable;
|
||||
case GAMEOBJECT_TYPE_GOOBER: return goober.consumable;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 GetLockId() const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GAMEOBJECT_TYPE_DOOR: return door.lockId;
|
||||
case GAMEOBJECT_TYPE_BUTTON: return button.lockId;
|
||||
case GAMEOBJECT_TYPE_QUESTGIVER: return questgiver.lockId;
|
||||
case GAMEOBJECT_TYPE_CHEST: return chest.lockId;
|
||||
case GAMEOBJECT_TYPE_TRAP: return trap.lockId;
|
||||
case GAMEOBJECT_TYPE_GOOBER: return goober.lockId;
|
||||
case GAMEOBJECT_TYPE_AREADAMAGE: return areadamage.lockId;
|
||||
case GAMEOBJECT_TYPE_CAMERA: return camera.lockId;
|
||||
case GAMEOBJECT_TYPE_FLAGSTAND: return flagstand.lockId;
|
||||
case GAMEOBJECT_TYPE_FISHINGHOLE: return fishinghole.lockId;
|
||||
case GAMEOBJECT_TYPE_FLAGDROP: return flagdrop.lockId;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool GetDespawnPossibility() const // despawn at targeting of cast?
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GAMEOBJECT_TYPE_DOOR: return door.noDamageImmune;
|
||||
case GAMEOBJECT_TYPE_BUTTON: return button.noDamageImmune;
|
||||
case GAMEOBJECT_TYPE_QUESTGIVER: return questgiver.noDamageImmune;
|
||||
case GAMEOBJECT_TYPE_GOOBER: return goober.noDamageImmune;
|
||||
case GAMEOBJECT_TYPE_FLAGSTAND: return flagstand.noDamageImmune;
|
||||
case GAMEOBJECT_TYPE_FLAGDROP: return flagdrop.noDamageImmune;
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 GetCharges() const // despawn at uses amount
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GAMEOBJECT_TYPE_TRAP: return trap.charges;
|
||||
case GAMEOBJECT_TYPE_GUARDPOST: return guardpost.charges;
|
||||
case GAMEOBJECT_TYPE_SPELLCASTER: return spellcaster.charges;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 GetCooldown() const // not triggering at detection target or use until coolodwn expire
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GAMEOBJECT_TYPE_TRAP: return trap.cooldown;
|
||||
case GAMEOBJECT_TYPE_GOOBER: return goober.cooldown;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 GetLinkedGameObjectEntry() const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GAMEOBJECT_TYPE_BUTTON: return button.linkedTrapId;
|
||||
case GAMEOBJECT_TYPE_CHEST: return chest.linkedTrapId;
|
||||
case GAMEOBJECT_TYPE_SPELL_FOCUS: return spellFocus.linkedTrapId;
|
||||
case GAMEOBJECT_TYPE_GOOBER: return goober.linkedTrapId;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 GetAutoCloseTime() const
|
||||
{
|
||||
uint32 autoCloseTime = 0;
|
||||
switch (type)
|
||||
{
|
||||
case GAMEOBJECT_TYPE_DOOR: autoCloseTime = door.autoCloseTime; break;
|
||||
case GAMEOBJECT_TYPE_BUTTON: autoCloseTime = button.autoCloseTime; break;
|
||||
case GAMEOBJECT_TYPE_TRAP: autoCloseTime = trap.autoCloseTime; break;
|
||||
case GAMEOBJECT_TYPE_GOOBER: autoCloseTime = goober.autoCloseTime; break;
|
||||
case GAMEOBJECT_TYPE_TRANSPORT: autoCloseTime = transport.autoCloseTime; break;
|
||||
case GAMEOBJECT_TYPE_AREADAMAGE: autoCloseTime = areadamage.autoCloseTime; break;
|
||||
default: break;
|
||||
}
|
||||
return autoCloseTime / IN_MILLISECONDS; // prior to 3.0.3, conversion was / 0x10000;
|
||||
}
|
||||
|
||||
uint32 GetLootId() const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GAMEOBJECT_TYPE_CHEST: return chest.lootId;
|
||||
case GAMEOBJECT_TYPE_FISHINGHOLE: return fishinghole.lootId;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 GetGossipMenuId() const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GAMEOBJECT_TYPE_QUESTGIVER: return questgiver.gossipID;
|
||||
case GAMEOBJECT_TYPE_GOOBER: return goober.gossipID;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform
|
||||
#if defined( __GNUC__ )
|
||||
#pragma pack()
|
||||
#else
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
struct GameObjectLocale
|
||||
{
|
||||
std::vector<std::string> Name;
|
||||
std::vector<std::string> CastBarCaption;
|
||||
};
|
||||
|
||||
// client side GO show states
|
||||
enum GOState
|
||||
{
|
||||
GO_STATE_ACTIVE = 0x00, // show in world as used and not reset (closed door open)
|
||||
GO_STATE_READY = 0x01, // show in world as ready (closed door close)
|
||||
GO_STATE_ACTIVE_ALTERNATIVE = 0x02, // show in world as used in alt way and not reset (closed door open by cannon fire)
|
||||
GO_STATE_TRANSPORT_SPEC = 0x18, // additional mask that have all transport gameobjects
|
||||
};
|
||||
|
||||
#define MAX_GO_STATE 3
|
||||
|
||||
struct QuaternionData
|
||||
{
|
||||
float x, y, z, w;
|
||||
|
||||
QuaternionData() : x(0.f), y(0.f), z(0.f), w(0.f) {}
|
||||
QuaternionData(float X, float Y, float Z, float W) : x(X), y(Y), z(Z), w(W) {}
|
||||
|
||||
bool isUnit() const { return fabs(x * x + y * y + z * z + w * w - 1.f) < 1e-5;}
|
||||
};
|
||||
|
||||
// from `gameobject`
|
||||
struct GameObjectData
|
||||
{
|
||||
uint32 id; // entry in gamobject_template
|
||||
uint16 mapid;
|
||||
uint32 phaseMask;
|
||||
float posX;
|
||||
float posY;
|
||||
float posZ;
|
||||
float orientation;
|
||||
QuaternionData rotation;
|
||||
int32 spawntimesecs;
|
||||
uint32 animprogress;
|
||||
GOState go_state;
|
||||
uint8 spawnMask;
|
||||
};
|
||||
|
||||
// from `gameobject_addon`
|
||||
struct GameObjectDataAddon
|
||||
{
|
||||
uint32 guid;
|
||||
QuaternionData path_rotation;
|
||||
};
|
||||
|
||||
// For containers: [GO_NOT_READY]->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED->GO_READY -> ...
|
||||
// For bobber: GO_NOT_READY ->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED-><deleted>
|
||||
// For door(closed):[GO_NOT_READY]->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED->GO_READY(close) -> ...
|
||||
// For door(open): [GO_NOT_READY]->GO_READY (open) ->GO_ACTIVATED (close)->GO_JUST_DEACTIVATED->GO_READY(open) -> ...
|
||||
enum LootState
|
||||
{
|
||||
GO_NOT_READY = 0,
|
||||
GO_READY, // can be ready but despawned, and then not possible activate until spawn
|
||||
GO_ACTIVATED,
|
||||
GO_JUST_DEACTIVATED
|
||||
};
|
||||
|
||||
enum CapturePointState
|
||||
{
|
||||
CAPTURE_STATE_NEUTRAL = 0,
|
||||
CAPTURE_STATE_PROGRESS_ALLIANCE,
|
||||
CAPTURE_STATE_PROGRESS_HORDE,
|
||||
CAPTURE_STATE_CONTEST_ALLIANCE,
|
||||
CAPTURE_STATE_CONTEST_HORDE,
|
||||
CAPTURE_STATE_WIN_ALLIANCE,
|
||||
CAPTURE_STATE_WIN_HORDE
|
||||
};
|
||||
|
||||
enum CapturePointSlider
|
||||
{
|
||||
CAPTURE_SLIDER_ALLIANCE = 100, // full alliance
|
||||
CAPTURE_SLIDER_HORDE = 0, // full horde
|
||||
CAPTURE_SLIDER_MIDDLE = 50 // middle
|
||||
};
|
||||
|
||||
class Unit;
|
||||
class GameObjectModel;
|
||||
struct GameObjectDisplayInfoEntry;
|
||||
|
||||
// 5 sec for bobber catch
|
||||
#define FISHING_BOBBER_READY_TIME 5
|
||||
|
||||
#define GO_ANIMPROGRESS_DEFAULT 0xFF
|
||||
|
||||
class MANGOS_DLL_SPEC GameObject : public WorldObject
|
||||
{
|
||||
public:
|
||||
explicit GameObject();
|
||||
~GameObject();
|
||||
|
||||
void AddToWorld() override;
|
||||
void RemoveFromWorld() override;
|
||||
|
||||
bool Create(uint32 guidlow, uint32 name_id, Map* map, uint32 phaseMask, float x, float y, float z, float ang,
|
||||
QuaternionData rotation = QuaternionData(), uint8 animprogress = GO_ANIMPROGRESS_DEFAULT, GOState go_state = GO_STATE_READY);
|
||||
void Update(uint32 update_diff, uint32 p_time) override;
|
||||
GameObjectInfo const* GetGOInfo() const;
|
||||
|
||||
bool IsTransport() const;
|
||||
|
||||
bool HasStaticDBSpawnData() const; // listed in `gameobject` table and have fixed in DB guid
|
||||
|
||||
// z_rot, y_rot, x_rot - rotation angles around z, y and x axes
|
||||
void SetWorldRotationAngles(float z_rot, float y_rot, float x_rot);
|
||||
void SetWorldRotation(float qx, float qy, float qz, float qw);
|
||||
void SetTransportPathRotation(QuaternionData rotation); // transforms(rotates) transport's path
|
||||
int64 GetPackedWorldRotation() const { return m_packedRotation; }
|
||||
|
||||
// overwrite WorldObject function for proper name localization
|
||||
const char* GetNameForLocaleIdx(int32 locale_idx) const override;
|
||||
|
||||
void SaveToDB();
|
||||
void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask);
|
||||
bool LoadFromDB(uint32 guid, Map* map);
|
||||
void DeleteFromDB();
|
||||
|
||||
void SetOwnerGuid(ObjectGuid ownerGuid)
|
||||
{
|
||||
m_spawnedByDefault = false; // all object with owner is despawned after delay
|
||||
SetGuidValue(OBJECT_FIELD_CREATED_BY, ownerGuid);
|
||||
}
|
||||
ObjectGuid const& GetOwnerGuid() const { return GetGuidValue(OBJECT_FIELD_CREATED_BY); }
|
||||
Unit* GetOwner() const;
|
||||
|
||||
void SetSpellId(uint32 id)
|
||||
{
|
||||
m_spawnedByDefault = false; // all summoned object is despawned after delay
|
||||
m_spellId = id;
|
||||
}
|
||||
uint32 GetSpellId() const { return m_spellId;}
|
||||
|
||||
time_t GetRespawnTime() const { return m_respawnTime; }
|
||||
time_t GetRespawnTimeEx() const
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
if (m_respawnTime > now)
|
||||
return m_respawnTime;
|
||||
else
|
||||
return now;
|
||||
}
|
||||
|
||||
void SetRespawnTime(time_t respawn)
|
||||
{
|
||||
m_respawnTime = respawn > 0 ? time(NULL) + respawn : 0;
|
||||
m_respawnDelayTime = respawn > 0 ? uint32(respawn) : 0;
|
||||
}
|
||||
void Respawn();
|
||||
bool isSpawned() const
|
||||
{
|
||||
return m_respawnDelayTime == 0 ||
|
||||
(m_respawnTime > 0 && !m_spawnedByDefault) ||
|
||||
(m_respawnTime == 0 && m_spawnedByDefault);
|
||||
}
|
||||
bool isSpawnedByDefault() const { return m_spawnedByDefault; }
|
||||
uint32 GetRespawnDelay() const { return m_respawnDelayTime; }
|
||||
void Refresh();
|
||||
void Delete();
|
||||
|
||||
// Functions spawn/remove gameobject with DB guid in all loaded map copies (if point grid loaded in map)
|
||||
static void AddToRemoveListInMaps(uint32 db_guid, GameObjectData const* data);
|
||||
static void SpawnInMaps(uint32 db_guid, GameObjectData const* data);
|
||||
|
||||
GameobjectTypes GetGoType() const { return GameobjectTypes(GetByteValue(GAMEOBJECT_BYTES_1, 1)); }
|
||||
void SetGoType(GameobjectTypes type) { SetByteValue(GAMEOBJECT_BYTES_1, 1, type); }
|
||||
GOState GetGoState() const { return GOState(GetByteValue(GAMEOBJECT_BYTES_1, 0)); }
|
||||
void SetGoState(GOState state);
|
||||
uint8 GetGoArtKit() const { return GetByteValue(GAMEOBJECT_BYTES_1, 2); }
|
||||
void SetGoArtKit(uint8 artkit) { SetByteValue(GAMEOBJECT_BYTES_1, 2, artkit); }
|
||||
uint8 GetGoAnimProgress() const { return GetByteValue(GAMEOBJECT_BYTES_1, 3); }
|
||||
void SetGoAnimProgress(uint8 animprogress) { SetByteValue(GAMEOBJECT_BYTES_1, 3, animprogress); }
|
||||
uint32 GetDisplayId() const { return GetUInt32Value(GAMEOBJECT_DISPLAYID); }
|
||||
void SetDisplayId(uint32 modelId);
|
||||
void SetPhaseMask(uint32 newPhaseMask, bool update);
|
||||
|
||||
float GetObjectBoundingRadius() const override; // overwrite WorldObject version
|
||||
|
||||
void Use(Unit* user);
|
||||
|
||||
LootState getLootState() const { return m_lootState; }
|
||||
void SetLootState(LootState s);
|
||||
|
||||
void AddToSkillupList(Player* player);
|
||||
bool IsInSkillupList(Player* player) const;
|
||||
void ClearSkillupList() { m_SkillupSet.clear(); }
|
||||
void ClearAllUsesData()
|
||||
{
|
||||
ClearSkillupList();
|
||||
m_useTimes = 0;
|
||||
m_firstUser.Clear();
|
||||
m_UniqueUsers.clear();
|
||||
}
|
||||
|
||||
void AddUniqueUse(Player* player);
|
||||
void AddUse() { ++m_useTimes; }
|
||||
|
||||
uint32 GetUseCount() const { return m_useTimes; }
|
||||
uint32 GetUniqueUseCount() const { return m_UniqueUsers.size(); }
|
||||
|
||||
void SaveRespawnTime() override;
|
||||
|
||||
// Loot System
|
||||
Loot loot;
|
||||
void StartGroupLoot(Group* group, uint32 timer) override;
|
||||
|
||||
ObjectGuid GetLootRecipientGuid() const { return m_lootRecipientGuid; }
|
||||
uint32 GetLootGroupRecipientId() const { return m_lootGroupRecipientId; }
|
||||
Player* GetLootRecipient() const; // use group cases as prefered
|
||||
Group* GetGroupLootRecipient() const;
|
||||
bool HasLootRecipient() const { return m_lootGroupRecipientId || !m_lootRecipientGuid.IsEmpty(); }
|
||||
bool IsGroupLootRecipient() const { return m_lootGroupRecipientId; }
|
||||
void SetLootRecipient(Unit* pUnit);
|
||||
Player* GetOriginalLootRecipient() const; // ignore group changes/etc, not for looting
|
||||
|
||||
bool HasQuest(uint32 quest_id) const override;
|
||||
bool HasInvolvedQuest(uint32 quest_id) const override;
|
||||
bool ActivateToQuest(Player* pTarget) const;
|
||||
void UseDoorOrButton(uint32 time_to_restore = 0, bool alternative = false);
|
||||
// 0 = use `gameobject`.`spawntimesecs`
|
||||
void ResetDoorOrButton();
|
||||
|
||||
bool IsHostileTo(Unit const* unit) const override;
|
||||
bool IsFriendlyTo(Unit const* unit) const override;
|
||||
|
||||
void SummonLinkedTrapIfAny();
|
||||
void TriggerLinkedGameObject(Unit* target);
|
||||
|
||||
// Destructible GO handling
|
||||
void DealGameObjectDamage(uint32 damage, uint32 spell, Unit* caster);
|
||||
void RebuildGameObject(uint32 spell, Unit* caster);
|
||||
void ForceGameObjectHealth(int32 diff, Unit* caster);
|
||||
uint32 GetHealth() const { return m_useTimes; }
|
||||
uint32 GetMaxHealth() const { return m_goInfo->destructibleBuilding.intactNumHits + m_goInfo->destructibleBuilding.damagedNumHits; }
|
||||
|
||||
bool isVisibleForInState(Player const* u, WorldObject const* viewPoint, bool inVisibleList) const override;
|
||||
|
||||
bool IsCollisionEnabled() const; // Check if a go should collide. Like if a door is closed
|
||||
|
||||
GameObject* LookupFishingHoleAround(float range);
|
||||
|
||||
void SetCapturePointSlider(float value);
|
||||
float GetCapturePointSlider() const { return m_captureSlider; }
|
||||
|
||||
GridReference<GameObject>& GetGridRef() { return m_gridRef; }
|
||||
|
||||
GameObjectModel* m_model;
|
||||
|
||||
protected:
|
||||
uint32 m_spellId;
|
||||
time_t m_respawnTime; // (secs) time of next respawn (or despawn if GO have owner()),
|
||||
uint32 m_respawnDelayTime; // (secs) if 0 then current GO state no dependent from timer
|
||||
LootState m_lootState;
|
||||
bool m_spawnedByDefault;
|
||||
time_t m_cooldownTime; // used as internal reaction delay time store (not state change reaction).
|
||||
// For traps/goober this: spell casting cooldown, for doors/buttons: reset time.
|
||||
|
||||
uint32 m_captureTimer; // (msecs) timer used for capture points
|
||||
float m_captureSlider; // capture point slider value in range of [0..100]
|
||||
CapturePointState m_captureState;
|
||||
|
||||
GuidSet m_SkillupSet; // players that already have skill-up at GO use
|
||||
|
||||
uint32 m_useTimes; // amount uses/charges triggered - also used for health for DESTRUCTIBLE_BUILDING
|
||||
|
||||
// collected only for GAMEOBJECT_TYPE_SUMMONING_RITUAL
|
||||
ObjectGuid m_firstUser; // first GO user, in most used cases owner, but in some cases no, for example non-summoned multi-use GAMEOBJECT_TYPE_SUMMONING_RITUAL
|
||||
GuidSet m_UniqueUsers; // all players who use item, some items activated after specific amount unique uses
|
||||
|
||||
GameObjectInfo const* m_goInfo;
|
||||
GameObjectDisplayInfoEntry const* m_displayInfo;
|
||||
int64 m_packedRotation;
|
||||
QuaternionData m_worldRotation;
|
||||
|
||||
// Loot System
|
||||
uint32 m_groupLootTimer; // (msecs)timer used for group loot
|
||||
uint32 m_groupLootId; // used to find group which is looting
|
||||
void StopGroupLoot() override;
|
||||
ObjectGuid m_lootRecipientGuid; // player who will have rights for looting if m_lootGroupRecipient==0 or group disbanded
|
||||
uint32 m_lootGroupRecipientId; // group who will have rights for looting if set and exist
|
||||
|
||||
private:
|
||||
void SwitchDoorOrButton(bool activate, bool alternative = false);
|
||||
void TickCapturePoint();
|
||||
void UpdateModel(); // updates model in case displayId were changed
|
||||
void UpdateCollisionState() const; // updates state in Map's dynamic collision tree
|
||||
|
||||
GridReference<GameObject> m_gridRef;
|
||||
};
|
||||
|
||||
#endif
|
||||
150
src/game/Object/GuardAI.cpp
Normal file
150
src/game/Object/GuardAI.cpp
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include "GuardAI.h"
|
||||
#include "Errors.h"
|
||||
#include "Creature.h"
|
||||
#include "Player.h"
|
||||
#include "World.h"
|
||||
|
||||
int GuardAI::Permissible(const Creature* creature)
|
||||
{
|
||||
if (creature->IsGuard())
|
||||
return PERMIT_BASE_SPECIAL;
|
||||
|
||||
return PERMIT_BASE_NO;
|
||||
}
|
||||
|
||||
GuardAI::GuardAI(Creature* c) : CreatureAI(c), i_state(STATE_NORMAL), i_tracker(TIME_INTERVAL_LOOK)
|
||||
{
|
||||
}
|
||||
|
||||
void GuardAI::MoveInLineOfSight(Unit* u)
|
||||
{
|
||||
// Ignore Z for flying creatures
|
||||
if (!m_creature->CanFly() && m_creature->GetDistanceZ(u) > CREATURE_Z_ATTACK_RANGE)
|
||||
return;
|
||||
|
||||
if (!m_creature->getVictim() && u->isTargetableForAttack() &&
|
||||
(u->IsHostileToPlayers() || m_creature->IsHostileTo(u) /*|| u->getVictim() && m_creature->IsFriendlyTo(u->getVictim())*/) &&
|
||||
u->isInAccessablePlaceFor(m_creature))
|
||||
{
|
||||
float attackRadius = m_creature->GetAttackDistance(u);
|
||||
if (m_creature->IsWithinDistInMap(u, attackRadius))
|
||||
{
|
||||
// Need add code to let guard support player
|
||||
AttackStart(u);
|
||||
u->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GuardAI::EnterEvadeMode()
|
||||
{
|
||||
if (!m_creature->isAlive())
|
||||
{
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature stopped attacking because he's dead [guid=%u]", m_creature->GetGUIDLow());
|
||||
m_creature->StopMoving();
|
||||
m_creature->GetMotionMaster()->MoveIdle();
|
||||
|
||||
i_state = STATE_NORMAL;
|
||||
|
||||
i_victimGuid.Clear();
|
||||
m_creature->CombatStop(true);
|
||||
m_creature->DeleteThreatList();
|
||||
return;
|
||||
}
|
||||
|
||||
Unit* victim = m_creature->GetMap()->GetUnit(i_victimGuid);
|
||||
|
||||
if (!victim)
|
||||
{
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature stopped attacking, no victim [guid=%u]", m_creature->GetGUIDLow());
|
||||
}
|
||||
else if (!victim->isAlive())
|
||||
{
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature stopped attacking, victim is dead [guid=%u]", m_creature->GetGUIDLow());
|
||||
}
|
||||
else if (victim->HasStealthAura())
|
||||
{
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature stopped attacking, victim is in stealth [guid=%u]", m_creature->GetGUIDLow());
|
||||
}
|
||||
else if (victim->IsTaxiFlying())
|
||||
{
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature stopped attacking, victim is in flight [guid=%u]", m_creature->GetGUIDLow());
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature stopped attacking, victim out run him [guid=%u]", m_creature->GetGUIDLow());
|
||||
}
|
||||
|
||||
m_creature->RemoveAllAurasOnEvade();
|
||||
m_creature->DeleteThreatList();
|
||||
i_victimGuid.Clear();
|
||||
m_creature->CombatStop(true);
|
||||
i_state = STATE_NORMAL;
|
||||
|
||||
// Remove ChaseMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead
|
||||
if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
|
||||
m_creature->GetMotionMaster()->MoveTargetedHome();
|
||||
}
|
||||
|
||||
void GuardAI::UpdateAI(const uint32 /*diff*/)
|
||||
{
|
||||
// update i_victimGuid if i_creature.getVictim() !=0 and changed
|
||||
if (!m_creature->SelectHostileTarget() || !m_creature->getVictim())
|
||||
return;
|
||||
|
||||
i_victimGuid = m_creature->getVictim()->GetObjectGuid();
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
|
||||
bool GuardAI::IsVisible(Unit* pl) const
|
||||
{
|
||||
return m_creature->IsWithinDist(pl, sWorld.getConfig(CONFIG_FLOAT_SIGHT_GUARDER))
|
||||
&& pl->isVisibleForOrDetect(m_creature, m_creature, true);
|
||||
}
|
||||
|
||||
void GuardAI::AttackStart(Unit* u)
|
||||
{
|
||||
if (!u)
|
||||
return;
|
||||
|
||||
if (m_creature->Attack(u, true))
|
||||
{
|
||||
i_victimGuid = u->GetObjectGuid();
|
||||
m_creature->AddThreat(u);
|
||||
m_creature->SetInCombatWith(u);
|
||||
u->SetInCombatWith(m_creature);
|
||||
|
||||
HandleMovementOnAttackStart(u);
|
||||
}
|
||||
}
|
||||
|
||||
void GuardAI::JustDied(Unit* killer)
|
||||
{
|
||||
if (Player* pkiller = killer->GetCharmerOrOwnerPlayerOrPlayerItself())
|
||||
m_creature->SendZoneUnderAttackMessage(pkiller);
|
||||
}
|
||||
60
src/game/Object/GuardAI.h
Normal file
60
src/game/Object/GuardAI.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOS_GUARDAI_H
|
||||
#define MANGOS_GUARDAI_H
|
||||
|
||||
#include "CreatureAI.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Timer.h"
|
||||
|
||||
class Creature;
|
||||
|
||||
class MANGOS_DLL_DECL GuardAI : public CreatureAI
|
||||
{
|
||||
enum GuardState
|
||||
{
|
||||
STATE_NORMAL = 1,
|
||||
STATE_LOOK_AT_VICTIM = 2
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
explicit GuardAI(Creature* c);
|
||||
|
||||
void MoveInLineOfSight(Unit*) override;
|
||||
void AttackStart(Unit*) override;
|
||||
void EnterEvadeMode() override;
|
||||
void JustDied(Unit*) override;
|
||||
bool IsVisible(Unit*) const override;
|
||||
|
||||
void UpdateAI(const uint32) override;
|
||||
static int Permissible(const Creature*);
|
||||
|
||||
private:
|
||||
ObjectGuid i_victimGuid;
|
||||
GuardState i_state;
|
||||
TimeTracker i_tracker;
|
||||
};
|
||||
#endif
|
||||
2684
src/game/Object/Guild.cpp
Normal file
2684
src/game/Object/Guild.cpp
Normal file
File diff suppressed because it is too large
Load diff
555
src/game/Object/Guild.h
Normal file
555
src/game/Object/Guild.h
Normal file
|
|
@ -0,0 +1,555 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOSSERVER_GUILD_H
|
||||
#define MANGOSSERVER_GUILD_H
|
||||
|
||||
#define WITHDRAW_MONEY_UNLIMITED UI64LIT(0xFFFFFFFFFFFFFFFF)
|
||||
#define WITHDRAW_SLOT_UNLIMITED 0xFFFFFFFF
|
||||
|
||||
#include "Common.h"
|
||||
#include "Item.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "SharedDefines.h"
|
||||
|
||||
class Item;
|
||||
|
||||
enum GuildDefaultRanks
|
||||
{
|
||||
// these ranks can be modified, but they cannot be deleted
|
||||
GR_GUILDMASTER = 0,
|
||||
GR_OFFICER = 1,
|
||||
GR_VETERAN = 2,
|
||||
GR_MEMBER = 3,
|
||||
GR_INITIATE = 4,
|
||||
// When promoting member server does: rank--;!
|
||||
// When demoting member server does: rank++;!
|
||||
};
|
||||
|
||||
enum GuildRankRights
|
||||
{
|
||||
GR_RIGHT_EMPTY = 0x00000040,
|
||||
GR_RIGHT_GCHATLISTEN = 0x00000041,
|
||||
GR_RIGHT_GCHATSPEAK = 0x00000042,
|
||||
GR_RIGHT_OFFCHATLISTEN = 0x00000044,
|
||||
GR_RIGHT_OFFCHATSPEAK = 0x00000048,
|
||||
GR_RIGHT_PROMOTE = 0x000000C0,
|
||||
GR_RIGHT_DEMOTE = 0x00000140,
|
||||
GR_RIGHT_INVITE = 0x00000050,
|
||||
GR_RIGHT_REMOVE = 0x00000060,
|
||||
GR_RIGHT_SETMOTD = 0x00001040,
|
||||
GR_RIGHT_EPNOTE = 0x00002040,
|
||||
GR_RIGHT_VIEWOFFNOTE = 0x00004040,
|
||||
GR_RIGHT_EOFFNOTE = 0x00008040,
|
||||
GR_RIGHT_MODIFY_GUILD_INFO = 0x00010040,
|
||||
GR_RIGHT_WITHDRAW_GOLD_LOCK = 0x00020000, // remove money withdraw capacity
|
||||
GR_RIGHT_WITHDRAW_REPAIR = 0x00040000, // withdraw for repair
|
||||
GR_RIGHT_WITHDRAW_GOLD = 0x00080000, // withdraw gold
|
||||
GR_RIGHT_CREATE_GUILD_EVENT = 0x00100000, // wotlk
|
||||
GR_RIGHT_REQUIRES_AUTHENTICATOR = 0x00200000,
|
||||
GR_RIGHT_MODIFY_BANK_TABS = 0x00400000, // cata?
|
||||
GR_RIGHT_REMOVE_GUILD_EVENT = 0x00800000, // wotlk
|
||||
GR_RIGHT_ALL = 0x00DDF1FF,
|
||||
};
|
||||
|
||||
enum Typecommand
|
||||
{
|
||||
GUILD_CREATE_S = 0x00,
|
||||
GUILD_INVITE_S = 0x01,
|
||||
GUILD_QUIT_S = 0x03,
|
||||
// 0x05?
|
||||
GUILD_FOUNDER_S = 0x0E,
|
||||
GUILD_UNK1 = 0x14,
|
||||
GUILD_UNK2 = 0x15,
|
||||
};
|
||||
|
||||
enum CommandErrors
|
||||
{
|
||||
ERR_PLAYER_NO_MORE_IN_GUILD = 0x00,
|
||||
ERR_GUILD_INTERNAL = 0x01,
|
||||
ERR_ALREADY_IN_GUILD = 0x02,
|
||||
ERR_ALREADY_IN_GUILD_S = 0x03,
|
||||
ERR_INVITED_TO_GUILD = 0x04,
|
||||
ERR_ALREADY_INVITED_TO_GUILD_S = 0x05,
|
||||
ERR_GUILD_NAME_INVALID = 0x06,
|
||||
ERR_GUILD_NAME_EXISTS_S = 0x07,
|
||||
ERR_GUILD_LEADER_LEAVE = 0x08,
|
||||
ERR_GUILD_PERMISSIONS = 0x08,
|
||||
ERR_GUILD_PLAYER_NOT_IN_GUILD = 0x09,
|
||||
ERR_GUILD_PLAYER_NOT_IN_GUILD_S = 0x0A,
|
||||
ERR_GUILD_PLAYER_NOT_FOUND_S = 0x0B,
|
||||
ERR_GUILD_NOT_ALLIED = 0x0C,
|
||||
ERR_GUILD_RANK_TOO_HIGH_S = 0x0D,
|
||||
ERR_GUILD_RANK_TOO_LOW_S = 0x0E,
|
||||
ERR_GUILD_RANKS_LOCKED = 0x11,
|
||||
ERR_GUILD_RANK_IN_USE = 0x12,
|
||||
ERR_GUILD_IGNORING_YOU_S = 0x13,
|
||||
ERR_GUILD_UNK1 = 0x14,
|
||||
ERR_GUILD_WITHDRAW_LIMIT = 0x19,
|
||||
ERR_GUILD_NOT_ENOUGH_MONEY = 0x1A,
|
||||
ERR_GUILD_BANK_FULL = 0x1C,
|
||||
ERR_GUILD_ITEM_NOT_FOUND = 0x1D,
|
||||
ERR_GUILD_TOO_MUCH_MONEY = 0x1F,
|
||||
ERR_GUILD_BANK_WRONG_TAB = 0x20,
|
||||
ERR_RANK_REQUIRES_AUTHENTICATOR = 0x22,
|
||||
ERR_GUILD_BANK_VOUCHER_FAILED = 0x23,
|
||||
ERR_GUILD_TRIAL_ACCOUNT = 0x24,
|
||||
ERR_GUILD_UNDELETABLE_DUE_TO_LEVEL = 0x25,
|
||||
ERR_GUILD_MOVE_STARTING = 0x26,
|
||||
ERR_GUILD_REP_TOO_LOW = 0x27,
|
||||
};
|
||||
|
||||
enum GuildEvents
|
||||
{
|
||||
GE_PROMOTION = 0x01,
|
||||
GE_DEMOTION = 0x02,
|
||||
GE_MOTD = 0x03,
|
||||
GE_JOINED = 0x04,
|
||||
GE_LEFT = 0x05,
|
||||
GE_REMOVED = 0x06,
|
||||
GE_LEADER_IS = 0x07,
|
||||
GE_LEADER_CHANGED = 0x08,
|
||||
GE_DISBANDED = 0x09,
|
||||
//GE_TABARDCHANGE = 0x0A, // not exists in 4.3.4
|
||||
GE_UPDATE_RANK = 0x0B, // string, string EVENT_GUILD_ROSTER_UPDATE tab content change?
|
||||
GE_CREATE_RANK = 0x0C, // EVENT_GUILD_ROSTER_UPDATE
|
||||
GE_DELETE_RANK = 0x0D,
|
||||
GE_RANK_ORDER_CHANGE = 0x0E,
|
||||
GE_UNK = 0x0F,
|
||||
GE_SIGNED_ON = 0x10, // ERR_FRIEND_ONLINE_SS
|
||||
GE_SIGNED_OFF = 0x11, // ERR_FRIEND_OFFLINE_S
|
||||
GE_GUILDBANKBAGSLOTS_CHANGED = 0x12, // EVENT_GUILDBANKBAGSLOTS_CHANGED
|
||||
GE_BANKTAB_PURCHASED = 0x13, // EVENT_GUILDBANK_UPDATE_TABS
|
||||
GE_BANKTAB_UPDATED = 0x14, // EVENT_GUILDBANK_UPDATE_TABS
|
||||
GE_GUILDBANK_UPDATE_MONEY = 0x15, // EVENT_GUILDBANK_UPDATE_MONEY, string 0000000000002710 is 1 gold
|
||||
//GE_GUILD_BANK_MONEY_WITHDRAWN = 0x16, // not exists in 4.3.4
|
||||
GE_GUILDBANK_TEXT_CHANGED = 0x17 // EVENT_GUILDBANK_TEXT_CHANGED
|
||||
};
|
||||
|
||||
enum PetitionTurns
|
||||
{
|
||||
PETITION_TURN_OK = 0,
|
||||
PETITION_TURN_ALREADY_IN_GUILD = 2,
|
||||
PETITION_TURN_NEED_MORE_SIGNATURES = 4,
|
||||
PETITION_TURN_GUILD_PERMISSIONS = 11,
|
||||
PETITION_TURN_GUILD_NAME_INVALID = 12,
|
||||
};
|
||||
|
||||
enum PetitionSigns
|
||||
{
|
||||
PETITION_SIGN_OK = 0,
|
||||
PETITION_SIGN_ALREADY_SIGNED = 1,
|
||||
PETITION_SIGN_ALREADY_IN_GUILD = 2,
|
||||
PETITION_SIGN_CANT_SIGN_OWN = 3,
|
||||
PETITION_SIGN_NOT_SAME_SERVER = 5,
|
||||
PETITION_SIGN_PETITION_FULL = 8,
|
||||
PETITION_SIGN_ALREADY_SIGNED_OTHER = 10,
|
||||
PETITION_SIGN_RESTRICTED_ACCOUNT = 11,
|
||||
};
|
||||
|
||||
enum GuildBankRights
|
||||
{
|
||||
GUILD_BANK_RIGHT_VIEW_TAB = 0x01,
|
||||
GUILD_BANK_RIGHT_PUT_ITEM = 0x02,
|
||||
GUILD_BANK_RIGHT_UPDATE_TEXT = 0x04,
|
||||
|
||||
GUILD_BANK_RIGHT_DEPOSIT_ITEM = GUILD_BANK_RIGHT_VIEW_TAB | GUILD_BANK_RIGHT_PUT_ITEM,
|
||||
GUILD_BANK_RIGHT_FULL = 0xFF,
|
||||
};
|
||||
|
||||
enum GuildBankEventLogTypes
|
||||
{
|
||||
GUILD_BANK_LOG_DEPOSIT_ITEM = 1,
|
||||
GUILD_BANK_LOG_WITHDRAW_ITEM = 2,
|
||||
GUILD_BANK_LOG_MOVE_ITEM = 3,
|
||||
GUILD_BANK_LOG_DEPOSIT_MONEY = 4,
|
||||
GUILD_BANK_LOG_WITHDRAW_MONEY = 5,
|
||||
GUILD_BANK_LOG_REPAIR_MONEY = 6,
|
||||
GUILD_BANK_LOG_MOVE_ITEM2 = 7,
|
||||
GUILD_BANK_LOG_UNK1 = 8,
|
||||
GUILD_BANK_LOG_BUY_SLOT = 9,
|
||||
GUILD_BANK_LOG_CASH_FLOW_DEPOSIT = 10,
|
||||
};
|
||||
|
||||
enum GuildEventLogTypes
|
||||
{
|
||||
GUILD_EVENT_LOG_INVITE_PLAYER = 1,
|
||||
GUILD_EVENT_LOG_JOIN_GUILD = 2,
|
||||
GUILD_EVENT_LOG_PROMOTE_PLAYER = 3,
|
||||
GUILD_EVENT_LOG_DEMOTE_PLAYER = 4,
|
||||
GUILD_EVENT_LOG_UNINVITE_PLAYER = 5,
|
||||
GUILD_EVENT_LOG_LEAVE_GUILD = 6,
|
||||
};
|
||||
|
||||
enum GuildEmblem
|
||||
{
|
||||
ERR_GUILDEMBLEM_SUCCESS = 0,
|
||||
ERR_GUILDEMBLEM_INVALID_TABARD_COLORS = 1,
|
||||
ERR_GUILDEMBLEM_NOGUILD = 2,
|
||||
ERR_GUILDEMBLEM_NOTGUILDMASTER = 3,
|
||||
ERR_GUILDEMBLEM_NOTENOUGHMONEY = 4,
|
||||
ERR_GUILDEMBLEM_INVALIDVENDOR = 5
|
||||
};
|
||||
|
||||
enum GuildMemberFlags
|
||||
{
|
||||
GUILDMEMBER_STATUS_NONE = 0x0000,
|
||||
GUILDMEMBER_STATUS_ONLINE = 0x0001,
|
||||
GUILDMEMBER_STATUS_AFK = 0x0002,
|
||||
GUILDMEMBER_STATUS_DND = 0x0004,
|
||||
GUILDMEMBER_STATUS_MOBILE = 0x0008,
|
||||
};
|
||||
|
||||
inline uint64 GetGuildBankTabPrice(uint8 Index)
|
||||
{
|
||||
switch (Index)
|
||||
{
|
||||
case 0: return 100;
|
||||
case 1: return 250;
|
||||
case 2: return 500;
|
||||
case 3: return 1000;
|
||||
case 4: return 2500;
|
||||
case 5: return 5000;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct GuildEventLogEntry
|
||||
{
|
||||
uint8 EventType;
|
||||
uint32 PlayerGuid1;
|
||||
uint32 PlayerGuid2;
|
||||
uint8 NewRank;
|
||||
uint64 TimeStamp;
|
||||
|
||||
void WriteData(WorldPacket& data, ByteBuffer& buffer);
|
||||
};
|
||||
|
||||
struct GuildBankEventLogEntry
|
||||
{
|
||||
uint8 EventType;
|
||||
uint32 PlayerGuid;
|
||||
uint32 ItemOrMoney;
|
||||
uint8 ItemStackCount;
|
||||
uint8 DestTabId;
|
||||
uint64 TimeStamp;
|
||||
|
||||
bool isMoneyEvent() const
|
||||
{
|
||||
return EventType == GUILD_BANK_LOG_DEPOSIT_MONEY ||
|
||||
EventType == GUILD_BANK_LOG_WITHDRAW_MONEY ||
|
||||
EventType == GUILD_BANK_LOG_REPAIR_MONEY;
|
||||
}
|
||||
|
||||
void WriteData(WorldPacket& data, ByteBuffer& buffer);
|
||||
};
|
||||
|
||||
struct GuildBankTab
|
||||
{
|
||||
GuildBankTab() { memset(Slots, 0, GUILD_BANK_MAX_SLOTS * sizeof(Item*)); }
|
||||
|
||||
Item* Slots[GUILD_BANK_MAX_SLOTS];
|
||||
std::string Name;
|
||||
std::string Icon;
|
||||
std::string Text;
|
||||
};
|
||||
|
||||
struct GuildItemPosCount
|
||||
{
|
||||
GuildItemPosCount(uint8 _slot, uint32 _count) : Slot(_slot), Count(_count) {}
|
||||
|
||||
bool isContainedIn(std::vector<GuildItemPosCount> const& vec) const;
|
||||
|
||||
uint8 Slot;
|
||||
uint32 Count;
|
||||
};
|
||||
typedef std::vector<GuildItemPosCount> GuildItemPosCountVec;
|
||||
|
||||
struct MemberSlot
|
||||
{
|
||||
void SetMemberStats(Player* player);
|
||||
void UpdateLogoutTime();
|
||||
void SetPNOTE(std::string pnote);
|
||||
void SetOFFNOTE(std::string offnote);
|
||||
void ChangeRank(uint32 newRank);
|
||||
|
||||
ObjectGuid guid;
|
||||
uint32 accountId;
|
||||
std::string Name;
|
||||
uint32 RankId;
|
||||
uint8 Level;
|
||||
uint8 Class;
|
||||
uint32 ZoneId;
|
||||
uint64 LogoutTime;
|
||||
std::string Pnote;
|
||||
std::string OFFnote;
|
||||
uint32 BankResetTimeMoney;
|
||||
uint32 BankRemMoney;
|
||||
uint32 BankResetTimeTab[GUILD_BANK_MAX_TABS];
|
||||
uint32 BankRemSlotsTab[GUILD_BANK_MAX_TABS];
|
||||
};
|
||||
|
||||
struct RankInfo
|
||||
{
|
||||
RankInfo(const std::string& _name, uint32 _rights, uint32 _money) : Name(_name), Rights(_rights), BankMoneyPerDay(_money)
|
||||
{
|
||||
for (uint8 i = 0; i < GUILD_BANK_MAX_TABS; ++i)
|
||||
{
|
||||
TabRight[i] = 0;
|
||||
TabSlotPerDay[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Name;
|
||||
uint32 Rights;
|
||||
uint32 BankMoneyPerDay;
|
||||
uint32 TabRight[GUILD_BANK_MAX_TABS];
|
||||
uint32 TabSlotPerDay[GUILD_BANK_MAX_TABS];
|
||||
};
|
||||
|
||||
class Guild
|
||||
{
|
||||
public:
|
||||
Guild();
|
||||
~Guild();
|
||||
|
||||
bool Create(Player* leader, std::string gname);
|
||||
void CreateDefaultGuildRanks(int locale_idx);
|
||||
void Disband();
|
||||
|
||||
void DeleteGuildBankItems(bool alsoInDB = false);
|
||||
typedef UNORDERED_MAP<uint32, MemberSlot> MemberList;
|
||||
typedef std::vector<RankInfo> RankList;
|
||||
|
||||
uint32 GetId() const { return m_Id; }
|
||||
uint32 GetLevel() const { return m_Level; }
|
||||
ObjectGuid GetObjectGuid() const { return ObjectGuid(HIGHGUID_GUILD, 0, m_Id); }
|
||||
ObjectGuid GetLeaderGuid() const { return m_LeaderGuid; }
|
||||
std::string const& GetName() const { return m_Name; }
|
||||
std::string const& GetMOTD() const { return MOTD; }
|
||||
std::string const& GetGINFO() const { return GINFO; }
|
||||
|
||||
time_t GetCreatedDate() const { return m_CreatedDate; }
|
||||
|
||||
uint32 GetEmblemStyle() const { return m_EmblemStyle; }
|
||||
uint32 GetEmblemColor() const { return m_EmblemColor; }
|
||||
uint32 GetBorderStyle() const { return m_BorderStyle; }
|
||||
uint32 GetBorderColor() const { return m_BorderColor; }
|
||||
uint32 GetBackgroundColor() const { return m_BackgroundColor; }
|
||||
|
||||
void SetLeader(ObjectGuid guid);
|
||||
bool AddMember(ObjectGuid plGuid, uint32 plRank);
|
||||
bool DelMember(ObjectGuid guid, bool isDisbanding = false);
|
||||
// lowest rank is the count of ranks - 1 (the highest rank_id in table)
|
||||
uint32 GetLowestRank() const { return m_Ranks.size() - 1; }
|
||||
|
||||
void SetMOTD(std::string motd);
|
||||
void SetGINFO(std::string ginfo);
|
||||
void SetEmblem(uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor, uint32 backgroundColor);
|
||||
|
||||
uint32 GetMemberSize() const { return members.size(); }
|
||||
uint32 GetAccountsNumber();
|
||||
|
||||
bool LoadGuildFromDB(QueryResult* guildDataResult);
|
||||
bool CheckGuildStructure();
|
||||
bool LoadRanksFromDB(QueryResult* guildRanksResult);
|
||||
bool LoadMembersFromDB(QueryResult* guildMembersResult);
|
||||
|
||||
void BroadcastToGuild(WorldSession* session, const std::string& msg, uint32 language = LANG_UNIVERSAL);
|
||||
void BroadcastAddonToGuild(WorldSession* session, const std::string& msg, const std::string& prefix);
|
||||
void BroadcastToOfficers(WorldSession* session, const std::string& msg, uint32 language = LANG_UNIVERSAL);
|
||||
void BroadcastAddonToOfficers(WorldSession* session, const std::string& msg, const std::string& prefix);
|
||||
void BroadcastPacketToRank(WorldPacket* packet, uint32 rankId);
|
||||
void BroadcastPacket(WorldPacket* packet);
|
||||
// for calendar
|
||||
void MassInviteToEvent(WorldSession* session, uint32 minLevel, uint32 maxLevel, uint32 minRank);
|
||||
|
||||
void BroadcastEvent(GuildEvents event, ObjectGuid guid, char const* str1 = NULL, char const* str2 = NULL, char const* str3 = NULL);
|
||||
void BroadcastEvent(GuildEvents event, char const* str1 = NULL, char const* str2 = NULL, char const* str3 = NULL)
|
||||
{
|
||||
BroadcastEvent(event, ObjectGuid(), str1, str2, str3);
|
||||
}
|
||||
|
||||
template<class Do>
|
||||
void BroadcastWorker(Do& _do, Player* except = NULL)
|
||||
{
|
||||
for (MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
|
||||
if (Player* player = ObjectAccessor::FindPlayer(ObjectGuid(HIGHGUID_PLAYER, itr->first)))
|
||||
if (player != except)
|
||||
_do(player);
|
||||
}
|
||||
|
||||
void CreateRank(std::string name, uint32 rights);
|
||||
void DelRank(uint32 rankId);
|
||||
void SwitchRank(uint32 rankId, bool up);
|
||||
std::string GetRankName(uint32 rankId);
|
||||
uint32 GetRankRights(uint32 rankId);
|
||||
uint32 GetRanksSize() const { return m_Ranks.size(); }
|
||||
|
||||
void SetRankName(uint32 rankId, std::string name);
|
||||
void SetRankRights(uint32 rankId, uint32 rights);
|
||||
bool HasRankRight(uint32 rankId, uint32 right)
|
||||
{
|
||||
return ((GetRankRights(rankId) & right) != GR_RIGHT_EMPTY) ? true : false;
|
||||
}
|
||||
|
||||
bool HasMembersWithRank(uint32 rankId) const
|
||||
{
|
||||
for (MemberList::const_iterator itr = members.begin(); itr != members.end(); ++itr)
|
||||
if (itr->second.RankId == rankId)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int32 GetRank(ObjectGuid guid)
|
||||
{
|
||||
MemberSlot* slot = GetMemberSlot(guid);
|
||||
return slot ? slot->RankId : -1;
|
||||
}
|
||||
|
||||
MemberSlot* GetMemberSlot(ObjectGuid guid)
|
||||
{
|
||||
MemberList::iterator itr = members.find(guid.GetCounter());
|
||||
return itr != members.end() ? &itr->second : NULL;
|
||||
}
|
||||
|
||||
MemberSlot* GetMemberSlot(const std::string& name)
|
||||
{
|
||||
for (MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
|
||||
if (itr->second.Name == name)
|
||||
return &itr->second;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Roster(WorldSession* session = NULL); // NULL = broadcast
|
||||
void Query(WorldSession* session);
|
||||
void QueryRanks(WorldSession* session);
|
||||
|
||||
// Guild EventLog
|
||||
void LoadGuildEventLogFromDB();
|
||||
void DisplayGuildEventLog(WorldSession* session);
|
||||
void LogGuildEvent(uint8 EventType, ObjectGuid playerGuid1, ObjectGuid playerGuid2 = ObjectGuid(), uint8 newRank = 0);
|
||||
|
||||
// ** Guild bank **
|
||||
// Content & item deposit/withdraw
|
||||
void DisplayGuildBankContent(WorldSession* session, uint8 TabId);
|
||||
void DisplayGuildBankMoneyUpdate(WorldSession* session);
|
||||
|
||||
void SwapItems(Player* pl, uint8 BankTab, uint8 BankTabSlot, uint8 BankTabDst, uint8 BankTabSlotDst, uint32 SplitedAmount);
|
||||
void MoveFromBankToChar(Player* pl, uint8 BankTab, uint8 BankTabSlot, uint8 PlayerBag, uint8 PlayerSlot, uint32 SplitedAmount);
|
||||
void MoveFromCharToBank(Player* pl, uint8 PlayerBag, uint8 PlayerSlot, uint8 BankTab, uint8 BankTabSlot, uint32 SplitedAmount);
|
||||
|
||||
// Tabs
|
||||
void DisplayGuildBankTabsInfo(WorldSession* session);
|
||||
void CreateNewBankTab();
|
||||
void SetGuildBankTabText(uint8 TabId, std::string text);
|
||||
void SendGuildBankTabText(WorldSession* session, uint8 TabId);
|
||||
void SetGuildBankTabInfo(uint8 TabId, std::string name, std::string icon);
|
||||
uint8 GetPurchasedTabs() const { return m_TabListMap.size(); }
|
||||
uint32 GetBankRights(uint32 rankId, uint8 TabId) const;
|
||||
bool IsMemberHaveRights(uint32 LowGuid, uint8 TabId, uint32 rights) const;
|
||||
bool CanMemberViewTab(uint32 LowGuid, uint8 TabId) const;
|
||||
// Load
|
||||
void LoadGuildBankFromDB();
|
||||
// Money deposit/withdraw
|
||||
void SendMoneyInfo(WorldSession* session, uint32 LowGuid);
|
||||
bool MemberMoneyWithdraw(uint64 amount, uint32 LowGuid);
|
||||
uint64 GetGuildBankMoney() { return m_GuildBankMoney; }
|
||||
void SetBankMoney(int64 money);
|
||||
// per days
|
||||
bool MemberItemWithdraw(uint8 TabId, uint32 LowGuid);
|
||||
uint32 GetMemberSlotWithdrawRem(uint32 LowGuid, uint8 TabId);
|
||||
uint64 GetMemberMoneyWithdrawRem(uint32 LowGuid);
|
||||
void SetBankMoneyPerDay(uint32 rankId, uint32 money);
|
||||
void SetBankRightsAndSlots(uint32 rankId, uint8 TabId, uint32 right, uint32 SlotPerDay, bool db);
|
||||
uint32 GetBankMoneyPerDay(uint32 rankId);
|
||||
uint32 GetBankSlotPerDay(uint32 rankId, uint8 TabId);
|
||||
// rights per day
|
||||
bool LoadBankRightsFromDB(QueryResult* guildBankTabRightsResult);
|
||||
// Guild Bank Event Logs
|
||||
void LoadGuildBankEventLogFromDB();
|
||||
void DisplayGuildBankLogs(WorldSession* session, uint8 TabId);
|
||||
void LogBankEvent(uint8 EventType, uint8 TabId, uint32 PlayerGuidLow, uint32 ItemOrMoney, uint8 ItemStackCount = 0, uint8 DestTabId = 0);
|
||||
bool AddGBankItemToDB(uint32 GuildId, uint32 BankTab , uint32 BankTabSlot , uint32 GUIDLow, uint32 Entry);
|
||||
|
||||
protected:
|
||||
void AddRank(const std::string& name, uint32 rights, uint32 money);
|
||||
|
||||
uint32 m_Id;
|
||||
uint32 m_Level;
|
||||
std::string m_Name;
|
||||
ObjectGuid m_LeaderGuid;
|
||||
std::string MOTD;
|
||||
std::string GINFO;
|
||||
time_t m_CreatedDate;
|
||||
|
||||
uint32 m_EmblemStyle;
|
||||
uint32 m_EmblemColor;
|
||||
uint32 m_BorderStyle;
|
||||
uint32 m_BorderColor;
|
||||
uint32 m_BackgroundColor;
|
||||
uint32 m_accountsNumber; // 0 used as marker for need lazy calculation at request
|
||||
|
||||
RankList m_Ranks;
|
||||
|
||||
MemberList members;
|
||||
|
||||
typedef std::vector<GuildBankTab*> TabListMap;
|
||||
TabListMap m_TabListMap;
|
||||
|
||||
/** These are actually ordered lists. The first element is the oldest entry.*/
|
||||
typedef std::list<GuildEventLogEntry> GuildEventLog;
|
||||
typedef std::list<GuildBankEventLogEntry> GuildBankEventLog;
|
||||
GuildEventLog m_GuildEventLog;
|
||||
GuildBankEventLog m_GuildBankEventLog_Money;
|
||||
GuildBankEventLog m_GuildBankEventLog_Item[GUILD_BANK_MAX_TABS];
|
||||
|
||||
uint32 m_GuildEventLogNextGuid;
|
||||
uint32 m_GuildBankEventLogNextGuid_Money;
|
||||
uint32 m_GuildBankEventLogNextGuid_Item[GUILD_BANK_MAX_TABS];
|
||||
|
||||
uint64 m_GuildBankMoney;
|
||||
|
||||
private:
|
||||
void UpdateAccountsNumber() { m_accountsNumber = 0;}// mark for lazy calculation at request in GetAccountsNumber
|
||||
void _ChangeRank(ObjectGuid guid, MemberSlot* slot, uint32 newRank);
|
||||
|
||||
// used only from high level Swap/Move functions
|
||||
Item* GetItem(uint8 TabId, uint8 SlotId);
|
||||
InventoryResult CanStoreItem(uint8 tab, uint8 slot, GuildItemPosCountVec& dest, uint32 count, Item* pItem, bool swap = false) const;
|
||||
Item* StoreItem(uint8 tab, GuildItemPosCountVec const& pos, Item* pItem);
|
||||
void RemoveItem(uint8 tab, uint8 slot);
|
||||
void DisplayGuildBankContentUpdate(uint8 TabId, int32 slot1, int32 slot2 = -1);
|
||||
void DisplayGuildBankContentUpdate(uint8 TabId, GuildItemPosCountVec const& slots);
|
||||
|
||||
// internal common parts for CanStore/StoreItem functions
|
||||
void AppendDisplayGuildBankSlot(WorldPacket& data, ByteBuffer& buffer, GuildBankTab const* tab, int32 slot);
|
||||
InventoryResult _CanStoreItem_InSpecificSlot(uint8 tab, uint8 slot, GuildItemPosCountVec& dest, uint32& count, bool swap, Item* pSrcItem) const;
|
||||
InventoryResult _CanStoreItem_InTab(uint8 tab, GuildItemPosCountVec& dest, uint32& count, bool merge, Item* pSrcItem, uint8 skip_slot) const;
|
||||
Item* _StoreItem(uint8 tab, uint8 slot, Item* pItem, uint32 count, bool clone);
|
||||
};
|
||||
#endif
|
||||
1501
src/game/Object/Item.cpp
Normal file
1501
src/game/Object/Item.cpp
Normal file
File diff suppressed because it is too large
Load diff
412
src/game/Object/Item.h
Normal file
412
src/game/Object/Item.h
Normal file
|
|
@ -0,0 +1,412 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOSSERVER_ITEM_H
|
||||
#define MANGOSSERVER_ITEM_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "Object.h"
|
||||
#include "LootMgr.h"
|
||||
#include "ItemPrototype.h"
|
||||
|
||||
struct SpellEntry;
|
||||
class Bag;
|
||||
class Field;
|
||||
class QueryResult;
|
||||
class Unit;
|
||||
|
||||
struct ItemSetEffect
|
||||
{
|
||||
uint32 setid;
|
||||
uint32 item_count;
|
||||
SpellEntry const* spells[8];
|
||||
};
|
||||
|
||||
enum InventoryResult
|
||||
{
|
||||
EQUIP_ERR_OK = 0,
|
||||
EQUIP_ERR_CANT_EQUIP_LEVEL_I = 1, // ERR_CANT_EQUIP_LEVEL_I
|
||||
EQUIP_ERR_CANT_EQUIP_SKILL = 2, // ERR_CANT_EQUIP_SKILL
|
||||
EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT = 3, // ERR_WRONG_SLOT
|
||||
EQUIP_ERR_BAG_FULL = 4, // ERR_BAG_FULL
|
||||
EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG = 5, // ERR_BAG_IN_BAG
|
||||
EQUIP_ERR_CANT_TRADE_EQUIP_BAGS = 6, // ERR_TRADE_EQUIPPED_BAG
|
||||
EQUIP_ERR_ONLY_AMMO_CAN_GO_HERE = 7, // ERR_AMMO_ONLY
|
||||
EQUIP_ERR_NO_REQUIRED_PROFICIENCY = 8, // ERR_PROFICIENCY_NEEDED
|
||||
EQUIP_ERR_NO_EQUIPMENT_SLOT_AVAILABLE = 9, // ERR_NO_SLOT_AVAILABLE
|
||||
EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM = 10, // ERR_CANT_EQUIP_EVER
|
||||
EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM2 = 11, // ERR_CANT_EQUIP_EVER
|
||||
EQUIP_ERR_NO_EQUIPMENT_SLOT_AVAILABLE2 = 12, // ERR_NO_SLOT_AVAILABLE
|
||||
EQUIP_ERR_CANT_EQUIP_WITH_TWOHANDED = 13, // ERR_2HANDED_EQUIPPED
|
||||
EQUIP_ERR_CANT_DUAL_WIELD = 14, // ERR_2HSKILLNOTFOUND
|
||||
EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG = 15, // ERR_WRONG_BAG_TYPE
|
||||
EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG2 = 16, // ERR_WRONG_BAG_TYPE
|
||||
EQUIP_ERR_CANT_CARRY_MORE_OF_THIS = 17, // ERR_ITEM_MAX_COUNT
|
||||
EQUIP_ERR_NO_EQUIPMENT_SLOT_AVAILABLE3 = 18, // ERR_NO_SLOT_AVAILABLE
|
||||
EQUIP_ERR_ITEM_CANT_STACK = 19, // ERR_CANT_STACK
|
||||
EQUIP_ERR_ITEM_CANT_BE_EQUIPPED = 20, // ERR_NOT_EQUIPPABLE
|
||||
EQUIP_ERR_ITEMS_CANT_BE_SWAPPED = 21, // ERR_CANT_SWAP
|
||||
EQUIP_ERR_SLOT_IS_EMPTY = 22, // ERR_SLOT_EMPTY
|
||||
EQUIP_ERR_ITEM_NOT_FOUND = 23, // ERR_ITEM_NOT_FOUND
|
||||
EQUIP_ERR_CANT_DROP_SOULBOUND = 24, // ERR_DROP_BOUND_ITEM
|
||||
EQUIP_ERR_OUT_OF_RANGE = 25, // ERR_OUT_OF_RANGE
|
||||
EQUIP_ERR_TRIED_TO_SPLIT_MORE_THAN_COUNT = 26, // ERR_TOO_FEW_TO_SPLIT
|
||||
EQUIP_ERR_COULDNT_SPLIT_ITEMS = 27, // ERR_SPLIT_FAILED
|
||||
EQUIP_ERR_MISSING_REAGENT = 28, // ERR_SPELL_FAILED_REAGENTS_GENERIC
|
||||
EQUIP_ERR_NOT_ENOUGH_MONEY = 29, // ERR_NOT_ENOUGH_MONEY
|
||||
EQUIP_ERR_NOT_A_BAG = 30, // ERR_NOT_A_BAG
|
||||
EQUIP_ERR_CAN_ONLY_DO_WITH_EMPTY_BAGS = 31, // ERR_DESTROY_NONEMPTY_BAG
|
||||
EQUIP_ERR_DONT_OWN_THAT_ITEM = 32, // ERR_NOT_OWNER
|
||||
EQUIP_ERR_CAN_EQUIP_ONLY1_QUIVER = 33, // ERR_ONLY_ONE_QUIVER
|
||||
EQUIP_ERR_MUST_PURCHASE_THAT_BAG_SLOT = 34, // ERR_NO_BANK_SLOT
|
||||
EQUIP_ERR_TOO_FAR_AWAY_FROM_BANK = 35, // ERR_NO_BANK_HERE
|
||||
EQUIP_ERR_ITEM_LOCKED = 36, // ERR_ITEM_LOCKED
|
||||
EQUIP_ERR_YOU_ARE_STUNNED = 37, // ERR_GENERIC_STUNNED
|
||||
EQUIP_ERR_YOU_ARE_DEAD = 38, // ERR_PLAYER_DEAD
|
||||
EQUIP_ERR_CANT_DO_RIGHT_NOW = 39, // ERR_CLIENT_LOCKED_OUT
|
||||
EQUIP_ERR_INT_BAG_ERROR = 40, // ERR_INTERNAL_BAG_ERROR
|
||||
EQUIP_ERR_CAN_EQUIP_ONLY1_BOLT = 41, // ERR_ONLY_ONE_BOLT
|
||||
EQUIP_ERR_CAN_EQUIP_ONLY1_AMMOPOUCH = 42, // ERR_ONLY_ONE_AMMO
|
||||
EQUIP_ERR_STACKABLE_CANT_BE_WRAPPED = 43, // ERR_CANT_WRAP_STACKABLE
|
||||
EQUIP_ERR_EQUIPPED_CANT_BE_WRAPPED = 44, // ERR_CANT_WRAP_EQUIPPED
|
||||
EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED = 45, // ERR_CANT_WRAP_WRAPPED
|
||||
EQUIP_ERR_BOUND_CANT_BE_WRAPPED = 46, // ERR_CANT_WRAP_BOUND
|
||||
EQUIP_ERR_UNIQUE_CANT_BE_WRAPPED = 47, // ERR_CANT_WRAP_UNIQUE
|
||||
EQUIP_ERR_BAGS_CANT_BE_WRAPPED = 48, // ERR_CANT_WRAP_BAGS
|
||||
EQUIP_ERR_ALREADY_LOOTED = 49, // ERR_LOOT_GONE
|
||||
EQUIP_ERR_INVENTORY_FULL = 50, // ERR_INV_FULL
|
||||
EQUIP_ERR_BANK_FULL = 51, // ERR_BAG_FULL
|
||||
EQUIP_ERR_ITEM_IS_CURRENTLY_SOLD_OUT = 52, // ERR_VENDOR_SOLD_OUT
|
||||
EQUIP_ERR_BAG_FULL3 = 53, // ERR_BAG_FULL
|
||||
EQUIP_ERR_ITEM_NOT_FOUND2 = 54, // ERR_ITEM_NOT_FOUND
|
||||
EQUIP_ERR_ITEM_CANT_STACK2 = 55, // ERR_CANT_STACK
|
||||
EQUIP_ERR_BAG_FULL4 = 56, // ERR_BAG_FULL
|
||||
EQUIP_ERR_ITEM_SOLD_OUT = 57, // ERR_VENDOR_SOLD_OUT
|
||||
EQUIP_ERR_OBJECT_IS_BUSY = 58, // ERR_OBJECT_IS_BUSY
|
||||
EQUIP_ERR_NONE = 59, // ERR_CANT_BE_DISENCHANTED
|
||||
EQUIP_ERR_NOT_IN_COMBAT = 60, // ERR_NOT_IN_COMBAT
|
||||
EQUIP_ERR_NOT_WHILE_DISARMED = 61, // ERR_NOT_WHILE_DISARMED
|
||||
EQUIP_ERR_BAG_FULL6 = 62, // ERR_BAG_FULL
|
||||
EQUIP_ERR_CANT_EQUIP_RANK = 63, // ERR_CANT_EQUIP_RANK
|
||||
EQUIP_ERR_CANT_EQUIP_REPUTATION = 64, // ERR_CANT_EQUIP_REPUTATION
|
||||
EQUIP_ERR_TOO_MANY_SPECIAL_BAGS = 65, // ERR_TOO_MANY_SPECIAL_BAGS
|
||||
EQUIP_ERR_LOOT_CANT_LOOT_THAT_NOW = 66, // ERR_LOOT_CANT_LOOT_THAT_NOW
|
||||
EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE = 67, // ERR_ITEM_UNIQUE_EQUIPPABLE
|
||||
EQUIP_ERR_VENDOR_MISSING_TURNINS = 68, // ERR_VENDOR_MISSING_TURNINS
|
||||
EQUIP_ERR_NOT_ENOUGH_HONOR_POINTS = 69, // ERR_NOT_ENOUGH_HONOR_POINTS
|
||||
EQUIP_ERR_NOT_ENOUGH_ARENA_POINTS = 70, // ERR_NOT_ENOUGH_ARENA_POINTS
|
||||
EQUIP_ERR_ITEM_MAX_COUNT_SOCKETED = 71, // ERR_ITEM_MAX_COUNT_SOCKETED
|
||||
EQUIP_ERR_MAIL_BOUND_ITEM = 72, // ERR_MAIL_BOUND_ITEM
|
||||
EQUIP_ERR_NO_SPLIT_WHILE_PROSPECTING = 73, // ERR_INTERNAL_BAG_ERROR
|
||||
EQUIP_ERR_BAG_FULL7 = 74, // ERR_BAG_FULL
|
||||
EQUIP_ERR_ITEM_MAX_COUNT_EQUIPPED_SOCKETED = 75, // ERR_ITEM_MAX_COUNT_EQUIPPED_SOCKETED
|
||||
EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED = 76, // ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED
|
||||
EQUIP_ERR_TOO_MUCH_GOLD = 77, // ERR_TOO_MUCH_GOLD
|
||||
EQUIP_ERR_NOT_DURING_ARENA_MATCH = 78, // ERR_NOT_DURING_ARENA_MATCH
|
||||
EQUIP_ERR_CANNOT_TRADE_THAT = 79, // ERR_TRADE_BOUND_ITEM
|
||||
EQUIP_ERR_PERSONAL_ARENA_RATING_TOO_LOW = 80, // ERR_CANT_EQUIP_RATING
|
||||
EQUIP_ERR_EVENT_AUTOEQUIP_BIND_CONFIRM = 81, // EQUIP_ERR_OK, EVENT_AUTOEQUIP_BIND_CONFIRM
|
||||
EQUIP_ERR_ARTEFACTS_ONLY_FOR_OWN_CHARACTERS = 82, // ERR_NOT_SAME_ACCOUNT
|
||||
EQUIP_ERR_OK2 = 83, // EQUIP_ERR_OK
|
||||
EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_COUNT_EXCEEDED_IS = 84, // You can only carry %d %s
|
||||
EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_SOCKETED_EXCEEDED_IS = 85, // You can only equip %d |4item:items in the %s category
|
||||
EQUIP_ERR_SCALING_STAT_ITEM_LEVEL_EXCEEDED = 86, // Your level is too high to use that item
|
||||
EQUIP_ERR_PURCHASE_LEVEL_TOO_LOW = 87, // You must reach level %d to purchase that item.
|
||||
EQUIP_ERR_CANT_EQUIP_NEED_TALENT = 88, // You do not have the required talent to equip that
|
||||
EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED_IS = 89, // You can only equip %d |4item:items in the %s category
|
||||
EQUIP_ERR_SHAPESHIFT_FORM_CANNOT_EQUIP = 90, // Cannot equip item in this form
|
||||
EQUIP_ERR_ITEM_INVENTORY_FULL_SATCHEL = 91, // Your inventory is full. Your satchel has been delivered to your mailbox.
|
||||
EQUIP_ERR_SCALING_STAT_ITEM_LEVEL_TOO_LOW = 92, // Your level is too low to use that item
|
||||
EQUIP_ERR_CANT_BUY_QUANTITY = 93, // You can't buy the specified quantity of that item.
|
||||
};
|
||||
|
||||
enum BuyResult
|
||||
{
|
||||
BUY_ERR_CANT_FIND_ITEM = 0,
|
||||
BUY_ERR_ITEM_ALREADY_SOLD = 1,
|
||||
BUY_ERR_NOT_ENOUGHT_MONEY = 2,
|
||||
BUY_ERR_SELLER_DONT_LIKE_YOU = 4,
|
||||
BUY_ERR_DISTANCE_TOO_FAR = 5,
|
||||
BUY_ERR_ITEM_SOLD_OUT = 7,
|
||||
BUY_ERR_CANT_CARRY_MORE = 8,
|
||||
BUY_ERR_RANK_REQUIRE = 11,
|
||||
BUY_ERR_REPUTATION_REQUIRE = 12
|
||||
};
|
||||
|
||||
enum SellResult
|
||||
{
|
||||
SELL_ERR_CANT_FIND_ITEM = 1,
|
||||
SELL_ERR_CANT_SELL_ITEM = 2, // merchant doesn't like that item
|
||||
SELL_ERR_CANT_FIND_VENDOR = 3, // merchant doesn't like you
|
||||
SELL_ERR_YOU_DONT_OWN_THAT_ITEM = 4, // you don't own that item
|
||||
SELL_ERR_UNK = 5, // nothing appears...
|
||||
SELL_ERR_ONLY_EMPTY_BAG = 6 // can only do with empty bags
|
||||
};
|
||||
|
||||
// -1 from client enchantment slot number
|
||||
enum EnchantmentSlot
|
||||
{
|
||||
PERM_ENCHANTMENT_SLOT = 0,
|
||||
TEMP_ENCHANTMENT_SLOT = 1,
|
||||
SOCK_ENCHANTMENT_SLOT = 2,
|
||||
SOCK_ENCHANTMENT_SLOT_2 = 3,
|
||||
SOCK_ENCHANTMENT_SLOT_3 = 4,
|
||||
BONUS_ENCHANTMENT_SLOT = 5,
|
||||
PRISMATIC_ENCHANTMENT_SLOT = 6, // added at apply special permanent enchantment
|
||||
// = 7,
|
||||
REFORGE_ENCHANTMENT_SLOT = 8,
|
||||
TRANSMOGRIFY_ENCHANTMENT_SLOT = 9,
|
||||
MAX_INSPECTED_ENCHANTMENT_SLOT = 10,
|
||||
|
||||
PROP_ENCHANTMENT_SLOT_0 = 10, // used with RandomSuffix
|
||||
PROP_ENCHANTMENT_SLOT_1 = 11, // used with RandomSuffix
|
||||
PROP_ENCHANTMENT_SLOT_2 = 12, // used with RandomSuffix and RandomProperty
|
||||
PROP_ENCHANTMENT_SLOT_3 = 13, // used with RandomProperty
|
||||
PROP_ENCHANTMENT_SLOT_4 = 14, // used with RandomProperty
|
||||
MAX_ENCHANTMENT_SLOT = 15,
|
||||
};
|
||||
|
||||
#define MAX_VISIBLE_ITEM_OFFSET 2 // 2 fields per visible item (entry+enchantment)
|
||||
|
||||
#define MAX_GEM_SOCKETS MAX_ITEM_PROTO_SOCKETS// (BONUS_ENCHANTMENT_SLOT-SOCK_ENCHANTMENT_SLOT) and item proto size, equal value expected
|
||||
|
||||
enum EnchantmentOffset
|
||||
{
|
||||
ENCHANTMENT_ID_OFFSET = 0,
|
||||
ENCHANTMENT_DURATION_OFFSET = 1,
|
||||
ENCHANTMENT_CHARGES_OFFSET = 2 // now here not only charges, but something new in wotlk
|
||||
};
|
||||
|
||||
#define MAX_ENCHANTMENT_OFFSET 3
|
||||
|
||||
enum EnchantmentSlotMask
|
||||
{
|
||||
ENCHANTMENT_CAN_SOULBOUND = 0x01,
|
||||
ENCHANTMENT_UNK1 = 0x02,
|
||||
ENCHANTMENT_UNK2 = 0x04,
|
||||
ENCHANTMENT_UNK3 = 0x08
|
||||
};
|
||||
|
||||
enum ItemUpdateState
|
||||
{
|
||||
ITEM_UNCHANGED = 0,
|
||||
ITEM_CHANGED = 1,
|
||||
ITEM_NEW = 2,
|
||||
ITEM_REMOVED = 3
|
||||
};
|
||||
|
||||
enum ItemLootUpdateState
|
||||
{
|
||||
ITEM_LOOT_NONE = 0, // loot not generated
|
||||
ITEM_LOOT_TEMPORARY = 1, // generated loot is temporary (will deleted at loot window close)
|
||||
ITEM_LOOT_UNCHANGED = 2,
|
||||
ITEM_LOOT_CHANGED = 3,
|
||||
ITEM_LOOT_NEW = 4,
|
||||
ITEM_LOOT_REMOVED = 5
|
||||
};
|
||||
|
||||
// masks for ITEM_FIELD_FLAGS field
|
||||
enum ItemDynFlags
|
||||
{
|
||||
ITEM_DYNFLAG_BINDED = 0x00000001, // set in game at binding
|
||||
ITEM_DYNFLAG_UNK1 = 0x00000002,
|
||||
ITEM_DYNFLAG_UNLOCKED = 0x00000004, // have meaning only for item with proto->LockId, if not set show as "Locked, req. lockpicking N"
|
||||
ITEM_DYNFLAG_WRAPPED = 0x00000008, // mark item as wrapped into wrapper container
|
||||
ITEM_DYNFLAG_UNK4 = 0x00000010, // can't repeat old note: appears red icon (like when item durability==0)
|
||||
ITEM_DYNFLAG_UNK5 = 0x00000020,
|
||||
ITEM_DYNFLAG_UNK6 = 0x00000040, // ? old note: usable
|
||||
ITEM_DYNFLAG_UNK7 = 0x00000080,
|
||||
ITEM_DYNFLAG_UNK8 = 0x00000100,
|
||||
ITEM_DYNFLAG_READABLE = 0x00000200, // can be open for read, it or item proto pagetText make show "Right click to read"
|
||||
ITEM_DYNFLAG_UNK10 = 0x00000400,
|
||||
ITEM_DYNFLAG_UNK11 = 0x00000800,
|
||||
ITEM_DYNFLAG_UNK12 = 0x00001000,
|
||||
ITEM_DYNFLAG_UNK13 = 0x00002000,
|
||||
ITEM_DYNFLAG_UNK14 = 0x00004000,
|
||||
ITEM_DYNFLAG_UNK15 = 0x00008000,
|
||||
ITEM_DYNFLAG_UNK16 = 0x00010000,
|
||||
ITEM_DYNFLAG_UNK17 = 0x00020000,
|
||||
ITEM_DYNFLAG_UNK18 = 0x00040000,
|
||||
ITEM_DYNFLAG_UNK19 = 0x00080000,
|
||||
ITEM_DYNFLAG_UNK20 = 0x00100000,
|
||||
ITEM_DYNFLAG_UNK21 = 0x00200000,
|
||||
ITEM_DYNFLAG_UNK22 = 0x00400000,
|
||||
ITEM_DYNFLAG_UNK23 = 0x00800000,
|
||||
ITEM_DYNFLAG_UNK24 = 0x01000000,
|
||||
ITEM_DYNFLAG_UNK25 = 0x02000000,
|
||||
ITEM_DYNFLAG_UNK26 = 0x04000000,
|
||||
ITEM_DYNFLAG_UNK27 = 0x08000000,
|
||||
ITEM_DYNFLAG_UNK28 = 0x10000000,
|
||||
ITEM_DYNFLAG_UNK29 = 0x20000000,
|
||||
ITEM_DYNFLAG_UNK30 = 0x40000000,
|
||||
ITEM_DYNFLAG_UNK31 = 0x80000000
|
||||
};
|
||||
|
||||
enum ItemRequiredTargetType
|
||||
{
|
||||
ITEM_TARGET_TYPE_CREATURE = 1,
|
||||
ITEM_TARGET_TYPE_DEAD = 2
|
||||
};
|
||||
|
||||
#define MAX_ITEM_REQ_TARGET_TYPE 2
|
||||
|
||||
struct ItemRequiredTarget
|
||||
{
|
||||
ItemRequiredTarget(ItemRequiredTargetType uiType, uint32 uiTargetEntry) : m_uiType(uiType), m_uiTargetEntry(uiTargetEntry) {}
|
||||
ItemRequiredTargetType m_uiType;
|
||||
uint32 m_uiTargetEntry;
|
||||
|
||||
// helpers
|
||||
bool IsFitToRequirements(Unit* pUnitTarget) const;
|
||||
};
|
||||
|
||||
bool ItemCanGoIntoBag(ItemPrototype const* proto, ItemPrototype const* pBagProto);
|
||||
|
||||
class MANGOS_DLL_SPEC Item : public Object
|
||||
{
|
||||
public:
|
||||
static Item* CreateItem(uint32 item, uint32 count, Player const* player = NULL, uint32 randomPropertyId = 0);
|
||||
Item* CloneItem(uint32 count, Player const* player = NULL) const;
|
||||
|
||||
Item();
|
||||
|
||||
virtual bool Create(uint32 guidlow, uint32 itemid, Player const* owner);
|
||||
|
||||
ItemPrototype const* GetProto() const;
|
||||
|
||||
ObjectGuid const& GetOwnerGuid() const { return GetGuidValue(ITEM_FIELD_OWNER); }
|
||||
void SetOwnerGuid(ObjectGuid guid) { SetGuidValue(ITEM_FIELD_OWNER, guid); }
|
||||
Player* GetOwner()const;
|
||||
|
||||
void SetBinding(bool val) { ApplyModFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_BINDED, val); }
|
||||
bool IsSoulBound() const { return HasFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_BINDED); }
|
||||
bool IsBoundAccountWide() const { return GetProto()->Flags & ITEM_FLAG_BOA; }
|
||||
bool IsBindedNotWith(Player const* player) const;
|
||||
bool IsBoundByEnchant() const;
|
||||
virtual void SaveToDB();
|
||||
virtual bool LoadFromDB(uint32 guidLow, Field* fields, ObjectGuid ownerGuid = ObjectGuid());
|
||||
virtual void DeleteFromDB();
|
||||
void DeleteFromInventoryDB();
|
||||
void LoadLootFromDB(Field* fields);
|
||||
|
||||
bool IsBag() const { return GetProto()->InventoryType == INVTYPE_BAG; }
|
||||
bool IsBroken() const { return GetUInt32Value(ITEM_FIELD_MAXDURABILITY) > 0 && GetUInt32Value(ITEM_FIELD_DURABILITY) == 0; }
|
||||
bool CanBeTraded(bool mail = false) const;
|
||||
void SetInTrade(bool b = true) { mb_in_trade = b; }
|
||||
bool IsInTrade() const { return mb_in_trade; }
|
||||
|
||||
bool IsFitToSpellRequirements(SpellEntry const* spellInfo) const;
|
||||
bool IsTargetValidForItemUse(Unit* pUnitTarget);
|
||||
bool IsLimitedToAnotherMapOrZone(uint32 cur_mapId, uint32 cur_zoneId) const;
|
||||
bool GemsFitSockets() const;
|
||||
|
||||
uint32 GetCount() const { return GetUInt32Value(ITEM_FIELD_STACK_COUNT); }
|
||||
void SetCount(uint32 value) { SetUInt32Value(ITEM_FIELD_STACK_COUNT, value); }
|
||||
uint32 GetMaxStackCount() const { return GetProto()->GetMaxStackSize(); }
|
||||
uint8 GetGemCountWithID(uint32 GemID) const;
|
||||
uint8 GetGemCountWithLimitCategory(uint32 limitCategory) const;
|
||||
InventoryResult CanBeMergedPartlyWith(ItemPrototype const* proto) const;
|
||||
|
||||
uint8 GetSlot() const {return m_slot;}
|
||||
Bag* GetContainer() { return m_container; }
|
||||
uint8 GetBagSlot() const;
|
||||
void SetSlot(uint8 slot) {m_slot = slot;}
|
||||
uint16 GetPos() const { return uint16(GetBagSlot()) << 8 | GetSlot(); }
|
||||
void SetContainer(Bag* container) { m_container = container; }
|
||||
|
||||
bool IsInBag() const { return m_container != NULL; }
|
||||
bool IsEquipped() const;
|
||||
|
||||
uint32 GetSkill();
|
||||
|
||||
// RandomPropertyId (signed but stored as unsigned)
|
||||
int32 GetItemRandomPropertyId() const { return GetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID); }
|
||||
uint32 GetItemSuffixFactor() const { return GetUInt32Value(ITEM_FIELD_PROPERTY_SEED); }
|
||||
void SetItemRandomProperties(int32 randomPropId);
|
||||
bool UpdateItemSuffixFactor();
|
||||
static int32 GenerateItemRandomPropertyId(uint32 item_id);
|
||||
void SetEnchantment(EnchantmentSlot slot, uint32 id, uint32 duration, uint32 charges);
|
||||
void SetEnchantmentDuration(EnchantmentSlot slot, uint32 duration);
|
||||
void SetEnchantmentCharges(EnchantmentSlot slot, uint32 charges);
|
||||
void ClearEnchantment(EnchantmentSlot slot);
|
||||
uint32 GetEnchantmentId(EnchantmentSlot slot) const { return GetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot * MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_ID_OFFSET);}
|
||||
uint32 GetEnchantmentDuration(EnchantmentSlot slot) const { return GetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot * MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET);}
|
||||
uint32 GetEnchantmentCharges(EnchantmentSlot slot) const { return GetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot * MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_CHARGES_OFFSET);}
|
||||
|
||||
std::string const& GetText() const { return m_text; }
|
||||
void SetText(std::string const& text) { m_text = text; }
|
||||
|
||||
void SendTimeUpdate(Player* owner);
|
||||
void UpdateDuration(Player* owner, uint32 diff);
|
||||
|
||||
// spell charges (signed but stored as unsigned)
|
||||
int32 GetSpellCharges(uint8 index/*0..5*/ = 0) const { return GetInt32Value(ITEM_FIELD_SPELL_CHARGES + index); }
|
||||
void SetSpellCharges(uint8 index/*0..5*/, int32 value) { SetInt32Value(ITEM_FIELD_SPELL_CHARGES + index, value); }
|
||||
bool HasMaxCharges() const;
|
||||
void RestoreCharges();
|
||||
|
||||
Loot loot;
|
||||
|
||||
void SetLootState(ItemLootUpdateState state);
|
||||
bool HasGeneratedLoot() const { return m_lootState != ITEM_LOOT_NONE && m_lootState != ITEM_LOOT_REMOVED; }
|
||||
bool HasTemporaryLoot() const { return m_lootState == ITEM_LOOT_TEMPORARY; }
|
||||
|
||||
bool HasSavedLoot() const { return m_lootState != ITEM_LOOT_NONE && m_lootState != ITEM_LOOT_NEW && m_lootState != ITEM_LOOT_TEMPORARY; }
|
||||
|
||||
// Update States
|
||||
ItemUpdateState GetState() const { return uState; }
|
||||
void SetState(ItemUpdateState state, Player* forplayer = NULL);
|
||||
void AddToUpdateQueueOf(Player* player);
|
||||
void RemoveFromUpdateQueueOf(Player* player);
|
||||
bool IsInUpdateQueue() const { return uQueuePos != -1; }
|
||||
uint16 GetQueuePos() const { return uQueuePos; }
|
||||
void FSetState(ItemUpdateState state) // forced
|
||||
{
|
||||
uState = state;
|
||||
}
|
||||
|
||||
bool HasQuest(uint32 quest_id) const override { return GetProto()->StartQuest == quest_id; }
|
||||
bool HasInvolvedQuest(uint32 /*quest_id*/) const override { return false; }
|
||||
bool IsPotion() const { return GetProto()->IsPotion(); }
|
||||
bool IsConjuredConsumable() const { return GetProto()->IsConjuredConsumable(); }
|
||||
|
||||
void AddToClientUpdateList() override;
|
||||
void RemoveFromClientUpdateList() override;
|
||||
void BuildUpdateData(UpdateDataMapType& update_players) override;
|
||||
|
||||
// Reforge
|
||||
static uint32 GetSpecialPrice(ItemPrototype const* proto, uint32 minimumPrice = 10000);
|
||||
uint32 GetSpecialPrice(uint32 minimumPrice = 10000) const { return Item::GetSpecialPrice(GetProto(), minimumPrice); }
|
||||
int32 GetReforgableStat(ItemModType statType) const;
|
||||
private:
|
||||
std::string m_text;
|
||||
uint8 m_slot;
|
||||
Bag* m_container;
|
||||
ItemUpdateState uState;
|
||||
int16 uQueuePos;
|
||||
bool mb_in_trade; // true if item is currently in trade-window
|
||||
ItemLootUpdateState m_lootState;
|
||||
};
|
||||
|
||||
#endif
|
||||
234
src/game/Object/ItemEnchantmentMgr.cpp
Normal file
234
src/game/Object/ItemEnchantmentMgr.cpp
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <functional>
|
||||
#include "ItemEnchantmentMgr.h"
|
||||
#include "Database/DatabaseEnv.h"
|
||||
#include "Log.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "ProgressBar.h"
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include "Util.h"
|
||||
|
||||
struct EnchStoreItem
|
||||
{
|
||||
uint32 ench;
|
||||
float chance;
|
||||
|
||||
EnchStoreItem()
|
||||
: ench(0), chance(0) {}
|
||||
|
||||
EnchStoreItem(uint32 _ench, float _chance)
|
||||
: ench(_ench), chance(_chance) {}
|
||||
};
|
||||
|
||||
typedef std::vector<EnchStoreItem> EnchStoreList;
|
||||
typedef UNORDERED_MAP<uint32, EnchStoreList> EnchantmentStore;
|
||||
|
||||
static EnchantmentStore RandomItemPropEnch;
|
||||
static EnchantmentStore RandomItemSuffixEnch;
|
||||
|
||||
void LoadRandomEnchantmentsTable()
|
||||
{
|
||||
RandomItemPropEnch.clear(); // for reload case
|
||||
RandomItemSuffixEnch.clear(); // for reload case
|
||||
|
||||
uint32 count = 0;
|
||||
QueryResult* result = WorldDatabase.Query("SELECT entry, ench, chance FROM item_enchantment_template");
|
||||
|
||||
if (result)
|
||||
{
|
||||
BarGoLink bar(result->GetRowCount());
|
||||
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
bar.step();
|
||||
|
||||
int32 entry = fields[0].GetInt32();
|
||||
uint32 ench = fields[1].GetUInt32();
|
||||
float chance = fields[2].GetFloat();
|
||||
|
||||
if (chance > 0.000001f && chance <= 100.0f)
|
||||
{
|
||||
if (entry > 0)
|
||||
RandomItemPropEnch[entry].push_back(EnchStoreItem(ench, chance));
|
||||
else
|
||||
RandomItemSuffixEnch[-entry].push_back(EnchStoreItem(ench, chance));
|
||||
}
|
||||
else
|
||||
{
|
||||
sLog.outErrorDb("Item Enchantment %u for entry %i has too high or too low chance %f, skipped.", ench, entry, chance);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
++count;
|
||||
}
|
||||
while (result->NextRow());
|
||||
|
||||
delete result;
|
||||
|
||||
sLog.outString();
|
||||
sLog.outString(">> Loaded %u Item Enchantment definitions", count);
|
||||
}
|
||||
else
|
||||
{
|
||||
sLog.outString();
|
||||
sLog.outErrorDb(">> Loaded 0 Item Enchantment definitions. DB table `item_enchantment_template` is empty.");
|
||||
}
|
||||
}
|
||||
|
||||
uint32 GetItemEnchantMod(int32 entry)
|
||||
{
|
||||
if (!entry)
|
||||
return 0;
|
||||
|
||||
EnchantmentStore::const_iterator tab;
|
||||
if (entry > 0)
|
||||
{
|
||||
tab = RandomItemPropEnch.find(entry);
|
||||
if (tab == RandomItemPropEnch.end())
|
||||
{
|
||||
sLog.outErrorDb("Item RandomProperty id #%u used in `item_template` but it doesn't have records in `item_enchantment_template` table.", entry);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tab = RandomItemSuffixEnch.find(-entry);
|
||||
if (tab == RandomItemSuffixEnch.end())
|
||||
{
|
||||
sLog.outErrorDb("Item RandomSuffix id #%u used in `item_template` but it doesn't have records in `item_enchantment_template` table.", -entry);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
double dRoll = rand_chance();
|
||||
float fCount = 0;
|
||||
|
||||
for (EnchStoreList::const_iterator ench_iter = tab->second.begin(); ench_iter != tab->second.end(); ++ench_iter)
|
||||
{
|
||||
fCount += ench_iter->chance;
|
||||
|
||||
if (fCount > dRoll) return ench_iter->ench;
|
||||
}
|
||||
|
||||
// we could get here only if sum of all enchantment chances is lower than 100%
|
||||
dRoll = (irand(0, (int)floor(fCount * 100) + 1)) / 100;
|
||||
fCount = 0;
|
||||
|
||||
for (EnchStoreList::const_iterator ench_iter = tab->second.begin(); ench_iter != tab->second.end(); ++ench_iter)
|
||||
{
|
||||
fCount += ench_iter->chance;
|
||||
|
||||
if (fCount > dRoll) return ench_iter->ench;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 GetItemRandomPropertyMod(uint32 entry) { return GetItemEnchantMod(entry); }
|
||||
uint32 GetItemRandomSuffixMod(uint32 entry) { return GetItemEnchantMod(-int32(entry)); }
|
||||
|
||||
uint32 GenerateEnchSuffixFactor(uint32 item_id)
|
||||
{
|
||||
ItemPrototype const* itemProto = ObjectMgr::GetItemPrototype(item_id);
|
||||
|
||||
if (!itemProto)
|
||||
return 0;
|
||||
if (!itemProto->RandomSuffix)
|
||||
return 0;
|
||||
|
||||
RandomPropertiesPointsEntry const* randomProperty = sRandomPropertiesPointsStore.LookupEntry(itemProto->ItemLevel);
|
||||
if (!randomProperty)
|
||||
return 0;
|
||||
|
||||
uint32 suffixFactor;
|
||||
switch (itemProto->InventoryType)
|
||||
{
|
||||
// Items of that type don`t have points
|
||||
case INVTYPE_NON_EQUIP:
|
||||
case INVTYPE_BAG:
|
||||
case INVTYPE_TABARD:
|
||||
case INVTYPE_AMMO:
|
||||
case INVTYPE_QUIVER:
|
||||
case INVTYPE_RELIC:
|
||||
return 0;
|
||||
// Select point coefficient
|
||||
case INVTYPE_HEAD:
|
||||
case INVTYPE_BODY:
|
||||
case INVTYPE_CHEST:
|
||||
case INVTYPE_LEGS:
|
||||
case INVTYPE_2HWEAPON:
|
||||
case INVTYPE_ROBE:
|
||||
suffixFactor = 0;
|
||||
break;
|
||||
case INVTYPE_SHOULDERS:
|
||||
case INVTYPE_WAIST:
|
||||
case INVTYPE_FEET:
|
||||
case INVTYPE_HANDS:
|
||||
case INVTYPE_TRINKET:
|
||||
suffixFactor = 1;
|
||||
break;
|
||||
case INVTYPE_NECK:
|
||||
case INVTYPE_WRISTS:
|
||||
case INVTYPE_FINGER:
|
||||
case INVTYPE_SHIELD:
|
||||
case INVTYPE_CLOAK:
|
||||
case INVTYPE_HOLDABLE:
|
||||
suffixFactor = 2;
|
||||
break;
|
||||
case INVTYPE_WEAPON:
|
||||
case INVTYPE_WEAPONMAINHAND:
|
||||
case INVTYPE_WEAPONOFFHAND:
|
||||
suffixFactor = 3;
|
||||
break;
|
||||
case INVTYPE_RANGED:
|
||||
case INVTYPE_THROWN:
|
||||
case INVTYPE_RANGEDRIGHT:
|
||||
suffixFactor = 4;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
// Select rare/epic modifier
|
||||
switch (itemProto->Quality)
|
||||
{
|
||||
case ITEM_QUALITY_UNCOMMON:
|
||||
return randomProperty->UncommonPropertiesPoints[suffixFactor];
|
||||
case ITEM_QUALITY_RARE:
|
||||
return randomProperty->RarePropertiesPoints[suffixFactor];
|
||||
case ITEM_QUALITY_EPIC:
|
||||
return randomProperty->EpicPropertiesPoints[suffixFactor];
|
||||
case ITEM_QUALITY_LEGENDARY:
|
||||
case ITEM_QUALITY_ARTIFACT:
|
||||
return 0; // not have random properties
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
34
src/game/Object/ItemEnchantmentMgr.h
Normal file
34
src/game/Object/ItemEnchantmentMgr.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef _ITEM_ENCHANTMENT_MGR_H
|
||||
#define _ITEM_ENCHANTMENT_MGR_H
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
void LoadRandomEnchantmentsTable();
|
||||
uint32 GetItemRandomPropertyMod(uint32 entry);
|
||||
uint32 GetItemRandomSuffixMod(uint32 entry);
|
||||
uint32 GenerateEnchSuffixFactor(uint32 item_id);
|
||||
#endif
|
||||
685
src/game/Object/ItemPrototype.h
Normal file
685
src/game/Object/ItemPrototype.h
Normal file
|
|
@ -0,0 +1,685 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef _ITEMPROTOTYPE_H
|
||||
#define _ITEMPROTOTYPE_H
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
enum ItemModType
|
||||
{
|
||||
ITEM_MOD_NONE = 0,
|
||||
ITEM_MOD_HEALTH = 1, // deprecated
|
||||
ITEM_MOD_AGILITY = 3,
|
||||
ITEM_MOD_STRENGTH = 4,
|
||||
ITEM_MOD_INTELLECT = 5,
|
||||
ITEM_MOD_SPIRIT = 6,
|
||||
ITEM_MOD_STAMINA = 7,
|
||||
ITEM_MOD_DEFENSE_SKILL_RATING = 12, // deprecated
|
||||
ITEM_MOD_DODGE_RATING = 13,
|
||||
ITEM_MOD_PARRY_RATING = 14,
|
||||
ITEM_MOD_BLOCK_RATING = 15, // deprecated
|
||||
ITEM_MOD_HIT_MELEE_RATING = 16, // deprecated
|
||||
ITEM_MOD_HIT_RANGED_RATING = 17, // deprecated
|
||||
ITEM_MOD_HIT_SPELL_RATING = 18, // deprecated
|
||||
ITEM_MOD_CRIT_MELEE_RATING = 19, // deprecated
|
||||
ITEM_MOD_CRIT_RANGED_RATING = 20,
|
||||
ITEM_MOD_CRIT_SPELL_RATING = 21, // deprecated
|
||||
ITEM_MOD_HIT_TAKEN_MELEE_RATING = 22, // deprecated
|
||||
ITEM_MOD_HIT_TAKEN_RANGED_RATING = 23, // deprecated
|
||||
ITEM_MOD_HIT_TAKEN_SPELL_RATING = 24, // deprecated
|
||||
ITEM_MOD_CRIT_TAKEN_MELEE_RATING = 25, // deprecated
|
||||
ITEM_MOD_CRIT_TAKEN_RANGED_RATING = 26, // deprecated
|
||||
ITEM_MOD_CRIT_TAKEN_SPELL_RATING = 27, // deprecated
|
||||
ITEM_MOD_HASTE_MELEE_RATING = 28, // deprecated
|
||||
ITEM_MOD_HASTE_RANGED_RATING = 29, // deprecated
|
||||
ITEM_MOD_HASTE_SPELL_RATING = 30, // deprecated
|
||||
ITEM_MOD_HIT_RATING = 31,
|
||||
ITEM_MOD_CRIT_RATING = 32,
|
||||
ITEM_MOD_HIT_TAKEN_RATING = 33, // deprecated
|
||||
ITEM_MOD_CRIT_TAKEN_RATING = 34, // deprecated
|
||||
ITEM_MOD_RESILIENCE_RATING = 35,
|
||||
ITEM_MOD_HASTE_RATING = 36,
|
||||
ITEM_MOD_EXPERTISE_RATING = 37,
|
||||
ITEM_MOD_ATTACK_POWER = 38,
|
||||
ITEM_MOD_RANGED_ATTACK_POWER = 39,
|
||||
ITEM_MOD_FERAL_ATTACK_POWER = 40, // deprecated
|
||||
ITEM_MOD_SPELL_HEALING_DONE = 41, // deprecated
|
||||
ITEM_MOD_SPELL_DAMAGE_DONE = 42, // deprecated
|
||||
ITEM_MOD_MANA_REGENERATION = 43, // deprecated
|
||||
ITEM_MOD_ARMOR_PENETRATION_RATING = 44, // deprecated
|
||||
ITEM_MOD_SPELL_POWER = 45,
|
||||
ITEM_MOD_HEALTH_REGEN = 46,
|
||||
ITEM_MOD_SPELL_PENETRATION = 47,
|
||||
ITEM_MOD_BLOCK_VALUE = 48, // deprecated
|
||||
ITEM_MOD_MASTERY_RATING = 49,
|
||||
ITEM_MOD_EXTRA_ARMOR = 50,
|
||||
ITEM_MOD_FIRE_RESISTANCE = 51,
|
||||
ITEM_MOD_FROST_RESISTANCE = 52,
|
||||
ITEM_MOD_HOLY_RESISTANCE = 53,
|
||||
ITEM_MOD_SHADOW_RESISTANCE = 54,
|
||||
ITEM_MOD_NATURE_RESISTANCE = 55,
|
||||
ITEM_MOD_ARCANE_RESISTANCE = 56
|
||||
};
|
||||
|
||||
#define MAX_ITEM_MOD 57
|
||||
|
||||
enum ItemSpelltriggerType
|
||||
{
|
||||
ITEM_SPELLTRIGGER_ON_USE = 0, // use after equip cooldown
|
||||
ITEM_SPELLTRIGGER_ON_EQUIP = 1,
|
||||
ITEM_SPELLTRIGGER_CHANCE_ON_HIT = 2,
|
||||
ITEM_SPELLTRIGGER_SOULSTONE = 4,
|
||||
/*
|
||||
* ItemSpelltriggerType 5 might have changed on 2.4.3/3.0.3: Such auras
|
||||
* will be applied on item pickup and removed on item loss - maybe on the
|
||||
* other hand the item is destroyed if the aura is removed ("removed on
|
||||
* death" of spell 57348 makes me think so)
|
||||
*/
|
||||
ITEM_SPELLTRIGGER_ON_STORE = 5, // casted at add item to inventory/equip, applied aura removed at remove item, item deleted at aura cancel/expire/etc
|
||||
ITEM_SPELLTRIGGER_LEARN_SPELL_ID = 6 // used in item_template.spell_2 with spell_id with SPELL_GENERIC_LEARN in spell_1
|
||||
};
|
||||
|
||||
#define MAX_ITEM_SPELLTRIGGER 7
|
||||
|
||||
enum ItemBondingType
|
||||
{
|
||||
NO_BIND = 0,
|
||||
BIND_WHEN_PICKED_UP = 1,
|
||||
BIND_WHEN_EQUIPPED = 2,
|
||||
BIND_WHEN_USE = 3,
|
||||
BIND_QUEST_ITEM = 4,
|
||||
BIND_QUEST_ITEM1 = 5 // not used in game
|
||||
};
|
||||
|
||||
#define MAX_BIND_TYPE 6
|
||||
|
||||
// Mask for ItemPrototype.Flags field
|
||||
enum ItemPrototypeFlags
|
||||
{
|
||||
ITEM_FLAG_UNK0 = 0x00000001, // not used
|
||||
ITEM_FLAG_CONJURED = 0x00000002,
|
||||
ITEM_FLAG_LOOTABLE = 0x00000004, // affect only non container items that can be "open" for loot. It or lockid set enable for client show "Right click to open". See also ITEM_DYNFLAG_UNLOCKED
|
||||
ITEM_FLAG_HEROIC = 0x00000008, // heroic item version
|
||||
ITEM_FLAG_UNK4 = 0x00000010, // can't repeat old note: appears red icon (like when item durability==0)
|
||||
ITEM_FLAG_INDESTRUCTIBLE = 0x00000020, // used for totem. Item can not be destroyed, except by using spell (item can be reagent for spell and then allowed)
|
||||
ITEM_FLAG_UNK6 = 0x00000040, // ? old note: usable
|
||||
ITEM_FLAG_NO_EQUIP_COOLDOWN = 0x00000080,
|
||||
ITEM_FLAG_UNK8 = 0x00000100, // saw this on item 47115, 49295...
|
||||
ITEM_FLAG_WRAPPER = 0x00000200, // used or not used wrapper
|
||||
ITEM_FLAG_IGNORE_BAG_SPACE = 0x00000400, // ignore bag space at new item creation?
|
||||
ITEM_FLAG_PARTY_LOOT = 0x00000800, // determines if item is party loot or not
|
||||
ITEM_FLAG_REFUNDABLE = 0x00001000, // item cost can be refunded within 2 hours after purchase
|
||||
ITEM_FLAG_CHARTER = 0x00002000, // arena/guild charter
|
||||
ITEM_FLAG_UNK14 = 0x00004000,
|
||||
ITEM_FLAG_UNK15 = 0x00008000, // a lot of items have this
|
||||
ITEM_FLAG_UNK16 = 0x00010000, // a lot of items have this
|
||||
ITEM_FLAG_UNK17 = 0x00020000,
|
||||
ITEM_FLAG_PROSPECTABLE = 0x00040000, // item can have prospecting loot (in fact some items expected have empty loot)
|
||||
ITEM_FLAG_UNIQUE_EQUIPPED = 0x00080000,
|
||||
ITEM_FLAG_UNK20 = 0x00100000,
|
||||
ITEM_FLAG_USEABLE_IN_ARENA = 0x00200000,
|
||||
ITEM_FLAG_THROWABLE = 0x00400000, // Only items of ITEM_SUBCLASS_WEAPON_THROWN have it but not all, so can't be used as in game check
|
||||
ITEM_FLAG_SPECIALUSE = 0x00800000, // last used flag in 2.3.0
|
||||
ITEM_FLAG_UNK24 = 0x01000000,
|
||||
ITEM_FLAG_UNK25 = 0x02000000,
|
||||
ITEM_FLAG_UNK26 = 0x04000000,
|
||||
ITEM_FLAG_BOA = 0x08000000, // bind on account (set in template for items that can binded in like way)
|
||||
ITEM_FLAG_ENCHANT_SCROLL = 0x10000000, // for enchant scrolls
|
||||
ITEM_FLAG_MILLABLE = 0x20000000, // item can have milling loot
|
||||
ITEM_FLAG_UNK30 = 0x40000000,
|
||||
ITEM_FLAG_BOP_TRADEABLE = 0x80000000, // bound item that can be traded
|
||||
};
|
||||
|
||||
enum ItemPrototypeFlags2
|
||||
{
|
||||
ITEM_FLAG2_HORDE_ONLY = 0x00000001, // drop in loot, sell by vendor and equipping only for horde
|
||||
ITEM_FLAG2_ALLIANCE_ONLY = 0x00000002, // drop in loot, sell by vendor and equipping only for alliance
|
||||
ITEM_FLAG2_EXT_COST_REQUIRES_GOLD = 0x00000004, // item cost include gold part in case extended cost use also
|
||||
ITEM_FLAG2_UNK4 = 0x00000008,
|
||||
ITEM_FLAG2_UNK5 = 0x00000010,
|
||||
ITEM_FLAG2_UNK6 = 0x00000020,
|
||||
ITEM_FLAG2_UNK7 = 0x00000040,
|
||||
ITEM_FLAG2_UNK8 = 0x00000080,
|
||||
ITEM_FLAG2_NEED_ROLL_DISABLED = 0x00000100, // need roll during looting is not allowed for this item
|
||||
ITEM_FLAG2_CASTER_WEAPON = 0x00000200, // uses caster specific dbc file for DPS calculations
|
||||
ITEM_FLAG2_HAS_NORMAL_PRICE = 0x00004000,
|
||||
};
|
||||
|
||||
enum BagFamilyMask
|
||||
{
|
||||
BAG_FAMILY_MASK_NONE = 0x00000000,
|
||||
BAG_FAMILY_MASK_ARROWS = 0x00000001,
|
||||
BAG_FAMILY_MASK_BULLETS = 0x00000002,
|
||||
BAG_FAMILY_MASK_SOUL_SHARDS = 0x00000004,
|
||||
BAG_FAMILY_MASK_LEATHERWORKING_SUPP = 0x00000008,
|
||||
BAG_FAMILY_MASK_INSCRIPTION_SUPP = 0x00000010,
|
||||
BAG_FAMILY_MASK_HERBS = 0x00000020,
|
||||
BAG_FAMILY_MASK_ENCHANTING_SUPP = 0x00000040,
|
||||
BAG_FAMILY_MASK_ENGINEERING_SUPP = 0x00000080,
|
||||
BAG_FAMILY_MASK_KEYS = 0x00000100,
|
||||
BAG_FAMILY_MASK_GEMS = 0x00000200,
|
||||
BAG_FAMILY_MASK_MINING_SUPP = 0x00000400,
|
||||
BAG_FAMILY_MASK_SOULBOUND_EQUIPMENT = 0x00000800,
|
||||
BAG_FAMILY_MASK_VANITY_PETS = 0x00001000,
|
||||
BAG_FAMILY_MASK_CURRENCY_TOKENS = 0x00002000,
|
||||
BAG_FAMILY_MASK_QUEST_ITEMS = 0x00004000,
|
||||
BAG_FAMILY_MASK_FISHING_SUPP = 0x00008000,
|
||||
};
|
||||
|
||||
enum SocketColor
|
||||
{
|
||||
SOCKET_COLOR_META = 1,
|
||||
SOCKET_COLOR_RED = 2,
|
||||
SOCKET_COLOR_YELLOW = 4,
|
||||
SOCKET_COLOR_BLUE = 8,
|
||||
SOCKET_COLOR_HYDRAULIC = 16, // unused
|
||||
SOCKET_COLOR_COGWHEEL = 32,
|
||||
};
|
||||
|
||||
#define SOCKET_COLOR_ALL (SOCKET_COLOR_META | SOCKET_COLOR_RED | SOCKET_COLOR_YELLOW | SOCKET_COLOR_BLUE | SOCKET_COLOR_COGWHEEL)
|
||||
|
||||
enum InventoryType
|
||||
{
|
||||
INVTYPE_NON_EQUIP = 0,
|
||||
INVTYPE_HEAD = 1,
|
||||
INVTYPE_NECK = 2,
|
||||
INVTYPE_SHOULDERS = 3,
|
||||
INVTYPE_BODY = 4,
|
||||
INVTYPE_CHEST = 5,
|
||||
INVTYPE_WAIST = 6,
|
||||
INVTYPE_LEGS = 7,
|
||||
INVTYPE_FEET = 8,
|
||||
INVTYPE_WRISTS = 9,
|
||||
INVTYPE_HANDS = 10,
|
||||
INVTYPE_FINGER = 11,
|
||||
INVTYPE_TRINKET = 12,
|
||||
INVTYPE_WEAPON = 13,
|
||||
INVTYPE_SHIELD = 14,
|
||||
INVTYPE_RANGED = 15,
|
||||
INVTYPE_CLOAK = 16,
|
||||
INVTYPE_2HWEAPON = 17,
|
||||
INVTYPE_BAG = 18,
|
||||
INVTYPE_TABARD = 19,
|
||||
INVTYPE_ROBE = 20,
|
||||
INVTYPE_WEAPONMAINHAND = 21,
|
||||
INVTYPE_WEAPONOFFHAND = 22,
|
||||
INVTYPE_HOLDABLE = 23,
|
||||
INVTYPE_AMMO = 24,
|
||||
INVTYPE_THROWN = 25,
|
||||
INVTYPE_RANGEDRIGHT = 26,
|
||||
INVTYPE_QUIVER = 27,
|
||||
INVTYPE_RELIC = 28
|
||||
};
|
||||
|
||||
#define MAX_INVTYPE 29
|
||||
|
||||
enum ItemClass
|
||||
{
|
||||
ITEM_CLASS_CONSUMABLE = 0,
|
||||
ITEM_CLASS_CONTAINER = 1,
|
||||
ITEM_CLASS_WEAPON = 2,
|
||||
ITEM_CLASS_GEM = 3,
|
||||
ITEM_CLASS_ARMOR = 4,
|
||||
ITEM_CLASS_REAGENT = 5,
|
||||
ITEM_CLASS_PROJECTILE = 6,
|
||||
ITEM_CLASS_TRADE_GOODS = 7,
|
||||
ITEM_CLASS_GENERIC = 8,
|
||||
ITEM_CLASS_RECIPE = 9,
|
||||
ITEM_CLASS_MONEY = 10,
|
||||
ITEM_CLASS_QUIVER = 11,
|
||||
ITEM_CLASS_QUEST = 12,
|
||||
ITEM_CLASS_KEY = 13,
|
||||
ITEM_CLASS_PERMANENT = 14,
|
||||
ITEM_CLASS_MISC = 15,
|
||||
ITEM_CLASS_GLYPH = 16
|
||||
};
|
||||
|
||||
#define MAX_ITEM_CLASS 17
|
||||
|
||||
enum ItemSubclassConsumable
|
||||
{
|
||||
ITEM_SUBCLASS_CONSUMABLE = 0,
|
||||
ITEM_SUBCLASS_POTION = 1,
|
||||
ITEM_SUBCLASS_ELIXIR = 2,
|
||||
ITEM_SUBCLASS_FLASK = 3,
|
||||
ITEM_SUBCLASS_SCROLL = 4,
|
||||
ITEM_SUBCLASS_FOOD = 5,
|
||||
ITEM_SUBCLASS_ITEM_ENHANCEMENT = 6,
|
||||
ITEM_SUBCLASS_BANDAGE = 7,
|
||||
ITEM_SUBCLASS_CONSUMABLE_OTHER = 8
|
||||
};
|
||||
|
||||
#define MAX_ITEM_SUBCLASS_CONSUMABLE 9
|
||||
|
||||
enum ItemSubclassContainer
|
||||
{
|
||||
ITEM_SUBCLASS_CONTAINER = 0,
|
||||
ITEM_SUBCLASS_SOUL_CONTAINER = 1,
|
||||
ITEM_SUBCLASS_HERB_CONTAINER = 2,
|
||||
ITEM_SUBCLASS_ENCHANTING_CONTAINER = 3,
|
||||
ITEM_SUBCLASS_ENGINEERING_CONTAINER = 4,
|
||||
ITEM_SUBCLASS_GEM_CONTAINER = 5,
|
||||
ITEM_SUBCLASS_MINING_CONTAINER = 6,
|
||||
ITEM_SUBCLASS_LEATHERWORKING_CONTAINER = 7,
|
||||
ITEM_SUBCLASS_INSCRIPTION_CONTAINER = 8,
|
||||
ITEM_SUBCLASS_FISHING_CONTAINER = 9,
|
||||
};
|
||||
|
||||
#define MAX_ITEM_SUBCLASS_CONTAINER 10
|
||||
|
||||
enum ItemSubclassWeapon
|
||||
{
|
||||
ITEM_SUBCLASS_WEAPON_AXE = 0,
|
||||
ITEM_SUBCLASS_WEAPON_AXE2 = 1,
|
||||
ITEM_SUBCLASS_WEAPON_BOW = 2,
|
||||
ITEM_SUBCLASS_WEAPON_GUN = 3,
|
||||
ITEM_SUBCLASS_WEAPON_MACE = 4,
|
||||
ITEM_SUBCLASS_WEAPON_MACE2 = 5,
|
||||
ITEM_SUBCLASS_WEAPON_POLEARM = 6,
|
||||
ITEM_SUBCLASS_WEAPON_SWORD = 7,
|
||||
ITEM_SUBCLASS_WEAPON_SWORD2 = 8,
|
||||
ITEM_SUBCLASS_WEAPON_obsolete = 9,
|
||||
ITEM_SUBCLASS_WEAPON_STAFF = 10,
|
||||
ITEM_SUBCLASS_WEAPON_EXOTIC = 11,
|
||||
ITEM_SUBCLASS_WEAPON_EXOTIC2 = 12,
|
||||
ITEM_SUBCLASS_WEAPON_FIST = 13,
|
||||
ITEM_SUBCLASS_WEAPON_MISC = 14,
|
||||
ITEM_SUBCLASS_WEAPON_DAGGER = 15,
|
||||
ITEM_SUBCLASS_WEAPON_THROWN = 16,
|
||||
ITEM_SUBCLASS_WEAPON_SPEAR = 17,
|
||||
ITEM_SUBCLASS_WEAPON_CROSSBOW = 18,
|
||||
ITEM_SUBCLASS_WEAPON_WAND = 19,
|
||||
ITEM_SUBCLASS_WEAPON_FISHING_POLE = 20
|
||||
};
|
||||
|
||||
#define MAX_ITEM_SUBCLASS_WEAPON 21
|
||||
|
||||
enum ItemSubclassGem
|
||||
{
|
||||
ITEM_SUBCLASS_GEM_RED = 0,
|
||||
ITEM_SUBCLASS_GEM_BLUE = 1,
|
||||
ITEM_SUBCLASS_GEM_YELLOW = 2,
|
||||
ITEM_SUBCLASS_GEM_PURPLE = 3,
|
||||
ITEM_SUBCLASS_GEM_GREEN = 4,
|
||||
ITEM_SUBCLASS_GEM_ORANGE = 5,
|
||||
ITEM_SUBCLASS_GEM_META = 6,
|
||||
ITEM_SUBCLASS_GEM_SIMPLE = 7,
|
||||
ITEM_SUBCLASS_GEM_PRISMATIC = 8,
|
||||
ITEM_SUBCLASS_GEM_HYDRAULIC = 9, // ABS
|
||||
ITEM_SUBCLASS_GEM_COGWHEEL = 10,
|
||||
};
|
||||
|
||||
#define MAX_ITEM_SUBCLASS_GEM 11
|
||||
|
||||
enum ItemSubclassArmor
|
||||
{
|
||||
ITEM_SUBCLASS_ARMOR_MISC = 0,
|
||||
ITEM_SUBCLASS_ARMOR_CLOTH = 1,
|
||||
ITEM_SUBCLASS_ARMOR_LEATHER = 2,
|
||||
ITEM_SUBCLASS_ARMOR_MAIL = 3,
|
||||
ITEM_SUBCLASS_ARMOR_PLATE = 4,
|
||||
ITEM_SUBCLASS_ARMOR_BUCKLER = 5,
|
||||
ITEM_SUBCLASS_ARMOR_SHIELD = 6,
|
||||
ITEM_SUBCLASS_ARMOR_LIBRAM = 7,
|
||||
ITEM_SUBCLASS_ARMOR_IDOL = 8,
|
||||
ITEM_SUBCLASS_ARMOR_TOTEM = 9,
|
||||
ITEM_SUBCLASS_ARMOR_SIGIL = 10,
|
||||
ITEM_SUBCLASS_ARMOR_RELIC = 11,
|
||||
};
|
||||
|
||||
#define MAX_ITEM_SUBCLASS_ARMOR 12
|
||||
|
||||
enum ItemSubclassReagent
|
||||
{
|
||||
ITEM_SUBCLASS_REAGENT = 0
|
||||
};
|
||||
|
||||
#define MAX_ITEM_SUBCLASS_REAGENT 1
|
||||
|
||||
enum ItemSubclassProjectile
|
||||
{
|
||||
ITEM_SUBCLASS_WAND = 0, // ABS
|
||||
ITEM_SUBCLASS_BOLT = 1, // ABS
|
||||
ITEM_SUBCLASS_ARROW = 2,
|
||||
ITEM_SUBCLASS_BULLET = 3,
|
||||
ITEM_SUBCLASS_THROWN = 4 // ABS
|
||||
};
|
||||
|
||||
#define MAX_ITEM_SUBCLASS_PROJECTILE 4
|
||||
|
||||
enum ItemSubclassTradeGoods
|
||||
{
|
||||
ITEM_SUBCLASS_TRADE_GOODS = 0,
|
||||
ITEM_SUBCLASS_PARTS = 1,
|
||||
ITEM_SUBCLASS_EXPLOSIVES = 2,
|
||||
ITEM_SUBCLASS_DEVICES = 3,
|
||||
ITEM_SUBCLASS_JEWELCRAFTING = 4,
|
||||
ITEM_SUBCLASS_CLOTH = 5,
|
||||
ITEM_SUBCLASS_LEATHER = 6,
|
||||
ITEM_SUBCLASS_METAL_STONE = 7,
|
||||
ITEM_SUBCLASS_MEAT = 8,
|
||||
ITEM_SUBCLASS_HERB = 9,
|
||||
ITEM_SUBCLASS_ELEMENTAL = 10,
|
||||
ITEM_SUBCLASS_TRADE_GOODS_OTHER = 11,
|
||||
ITEM_SUBCLASS_ENCHANTING = 12,
|
||||
ITEM_SUBCLASS_MATERIAL = 13,
|
||||
ITEM_SUBCLASS_VELLUM = 14,
|
||||
};
|
||||
|
||||
#define MAX_ITEM_SUBCLASS_TRADE_GOODS 15
|
||||
|
||||
enum ItemSubclassGeneric
|
||||
{
|
||||
ITEM_SUBCLASS_GENERIC = 0
|
||||
};
|
||||
|
||||
#define MAX_ITEM_SUBCLASS_GENERIC 1
|
||||
|
||||
enum ItemSubclassRecipe
|
||||
{
|
||||
ITEM_SUBCLASS_BOOK = 0,
|
||||
ITEM_SUBCLASS_LEATHERWORKING_PATTERN = 1,
|
||||
ITEM_SUBCLASS_TAILORING_PATTERN = 2,
|
||||
ITEM_SUBCLASS_ENGINEERING_SCHEMATIC = 3,
|
||||
ITEM_SUBCLASS_BLACKSMITHING = 4,
|
||||
ITEM_SUBCLASS_COOKING_RECIPE = 5,
|
||||
ITEM_SUBCLASS_ALCHEMY_RECIPE = 6,
|
||||
ITEM_SUBCLASS_FIRST_AID_MANUAL = 7,
|
||||
ITEM_SUBCLASS_ENCHANTING_FORMULA = 8,
|
||||
ITEM_SUBCLASS_FISHING_MANUAL = 9,
|
||||
ITEM_SUBCLASS_JEWELCRAFTING_RECIPE = 10,
|
||||
ITEM_SUBCLASS_INSCRIPTION_RECIPE = 11,
|
||||
};
|
||||
|
||||
#define MAX_ITEM_SUBCLASS_RECIPE 12
|
||||
|
||||
enum ItemSubclassMoney
|
||||
{
|
||||
ITEM_SUBCLASS_MONEY = 0
|
||||
// = 7 one test item
|
||||
};
|
||||
|
||||
#define MAX_ITEM_SUBCLASS_MONEY 8
|
||||
|
||||
enum ItemSubclassQuiver
|
||||
{
|
||||
ITEM_SUBCLASS_QUIVER0 = 0, // ABS
|
||||
ITEM_SUBCLASS_QUIVER1 = 1, // ABS
|
||||
ITEM_SUBCLASS_QUIVER = 2,
|
||||
ITEM_SUBCLASS_AMMO_POUCH = 3
|
||||
};
|
||||
|
||||
#define MAX_ITEM_SUBCLASS_QUIVER 4
|
||||
|
||||
enum ItemSubclassQuest
|
||||
{
|
||||
ITEM_SUBCLASS_QUEST = 0
|
||||
// 3 one unavailable item
|
||||
// 8 two quest items
|
||||
};
|
||||
|
||||
#define MAX_ITEM_SUBCLASS_QUEST 9
|
||||
|
||||
enum ItemSubclassKey
|
||||
{
|
||||
ITEM_SUBCLASS_KEY = 0,
|
||||
ITEM_SUBCLASS_LOCKPICK = 1
|
||||
};
|
||||
|
||||
#define MAX_ITEM_SUBCLASS_KEY 2
|
||||
|
||||
enum ItemSubclassPermanent
|
||||
{
|
||||
ITEM_SUBCLASS_PERMANENT = 0
|
||||
};
|
||||
|
||||
#define MAX_ITEM_SUBCLASS_PERMANENT 1
|
||||
|
||||
enum ItemSubclassJunk
|
||||
{
|
||||
ITEM_SUBCLASS_JUNK = 0,
|
||||
ITEM_SUBCLASS_JUNK_REAGENT = 1,
|
||||
ITEM_SUBCLASS_JUNK_PET = 2,
|
||||
ITEM_SUBCLASS_JUNK_HOLIDAY = 3,
|
||||
ITEM_SUBCLASS_JUNK_OTHER = 4,
|
||||
ITEM_SUBCLASS_JUNK_MOUNT = 5
|
||||
// 12 single item 37677
|
||||
};
|
||||
|
||||
#define MAX_ITEM_SUBCLASS_JUNK 13
|
||||
|
||||
enum ItemSubclassGlyph
|
||||
{
|
||||
ITEM_SUBCLASS_GLYPH_WARRIOR = 1,
|
||||
ITEM_SUBCLASS_GLYPH_PALADIN = 2,
|
||||
ITEM_SUBCLASS_GLYPH_HUNTER = 3,
|
||||
ITEM_SUBCLASS_GLYPH_ROGUE = 4,
|
||||
ITEM_SUBCLASS_GLYPH_PRIEST = 5,
|
||||
ITEM_SUBCLASS_GLYPH_DEATH_KNIGHT = 6,
|
||||
ITEM_SUBCLASS_GLYPH_SHAMAN = 7,
|
||||
ITEM_SUBCLASS_GLYPH_MAGE = 8,
|
||||
ITEM_SUBCLASS_GLYPH_WARLOCK = 9,
|
||||
ITEM_SUBCLASS_GLYPH_DRUID = 11
|
||||
};
|
||||
|
||||
#define MAX_ITEM_SUBCLASS_GLYPH 12
|
||||
|
||||
const uint32 MaxItemSubclassValues[MAX_ITEM_CLASS] =
|
||||
{
|
||||
MAX_ITEM_SUBCLASS_CONSUMABLE,
|
||||
MAX_ITEM_SUBCLASS_CONTAINER,
|
||||
MAX_ITEM_SUBCLASS_WEAPON,
|
||||
MAX_ITEM_SUBCLASS_GEM,
|
||||
MAX_ITEM_SUBCLASS_ARMOR,
|
||||
MAX_ITEM_SUBCLASS_REAGENT,
|
||||
MAX_ITEM_SUBCLASS_PROJECTILE,
|
||||
MAX_ITEM_SUBCLASS_TRADE_GOODS,
|
||||
MAX_ITEM_SUBCLASS_GENERIC,
|
||||
MAX_ITEM_SUBCLASS_RECIPE,
|
||||
MAX_ITEM_SUBCLASS_MONEY,
|
||||
MAX_ITEM_SUBCLASS_QUIVER,
|
||||
MAX_ITEM_SUBCLASS_QUEST,
|
||||
MAX_ITEM_SUBCLASS_KEY,
|
||||
MAX_ITEM_SUBCLASS_PERMANENT,
|
||||
MAX_ITEM_SUBCLASS_JUNK,
|
||||
MAX_ITEM_SUBCLASS_GLYPH
|
||||
};
|
||||
|
||||
inline uint8 ItemSubClassToDurabilityMultiplierId(uint32 ItemClass, uint32 ItemSubClass)
|
||||
{
|
||||
switch (ItemClass)
|
||||
{
|
||||
case ITEM_CLASS_WEAPON: return ItemSubClass;
|
||||
case ITEM_CLASS_ARMOR: return ItemSubClass + 21;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum ItemExtraFlags
|
||||
{
|
||||
ITEM_EXTRA_NON_CONSUMABLE = 0x01, // use as additional flag to spellcharges_N negative values, item not expire at no chanrges
|
||||
ITEM_EXTRA_REAL_TIME_DURATION = 0x02, // if set and have Duration time, then offline time included in counting, if not set then counted only in game time
|
||||
|
||||
ITEM_EXTRA_ALL = 0x03 // all used flags, used for check DB data (mask all above flags)
|
||||
};
|
||||
|
||||
// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform
|
||||
#if defined( __GNUC__ )
|
||||
#pragma pack(1)
|
||||
#else
|
||||
#pragma pack(push,1)
|
||||
#endif
|
||||
|
||||
struct _ItemStat
|
||||
{
|
||||
uint32 ItemStatType;
|
||||
int32 ItemStatValue;
|
||||
uint32 ItemStatType2;
|
||||
int32 ItemStatValue2;
|
||||
};
|
||||
struct _Spell
|
||||
{
|
||||
uint32 SpellId; // id from Spell.dbc
|
||||
uint32 SpellTrigger;
|
||||
int32 SpellCharges;
|
||||
float SpellPPMRate;
|
||||
int32 SpellCooldown;
|
||||
uint32 SpellCategory; // id from SpellCategory.dbc
|
||||
int32 SpellCategoryCooldown;
|
||||
};
|
||||
|
||||
struct _Socket
|
||||
{
|
||||
uint32 Color;
|
||||
uint32 Content;
|
||||
};
|
||||
|
||||
#define MAX_ITEM_PROTO_SOCKETS 3
|
||||
#define MAX_ITEM_PROTO_SPELLS 5
|
||||
#define MAX_ITEM_PROTO_STATS 10
|
||||
|
||||
struct ItemPrototype
|
||||
{
|
||||
uint32 ItemId;
|
||||
uint32 Class; // id from ItemClass.dbc
|
||||
uint32 SubClass; // id from ItemSubClass.dbc
|
||||
int32 Unk0;
|
||||
char* Name1;
|
||||
uint32 DisplayInfoID; // id from ItemDisplayInfo.dbc
|
||||
uint32 Quality;
|
||||
uint32 Flags;
|
||||
uint32 Flags2;
|
||||
float Unknown;
|
||||
float Unknown1;
|
||||
uint32 Unknown2;
|
||||
uint32 BuyCount;
|
||||
uint32 BuyPrice;
|
||||
uint32 SellPrice;
|
||||
uint32 InventoryType;
|
||||
uint32 AllowableClass;
|
||||
uint32 AllowableRace;
|
||||
uint32 ItemLevel;
|
||||
uint32 RequiredLevel;
|
||||
uint32 RequiredSkill; // id from SkillLine.dbc
|
||||
uint32 RequiredSkillRank;
|
||||
uint32 RequiredSpell; // id from Spell.dbc
|
||||
uint32 RequiredHonorRank;
|
||||
uint32 RequiredCityRank;
|
||||
uint32 RequiredReputationFaction; // id from Faction.dbc
|
||||
uint32 RequiredReputationRank;
|
||||
int32 MaxCount; // <=0: no limit
|
||||
int32 Stackable; // 0: not allowed, -1: put in player coin info tab and don't limit stacking (so 1 slot)
|
||||
uint32 ContainerSlots;
|
||||
_ItemStat ItemStat[MAX_ITEM_PROTO_STATS];
|
||||
uint32 ScalingStatDistribution; // id from ScalingStatDistribution.dbc
|
||||
uint32 DamageType;
|
||||
uint32 Delay;
|
||||
float RangedModRange;
|
||||
_Spell Spells[MAX_ITEM_PROTO_SPELLS];
|
||||
uint32 Bonding;
|
||||
char* Description;
|
||||
uint32 PageText;
|
||||
uint32 LanguageID;
|
||||
uint32 PageMaterial;
|
||||
uint32 StartQuest; // id from QuestCache.wdb
|
||||
uint32 LockID;
|
||||
int32 Material; // id from Material.dbc
|
||||
uint32 Sheath;
|
||||
uint32 RandomProperty; // id from ItemRandomProperties.dbc
|
||||
uint32 RandomSuffix; // id from ItemRandomSuffix.dbc
|
||||
uint32 ItemSet; // id from ItemSet.dbc
|
||||
uint32 MaxDurability;
|
||||
uint32 Area; // id from AreaTable.dbc
|
||||
uint32 Map; // id from Map.dbc
|
||||
uint32 BagFamily; // bit mask (1 << id from ItemBagFamily.dbc)
|
||||
uint32 TotemCategory; // id from TotemCategory.dbc
|
||||
_Socket Socket[MAX_ITEM_PROTO_SOCKETS];
|
||||
uint32 socketBonus; // id from SpellItemEnchantment.dbc
|
||||
uint32 GemProperties; // id from GemProperties.dbc
|
||||
int32 RequiredDisenchantSkill;
|
||||
float ArmorDamageModifier;
|
||||
uint32 Duration;
|
||||
uint32 ItemLimitCategory; // id from ItemLimitCategory.dbc
|
||||
uint32 HolidayId; // id from Holidays.dbc
|
||||
uint32 ScriptId;
|
||||
uint32 DisenchantID;
|
||||
uint32 FoodType;
|
||||
float StatScalingFactor;
|
||||
uint32 Unknown400_1;
|
||||
uint32 Unknown400_2;
|
||||
uint32 MinMoneyLoot;
|
||||
uint32 MaxMoneyLoot;
|
||||
uint32 ExtraFlags; // see ItemExtraFlags
|
||||
|
||||
// helpers
|
||||
bool CanChangeEquipStateInCombat() const
|
||||
{
|
||||
switch (InventoryType)
|
||||
{
|
||||
case INVTYPE_RELIC:
|
||||
case INVTYPE_SHIELD:
|
||||
case INVTYPE_HOLDABLE:
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (Class)
|
||||
{
|
||||
case ITEM_CLASS_WEAPON:
|
||||
case ITEM_CLASS_PROJECTILE:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 GetMaxStackSize() const { return Stackable > 0 ? uint32(Stackable) : uint32(0x7FFFFFFF - 1); }
|
||||
float getDPS() const;
|
||||
|
||||
uint32 GetArmor() const;
|
||||
float GetMinDamage() const { return floor(getDPS() * float(Delay) / 1000.0f * 0.7f + 0.5f); }
|
||||
float GetMaxDamage() const { return floor(getDPS() * float(Delay) / 1000.0f * 1.3f + 0.5f); }
|
||||
|
||||
bool IsPotion() const { return Class==ITEM_CLASS_CONSUMABLE && SubClass==ITEM_SUBCLASS_POTION; }
|
||||
bool IsConjuredConsumable() const { return Class == ITEM_CLASS_CONSUMABLE && (Flags & ITEM_FLAG_CONJURED); }
|
||||
bool IsVellum() const
|
||||
{
|
||||
return Class == ITEM_CLASS_TRADE_GOODS && (1 << SubClass) & (1 << ITEM_SUBCLASS_VELLUM);
|
||||
}
|
||||
};
|
||||
|
||||
// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform
|
||||
#if defined( __GNUC__ )
|
||||
#pragma pack()
|
||||
#else
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
struct ItemLocale
|
||||
{
|
||||
std::vector<std::string> Name;
|
||||
std::vector<std::string> Description;
|
||||
};
|
||||
|
||||
#endif
|
||||
1558
src/game/Object/LootMgr.cpp
Normal file
1558
src/game/Object/LootMgr.cpp
Normal file
File diff suppressed because it is too large
Load diff
394
src/game/Object/LootMgr.h
Normal file
394
src/game/Object/LootMgr.h
Normal file
|
|
@ -0,0 +1,394 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOS_LOOTMGR_H
|
||||
#define MANGOS_LOOTMGR_H
|
||||
|
||||
#include "ItemEnchantmentMgr.h"
|
||||
#include "ByteBuffer.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Utilities/LinkedReference/RefManager.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
class Player;
|
||||
class LootStore;
|
||||
class WorldObject;
|
||||
|
||||
#define MAX_NR_LOOT_ITEMS 16
|
||||
// note: the client cannot show more than 16 items total
|
||||
#define MAX_NR_QUEST_ITEMS 32
|
||||
// unrelated to the number of quest items shown, just for reserve
|
||||
|
||||
enum PermissionTypes
|
||||
{
|
||||
ALL_PERMISSION = 0,
|
||||
GROUP_PERMISSION = 1,
|
||||
MASTER_PERMISSION = 2,
|
||||
OWNER_PERMISSION = 3, // for single player only loots
|
||||
NONE_PERMISSION = 4
|
||||
};
|
||||
|
||||
enum LootItemType
|
||||
{
|
||||
LOOT_ITEM_TYPE_ITEM = 0,
|
||||
LOOT_ITEM_TYPE_CURRENCY = 1,
|
||||
};
|
||||
|
||||
enum LootType
|
||||
{
|
||||
LOOT_CORPSE = 1,
|
||||
LOOT_PICKPOCKETING = 2,
|
||||
LOOT_FISHING = 3,
|
||||
LOOT_DISENCHANTING = 4,
|
||||
// ignored always by client
|
||||
LOOT_SKINNING = 6, // unsupported by client, sending LOOT_PICKPOCKETING instead
|
||||
LOOT_PROSPECTING = 7, // unsupported by client, sending LOOT_PICKPOCKETING instead
|
||||
LOOT_MILLING = 8,
|
||||
|
||||
LOOT_FISHINGHOLE = 20, // unsupported by client, sending LOOT_FISHING instead
|
||||
LOOT_FISHING_FAIL = 21, // unsupported by client, sending LOOT_FISHING instead
|
||||
LOOT_INSIGNIA = 22 // unsupported by client, sending LOOT_CORPSE instead
|
||||
};
|
||||
|
||||
enum LootSlotType
|
||||
{
|
||||
LOOT_SLOT_NORMAL = 0, // can be looted
|
||||
LOOT_SLOT_VIEW = 1, // can be only view (ignore any loot attempts)
|
||||
LOOT_SLOT_MASTER = 2, // can be looted only master (error message)
|
||||
LOOT_SLOT_REQS = 3, // can't be looted (error message about missing reqs)
|
||||
LOOT_SLOT_OWNER = 4, // ignore binding confirmation and etc, for single player looting
|
||||
MAX_LOOT_SLOT_TYPE // custom, use for mark skipped from show items
|
||||
};
|
||||
|
||||
struct LootStoreItem
|
||||
{
|
||||
uint32 itemid; // id of the item
|
||||
uint8 type; // 0 = item, 1 = currency
|
||||
float chance; // always positive, chance to drop for both quest and non-quest items, chance to be used for refs
|
||||
int32 mincountOrRef; // mincount for drop items (positive) or minus referenced TemplateleId (negative)
|
||||
uint32 maxcount; // max drop count for the item (mincountOrRef positive) or Ref multiplicator (mincountOrRef negative)
|
||||
uint8 group : 7;
|
||||
bool needs_quest : 1; // quest drop (negative ChanceOrQuestChance in DB)
|
||||
uint16 conditionId : 16; // additional loot condition Id
|
||||
|
||||
// Constructor, converting ChanceOrQuestChance -> (chance, needs_quest)
|
||||
// displayid is filled in IsValid() which must be called after
|
||||
LootStoreItem(uint32 _itemid, uint8 _type, float _chanceOrQuestChance, int8 _group, uint16 _conditionId, int32 _mincountOrRef, uint32 _maxcount)
|
||||
: itemid(_itemid), type(_type), chance(fabs(_chanceOrQuestChance)), mincountOrRef(_mincountOrRef),
|
||||
group(_group), needs_quest(_chanceOrQuestChance < 0), maxcount(_maxcount), conditionId(_conditionId)
|
||||
{}
|
||||
|
||||
bool Roll(bool rate) const; // Checks if the entry takes it's chance (at loot generation)
|
||||
bool IsValid(LootStore const& store, uint32 entry) const;
|
||||
// Checks correctness of values
|
||||
};
|
||||
|
||||
struct LootItem
|
||||
{
|
||||
uint32 itemid;
|
||||
uint8 type; // 0 = item, 1 = currency
|
||||
uint32 randomSuffix;
|
||||
int32 randomPropertyId;
|
||||
uint32 count;
|
||||
uint16 conditionId : 16; // allow compiler pack structure
|
||||
bool currency : 1;
|
||||
bool is_looted : 1;
|
||||
bool is_blocked : 1;
|
||||
bool freeforall : 1; // free for all
|
||||
bool is_underthreshold : 1;
|
||||
bool is_counted : 1;
|
||||
bool needs_quest : 1; // quest drop
|
||||
|
||||
// Constructor, copies most fields from LootStoreItem, generates random count and random suffixes/properties
|
||||
// Should be called for non-reference LootStoreItem entries only (mincountOrRef > 0)
|
||||
explicit LootItem(LootStoreItem const& li);
|
||||
|
||||
LootItem(uint32 itemid_, uint8 type_, uint32 count_, uint32 randomSuffix_ = 0, int32 randomPropertyId_ = 0);
|
||||
|
||||
// Basic checks for player/item compatibility - if false no chance to see the item in the loot
|
||||
bool AllowedForPlayer(Player const* player, WorldObject const* lootTarget) const;
|
||||
LootSlotType GetSlotTypeForSharedLoot(PermissionTypes permission, Player* viewer, WorldObject const* lootTarget, bool condition_ok = false) const;
|
||||
};
|
||||
|
||||
typedef std::vector<LootItem> LootItemList;
|
||||
|
||||
struct QuestItem
|
||||
{
|
||||
uint8 index; // position in quest_items;
|
||||
bool is_looted;
|
||||
|
||||
QuestItem()
|
||||
: index(0), is_looted(false) {}
|
||||
|
||||
QuestItem(uint8 _index, bool _islooted = false)
|
||||
: index(_index), is_looted(_islooted) {}
|
||||
};
|
||||
|
||||
struct Loot;
|
||||
class LootTemplate;
|
||||
|
||||
typedef std::vector<QuestItem> QuestItemList;
|
||||
typedef std::map<uint32, QuestItemList*> QuestItemMap;
|
||||
typedef std::vector<LootStoreItem> LootStoreItemList;
|
||||
typedef UNORDERED_MAP<uint32, LootTemplate*> LootTemplateMap;
|
||||
|
||||
typedef std::set<uint32> LootIdSet;
|
||||
|
||||
class LootStore
|
||||
{
|
||||
public:
|
||||
explicit LootStore(char const* name, char const* entryName, bool ratesAllowed)
|
||||
: m_name(name), m_entryName(entryName), m_ratesAllowed(ratesAllowed) {}
|
||||
virtual ~LootStore() { Clear(); }
|
||||
|
||||
void Verify() const;
|
||||
|
||||
void LoadAndCollectLootIds(LootIdSet& ids_set);
|
||||
void CheckLootRefs(LootIdSet* ref_set = NULL) const;// check existence reference and remove it from ref_set
|
||||
void ReportUnusedIds(LootIdSet const& ids_set) const;
|
||||
void ReportNotExistedId(uint32 id) const;
|
||||
|
||||
bool HaveLootFor(uint32 loot_id) const { return m_LootTemplates.find(loot_id) != m_LootTemplates.end(); }
|
||||
bool HaveQuestLootFor(uint32 loot_id) const;
|
||||
bool HaveQuestLootForPlayer(uint32 loot_id, Player* player) const;
|
||||
|
||||
LootTemplate const* GetLootFor(uint32 loot_id) const;
|
||||
|
||||
char const* GetName() const { return m_name; }
|
||||
char const* GetEntryName() const { return m_entryName; }
|
||||
bool IsRatesAllowed() const { return m_ratesAllowed; }
|
||||
protected:
|
||||
void LoadLootTable();
|
||||
void Clear();
|
||||
private:
|
||||
LootTemplateMap m_LootTemplates;
|
||||
char const* m_name;
|
||||
char const* m_entryName;
|
||||
bool m_ratesAllowed;
|
||||
};
|
||||
|
||||
class LootTemplate
|
||||
{
|
||||
class LootGroup; // A set of loot definitions for items (refs are not allowed inside)
|
||||
typedef std::vector<LootGroup> LootGroups;
|
||||
|
||||
public:
|
||||
// Adds an entry to the group (at loading stage)
|
||||
void AddEntry(LootStoreItem& item);
|
||||
// Rolls for every item in the template and adds the rolled items the the loot
|
||||
void Process(Loot& loot, LootStore const& store, bool rate, uint8 GroupId = 0) const;
|
||||
|
||||
// True if template includes at least 1 quest drop entry
|
||||
bool HasQuestDrop(LootTemplateMap const& store, uint8 GroupId = 0) const;
|
||||
// True if template includes at least 1 quest drop for an active quest of the player
|
||||
bool HasQuestDropForPlayer(LootTemplateMap const& store, Player const* player, uint8 GroupId = 0) const;
|
||||
|
||||
// Checks integrity of the template
|
||||
void Verify(LootStore const& store, uint32 Id) const;
|
||||
void CheckLootRefs(LootIdSet* ref_set) const;
|
||||
private:
|
||||
LootStoreItemList Entries; // not grouped only
|
||||
LootGroups Groups; // groups have own (optimised) processing, grouped entries go there
|
||||
};
|
||||
|
||||
//=====================================================
|
||||
|
||||
class LootValidatorRef : public Reference<Loot, LootValidatorRef>
|
||||
{
|
||||
public:
|
||||
LootValidatorRef() {}
|
||||
void targetObjectDestroyLink() override {}
|
||||
void sourceObjectDestroyLink() override {}
|
||||
};
|
||||
|
||||
//=====================================================
|
||||
|
||||
class LootValidatorRefManager : public RefManager<Loot, LootValidatorRef>
|
||||
{
|
||||
public:
|
||||
typedef LinkedListHead::Iterator< LootValidatorRef > iterator;
|
||||
|
||||
LootValidatorRef* getFirst() { return (LootValidatorRef*)RefManager<Loot, LootValidatorRef>::getFirst(); }
|
||||
LootValidatorRef* getLast() { return (LootValidatorRef*)RefManager<Loot, LootValidatorRef>::getLast(); }
|
||||
|
||||
iterator begin() { return iterator(getFirst()); }
|
||||
iterator end() { return iterator(NULL); }
|
||||
iterator rbegin() { return iterator(getLast()); }
|
||||
iterator rend() { return iterator(NULL); }
|
||||
};
|
||||
|
||||
//=====================================================
|
||||
struct LootView;
|
||||
|
||||
ByteBuffer& operator<<(ByteBuffer& b, LootItem const& li);
|
||||
ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv);
|
||||
|
||||
struct Loot
|
||||
{
|
||||
friend ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv);
|
||||
|
||||
QuestItemMap const& GetPlayerCurrencies() const { return m_playerCurrencies; }
|
||||
QuestItemMap const& GetPlayerQuestItems() const { return m_playerQuestItems; }
|
||||
QuestItemMap const& GetPlayerFFAItems() const { return m_playerFFAItems; }
|
||||
QuestItemMap const& GetPlayerNonQuestNonFFANonCurrencyConditionalItems() const { return m_playerNonQuestNonFFANonCurrencyConditionalItems; }
|
||||
|
||||
LootItemList items;
|
||||
uint32 gold;
|
||||
uint8 unlootedCount;
|
||||
LootType loot_type; // required for achievement system
|
||||
|
||||
Loot(WorldObject const* lootTarget, uint32 _gold = 0) : gold(_gold), unlootedCount(0), loot_type(LOOT_CORPSE), m_lootTarget(lootTarget) {}
|
||||
~Loot() { clear(); }
|
||||
|
||||
// if loot becomes invalid this reference is used to inform the listener
|
||||
void addLootValidatorRef(LootValidatorRef* pLootValidatorRef)
|
||||
{
|
||||
m_LootValidatorRefManager.insertFirst(pLootValidatorRef);
|
||||
}
|
||||
|
||||
// void clear();
|
||||
void clear()
|
||||
{
|
||||
for (QuestItemMap::const_iterator itr = m_playerCurrencies.begin(); itr != m_playerCurrencies.end(); ++itr)
|
||||
delete itr->second;
|
||||
m_playerCurrencies.clear();
|
||||
|
||||
for (QuestItemMap::const_iterator itr = m_playerQuestItems.begin(); itr != m_playerQuestItems.end(); ++itr)
|
||||
delete itr->second;
|
||||
m_playerQuestItems.clear();
|
||||
|
||||
for (QuestItemMap::const_iterator itr = m_playerFFAItems.begin(); itr != m_playerFFAItems.end(); ++itr)
|
||||
delete itr->second;
|
||||
m_playerFFAItems.clear();
|
||||
|
||||
for (QuestItemMap::const_iterator itr = m_playerNonQuestNonFFANonCurrencyConditionalItems.begin(); itr != m_playerNonQuestNonFFANonCurrencyConditionalItems.end(); ++itr)
|
||||
delete itr->second;
|
||||
m_playerNonQuestNonFFANonCurrencyConditionalItems.clear();
|
||||
|
||||
m_playersLooting.clear();
|
||||
items.clear();
|
||||
m_questItems.clear();
|
||||
gold = 0;
|
||||
unlootedCount = 0;
|
||||
m_LootValidatorRefManager.clearReferences();
|
||||
}
|
||||
|
||||
bool empty() const { return items.empty() && gold == 0; }
|
||||
bool isLooted() const { return gold == 0 && unlootedCount == 0; }
|
||||
|
||||
void NotifyItemRemoved(uint8 lootIndex);
|
||||
void NotifyQuestItemRemoved(uint8 questIndex);
|
||||
void NotifyMoneyRemoved();
|
||||
void AddLooter(ObjectGuid guid) { m_playersLooting.insert(guid); }
|
||||
void RemoveLooter(ObjectGuid guid) { m_playersLooting.erase(guid); }
|
||||
|
||||
void generateMoneyLoot(uint32 minAmount, uint32 maxAmount);
|
||||
bool FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner, bool personal, bool noEmptyError = false);
|
||||
|
||||
// Inserts the item into the loot (called by LootTemplate processors)
|
||||
void AddItem(LootStoreItem const& item);
|
||||
|
||||
LootItem* LootItemInSlot(uint32 lootslot, Player* player, QuestItem** qitem = NULL, QuestItem** ffaitem = NULL, QuestItem** conditem = NULL, QuestItem** currency = NULL);
|
||||
uint32 GetMaxSlotInLootFor(Player* player) const;
|
||||
|
||||
WorldObject const* GetLootTarget() const { return m_lootTarget; }
|
||||
|
||||
private:
|
||||
void FillNotNormalLootFor(Player* player);
|
||||
QuestItemList* FillCurrencyLoot(Player* player);
|
||||
QuestItemList* FillFFALoot(Player* player);
|
||||
QuestItemList* FillQuestLoot(Player* player);
|
||||
QuestItemList* FillNonQuestNonFFANonCurrencyConditionalLoot(Player* player);
|
||||
|
||||
LootItemList m_questItems;
|
||||
|
||||
GuidSet m_playersLooting;
|
||||
|
||||
QuestItemMap m_playerCurrencies;
|
||||
QuestItemMap m_playerQuestItems;
|
||||
QuestItemMap m_playerFFAItems;
|
||||
QuestItemMap m_playerNonQuestNonFFANonCurrencyConditionalItems;
|
||||
|
||||
// All rolls are registered here. They need to know, when the loot is not valid anymore
|
||||
LootValidatorRefManager m_LootValidatorRefManager;
|
||||
|
||||
// What is looted
|
||||
WorldObject const* m_lootTarget;
|
||||
};
|
||||
|
||||
struct LootView
|
||||
{
|
||||
Loot& loot;
|
||||
Player* viewer;
|
||||
PermissionTypes permission;
|
||||
LootView(Loot& _loot, Player* _viewer, PermissionTypes _permission = ALL_PERMISSION)
|
||||
: loot(_loot), viewer(_viewer), permission(_permission) {}
|
||||
};
|
||||
|
||||
extern LootStore LootTemplates_Creature;
|
||||
extern LootStore LootTemplates_Fishing;
|
||||
extern LootStore LootTemplates_Gameobject;
|
||||
extern LootStore LootTemplates_Item;
|
||||
extern LootStore LootTemplates_Mail;
|
||||
extern LootStore LootTemplates_Milling;
|
||||
extern LootStore LootTemplates_Pickpocketing;
|
||||
extern LootStore LootTemplates_Skinning;
|
||||
extern LootStore LootTemplates_Disenchant;
|
||||
extern LootStore LootTemplates_Prospecting;
|
||||
extern LootStore LootTemplates_Spell;
|
||||
|
||||
void LoadLootTemplates_Creature();
|
||||
void LoadLootTemplates_Fishing();
|
||||
void LoadLootTemplates_Gameobject();
|
||||
void LoadLootTemplates_Item();
|
||||
void LoadLootTemplates_Mail();
|
||||
void LoadLootTemplates_Milling();
|
||||
void LoadLootTemplates_Pickpocketing();
|
||||
void LoadLootTemplates_Skinning();
|
||||
void LoadLootTemplates_Disenchant();
|
||||
void LoadLootTemplates_Prospecting();
|
||||
|
||||
void LoadLootTemplates_Spell();
|
||||
void LoadLootTemplates_Reference();
|
||||
|
||||
inline void LoadLootTables()
|
||||
{
|
||||
LoadLootTemplates_Creature();
|
||||
LoadLootTemplates_Fishing();
|
||||
LoadLootTemplates_Gameobject();
|
||||
LoadLootTemplates_Item();
|
||||
LoadLootTemplates_Mail();
|
||||
LoadLootTemplates_Milling();
|
||||
LoadLootTemplates_Pickpocketing();
|
||||
LoadLootTemplates_Skinning();
|
||||
LoadLootTemplates_Disenchant();
|
||||
LoadLootTemplates_Prospecting();
|
||||
LoadLootTemplates_Spell();
|
||||
|
||||
LoadLootTemplates_Reference();
|
||||
}
|
||||
|
||||
#endif
|
||||
29
src/game/Object/NullCreatureAI.cpp
Normal file
29
src/game/Object/NullCreatureAI.cpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include "NullCreatureAI.h"
|
||||
|
||||
NullCreatureAI::~NullCreatureAI()
|
||||
{
|
||||
}
|
||||
47
src/game/Object/NullCreatureAI.h
Normal file
47
src/game/Object/NullCreatureAI.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOS_NULLCREATUREAI_H
|
||||
#define MANGOS_NULLCREATUREAI_H
|
||||
|
||||
#include "CreatureAI.h"
|
||||
|
||||
class MANGOS_DLL_DECL NullCreatureAI : public CreatureAI
|
||||
{
|
||||
public:
|
||||
|
||||
explicit NullCreatureAI(Creature* c) : CreatureAI(c) {}
|
||||
~NullCreatureAI();
|
||||
|
||||
void MoveInLineOfSight(Unit*) override {}
|
||||
void AttackStart(Unit*) override {}
|
||||
void AttackedBy(Unit*) override {}
|
||||
void EnterEvadeMode() override {}
|
||||
|
||||
bool IsVisible(Unit*) const override { return false; }
|
||||
|
||||
void UpdateAI(const uint32) override {}
|
||||
static int Permissible(const Creature*) { return PERMIT_BASE_IDLE; }
|
||||
};
|
||||
#endif
|
||||
2090
src/game/Object/Object.cpp
Normal file
2090
src/game/Object/Object.cpp
Normal file
File diff suppressed because it is too large
Load diff
655
src/game/Object/Object.h
Normal file
655
src/game/Object/Object.h
Normal file
|
|
@ -0,0 +1,655 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef _OBJECT_H
|
||||
#define _OBJECT_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "ByteBuffer.h"
|
||||
#include "UpdateFields.h"
|
||||
#include "UpdateData.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Camera.h"
|
||||
#include "Util.h"
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#define CONTACT_DISTANCE 0.5f
|
||||
#define INTERACTION_DISTANCE 5.0f
|
||||
#define ATTACK_DISTANCE 5.0f
|
||||
#define MAX_VISIBILITY_DISTANCE 333.0f // max distance for visible object show, limited in 333 yards
|
||||
#define DEFAULT_VISIBILITY_DISTANCE 90.0f // default visible distance, 90 yards on continents
|
||||
#define DEFAULT_VISIBILITY_INSTANCE 120.0f // default visible distance in instances, 120 yards
|
||||
#define DEFAULT_VISIBILITY_BGARENAS 180.0f // default visible distance in BG/Arenas, 180 yards
|
||||
|
||||
#define DEFAULT_WORLD_OBJECT_SIZE 0.388999998569489f // currently used (correctly?) for any non Unit world objects. This is actually the bounding_radius, like player/creature from creature_model_data
|
||||
#define DEFAULT_OBJECT_SCALE 1.0f // player/item scale as default, npc/go from database, pets from dbc
|
||||
|
||||
#define MAX_STEALTH_DETECT_RANGE 45.0f
|
||||
|
||||
enum TempSummonType
|
||||
{
|
||||
TEMPSUMMON_MANUAL_DESPAWN = 0, // despawns when UnSummon() is called
|
||||
TEMPSUMMON_DEAD_DESPAWN = 1, // despawns when the creature disappears
|
||||
TEMPSUMMON_CORPSE_DESPAWN = 2, // despawns instantly after death
|
||||
TEMPSUMMON_CORPSE_TIMED_DESPAWN = 3, // despawns after a specified time after death (or when the creature disappears)
|
||||
TEMPSUMMON_TIMED_DESPAWN = 4, // despawns after a specified time
|
||||
TEMPSUMMON_TIMED_OOC_DESPAWN = 5, // despawns after a specified time after the creature is out of combat
|
||||
TEMPSUMMON_TIMED_OR_DEAD_DESPAWN = 6, // despawns after a specified time OR when the creature disappears
|
||||
TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN = 7, // despawns after a specified time OR when the creature dies
|
||||
TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN = 8, // despawns after a specified time (OOC) OR when the creature disappears
|
||||
TEMPSUMMON_TIMED_OOC_OR_CORPSE_DESPAWN = 9, // despawns after a specified time (OOC) OR when the creature dies
|
||||
};
|
||||
|
||||
enum PhaseMasks
|
||||
{
|
||||
PHASEMASK_NORMAL = 0x00000001,
|
||||
PHASEMASK_ANYWHERE = 0xFFFFFFFF
|
||||
};
|
||||
|
||||
class WorldPacket;
|
||||
class UpdateData;
|
||||
class WorldSession;
|
||||
class Creature;
|
||||
class Player;
|
||||
class Unit;
|
||||
class Group;
|
||||
class Map;
|
||||
class UpdateMask;
|
||||
class InstanceData;
|
||||
class TerrainInfo;
|
||||
class TransportInfo;
|
||||
|
||||
typedef UNORDERED_MAP<Player*, UpdateData> UpdateDataMapType;
|
||||
|
||||
struct Position
|
||||
{
|
||||
Position() : x(0.0f), y(0.0f), z(0.0f), o(0.0f) {}
|
||||
Position(float _x, float _y, float _z, float _o) : x(_x), y(_y), z(_z), o(_o) {}
|
||||
float x, y, z, o;
|
||||
};
|
||||
|
||||
struct WorldLocation
|
||||
{
|
||||
uint32 mapid;
|
||||
float coord_x;
|
||||
float coord_y;
|
||||
float coord_z;
|
||||
float orientation;
|
||||
explicit WorldLocation(uint32 _mapid = 0, float _x = 0, float _y = 0, float _z = 0, float _o = 0)
|
||||
: mapid(_mapid), coord_x(_x), coord_y(_y), coord_z(_z), orientation(NormalizeOrientation(_o)) {}
|
||||
WorldLocation(WorldLocation const& loc)
|
||||
: mapid(loc.mapid), coord_x(loc.coord_x), coord_y(loc.coord_y), coord_z(loc.coord_z), orientation(NormalizeOrientation(loc.orientation)) {}
|
||||
};
|
||||
|
||||
// use this class to measure time between world update ticks
|
||||
// essential for units updating their spells after cells become active
|
||||
class WorldUpdateCounter
|
||||
{
|
||||
public:
|
||||
WorldUpdateCounter() : m_tmStart(0) {}
|
||||
|
||||
time_t timeElapsed()
|
||||
{
|
||||
if (!m_tmStart)
|
||||
m_tmStart = WorldTimer::tickPrevTime();
|
||||
|
||||
return WorldTimer::getMSTimeDiff(m_tmStart, WorldTimer::tickTime());
|
||||
}
|
||||
|
||||
void Reset() { m_tmStart = WorldTimer::tickTime(); }
|
||||
|
||||
private:
|
||||
uint32 m_tmStart;
|
||||
};
|
||||
|
||||
class MANGOS_DLL_SPEC Object
|
||||
{
|
||||
public:
|
||||
virtual ~Object();
|
||||
|
||||
const bool& IsInWorld() const { return m_inWorld; }
|
||||
virtual void AddToWorld()
|
||||
{
|
||||
if (m_inWorld)
|
||||
return;
|
||||
|
||||
m_inWorld = true;
|
||||
|
||||
// synchronize values mirror with values array (changes will send in updatecreate opcode any way
|
||||
ClearUpdateMask(false); // false - we can't have update data in update queue before adding to world
|
||||
}
|
||||
virtual void RemoveFromWorld()
|
||||
{
|
||||
// if we remove from world then sending changes not required
|
||||
ClearUpdateMask(true);
|
||||
m_inWorld = false;
|
||||
}
|
||||
|
||||
ObjectGuid const& GetObjectGuid() const { return GetGuidValue(OBJECT_FIELD_GUID); }
|
||||
uint32 GetGUIDLow() const { return GetObjectGuid().GetCounter(); }
|
||||
PackedGuid const& GetPackGUID() const { return m_PackGUID; }
|
||||
std::string GetGuidStr() const { return GetObjectGuid().GetString(); }
|
||||
|
||||
uint32 GetEntry() const { return GetUInt32Value(OBJECT_FIELD_ENTRY); }
|
||||
void SetEntry(uint32 entry) { SetUInt32Value(OBJECT_FIELD_ENTRY, entry); }
|
||||
|
||||
float GetObjectScale() const
|
||||
{
|
||||
return m_floatValues[OBJECT_FIELD_SCALE_X] ? m_floatValues[OBJECT_FIELD_SCALE_X] : DEFAULT_OBJECT_SCALE;
|
||||
}
|
||||
|
||||
void SetObjectScale(float newScale);
|
||||
|
||||
uint8 GetTypeId() const { return m_objectTypeId; }
|
||||
bool isType(TypeMask mask) const { return (mask & m_objectType); }
|
||||
|
||||
virtual void BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const;
|
||||
void SendCreateUpdateToPlayer(Player* player);
|
||||
|
||||
// must be overwrite in appropriate subclasses (WorldObject, Item currently), or will crash
|
||||
virtual void AddToClientUpdateList();
|
||||
virtual void RemoveFromClientUpdateList();
|
||||
virtual void BuildUpdateData(UpdateDataMapType& update_players);
|
||||
void MarkForClientUpdate();
|
||||
void SendForcedObjectUpdate();
|
||||
|
||||
void BuildValuesUpdateBlockForPlayer(UpdateData* data, Player* target) const;
|
||||
void BuildOutOfRangeUpdateBlock(UpdateData* data) const;
|
||||
|
||||
virtual void DestroyForPlayer(Player* target, bool anim = false) const;
|
||||
|
||||
const int32& GetInt32Value(uint16 index) const
|
||||
{
|
||||
MANGOS_ASSERT(index < m_valuesCount || PrintIndexError(index , false));
|
||||
return m_int32Values[ index ];
|
||||
}
|
||||
|
||||
const uint32& GetUInt32Value(uint16 index) const
|
||||
{
|
||||
MANGOS_ASSERT(index < m_valuesCount || PrintIndexError(index , false));
|
||||
return m_uint32Values[ index ];
|
||||
}
|
||||
|
||||
const uint64& GetUInt64Value(uint16 index) const
|
||||
{
|
||||
MANGOS_ASSERT(index + 1 < m_valuesCount || PrintIndexError(index , false));
|
||||
return *((uint64*) & (m_uint32Values[ index ]));
|
||||
}
|
||||
|
||||
const float& GetFloatValue(uint16 index) const
|
||||
{
|
||||
MANGOS_ASSERT(index < m_valuesCount || PrintIndexError(index , false));
|
||||
return m_floatValues[ index ];
|
||||
}
|
||||
|
||||
uint8 GetByteValue(uint16 index, uint8 offset) const
|
||||
{
|
||||
MANGOS_ASSERT(index < m_valuesCount || PrintIndexError(index , false));
|
||||
MANGOS_ASSERT(offset < 4);
|
||||
return *(((uint8*)&m_uint32Values[ index ]) + offset);
|
||||
}
|
||||
|
||||
uint16 GetUInt16Value(uint16 index, uint8 offset) const
|
||||
{
|
||||
MANGOS_ASSERT(index < m_valuesCount || PrintIndexError(index , false));
|
||||
MANGOS_ASSERT(offset < 2);
|
||||
return *(((uint16*)&m_uint32Values[ index ]) + offset);
|
||||
}
|
||||
|
||||
ObjectGuid const& GetGuidValue(uint16 index) const { return *reinterpret_cast<ObjectGuid const*>(&GetUInt64Value(index)); }
|
||||
|
||||
void SetInt32Value(uint16 index, int32 value);
|
||||
void SetUInt32Value(uint16 index, uint32 value);
|
||||
void SetUInt64Value(uint16 index, const uint64& value);
|
||||
void SetFloatValue(uint16 index, float value);
|
||||
void SetByteValue(uint16 index, uint8 offset, uint8 value);
|
||||
void SetUInt16Value(uint16 index, uint8 offset, uint16 value);
|
||||
void SetInt16Value(uint16 index, uint8 offset, int16 value) { SetUInt16Value(index, offset, (uint16)value); }
|
||||
void SetGuidValue(uint16 index, ObjectGuid const& value) { SetUInt64Value(index, value.GetRawValue()); }
|
||||
void SetStatFloatValue(uint16 index, float value);
|
||||
void SetStatInt32Value(uint16 index, int32 value);
|
||||
|
||||
void ApplyModUInt32Value(uint16 index, int32 val, bool apply);
|
||||
void ApplyModInt32Value(uint16 index, int32 val, bool apply);
|
||||
void ApplyModUInt64Value(uint16 index, int32 val, bool apply);
|
||||
void ApplyModPositiveFloatValue(uint16 index, float val, bool apply);
|
||||
void ApplyModSignedFloatValue(uint16 index, float val, bool apply);
|
||||
|
||||
void ApplyPercentModFloatValue(uint16 index, float val, bool apply)
|
||||
{
|
||||
val = val != -100.0f ? val : -99.9f ;
|
||||
SetFloatValue(index, GetFloatValue(index) * (apply ? (100.0f + val) / 100.0f : 100.0f / (100.0f + val)));
|
||||
}
|
||||
|
||||
void SetFlag(uint16 index, uint32 newFlag);
|
||||
void RemoveFlag(uint16 index, uint32 oldFlag);
|
||||
|
||||
void ToggleFlag(uint16 index, uint32 flag)
|
||||
{
|
||||
if (HasFlag(index, flag))
|
||||
RemoveFlag(index, flag);
|
||||
else
|
||||
SetFlag(index, flag);
|
||||
}
|
||||
|
||||
bool HasFlag(uint16 index, uint32 flag) const
|
||||
{
|
||||
MANGOS_ASSERT(index < m_valuesCount || PrintIndexError(index , false));
|
||||
return (m_uint32Values[ index ] & flag) != 0;
|
||||
}
|
||||
|
||||
void ApplyModFlag(uint16 index, uint32 flag, bool apply)
|
||||
{
|
||||
if (apply)
|
||||
SetFlag(index, flag);
|
||||
else
|
||||
RemoveFlag(index, flag);
|
||||
}
|
||||
|
||||
void SetByteFlag(uint16 index, uint8 offset, uint8 newFlag);
|
||||
void RemoveByteFlag(uint16 index, uint8 offset, uint8 newFlag);
|
||||
|
||||
void ToggleByteFlag(uint16 index, uint8 offset, uint8 flag)
|
||||
{
|
||||
if (HasByteFlag(index, offset, flag))
|
||||
RemoveByteFlag(index, offset, flag);
|
||||
else
|
||||
SetByteFlag(index, offset, flag);
|
||||
}
|
||||
|
||||
bool HasByteFlag(uint16 index, uint8 offset, uint8 flag) const
|
||||
{
|
||||
MANGOS_ASSERT(index < m_valuesCount || PrintIndexError(index , false));
|
||||
MANGOS_ASSERT(offset < 4);
|
||||
return (((uint8*)&m_uint32Values[index])[offset] & flag) != 0;
|
||||
}
|
||||
|
||||
void ApplyModByteFlag(uint16 index, uint8 offset, uint32 flag, bool apply)
|
||||
{
|
||||
if (apply)
|
||||
SetByteFlag(index, offset, flag);
|
||||
else
|
||||
RemoveByteFlag(index, offset, flag);
|
||||
}
|
||||
|
||||
void SetShortFlag(uint16 index, bool highpart, uint16 newFlag);
|
||||
void RemoveShortFlag(uint16 index, bool highpart, uint16 oldFlag);
|
||||
|
||||
void ToggleShortFlag(uint16 index, bool highpart, uint8 flag)
|
||||
{
|
||||
if (HasShortFlag(index, highpart, flag))
|
||||
RemoveShortFlag(index, highpart, flag);
|
||||
else
|
||||
SetShortFlag(index, highpart, flag);
|
||||
}
|
||||
|
||||
bool HasShortFlag(uint16 index, bool highpart, uint8 flag) const
|
||||
{
|
||||
MANGOS_ASSERT(index < m_valuesCount || PrintIndexError(index , false));
|
||||
return (((uint16*)&m_uint32Values[index])[highpart ? 1 : 0] & flag) != 0;
|
||||
}
|
||||
|
||||
void ApplyModShortFlag(uint16 index, bool highpart, uint32 flag, bool apply)
|
||||
{
|
||||
if (apply)
|
||||
SetShortFlag(index, highpart, flag);
|
||||
else
|
||||
RemoveShortFlag(index, highpart, flag);
|
||||
}
|
||||
|
||||
void SetFlag64(uint16 index, uint64 newFlag)
|
||||
{
|
||||
uint64 oldval = GetUInt64Value(index);
|
||||
uint64 newval = oldval | newFlag;
|
||||
SetUInt64Value(index, newval);
|
||||
}
|
||||
|
||||
void RemoveFlag64(uint16 index, uint64 oldFlag)
|
||||
{
|
||||
uint64 oldval = GetUInt64Value(index);
|
||||
uint64 newval = oldval & ~oldFlag;
|
||||
SetUInt64Value(index, newval);
|
||||
}
|
||||
|
||||
void ToggleFlag64(uint16 index, uint64 flag)
|
||||
{
|
||||
if (HasFlag64(index, flag))
|
||||
RemoveFlag64(index, flag);
|
||||
else
|
||||
SetFlag64(index, flag);
|
||||
}
|
||||
|
||||
bool HasFlag64(uint16 index, uint64 flag) const
|
||||
{
|
||||
MANGOS_ASSERT(index < m_valuesCount || PrintIndexError(index , false));
|
||||
return (GetUInt64Value(index) & flag) != 0;
|
||||
}
|
||||
|
||||
void ApplyModFlag64(uint16 index, uint64 flag, bool apply)
|
||||
{
|
||||
if (apply)
|
||||
SetFlag64(index, flag);
|
||||
else
|
||||
RemoveFlag64(index, flag);
|
||||
}
|
||||
|
||||
void ClearUpdateMask(bool remove);
|
||||
|
||||
bool LoadValues(const char* data);
|
||||
|
||||
uint16 GetValuesCount() const { return m_valuesCount; }
|
||||
|
||||
virtual bool HasQuest(uint32 /* quest_id */) const { return false; }
|
||||
virtual bool HasInvolvedQuest(uint32 /* quest_id */) const { return false; }
|
||||
|
||||
protected:
|
||||
Object();
|
||||
|
||||
void _InitValues();
|
||||
void _Create(uint32 guidlow, uint32 entry, HighGuid guidhigh);
|
||||
|
||||
virtual void _SetUpdateBits(UpdateMask* updateMask, Player* target) const;
|
||||
|
||||
virtual void _SetCreateBits(UpdateMask* updateMask, Player* target) const;
|
||||
|
||||
void BuildMovementUpdate(ByteBuffer* data, uint16 updateFlags) const;
|
||||
void BuildValuesUpdate(uint8 updatetype, ByteBuffer* data, UpdateMask* updateMask, Player* target) const;
|
||||
void BuildUpdateDataForPlayer(Player* pl, UpdateDataMapType& update_players);
|
||||
|
||||
uint16 m_objectType;
|
||||
|
||||
uint8 m_objectTypeId;
|
||||
uint16 m_updateFlag;
|
||||
|
||||
union
|
||||
{
|
||||
int32* m_int32Values;
|
||||
uint32* m_uint32Values;
|
||||
float* m_floatValues;
|
||||
};
|
||||
|
||||
std::vector<bool> m_changedValues;
|
||||
|
||||
uint16 m_valuesCount;
|
||||
|
||||
bool m_objectUpdated;
|
||||
|
||||
private:
|
||||
bool m_inWorld;
|
||||
|
||||
PackedGuid m_PackGUID;
|
||||
|
||||
Object(const Object&); // prevent generation copy constructor
|
||||
Object& operator=(Object const&); // prevent generation assigment operator
|
||||
|
||||
public:
|
||||
// for output helpfull error messages from ASSERTs
|
||||
bool PrintIndexError(uint32 index, bool set) const;
|
||||
bool PrintEntryError(char const* descr) const;
|
||||
};
|
||||
|
||||
struct WorldObjectChangeAccumulator;
|
||||
|
||||
class MANGOS_DLL_SPEC WorldObject : public Object
|
||||
{
|
||||
friend struct WorldObjectChangeAccumulator;
|
||||
|
||||
public:
|
||||
|
||||
// class is used to manipulate with WorldUpdateCounter
|
||||
// it is needed in order to get time diff between two object's Update() calls
|
||||
class MANGOS_DLL_SPEC UpdateHelper
|
||||
{
|
||||
public:
|
||||
explicit UpdateHelper(WorldObject* obj) : m_obj(obj) {}
|
||||
~UpdateHelper() { }
|
||||
|
||||
void Update(uint32 time_diff)
|
||||
{
|
||||
m_obj->Update(m_obj->m_updateTracker.timeElapsed(), time_diff);
|
||||
m_obj->m_updateTracker.Reset();
|
||||
}
|
||||
|
||||
private:
|
||||
UpdateHelper(const UpdateHelper&);
|
||||
UpdateHelper& operator=(const UpdateHelper&);
|
||||
|
||||
WorldObject* const m_obj;
|
||||
};
|
||||
|
||||
virtual ~WorldObject() {}
|
||||
|
||||
virtual void Update(uint32 /*update_diff*/, uint32 /*time_diff*/) {}
|
||||
|
||||
void _Create(uint32 guidlow, HighGuid guidhigh, uint32 phaseMask);
|
||||
|
||||
TransportInfo* GetTransportInfo() const { return m_transportInfo; }
|
||||
bool IsBoarded() const { return m_transportInfo != NULL; }
|
||||
void SetTransportInfo(TransportInfo* transportInfo) { m_transportInfo = transportInfo; }
|
||||
|
||||
void Relocate(float x, float y, float z, float orientation);
|
||||
void Relocate(float x, float y, float z);
|
||||
|
||||
void SetOrientation(float orientation);
|
||||
|
||||
float GetPositionX() const { return m_position.x; }
|
||||
float GetPositionY() const { return m_position.y; }
|
||||
float GetPositionZ() const { return m_position.z; }
|
||||
void GetPosition(float& x, float& y, float& z) const
|
||||
{ x = m_position.x; y = m_position.y; z = m_position.z; }
|
||||
void GetPosition(WorldLocation& loc) const
|
||||
{ loc.mapid = m_mapId; GetPosition(loc.coord_x, loc.coord_y, loc.coord_z); loc.orientation = GetOrientation(); }
|
||||
float GetOrientation() const { return m_position.o; }
|
||||
/// Gives a 2d-point in distance distance2d in direction absAngle around the current position (point-to-point)
|
||||
void GetNearPoint2D(float& x, float& y, float distance2d, float absAngle) const;
|
||||
/** Gives a "free" spot for searcher in distance distance2d in direction absAngle on "good" height
|
||||
* @param searcher - for whom a spot is searched for
|
||||
* @param x, y, z - position for the found spot of the searcher
|
||||
* @param searcher_bounding_radius - how much space the searcher will require
|
||||
* @param distance2d - distance between the middle-points
|
||||
* @param absAngle - angle in which the spot is preferred
|
||||
*/
|
||||
void GetNearPoint(WorldObject const* searcher, float& x, float& y, float& z, float searcher_bounding_radius, float distance2d, float absAngle) const;
|
||||
/** Gives a "free" spot for a searcher on the distance (including bounding-radius calculation)
|
||||
* @param x, y, z - position for the found spot
|
||||
* @param bounding_radius - radius for the searcher
|
||||
* @param distance2d - range in which to find a free spot. Default = 0.0f (which usually means the units will have contact)
|
||||
* @param angle - direction in which to look for a free spot. Default = 0.0f (direction in which 'this' is looking
|
||||
* @param obj - for whom to look for a spot. Default = NULL
|
||||
*/
|
||||
void GetClosePoint(float& x, float& y, float& z, float bounding_radius, float distance2d = 0.0f, float angle = 0.0f, const WorldObject* obj = NULL) const
|
||||
{
|
||||
// angle calculated from current orientation
|
||||
GetNearPoint(obj, x, y, z, bounding_radius, distance2d + GetObjectBoundingRadius() + bounding_radius, GetOrientation() + angle);
|
||||
}
|
||||
/** Gives a "free" spot for a searcher in contact-range of "this" (including bounding-radius calculation)
|
||||
* @param x, y, z - position for the found spot
|
||||
* @param obj - for whom to find a contact position. The position will be searched in direction from 'this' towards 'obj'
|
||||
* @param distance2d - distance which 'obj' and 'this' should have beetween their bounding radiuses. Default = CONTACT_DISTANCE
|
||||
*/
|
||||
void GetContactPoint(const WorldObject* obj, float& x, float& y, float& z, float distance2d = CONTACT_DISTANCE) const
|
||||
{
|
||||
// angle to face `obj` to `this` using distance includes size of `obj`
|
||||
GetNearPoint(obj, x, y, z, obj->GetObjectBoundingRadius(), distance2d + GetObjectBoundingRadius() + obj->GetObjectBoundingRadius(), GetAngle(obj));
|
||||
}
|
||||
|
||||
virtual float GetObjectBoundingRadius() const { return DEFAULT_WORLD_OBJECT_SIZE; }
|
||||
|
||||
bool IsPositionValid() const;
|
||||
void UpdateGroundPositionZ(float x, float y, float& z) const;
|
||||
void UpdateAllowedPositionZ(float x, float y, float& z) const;
|
||||
|
||||
void GetRandomPoint(float x, float y, float z, float distance, float& rand_x, float& rand_y, float& rand_z) const;
|
||||
|
||||
uint32 GetMapId() const { return m_mapId; }
|
||||
uint32 GetInstanceId() const { return m_InstanceId; }
|
||||
|
||||
virtual void SetPhaseMask(uint32 newPhaseMask, bool update);
|
||||
uint32 GetPhaseMask() const { return m_phaseMask; }
|
||||
bool InSamePhase(WorldObject const* obj) const { return InSamePhase(obj->GetPhaseMask()); }
|
||||
bool InSamePhase(uint32 phasemask) const { return (GetPhaseMask() & phasemask); }
|
||||
|
||||
uint32 GetZoneId() const;
|
||||
uint32 GetAreaId() const;
|
||||
void GetZoneAndAreaId(uint32& zoneid, uint32& areaid) const;
|
||||
|
||||
InstanceData* GetInstanceData() const;
|
||||
|
||||
const char* GetName() const { return m_name.c_str(); }
|
||||
void SetName(const std::string& newname) { m_name = newname; }
|
||||
|
||||
virtual const char* GetNameForLocaleIdx(int32 /*locale_idx*/) const { return GetName(); }
|
||||
|
||||
float GetDistance(const WorldObject* obj) const;
|
||||
float GetDistance(float x, float y, float z) const;
|
||||
float GetDistance2d(const WorldObject* obj) const;
|
||||
float GetDistance2d(float x, float y) const;
|
||||
float GetDistanceZ(const WorldObject* obj) const;
|
||||
bool IsInMap(const WorldObject* obj) const
|
||||
{
|
||||
return IsInWorld() && obj->IsInWorld() && (GetMap() == obj->GetMap()) && InSamePhase(obj);
|
||||
}
|
||||
bool IsWithinDist3d(float x, float y, float z, float dist2compare) const;
|
||||
bool IsWithinDist2d(float x, float y, float dist2compare) const;
|
||||
bool _IsWithinDist(WorldObject const* obj, float dist2compare, bool is3D) const;
|
||||
|
||||
// use only if you will sure about placing both object at same map
|
||||
bool IsWithinDist(WorldObject const* obj, float dist2compare, bool is3D = true) const
|
||||
{
|
||||
return obj && _IsWithinDist(obj, dist2compare, is3D);
|
||||
}
|
||||
|
||||
bool IsWithinDistInMap(WorldObject const* obj, float dist2compare, bool is3D = true) const
|
||||
{
|
||||
return obj && IsInMap(obj) && _IsWithinDist(obj, dist2compare, is3D);
|
||||
}
|
||||
bool IsWithinLOS(float x, float y, float z) const;
|
||||
bool IsWithinLOSInMap(const WorldObject* obj) const;
|
||||
bool GetDistanceOrder(WorldObject const* obj1, WorldObject const* obj2, bool is3D = true) const;
|
||||
bool IsInRange(WorldObject const* obj, float minRange, float maxRange, bool is3D = true) const;
|
||||
bool IsInRange2d(float x, float y, float minRange, float maxRange) const;
|
||||
bool IsInRange3d(float x, float y, float z, float minRange, float maxRange) const;
|
||||
|
||||
float GetAngle(const WorldObject* obj) const;
|
||||
float GetAngle(const float x, const float y) const;
|
||||
bool HasInArc(const float arcangle, const WorldObject* obj) const;
|
||||
bool isInFrontInMap(WorldObject const* target, float distance, float arc = M_PI) const;
|
||||
bool isInBackInMap(WorldObject const* target, float distance, float arc = M_PI) const;
|
||||
bool isInFront(WorldObject const* target, float distance, float arc = M_PI) const;
|
||||
bool isInBack(WorldObject const* target, float distance, float arc = M_PI) const;
|
||||
|
||||
virtual void CleanupsBeforeDelete(); // used in destructor or explicitly before mass creature delete to remove cross-references to already deleted units
|
||||
|
||||
virtual void SendMessageToSet(WorldPacket* data, bool self) const;
|
||||
virtual void SendMessageToSetInRange(WorldPacket* data, float dist, bool self) const;
|
||||
void SendMessageToSetExcept(WorldPacket* data, Player const* skipped_receiver) const;
|
||||
|
||||
void MonsterSay(const char* text, uint32 language, Unit const* target = NULL) const;
|
||||
void MonsterYell(const char* text, uint32 language, Unit const* target = NULL) const;
|
||||
void MonsterTextEmote(const char* text, Unit const* target, bool IsBossEmote = false) const;
|
||||
void MonsterWhisper(const char* text, Unit const* target, bool IsBossWhisper = false) const;
|
||||
void MonsterSay(int32 textId, uint32 language, Unit const* target = NULL) const;
|
||||
void MonsterYell(int32 textId, uint32 language, Unit const* target = NULL) const;
|
||||
void MonsterTextEmote(int32 textId, Unit const* target, bool IsBossEmote = false) const;
|
||||
void MonsterWhisper(int32 textId, Unit const* receiver, bool IsBossWhisper = false) const;
|
||||
void MonsterYellToZone(int32 textId, uint32 language, Unit const* target) const;
|
||||
static void BuildMonsterChat(WorldPacket* data, ObjectGuid senderGuid, uint8 msgtype, char const* text, uint32 language, char const* name, ObjectGuid targetGuid, char const* targetName);
|
||||
|
||||
void PlayDistanceSound(uint32 sound_id, Player const* target = NULL) const;
|
||||
void PlayDirectSound(uint32 sound_id, Player const* target = NULL) const;
|
||||
|
||||
void SendObjectDeSpawnAnim(ObjectGuid guid);
|
||||
void SendGameObjectCustomAnim(ObjectGuid guid, uint32 animId = 0);
|
||||
|
||||
virtual bool IsHostileTo(Unit const* unit) const = 0;
|
||||
virtual bool IsFriendlyTo(Unit const* unit) const = 0;
|
||||
bool IsControlledByPlayer() const;
|
||||
|
||||
virtual void SaveRespawnTime() {}
|
||||
void AddObjectToRemoveList();
|
||||
|
||||
void UpdateObjectVisibility();
|
||||
virtual void UpdateVisibilityAndView(); // update visibility for object and object for all around
|
||||
|
||||
// main visibility check function in normal case (ignore grey zone distance check)
|
||||
bool isVisibleFor(Player const* u, WorldObject const* viewPoint) const { return isVisibleForInState(u, viewPoint, false); }
|
||||
|
||||
// low level function for visibility change code, must be define in all main world object subclasses
|
||||
virtual bool isVisibleForInState(Player const* u, WorldObject const* viewPoint, bool inVisibleList) const = 0;
|
||||
|
||||
void SetMap(Map* map);
|
||||
Map* GetMap() const { MANGOS_ASSERT(m_currMap); return m_currMap; }
|
||||
// used to check all object's GetMap() calls when object is not in world!
|
||||
void ResetMap() { m_currMap = NULL; }
|
||||
|
||||
// obtain terrain data for map where this object belong...
|
||||
TerrainInfo const* GetTerrain() const;
|
||||
|
||||
void AddToClientUpdateList() override;
|
||||
void RemoveFromClientUpdateList() override;
|
||||
void BuildUpdateData(UpdateDataMapType&) override;
|
||||
|
||||
Creature* SummonCreature(uint32 id, float x, float y, float z, float ang, TempSummonType spwtype, uint32 despwtime, bool asActiveObject = false);
|
||||
|
||||
bool isActiveObject() const { return m_isActiveObject || m_viewPoint.hasViewers(); }
|
||||
void SetActiveObjectState(bool active);
|
||||
|
||||
ViewPoint& GetViewPoint() { return m_viewPoint; }
|
||||
|
||||
// ASSERT print helper
|
||||
bool PrintCoordinatesError(float x, float y, float z, char const* descr) const;
|
||||
|
||||
virtual void StartGroupLoot(Group* /*group*/, uint32 /*timer*/) {}
|
||||
|
||||
protected:
|
||||
explicit WorldObject();
|
||||
|
||||
// these functions are used mostly for Relocate() and Corpse/Player specific stuff...
|
||||
// use them ONLY in LoadFromDB()/Create() funcs and nowhere else!
|
||||
// mapId/instanceId should be set in SetMap() function!
|
||||
void SetLocationMapId(uint32 _mapId) { m_mapId = _mapId; }
|
||||
void SetLocationInstanceId(uint32 _instanceId) { m_InstanceId = _instanceId; }
|
||||
|
||||
virtual void StopGroupLoot() {}
|
||||
|
||||
std::string m_name;
|
||||
|
||||
TransportInfo* m_transportInfo;
|
||||
|
||||
private:
|
||||
Map* m_currMap; // current object's Map location
|
||||
|
||||
uint32 m_mapId; // object at map with map_id
|
||||
uint32 m_InstanceId; // in map copy with instance id
|
||||
uint32 m_phaseMask; // in area phase state
|
||||
|
||||
Position m_position;
|
||||
ViewPoint m_viewPoint;
|
||||
WorldUpdateCounter m_updateTracker;
|
||||
bool m_isActiveObject;
|
||||
};
|
||||
|
||||
#endif
|
||||
286
src/game/Object/ObjectAccessor.cpp
Normal file
286
src/game/Object/ObjectAccessor.cpp
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include "ObjectAccessor.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Policies/Singleton.h"
|
||||
#include "Player.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "Item.h"
|
||||
#include "Corpse.h"
|
||||
#include "GridNotifiers.h"
|
||||
#include "MapManager.h"
|
||||
#include "Map.h"
|
||||
#include "CellImpl.h"
|
||||
#include "GridNotifiersImpl.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "World.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#define CLASS_LOCK MaNGOS::ClassLevelLockable<ObjectAccessor, ACE_Thread_Mutex>
|
||||
INSTANTIATE_SINGLETON_2(ObjectAccessor, CLASS_LOCK);
|
||||
INSTANTIATE_CLASS_MUTEX(ObjectAccessor, ACE_Thread_Mutex);
|
||||
|
||||
ObjectAccessor::ObjectAccessor() {}
|
||||
ObjectAccessor::~ObjectAccessor()
|
||||
{
|
||||
for (Player2CorpsesMapType::const_iterator itr = i_player2corpse.begin(); itr != i_player2corpse.end(); ++itr)
|
||||
{
|
||||
itr->second->RemoveFromWorld();
|
||||
delete itr->second;
|
||||
}
|
||||
}
|
||||
|
||||
Unit*
|
||||
ObjectAccessor::GetUnit(WorldObject const& u, ObjectGuid guid)
|
||||
{
|
||||
if (!guid)
|
||||
return NULL;
|
||||
|
||||
if (guid.IsPlayer())
|
||||
return FindPlayer(guid);
|
||||
|
||||
if (!u.IsInWorld())
|
||||
return NULL;
|
||||
|
||||
return u.GetMap()->GetAnyTypeCreature(guid);
|
||||
}
|
||||
|
||||
Corpse* ObjectAccessor::GetCorpseInMap(ObjectGuid guid, uint32 mapid)
|
||||
{
|
||||
Corpse* ret = HashMapHolder<Corpse>::Find(guid);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
if (ret->GetMapId() != mapid)
|
||||
return NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Player* ObjectAccessor::FindPlayer(ObjectGuid guid, bool inWorld /*= true*/)
|
||||
{
|
||||
if (!guid)
|
||||
return NULL;
|
||||
|
||||
Player* plr = HashMapHolder<Player>::Find(guid);
|
||||
if (!plr || (!plr->IsInWorld() && inWorld))
|
||||
return NULL;
|
||||
|
||||
return plr;
|
||||
}
|
||||
|
||||
Player* ObjectAccessor::FindPlayerByName(const char* name)
|
||||
{
|
||||
HashMapHolder<Player>::ReadGuard g(HashMapHolder<Player>::GetLock());
|
||||
HashMapHolder<Player>::MapType& m = sObjectAccessor.GetPlayers();
|
||||
for (HashMapHolder<Player>::MapType::iterator iter = m.begin(); iter != m.end(); ++iter)
|
||||
if (iter->second->IsInWorld() && (::strcmp(name, iter->second->GetName()) == 0))
|
||||
return iter->second;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
ObjectAccessor::SaveAllPlayers()
|
||||
{
|
||||
HashMapHolder<Player>::ReadGuard g(HashMapHolder<Player>::GetLock());
|
||||
HashMapHolder<Player>::MapType& m = sObjectAccessor.GetPlayers();
|
||||
for (HashMapHolder<Player>::MapType::iterator itr = m.begin(); itr != m.end(); ++itr)
|
||||
itr->second->SaveToDB();
|
||||
}
|
||||
|
||||
void ObjectAccessor::KickPlayer(ObjectGuid guid)
|
||||
{
|
||||
if (Player* p = ObjectAccessor::FindPlayer(guid, false))
|
||||
{
|
||||
WorldSession* s = p->GetSession();
|
||||
s->KickPlayer(); // mark session to remove at next session list update
|
||||
s->LogoutPlayer(false); // logout player without waiting next session list update
|
||||
}
|
||||
}
|
||||
|
||||
Corpse*
|
||||
ObjectAccessor::GetCorpseForPlayerGUID(ObjectGuid guid)
|
||||
{
|
||||
Guard guard(i_corpseGuard);
|
||||
|
||||
Player2CorpsesMapType::iterator iter = i_player2corpse.find(guid);
|
||||
if (iter == i_player2corpse.end())
|
||||
return NULL;
|
||||
|
||||
MANGOS_ASSERT(iter->second->GetType() != CORPSE_BONES);
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
void
|
||||
ObjectAccessor::RemoveCorpse(Corpse* corpse)
|
||||
{
|
||||
MANGOS_ASSERT(corpse && corpse->GetType() != CORPSE_BONES);
|
||||
|
||||
Guard guard(i_corpseGuard);
|
||||
Player2CorpsesMapType::iterator iter = i_player2corpse.find(corpse->GetOwnerGuid());
|
||||
if (iter == i_player2corpse.end())
|
||||
return;
|
||||
|
||||
// build mapid*cellid -> guid_set map
|
||||
CellPair cell_pair = MaNGOS::ComputeCellPair(corpse->GetPositionX(), corpse->GetPositionY());
|
||||
uint32 cell_id = (cell_pair.y_coord * TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
|
||||
|
||||
sObjectMgr.DeleteCorpseCellData(corpse->GetMapId(), cell_id, corpse->GetOwnerGuid().GetCounter());
|
||||
corpse->RemoveFromWorld();
|
||||
|
||||
i_player2corpse.erase(iter);
|
||||
}
|
||||
|
||||
void
|
||||
ObjectAccessor::AddCorpse(Corpse* corpse)
|
||||
{
|
||||
MANGOS_ASSERT(corpse && corpse->GetType() != CORPSE_BONES);
|
||||
|
||||
Guard guard(i_corpseGuard);
|
||||
MANGOS_ASSERT(i_player2corpse.find(corpse->GetOwnerGuid()) == i_player2corpse.end());
|
||||
i_player2corpse[corpse->GetOwnerGuid()] = corpse;
|
||||
|
||||
// build mapid*cellid -> guid_set map
|
||||
CellPair cell_pair = MaNGOS::ComputeCellPair(corpse->GetPositionX(), corpse->GetPositionY());
|
||||
uint32 cell_id = (cell_pair.y_coord * TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
|
||||
|
||||
sObjectMgr.AddCorpseCellData(corpse->GetMapId(), cell_id, corpse->GetOwnerGuid().GetCounter(), corpse->GetInstanceId());
|
||||
}
|
||||
|
||||
void
|
||||
ObjectAccessor::AddCorpsesToGrid(GridPair const& gridpair, GridType& grid, Map* map)
|
||||
{
|
||||
Guard guard(i_corpseGuard);
|
||||
for (Player2CorpsesMapType::iterator iter = i_player2corpse.begin(); iter != i_player2corpse.end(); ++iter)
|
||||
if (iter->second->GetGrid() == gridpair)
|
||||
{
|
||||
// verify, if the corpse in our instance (add only corpses which are)
|
||||
if (map->Instanceable())
|
||||
{
|
||||
if (iter->second->GetInstanceId() == map->GetInstanceId())
|
||||
{
|
||||
grid.AddWorldObject(iter->second);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
grid.AddWorldObject(iter->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Corpse*
|
||||
ObjectAccessor::ConvertCorpseForPlayer(ObjectGuid player_guid, bool insignia)
|
||||
{
|
||||
Corpse* corpse = GetCorpseForPlayerGUID(player_guid);
|
||||
if (!corpse)
|
||||
{
|
||||
// in fact this function is called from several places
|
||||
// even when player doesn't have a corpse, not an error
|
||||
// sLog.outError("Try remove corpse that not in map for GUID %ul", player_guid);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEBUG_LOG("Deleting Corpse and spawning bones.");
|
||||
|
||||
// remove corpse from player_guid -> corpse map
|
||||
RemoveCorpse(corpse);
|
||||
|
||||
// remove resurrectable corpse from grid object registry (loaded state checked into call)
|
||||
// do not load the map if it's not loaded
|
||||
Map* map = sMapMgr.FindMap(corpse->GetMapId(), corpse->GetInstanceId());
|
||||
if (map)
|
||||
map->Remove(corpse, false);
|
||||
|
||||
// remove corpse from DB
|
||||
corpse->DeleteFromDB();
|
||||
|
||||
Corpse* bones = NULL;
|
||||
// create the bones only if the map and the grid is loaded at the corpse's location
|
||||
// ignore bones creating option in case insignia
|
||||
if (map && (insignia ||
|
||||
(map->IsBattleGroundOrArena() ? sWorld.getConfig(CONFIG_BOOL_DEATH_BONES_BG_OR_ARENA) : sWorld.getConfig(CONFIG_BOOL_DEATH_BONES_WORLD))) &&
|
||||
!map->IsRemovalGrid(corpse->GetPositionX(), corpse->GetPositionY()))
|
||||
{
|
||||
// Create bones, don't change Corpse
|
||||
bones = new Corpse;
|
||||
bones->Create(corpse->GetGUIDLow());
|
||||
|
||||
for (int i = 3; i < CORPSE_END; ++i) // don't overwrite guid and object type
|
||||
bones->SetUInt32Value(i, corpse->GetUInt32Value(i));
|
||||
|
||||
bones->SetGrid(corpse->GetGrid());
|
||||
// bones->m_time = m_time; // don't overwrite time
|
||||
// bones->m_inWorld = m_inWorld; // don't overwrite world state
|
||||
// bones->m_type = m_type; // don't overwrite type
|
||||
bones->Relocate(corpse->GetPositionX(), corpse->GetPositionY(), corpse->GetPositionZ(), corpse->GetOrientation());
|
||||
bones->SetPhaseMask(corpse->GetPhaseMask(), false);
|
||||
|
||||
bones->SetUInt32Value(CORPSE_FIELD_FLAGS, CORPSE_FLAG_UNK2 | CORPSE_FLAG_BONES);
|
||||
bones->SetGuidValue(CORPSE_FIELD_OWNER, ObjectGuid());
|
||||
|
||||
for (int i = 0; i < EQUIPMENT_SLOT_END; ++i)
|
||||
{
|
||||
if (corpse->GetUInt32Value(CORPSE_FIELD_ITEM + i))
|
||||
bones->SetUInt32Value(CORPSE_FIELD_ITEM + i, 0);
|
||||
}
|
||||
|
||||
// add bones in grid store if grid loaded where corpse placed
|
||||
map->Add(bones);
|
||||
}
|
||||
|
||||
// all references to the corpse should be removed at this point
|
||||
delete corpse;
|
||||
|
||||
return bones;
|
||||
}
|
||||
|
||||
void ObjectAccessor::RemoveOldCorpses()
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
Player2CorpsesMapType::iterator next;
|
||||
for (Player2CorpsesMapType::iterator itr = i_player2corpse.begin(); itr != i_player2corpse.end(); itr = next)
|
||||
{
|
||||
next = itr;
|
||||
++next;
|
||||
|
||||
if (!itr->second->IsExpired(now))
|
||||
continue;
|
||||
|
||||
ConvertCorpseForPlayer(itr->first);
|
||||
}
|
||||
}
|
||||
|
||||
/// Define the static member of HashMapHolder
|
||||
|
||||
template <class T> typename HashMapHolder<T>::MapType HashMapHolder<T>::m_objectMap;
|
||||
template <class T> ACE_RW_Thread_Mutex HashMapHolder<T>::i_lock;
|
||||
|
||||
/// Global definitions for the hashmap storage
|
||||
|
||||
template class HashMapHolder<Player>;
|
||||
template class HashMapHolder<Corpse>;
|
||||
148
src/game/Object/ObjectAccessor.h
Normal file
148
src/game/Object/ObjectAccessor.h
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOS_OBJECTACCESSOR_H
|
||||
#define MANGOS_OBJECTACCESSOR_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "Platform/Define.h"
|
||||
#include "Policies/Singleton.h"
|
||||
#include <ace/Thread_Mutex.h>
|
||||
#include <ace/RW_Thread_Mutex.h>
|
||||
#include "Utilities/UnorderedMapSet.h"
|
||||
#include "Policies/ThreadingModel.h"
|
||||
|
||||
#include "UpdateData.h"
|
||||
|
||||
#include "GridDefines.h"
|
||||
#include "Object.h"
|
||||
#include "Player.h"
|
||||
#include "Corpse.h"
|
||||
|
||||
#include <set>
|
||||
#include <list>
|
||||
|
||||
class Unit;
|
||||
class WorldObject;
|
||||
class Map;
|
||||
|
||||
template <class T>
|
||||
class HashMapHolder
|
||||
{
|
||||
public:
|
||||
|
||||
typedef UNORDERED_MAP<ObjectGuid, T*> MapType;
|
||||
typedef ACE_RW_Thread_Mutex LockType;
|
||||
typedef ACE_Read_Guard<LockType> ReadGuard;
|
||||
typedef ACE_Write_Guard<LockType> WriteGuard;
|
||||
|
||||
static void Insert(T* o)
|
||||
{
|
||||
WriteGuard guard(i_lock);
|
||||
m_objectMap[o->GetObjectGuid()] = o;
|
||||
}
|
||||
|
||||
static void Remove(T* o)
|
||||
{
|
||||
WriteGuard guard(i_lock);
|
||||
m_objectMap.erase(o->GetObjectGuid());
|
||||
}
|
||||
|
||||
static T* Find(ObjectGuid guid)
|
||||
{
|
||||
ReadGuard guard(i_lock);
|
||||
typename MapType::iterator itr = m_objectMap.find(guid);
|
||||
return (itr != m_objectMap.end()) ? itr->second : NULL;
|
||||
}
|
||||
|
||||
static MapType& GetContainer() { return m_objectMap; }
|
||||
|
||||
static LockType& GetLock() { return i_lock; }
|
||||
|
||||
private:
|
||||
|
||||
// Non instanceable only static
|
||||
HashMapHolder() {}
|
||||
|
||||
static LockType i_lock;
|
||||
static MapType m_objectMap;
|
||||
};
|
||||
|
||||
class MANGOS_DLL_DECL ObjectAccessor : public MaNGOS::Singleton<ObjectAccessor, MaNGOS::ClassLevelLockable<ObjectAccessor, ACE_Thread_Mutex> >
|
||||
{
|
||||
friend class MaNGOS::OperatorNew<ObjectAccessor>;
|
||||
|
||||
ObjectAccessor();
|
||||
~ObjectAccessor();
|
||||
ObjectAccessor(const ObjectAccessor&);
|
||||
ObjectAccessor& operator=(const ObjectAccessor&);
|
||||
|
||||
public:
|
||||
typedef UNORDERED_MAP<ObjectGuid, Corpse*> Player2CorpsesMapType;
|
||||
|
||||
// Search player at any map in world and other objects at same map with `obj`
|
||||
// Note: recommended use Map::GetUnit version if player also expected at same map only
|
||||
static Unit* GetUnit(WorldObject const& obj, ObjectGuid guid);
|
||||
|
||||
// Player access
|
||||
static Player* FindPlayer(ObjectGuid guid, bool inWorld = true);// if need player at specific map better use Map::GetPlayer
|
||||
static Player* FindPlayerByName(const char* name);
|
||||
static void KickPlayer(ObjectGuid guid);
|
||||
|
||||
HashMapHolder<Player>::MapType& GetPlayers()
|
||||
{
|
||||
return HashMapHolder<Player>::GetContainer();
|
||||
}
|
||||
|
||||
void SaveAllPlayers();
|
||||
|
||||
// Corpse access
|
||||
Corpse* GetCorpseForPlayerGUID(ObjectGuid guid);
|
||||
static Corpse* GetCorpseInMap(ObjectGuid guid, uint32 mapid);
|
||||
void RemoveCorpse(Corpse* corpse);
|
||||
void AddCorpse(Corpse* corpse);
|
||||
void AddCorpsesToGrid(GridPair const& gridpair, GridType& grid, Map* map);
|
||||
Corpse* ConvertCorpseForPlayer(ObjectGuid player_guid, bool insignia = false);
|
||||
void RemoveOldCorpses();
|
||||
|
||||
// For call from Player/Corpse AddToWorld/RemoveFromWorld only
|
||||
void AddObject(Corpse* object) { HashMapHolder<Corpse>::Insert(object); }
|
||||
void AddObject(Player* object) { HashMapHolder<Player>::Insert(object); }
|
||||
void RemoveObject(Corpse* object) { HashMapHolder<Corpse>::Remove(object); }
|
||||
void RemoveObject(Player* object) { HashMapHolder<Player>::Remove(object); }
|
||||
|
||||
private:
|
||||
|
||||
Player2CorpsesMapType i_player2corpse;
|
||||
|
||||
typedef ACE_Thread_Mutex LockType;
|
||||
typedef MaNGOS::GeneralLock<LockType > Guard;
|
||||
|
||||
LockType i_playerGuard;
|
||||
LockType i_corpseGuard;
|
||||
};
|
||||
|
||||
#define sObjectAccessor ObjectAccessor::Instance()
|
||||
|
||||
#endif
|
||||
118
src/game/Object/ObjectGuid.cpp
Normal file
118
src/game/Object/ObjectGuid.cpp
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include "ObjectGuid.h"
|
||||
|
||||
#include "World.h"
|
||||
#include "ObjectMgr.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
char const* ObjectGuid::GetTypeName(HighGuid high)
|
||||
{
|
||||
switch (high)
|
||||
{
|
||||
case HIGHGUID_ITEM: return "Item";
|
||||
case HIGHGUID_PLAYER: return "Player";
|
||||
case HIGHGUID_GAMEOBJECT: return "Gameobject";
|
||||
case HIGHGUID_TRANSPORT: return "Transport";
|
||||
case HIGHGUID_UNIT: return "Creature";
|
||||
case HIGHGUID_PET: return "Pet";
|
||||
case HIGHGUID_VEHICLE: return "Vehicle";
|
||||
case HIGHGUID_DYNAMICOBJECT:return "DynObject";
|
||||
case HIGHGUID_CORPSE: return "Corpse";
|
||||
case HIGHGUID_MO_TRANSPORT: return "MoTransport";
|
||||
case HIGHGUID_INSTANCE: return "InstanceID";
|
||||
case HIGHGUID_GROUP: return "Group";
|
||||
case HIGHGUID_BATTLEGROUND: return "Battleground";
|
||||
default:
|
||||
return "<unknown>";
|
||||
}
|
||||
}
|
||||
|
||||
std::string ObjectGuid::GetString() const
|
||||
{
|
||||
std::ostringstream str;
|
||||
str << GetTypeName();
|
||||
|
||||
if (IsPlayer())
|
||||
{
|
||||
std::string name;
|
||||
if (sObjectMgr.GetPlayerNameByGUID(*this, name))
|
||||
str << " " << name;
|
||||
}
|
||||
|
||||
str << " (";
|
||||
if (HasEntry())
|
||||
str << (IsPet() ? "Petnumber: " : "Entry: ") << GetEntry() << " ";
|
||||
str << "Guid: " << GetCounter() << ")";
|
||||
return str.str();
|
||||
}
|
||||
|
||||
template<HighGuid high>
|
||||
uint32 ObjectGuidGenerator<high>::Generate()
|
||||
{
|
||||
if (m_nextGuid >= ObjectGuid::GetMaxCounter(high) - 1)
|
||||
{
|
||||
sLog.outError("%s guid overflow!! Can't continue, shutting down server. ", ObjectGuid::GetTypeName(high));
|
||||
World::StopNow(ERROR_EXIT_CODE);
|
||||
}
|
||||
return m_nextGuid++;
|
||||
}
|
||||
|
||||
ByteBuffer& operator<< (ByteBuffer& buf, ObjectGuid const& guid)
|
||||
{
|
||||
buf << uint64(guid.GetRawValue());
|
||||
return buf;
|
||||
}
|
||||
|
||||
ByteBuffer& operator>>(ByteBuffer& buf, ObjectGuid& guid)
|
||||
{
|
||||
guid.Set(buf.read<uint64>());
|
||||
return buf;
|
||||
}
|
||||
|
||||
ByteBuffer& operator<< (ByteBuffer& buf, PackedGuid const& guid)
|
||||
{
|
||||
buf.append(guid.m_packedGuid);
|
||||
return buf;
|
||||
}
|
||||
|
||||
ByteBuffer& operator>>(ByteBuffer& buf, PackedGuidReader const& guid)
|
||||
{
|
||||
guid.m_guidPtr->Set(buf.readPackGUID());
|
||||
return buf;
|
||||
}
|
||||
|
||||
template uint32 ObjectGuidGenerator<HIGHGUID_ITEM>::Generate();
|
||||
template uint32 ObjectGuidGenerator<HIGHGUID_PLAYER>::Generate();
|
||||
template uint32 ObjectGuidGenerator<HIGHGUID_GAMEOBJECT>::Generate();
|
||||
template uint32 ObjectGuidGenerator<HIGHGUID_TRANSPORT>::Generate();
|
||||
template uint32 ObjectGuidGenerator<HIGHGUID_UNIT>::Generate();
|
||||
template uint32 ObjectGuidGenerator<HIGHGUID_PET>::Generate();
|
||||
template uint32 ObjectGuidGenerator<HIGHGUID_VEHICLE>::Generate();
|
||||
template uint32 ObjectGuidGenerator<HIGHGUID_DYNAMICOBJECT>::Generate();
|
||||
template uint32 ObjectGuidGenerator<HIGHGUID_CORPSE>::Generate();
|
||||
template uint32 ObjectGuidGenerator<HIGHGUID_INSTANCE>::Generate();
|
||||
template uint32 ObjectGuidGenerator<HIGHGUID_GROUP>::Generate();
|
||||
411
src/game/Object/ObjectGuid.h
Normal file
411
src/game/Object/ObjectGuid.h
Normal file
|
|
@ -0,0 +1,411 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOS_OBJECT_GUID_H
|
||||
#define MANGOS_OBJECT_GUID_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "ByteBuffer.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
enum TypeID
|
||||
{
|
||||
TYPEID_OBJECT = 0,
|
||||
TYPEID_ITEM = 1,
|
||||
TYPEID_CONTAINER = 2,
|
||||
TYPEID_UNIT = 3,
|
||||
TYPEID_PLAYER = 4,
|
||||
TYPEID_GAMEOBJECT = 5,
|
||||
TYPEID_DYNAMICOBJECT = 6,
|
||||
TYPEID_CORPSE = 7
|
||||
};
|
||||
|
||||
#define MAX_TYPE_ID 8
|
||||
|
||||
enum TypeMask
|
||||
{
|
||||
TYPEMASK_OBJECT = 0x0001,
|
||||
TYPEMASK_ITEM = 0x0002,
|
||||
TYPEMASK_CONTAINER = 0x0004,
|
||||
TYPEMASK_UNIT = 0x0008, // players also have it
|
||||
TYPEMASK_PLAYER = 0x0010,
|
||||
TYPEMASK_GAMEOBJECT = 0x0020,
|
||||
TYPEMASK_DYNAMICOBJECT = 0x0040,
|
||||
TYPEMASK_CORPSE = 0x0080,
|
||||
|
||||
// used combinations in Player::GetObjectByTypeMask (TYPEMASK_UNIT case ignore players in call)
|
||||
TYPEMASK_CREATURE_OR_GAMEOBJECT = TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT,
|
||||
TYPEMASK_CREATURE_GAMEOBJECT_OR_ITEM = TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT | TYPEMASK_ITEM,
|
||||
TYPEMASK_CREATURE_GAMEOBJECT_PLAYER_OR_ITEM = TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT | TYPEMASK_ITEM | TYPEMASK_PLAYER,
|
||||
|
||||
TYPEMASK_WORLDOBJECT = TYPEMASK_UNIT | TYPEMASK_PLAYER | TYPEMASK_GAMEOBJECT | TYPEMASK_DYNAMICOBJECT | TYPEMASK_CORPSE,
|
||||
};
|
||||
|
||||
enum HighGuid
|
||||
{
|
||||
HIGHGUID_ITEM = 0x470, // blizz 470
|
||||
HIGHGUID_CONTAINER = 0x470, // blizz 470
|
||||
HIGHGUID_PLAYER = 0x000, // blizz 070 (temporary reverted back to 0 high guid
|
||||
// in result unknown source visibility player with
|
||||
// player problems. please reapply only after its resolve)
|
||||
HIGHGUID_GAMEOBJECT = 0xF11, // blizz F11/F51
|
||||
HIGHGUID_TRANSPORT = 0xF12, // blizz F12/F52 (for GAMEOBJECT_TYPE_TRANSPORT)
|
||||
HIGHGUID_UNIT = 0xF13, // blizz F13/F53
|
||||
HIGHGUID_PET = 0xF14, // blizz F14/F54
|
||||
HIGHGUID_VEHICLE = 0xF15, // blizz F15/F55
|
||||
HIGHGUID_DYNAMICOBJECT = 0xF10, // blizz F10/F50
|
||||
HIGHGUID_CORPSE = 0xF50, // blizz F10/F50 used second variant to resolve conflict with HIGHGUID_DYNAMICOBJECT
|
||||
HIGHGUID_BATTLEGROUND = 0x1F1, // blizz 1F1, used for battlegrounds and battlefields
|
||||
HIGHGUID_MO_TRANSPORT = 0x1FC, // blizz 1FC (for GAMEOBJECT_TYPE_MO_TRANSPORT)
|
||||
HIGHGUID_INSTANCE = 0x1F4, // blizz 1F4
|
||||
HIGHGUID_GROUP = 0x1F5, // blizz 1F5
|
||||
HIGHGUID_GUILD = 0x1FF7, // blizz 1FF7
|
||||
};
|
||||
|
||||
class ObjectGuid;
|
||||
class PackedGuid;
|
||||
|
||||
struct PackedGuidReader
|
||||
{
|
||||
explicit PackedGuidReader(ObjectGuid& guid) : m_guidPtr(&guid) {}
|
||||
ObjectGuid* m_guidPtr;
|
||||
};
|
||||
|
||||
#define NUM_GUID_BYTES sizeof(uint64)
|
||||
|
||||
class MANGOS_DLL_SPEC ObjectGuid
|
||||
{
|
||||
public: // constructors
|
||||
ObjectGuid() : m_guid(0) {}
|
||||
explicit ObjectGuid(uint64 guid) : m_guid(guid) {}
|
||||
ObjectGuid(HighGuid hi, uint32 entry, uint32 counter) : m_guid(counter ? uint64(counter) | (uint64(entry) << 32) | (uint64(hi) << (IsLargeHigh(hi) ? 48 : 52)) : 0) {}
|
||||
ObjectGuid(HighGuid hi, uint32 counter) : m_guid(counter ? uint64(counter) | (uint64(hi) << (IsLargeHigh(hi) ? 48 : 52)) : 0) {}
|
||||
|
||||
operator uint64() const { return m_guid; }
|
||||
private:
|
||||
explicit ObjectGuid(uint32 const&); // no implementation, used for catch wrong type assign
|
||||
ObjectGuid(HighGuid, uint32, uint64 counter); // no implementation, used for catch wrong type assign
|
||||
ObjectGuid(HighGuid, uint64 counter); // no implementation, used for catch wrong type assign
|
||||
|
||||
public: // modifiers
|
||||
PackedGuidReader ReadAsPacked() { return PackedGuidReader(*this); }
|
||||
|
||||
void Set(uint64 guid) { m_guid = guid; }
|
||||
void Clear() { m_guid = 0; }
|
||||
|
||||
PackedGuid WriteAsPacked() const;
|
||||
public: // accessors
|
||||
uint64 GetRawValue() const { return m_guid; }
|
||||
HighGuid GetHigh() const
|
||||
{
|
||||
HighGuid high = HighGuid((m_guid >> 48) & 0xFFFF);
|
||||
return HighGuid(IsLargeHigh(high) ? high :
|
||||
(m_guid >> 52) & 0xFFF);
|
||||
}
|
||||
uint32 GetEntry() const { return HasEntry() ? uint32((m_guid >> 32) & UI64LIT(0xFFFF)) : 0; }
|
||||
uint32 GetCounter() const
|
||||
{
|
||||
return HasEntry()
|
||||
? uint32(m_guid & UI64LIT(0x00000000FFFFFFFF))
|
||||
: uint32(m_guid & UI64LIT(0x00000000FFFFFFFF)); // TODO: switch to 40 bits, but this needs rewrite code to use uint64 instead uint32
|
||||
}
|
||||
|
||||
static uint32 GetMaxCounter(HighGuid high)
|
||||
{
|
||||
return HasEntry(high)
|
||||
? uint32(0xFFFFFFFF)
|
||||
: uint32(0xFFFFFFFF); // TODO: switch to 40 bits
|
||||
}
|
||||
|
||||
uint32 GetMaxCounter() const { return GetMaxCounter(GetHigh()); }
|
||||
|
||||
bool IsEmpty() const { return m_guid == 0; }
|
||||
bool IsCreature() const { return GetHigh() == HIGHGUID_UNIT; }
|
||||
bool IsPet() const { return GetHigh() == HIGHGUID_PET; }
|
||||
bool IsVehicle() const { return GetHigh() == HIGHGUID_VEHICLE; }
|
||||
bool IsCreatureOrPet() const { return IsCreature() || IsPet(); }
|
||||
bool IsCreatureOrVehicle() const { return IsCreature() || IsVehicle(); }
|
||||
bool IsAnyTypeCreature() const { return IsCreature() || IsPet() || IsVehicle(); }
|
||||
bool IsPlayer() const { return !IsEmpty() && GetHigh() == HIGHGUID_PLAYER; }
|
||||
bool IsUnit() const { return IsAnyTypeCreature() || IsPlayer(); }
|
||||
bool IsItem() const { return GetHigh() == HIGHGUID_ITEM; }
|
||||
bool IsGameObject() const { return GetHigh() == HIGHGUID_GAMEOBJECT; }
|
||||
bool IsDynamicObject() const { return GetHigh() == HIGHGUID_DYNAMICOBJECT; }
|
||||
bool IsCorpse() const { return GetHigh() == HIGHGUID_CORPSE; }
|
||||
bool IsTransport() const { return GetHigh() == HIGHGUID_TRANSPORT; }
|
||||
bool IsMOTransport() const { return GetHigh() == HIGHGUID_MO_TRANSPORT; }
|
||||
bool IsInstance() const { return GetHigh() == HIGHGUID_INSTANCE; }
|
||||
bool IsGroup() const { return GetHigh() == HIGHGUID_GROUP; }
|
||||
bool IsBattleGround() const { return GetHigh() == HIGHGUID_BATTLEGROUND; }
|
||||
bool IsGuild() const { return GetHigh() == HIGHGUID_GUILD; }
|
||||
|
||||
static TypeID GetTypeId(HighGuid high)
|
||||
{
|
||||
switch (high)
|
||||
{
|
||||
case HIGHGUID_ITEM: return TYPEID_ITEM;
|
||||
// case HIGHGUID_CONTAINER: return TYPEID_CONTAINER; HIGHGUID_CONTAINER==HIGHGUID_ITEM currently
|
||||
case HIGHGUID_UNIT: return TYPEID_UNIT;
|
||||
case HIGHGUID_PET: return TYPEID_UNIT;
|
||||
case HIGHGUID_PLAYER: return TYPEID_PLAYER;
|
||||
case HIGHGUID_GAMEOBJECT: return TYPEID_GAMEOBJECT;
|
||||
case HIGHGUID_DYNAMICOBJECT:return TYPEID_DYNAMICOBJECT;
|
||||
case HIGHGUID_CORPSE: return TYPEID_CORPSE;
|
||||
case HIGHGUID_MO_TRANSPORT: return TYPEID_GAMEOBJECT;
|
||||
case HIGHGUID_VEHICLE: return TYPEID_UNIT;
|
||||
// unknown
|
||||
case HIGHGUID_INSTANCE:
|
||||
case HIGHGUID_GROUP:
|
||||
default: return TYPEID_OBJECT;
|
||||
}
|
||||
}
|
||||
|
||||
TypeID GetTypeId() const { return GetTypeId(GetHigh()); }
|
||||
|
||||
bool operator!() const { return IsEmpty(); }
|
||||
bool operator== (ObjectGuid const& guid) const { return GetRawValue() == guid.GetRawValue(); }
|
||||
bool operator!= (ObjectGuid const& guid) const { return GetRawValue() != guid.GetRawValue(); }
|
||||
bool operator< (ObjectGuid const& guid) const { return GetRawValue() < guid.GetRawValue(); }
|
||||
|
||||
uint8& operator[] (uint8 index)
|
||||
{
|
||||
MANGOS_ASSERT(index < NUM_GUID_BYTES);
|
||||
|
||||
#if MANGOS_ENDIAN == MANGOS_LITTLEENDIAN
|
||||
return m_guidBytes[index];
|
||||
#else
|
||||
return m_guidBytes[NUM_GUID_BYTES - 1 - index];
|
||||
#endif
|
||||
}
|
||||
|
||||
uint8 const& operator[] (uint8 index) const
|
||||
{
|
||||
MANGOS_ASSERT(index < NUM_GUID_BYTES);
|
||||
|
||||
#if MANGOS_ENDIAN == MANGOS_LITTLEENDIAN
|
||||
return m_guidBytes[index];
|
||||
#else
|
||||
return m_guidBytes[NUM_GUID_BYTES - 1 - index];
|
||||
#endif
|
||||
}
|
||||
|
||||
public: // accessors - for debug
|
||||
static char const* GetTypeName(HighGuid high);
|
||||
char const* GetTypeName() const { return !IsEmpty() ? GetTypeName(GetHigh()) : "None"; }
|
||||
std::string GetString() const;
|
||||
|
||||
private: // internal functions
|
||||
static bool HasEntry(HighGuid high)
|
||||
{
|
||||
switch (high)
|
||||
{
|
||||
case HIGHGUID_ITEM:
|
||||
case HIGHGUID_PLAYER:
|
||||
case HIGHGUID_DYNAMICOBJECT:
|
||||
case HIGHGUID_CORPSE:
|
||||
case HIGHGUID_MO_TRANSPORT:
|
||||
case HIGHGUID_INSTANCE:
|
||||
case HIGHGUID_GROUP:
|
||||
return false;
|
||||
case HIGHGUID_GAMEOBJECT:
|
||||
case HIGHGUID_TRANSPORT:
|
||||
case HIGHGUID_UNIT:
|
||||
case HIGHGUID_PET:
|
||||
case HIGHGUID_VEHICLE:
|
||||
case HIGHGUID_BATTLEGROUND:
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool HasEntry() const { return HasEntry(GetHigh()); }
|
||||
|
||||
static bool IsLargeHigh(HighGuid high)
|
||||
{
|
||||
switch(high)
|
||||
{
|
||||
case HIGHGUID_GUILD:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsLargeHigh() const { return IsLargeHigh(GetHigh()); }
|
||||
|
||||
private: // fields
|
||||
union
|
||||
{
|
||||
uint64 m_guid;
|
||||
uint8 m_guidBytes[NUM_GUID_BYTES];
|
||||
};
|
||||
};
|
||||
|
||||
// Some Shared defines
|
||||
typedef std::set<ObjectGuid> GuidSet;
|
||||
typedef std::list<ObjectGuid> GuidList;
|
||||
typedef std::vector<ObjectGuid> GuidVector;
|
||||
|
||||
// minimum buffer size for packed guid is 9 bytes
|
||||
#define PACKED_GUID_MIN_BUFFER_SIZE 9
|
||||
|
||||
class PackedGuid
|
||||
{
|
||||
friend ByteBuffer& operator<< (ByteBuffer& buf, PackedGuid const& guid);
|
||||
|
||||
public: // constructors
|
||||
explicit PackedGuid() : m_packedGuid(PACKED_GUID_MIN_BUFFER_SIZE) { m_packedGuid.appendPackGUID(0); }
|
||||
explicit PackedGuid(uint64 const& guid) : m_packedGuid(PACKED_GUID_MIN_BUFFER_SIZE) { m_packedGuid.appendPackGUID(guid); }
|
||||
explicit PackedGuid(ObjectGuid const& guid) : m_packedGuid(PACKED_GUID_MIN_BUFFER_SIZE) { m_packedGuid.appendPackGUID(guid.GetRawValue()); }
|
||||
|
||||
public: // modifiers
|
||||
void Set(uint64 const& guid) { m_packedGuid.wpos(0); m_packedGuid.appendPackGUID(guid); }
|
||||
void Set(ObjectGuid const& guid) { m_packedGuid.wpos(0); m_packedGuid.appendPackGUID(guid.GetRawValue()); }
|
||||
|
||||
public: // accessors
|
||||
size_t size() const { return m_packedGuid.size(); }
|
||||
|
||||
private: // fields
|
||||
ByteBuffer m_packedGuid;
|
||||
};
|
||||
|
||||
template<HighGuid high>
|
||||
class ObjectGuidGenerator
|
||||
{
|
||||
public: // constructors
|
||||
explicit ObjectGuidGenerator(uint32 start = 1) : m_nextGuid(start) {}
|
||||
|
||||
public: // modifiers
|
||||
void Set(uint32 val) { m_nextGuid = val; }
|
||||
uint32 Generate();
|
||||
|
||||
public: // accessors
|
||||
uint32 GetNextAfterMaxUsed() const { return m_nextGuid; }
|
||||
|
||||
private: // fields
|
||||
uint32 m_nextGuid;
|
||||
};
|
||||
|
||||
ByteBuffer& operator<< (ByteBuffer& buf, ObjectGuid const& guid);
|
||||
ByteBuffer& operator>> (ByteBuffer& buf, ObjectGuid& guid);
|
||||
|
||||
ByteBuffer& operator<< (ByteBuffer& buf, PackedGuid const& guid);
|
||||
ByteBuffer& operator>> (ByteBuffer& buf, PackedGuidReader const& guid);
|
||||
|
||||
inline PackedGuid ObjectGuid::WriteAsPacked() const { return PackedGuid(*this); }
|
||||
|
||||
#if defined(__FreeBSD__) && defined(__clang__)
|
||||
namespace std{
|
||||
# else
|
||||
HASH_NAMESPACE_START
|
||||
#endif
|
||||
template<>
|
||||
class hash<ObjectGuid>
|
||||
{
|
||||
public:
|
||||
|
||||
size_t operator()(ObjectGuid const& key) const
|
||||
{
|
||||
return hash<uint64>()(key.GetRawValue());
|
||||
}
|
||||
};
|
||||
#if defined(__FreeBSD__) && defined(__clang__)
|
||||
}
|
||||
# else
|
||||
HASH_NAMESPACE_END
|
||||
#endif
|
||||
|
||||
|
||||
#define DEFINE_READGUIDMASK(T1, T2) template <T1> \
|
||||
void ByteBuffer::ReadGuidMask(ObjectGuid& guid) \
|
||||
{ \
|
||||
uint8 maskArr[] = { T2 }; \
|
||||
for (uint8 i = 0; i < countof(maskArr); ++i) \
|
||||
guid[maskArr[i]] = ReadBit(); \
|
||||
}
|
||||
|
||||
#define DEFINE_WRITEGUIDMASK(T1, T2) template <T1> \
|
||||
void ByteBuffer::WriteGuidMask(ObjectGuid guid) \
|
||||
{ \
|
||||
uint8 maskArr[] = { T2 }; \
|
||||
for (uint8 i = 0; i < countof(maskArr); ++i) \
|
||||
WriteBit(guid[maskArr[i]]); \
|
||||
}
|
||||
|
||||
#define DEFINE_READGUIDBYTES(T1, T2) template <T1> \
|
||||
void ByteBuffer::ReadGuidBytes(ObjectGuid& guid) \
|
||||
{ \
|
||||
uint8 maskArr[] = { T2 }; \
|
||||
for (uint8 i = 0; i < countof(maskArr); ++i) \
|
||||
if (guid[maskArr[i]] != 0) \
|
||||
guid[maskArr[i]] ^= read<uint8>(); \
|
||||
}
|
||||
|
||||
#define DEFINE_WRITEGUIDBYTES(T1, T2) template <T1> \
|
||||
void ByteBuffer::WriteGuidBytes(ObjectGuid guid) \
|
||||
{ \
|
||||
uint8 maskArr[] = { T2 }; \
|
||||
for (uint8 i = 0; i < countof(maskArr); ++i) \
|
||||
if (guid[maskArr[i]] != 0) \
|
||||
(*this) << uint8(guid[maskArr[i]] ^ 1); \
|
||||
}
|
||||
|
||||
DEFINE_READGUIDMASK(BITS_1, BIT_VALS_1)
|
||||
DEFINE_READGUIDMASK(BITS_2, BIT_VALS_2)
|
||||
DEFINE_READGUIDMASK(BITS_3, BIT_VALS_3)
|
||||
DEFINE_READGUIDMASK(BITS_4, BIT_VALS_4)
|
||||
DEFINE_READGUIDMASK(BITS_5, BIT_VALS_5)
|
||||
DEFINE_READGUIDMASK(BITS_6, BIT_VALS_6)
|
||||
DEFINE_READGUIDMASK(BITS_7, BIT_VALS_7)
|
||||
DEFINE_READGUIDMASK(BITS_8, BIT_VALS_8)
|
||||
|
||||
DEFINE_WRITEGUIDMASK(BITS_1, BIT_VALS_1)
|
||||
DEFINE_WRITEGUIDMASK(BITS_2, BIT_VALS_2)
|
||||
DEFINE_WRITEGUIDMASK(BITS_3, BIT_VALS_3)
|
||||
DEFINE_WRITEGUIDMASK(BITS_4, BIT_VALS_4)
|
||||
DEFINE_WRITEGUIDMASK(BITS_5, BIT_VALS_5)
|
||||
DEFINE_WRITEGUIDMASK(BITS_6, BIT_VALS_6)
|
||||
DEFINE_WRITEGUIDMASK(BITS_7, BIT_VALS_7)
|
||||
DEFINE_WRITEGUIDMASK(BITS_8, BIT_VALS_8)
|
||||
|
||||
DEFINE_READGUIDBYTES(BITS_1, BIT_VALS_1)
|
||||
DEFINE_READGUIDBYTES(BITS_2, BIT_VALS_2)
|
||||
DEFINE_READGUIDBYTES(BITS_3, BIT_VALS_3)
|
||||
DEFINE_READGUIDBYTES(BITS_4, BIT_VALS_4)
|
||||
DEFINE_READGUIDBYTES(BITS_5, BIT_VALS_5)
|
||||
DEFINE_READGUIDBYTES(BITS_6, BIT_VALS_6)
|
||||
DEFINE_READGUIDBYTES(BITS_7, BIT_VALS_7)
|
||||
DEFINE_READGUIDBYTES(BITS_8, BIT_VALS_8)
|
||||
|
||||
DEFINE_WRITEGUIDBYTES(BITS_1, BIT_VALS_1)
|
||||
DEFINE_WRITEGUIDBYTES(BITS_2, BIT_VALS_2)
|
||||
DEFINE_WRITEGUIDBYTES(BITS_3, BIT_VALS_3)
|
||||
DEFINE_WRITEGUIDBYTES(BITS_4, BIT_VALS_4)
|
||||
DEFINE_WRITEGUIDBYTES(BITS_5, BIT_VALS_5)
|
||||
DEFINE_WRITEGUIDBYTES(BITS_6, BIT_VALS_6)
|
||||
DEFINE_WRITEGUIDBYTES(BITS_7, BIT_VALS_7)
|
||||
DEFINE_WRITEGUIDBYTES(BITS_8, BIT_VALS_8)
|
||||
|
||||
#endif
|
||||
10199
src/game/Object/ObjectMgr.cpp
Normal file
10199
src/game/Object/ObjectMgr.cpp
Normal file
File diff suppressed because it is too large
Load diff
1312
src/game/Object/ObjectMgr.h
Normal file
1312
src/game/Object/ObjectMgr.h
Normal file
File diff suppressed because it is too large
Load diff
238
src/game/Object/ObjectPosSelector.cpp
Normal file
238
src/game/Object/ObjectPosSelector.cpp
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include "ObjectPosSelector.h"
|
||||
#include "Object.h"
|
||||
|
||||
// The bigger this value, the more space npcs require around their target
|
||||
#define OCCUPY_POS_ANGLE_ATAN_FACTOR 1.8f
|
||||
|
||||
ObjectPosSelector::ObjectPosSelector(float x, float y, float dist, float searchedForSize, WorldObject const* searchPosFor) :
|
||||
m_centerX(x), m_centerY(y), m_searcherDist(dist), m_searchPosFor(searchPosFor)
|
||||
{
|
||||
// if size == 0, m_anglestep will become 0 -> freeze
|
||||
if (searchedForSize == 0.0f)
|
||||
searchedForSize = DEFAULT_WORLD_OBJECT_SIZE;
|
||||
|
||||
// undefined behaviour
|
||||
if (m_searcherDist == 0.0f)
|
||||
m_searcherDist = DEFAULT_WORLD_OBJECT_SIZE;
|
||||
|
||||
m_searchedForReqHAngle = atan(OCCUPY_POS_ANGLE_ATAN_FACTOR * searchedForSize / m_searcherDist);
|
||||
|
||||
// Really init in InitilizeAngle
|
||||
m_nextUsedAreaItr[USED_POS_PLUS] = m_UsedAreaLists[USED_POS_PLUS].begin();
|
||||
m_nextUsedAreaItr[USED_POS_MINUS] = m_UsedAreaLists[USED_POS_MINUS].begin();
|
||||
m_stepAngle[USED_POS_PLUS] = 0.0f;
|
||||
m_stepAngle[USED_POS_MINUS] = 0.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add used area (circle) near target object excluded from possible searcher position
|
||||
*
|
||||
*
|
||||
* @param obj Object that occupies area
|
||||
* @param angle Angle of used circle center point from target-searcher line
|
||||
* @param dist Distance from target object center point to used circle center point
|
||||
*
|
||||
* Used circles data stored as projections to searcher dist size circle as angle coordinate and half angle size
|
||||
*/
|
||||
void ObjectPosSelector::AddUsedArea(WorldObject const* obj, float angle, float dist)
|
||||
{
|
||||
MANGOS_ASSERT(obj);
|
||||
|
||||
// skip some unexpected results.
|
||||
if (dist == 0.0f)
|
||||
return;
|
||||
|
||||
// (half) angle that obj occupies
|
||||
float sr_angle = atan(OCCUPY_POS_ANGLE_ATAN_FACTOR * obj->GetObjectBoundingRadius() / dist);
|
||||
|
||||
if (angle >= 0)
|
||||
m_UsedAreaLists[USED_POS_PLUS].insert(UsedArea(angle, OccupiedArea(sr_angle, obj)));
|
||||
else
|
||||
m_UsedAreaLists[USED_POS_MINUS].insert(UsedArea(-angle, OccupiedArea(sr_angle, obj)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check searcher circle not intercepting with used circle
|
||||
*
|
||||
* @param usedArea Used circle as projection to searcher distance circle in angles form
|
||||
* @param side Side of used circle
|
||||
* @param angle Checked angle
|
||||
*
|
||||
* @return true, if used circle not intercepted with searcher circle in terms projection angles
|
||||
*/
|
||||
bool ObjectPosSelector::CheckAngle(UsedArea const& usedArea, UsedAreaSide side, float angle) const
|
||||
{
|
||||
float used_offset = usedArea.second.angleOffset;
|
||||
float used_angle = usedArea.first * SignOf(side);
|
||||
// check first left/right used angles if exists
|
||||
return fabs(used_angle - angle) > used_offset || (m_searchPosFor && usedArea.second.occupyingObj == m_searchPosFor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check original (0.0f) angle fit to existed used area excludes
|
||||
*
|
||||
* @return true, if 0.0f angle with m_searcher_halfangle*2 angle size not intercept with used circles
|
||||
*/
|
||||
bool ObjectPosSelector::CheckOriginalAngle() const
|
||||
{
|
||||
// check first left/right used angles if exists
|
||||
return (m_UsedAreaLists[USED_POS_PLUS].empty() || CheckAngle(*m_UsedAreaLists[USED_POS_PLUS].begin(), USED_POS_PLUS, 0.0f)) &&
|
||||
(m_UsedAreaLists[USED_POS_MINUS].empty() || CheckAngle(*m_UsedAreaLists[USED_POS_MINUS].begin(), USED_POS_MINUS, 0.0f));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize data for search angles starting from first possible angle at both sides
|
||||
*/
|
||||
void ObjectPosSelector::InitializeAngle()
|
||||
{
|
||||
InitializeAngle(USED_POS_PLUS);
|
||||
InitializeAngle(USED_POS_MINUS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize data for search angles starting from first possible angle at side
|
||||
*/
|
||||
void ObjectPosSelector::InitializeAngle(UsedAreaSide side)
|
||||
{
|
||||
m_nextUsedAreaItr[side] = m_UsedAreaLists[side].begin();
|
||||
// if another side not alow use 0.0f angle calculate possible value in 0..m_searchedForReqHAngle range
|
||||
if (!m_UsedAreaLists[~side].empty())
|
||||
{
|
||||
UsedArea const& otherArea = *m_UsedAreaLists[~side].begin();
|
||||
m_stepAngle[side] = std::max(m_searchedForReqHAngle + otherArea.second.angleOffset - otherArea.first, 0.0f);
|
||||
}
|
||||
else // Other side empty. start from 0
|
||||
m_stepAngle[side] = 0.0f;
|
||||
|
||||
// As m_stepAngle will be incremented first in ::NextSideAngle
|
||||
m_stepAngle[side] -= m_searchedForReqHAngle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find next angle in free area
|
||||
*
|
||||
* @param angle Return at success found angle
|
||||
*
|
||||
* @return true, if angle found
|
||||
*/
|
||||
bool ObjectPosSelector::NextAngle(float& angle)
|
||||
{
|
||||
// loop until both side fail and leave 0..PI
|
||||
for (;;)
|
||||
{
|
||||
// ++ direction less updated
|
||||
if (m_stepAngle[USED_POS_PLUS] < M_PI_F && m_stepAngle[USED_POS_PLUS] <= m_stepAngle[USED_POS_MINUS])
|
||||
{
|
||||
if (NextSideAngle(USED_POS_PLUS, angle))
|
||||
return true;
|
||||
}
|
||||
// -- direction less updated
|
||||
else if (m_stepAngle[USED_POS_MINUS] < M_PI_F)
|
||||
{
|
||||
if (NextSideAngle(USED_POS_MINUS, angle))
|
||||
return true;
|
||||
}
|
||||
// both sides finishes
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
// no angles
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find next angle at side
|
||||
*
|
||||
* @param side Side of angle
|
||||
* @param angle Return at success found angle
|
||||
*
|
||||
* @return true, if angle found
|
||||
*
|
||||
*/
|
||||
bool ObjectPosSelector::NextSideAngle(UsedAreaSide side, float& angle)
|
||||
{
|
||||
// next possible angle
|
||||
m_stepAngle[side] += (m_searchedForReqHAngle + 0.01);
|
||||
|
||||
// prevent jump to another side
|
||||
if (m_stepAngle[side] > M_PI_F)
|
||||
return false;
|
||||
|
||||
// no used area anymore on this side
|
||||
if (m_nextUsedAreaItr[side] == m_UsedAreaLists[side].end())
|
||||
{
|
||||
angle = m_stepAngle[side] * SignOf(side);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Already occupied and no better found
|
||||
if ((m_searchPosFor && m_nextUsedAreaItr[side]->second.occupyingObj == m_searchPosFor) ||
|
||||
// Next occupied is too far away
|
||||
(m_stepAngle[side] + m_searchedForReqHAngle < m_nextUsedAreaItr[side]->first - m_nextUsedAreaItr[side]->second.angleOffset))
|
||||
{
|
||||
angle = m_stepAngle[side] * SignOf(side);
|
||||
return true;
|
||||
}
|
||||
|
||||
// angle set at first possible pos after passed m_nextUsedAreaItr
|
||||
m_stepAngle[side] = m_nextUsedAreaItr[side]->first + m_nextUsedAreaItr[side]->second.angleOffset;
|
||||
|
||||
++m_nextUsedAreaItr[side];
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find next angle in used area, that used if no angle found in free area with LoS
|
||||
*
|
||||
* @param angle Return at success found angle
|
||||
*
|
||||
* @return true, if angle found
|
||||
*/
|
||||
bool ObjectPosSelector::NextUsedAngle(float& angle)
|
||||
{
|
||||
if (m_nextUsedAreaItr[USED_POS_PLUS] == m_UsedAreaLists[USED_POS_PLUS].end() &&
|
||||
m_nextUsedAreaItr[USED_POS_MINUS] == m_UsedAreaLists[USED_POS_MINUS].end())
|
||||
return false;
|
||||
|
||||
// ++ direction less updated
|
||||
if (m_nextUsedAreaItr[USED_POS_PLUS] != m_UsedAreaLists[USED_POS_PLUS].end() &&
|
||||
(m_nextUsedAreaItr[USED_POS_MINUS] == m_UsedAreaLists[USED_POS_MINUS].end() ||
|
||||
m_nextUsedAreaItr[USED_POS_PLUS]->first <= m_nextUsedAreaItr[USED_POS_MINUS]->first))
|
||||
{
|
||||
angle = m_nextUsedAreaItr[USED_POS_PLUS]->first * SignOf(USED_POS_PLUS);
|
||||
++m_nextUsedAreaItr[USED_POS_PLUS];
|
||||
}
|
||||
else
|
||||
{
|
||||
angle = m_nextUsedAreaItr[USED_POS_MINUS]->first * SignOf(USED_POS_MINUS);
|
||||
++m_nextUsedAreaItr[USED_POS_MINUS];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
86
src/game/Object/ObjectPosSelector.h
Normal file
86
src/game/Object/ObjectPosSelector.h
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef _OBJECT_POS_SELECTOR_H
|
||||
#define _OBJECT_POS_SELECTOR_H
|
||||
|
||||
#include<Common.h>
|
||||
|
||||
#include<map>
|
||||
|
||||
class WorldObject;
|
||||
|
||||
enum UsedAreaSide { USED_POS_PLUS, USED_POS_MINUS };
|
||||
|
||||
inline UsedAreaSide operator ~(UsedAreaSide side)
|
||||
{
|
||||
return side == USED_POS_PLUS ? USED_POS_MINUS : USED_POS_PLUS;
|
||||
}
|
||||
|
||||
inline float SignOf(UsedAreaSide side)
|
||||
{
|
||||
return side == USED_POS_PLUS ? 1.0f : -1.0f;
|
||||
}
|
||||
|
||||
struct ObjectPosSelector
|
||||
{
|
||||
struct OccupiedArea
|
||||
{
|
||||
OccupiedArea(float _angleOffset, WorldObject const* obj) : angleOffset(_angleOffset), occupyingObj(obj) {}
|
||||
float angleOffset;
|
||||
WorldObject const* occupyingObj;
|
||||
};
|
||||
// angle pos -> OccupiedArea
|
||||
typedef std::multimap<float, OccupiedArea> UsedAreaList;
|
||||
typedef UsedAreaList::value_type UsedArea;
|
||||
|
||||
ObjectPosSelector(float x, float y, float dist, float searchedForSize, WorldObject const* searchPosFor);
|
||||
|
||||
void AddUsedArea(WorldObject const* obj, float angle, float dist);
|
||||
|
||||
bool CheckOriginalAngle() const;
|
||||
|
||||
void InitializeAngle();
|
||||
|
||||
bool NextAngle(float& angle);
|
||||
bool NextUsedAngle(float& angle);
|
||||
|
||||
bool CheckAngle(UsedArea const& usedArea, UsedAreaSide side, float angle) const;
|
||||
void InitializeAngle(UsedAreaSide side);
|
||||
bool NextSideAngle(UsedAreaSide side, float& angle);
|
||||
|
||||
float m_centerX;
|
||||
float m_centerY;
|
||||
float m_searcherDist; // distance for searching pos
|
||||
float m_searchedForReqHAngle; // angle size/2 of searcher object (at dist distance)
|
||||
|
||||
UsedAreaList m_UsedAreaLists[2]; // list left/right side used angles (with angle size)
|
||||
|
||||
UsedAreaList::const_iterator m_nextUsedAreaItr[2]; // next used used areas for check at left/right side, possible angles selected in range m_smallStepAngle..m_nextUsedAreaItr
|
||||
|
||||
float m_stepAngle[2]; // current checked angle position at sides (less m_nextUsedArea), positive value
|
||||
|
||||
WorldObject const* m_searchPosFor; // For whom a position is searched (can be NULL)
|
||||
};
|
||||
#endif
|
||||
2114
src/game/Object/Pet.cpp
Normal file
2114
src/game/Object/Pet.cpp
Normal file
File diff suppressed because it is too large
Load diff
279
src/game/Object/Pet.h
Normal file
279
src/game/Object/Pet.h
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOSSERVER_PET_H
|
||||
#define MANGOSSERVER_PET_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Creature.h"
|
||||
#include "Unit.h"
|
||||
|
||||
enum PetType
|
||||
{
|
||||
SUMMON_PET = 0,
|
||||
HUNTER_PET = 1,
|
||||
GUARDIAN_PET = 2,
|
||||
MINI_PET = 3,
|
||||
PROTECTOR_PET = 4, // work as defensive guardian with mini pet suffix in name
|
||||
MAX_PET_TYPE = 5
|
||||
};
|
||||
|
||||
#define MAX_PET_STABLES 4
|
||||
|
||||
// stored in character_pet.slot
|
||||
enum PetSaveMode
|
||||
{
|
||||
PET_SAVE_AS_DELETED = -1, // not saved in fact
|
||||
PET_SAVE_AS_CURRENT = 0, // in current slot (with player)
|
||||
PET_SAVE_FIRST_STABLE_SLOT = 1,
|
||||
PET_SAVE_LAST_STABLE_SLOT = MAX_PET_STABLES, // last in DB stable slot index (including), all higher have same meaning as PET_SAVE_NOT_IN_SLOT
|
||||
PET_SAVE_NOT_IN_SLOT = 100, // for avoid conflict with stable size grow will use 100
|
||||
PET_SAVE_REAGENTS = 101 // PET_SAVE_NOT_IN_SLOT with reagents return
|
||||
};
|
||||
|
||||
// There might be a lot more
|
||||
enum PetModeFlags
|
||||
{
|
||||
PET_MODE_UNKNOWN_0 = 0x0000001,
|
||||
PET_MODE_UNKNOWN_2 = 0x0000100,
|
||||
PET_MODE_DISABLE_ACTIONS = 0x8000000,
|
||||
|
||||
// autoset in client at summon
|
||||
PET_MODE_DEFAULT = PET_MODE_UNKNOWN_0 | PET_MODE_UNKNOWN_2,
|
||||
};
|
||||
|
||||
enum PetSpellState
|
||||
{
|
||||
PETSPELL_UNCHANGED = 0,
|
||||
PETSPELL_CHANGED = 1,
|
||||
PETSPELL_NEW = 2,
|
||||
PETSPELL_REMOVED = 3
|
||||
};
|
||||
|
||||
enum PetSpellType
|
||||
{
|
||||
PETSPELL_NORMAL = 0,
|
||||
PETSPELL_FAMILY = 1,
|
||||
};
|
||||
|
||||
struct PetSpell
|
||||
{
|
||||
uint8 active; // use instead enum (not good use *uint8* limited enum in case when value in enum not possitive in *int8*)
|
||||
|
||||
PetSpellState state : 8;
|
||||
PetSpellType type : 8;
|
||||
};
|
||||
|
||||
enum ActionFeedback
|
||||
{
|
||||
FEEDBACK_NONE = 0,
|
||||
FEEDBACK_PET_DEAD = 1,
|
||||
FEEDBACK_NOTHING_TO_ATT = 2,
|
||||
FEEDBACK_CANT_ATT_TARGET = 3,
|
||||
FEEDBACK_NO_PATH = 4,
|
||||
};
|
||||
|
||||
enum PetTalk
|
||||
{
|
||||
PET_TALK_SPECIAL_SPELL = 0,
|
||||
PET_TALK_ATTACK = 1
|
||||
};
|
||||
|
||||
enum PetNameInvalidReason
|
||||
{
|
||||
// custom, not send
|
||||
PET_NAME_SUCCESS = 0,
|
||||
|
||||
PET_NAME_INVALID = 1,
|
||||
PET_NAME_NO_NAME = 2,
|
||||
PET_NAME_TOO_SHORT = 3,
|
||||
PET_NAME_TOO_LONG = 4,
|
||||
PET_NAME_MIXED_LANGUAGES = 6,
|
||||
PET_NAME_PROFANE = 7,
|
||||
PET_NAME_RESERVED = 8,
|
||||
PET_NAME_THREE_CONSECUTIVE = 11,
|
||||
PET_NAME_INVALID_SPACE = 12,
|
||||
PET_NAME_CONSECUTIVE_SPACES = 13,
|
||||
PET_NAME_RUSSIAN_CONSECUTIVE_SILENT_CHARACTERS = 14,
|
||||
PET_NAME_RUSSIAN_SILENT_CHARACTER_AT_BEGINNING_OR_END = 15,
|
||||
PET_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME = 16
|
||||
};
|
||||
|
||||
typedef UNORDERED_MAP<uint32, PetSpell> PetSpellMap;
|
||||
typedef std::vector<uint32> AutoSpellList;
|
||||
|
||||
#define HAPPINESS_LEVEL_SIZE 333000
|
||||
|
||||
#define ACTIVE_SPELLS_MAX 4
|
||||
|
||||
#define PET_FOLLOW_DIST 1.0f
|
||||
#define PET_FOLLOW_ANGLE (M_PI_F/2.0f)
|
||||
|
||||
class Player;
|
||||
|
||||
class MANGOS_DLL_SPEC Pet : public Creature
|
||||
{
|
||||
public:
|
||||
explicit Pet(PetType type = MAX_PET_TYPE);
|
||||
virtual ~Pet();
|
||||
|
||||
void AddToWorld() override;
|
||||
void RemoveFromWorld() override;
|
||||
|
||||
PetType getPetType() const { return m_petType; }
|
||||
void setPetType(PetType type) { m_petType = type; }
|
||||
bool isControlled() const { return getPetType() == SUMMON_PET || getPetType() == HUNTER_PET; }
|
||||
bool isTemporarySummoned() const { return m_duration > 0; }
|
||||
|
||||
bool IsPermanentPetFor(Player* owner); // pet have tab in character windows and set UNIT_FIELD_PETNUMBER
|
||||
|
||||
bool Create(uint32 guidlow, CreatureCreatePos& cPos, CreatureInfo const* cinfo, uint32 pet_number);
|
||||
bool CreateBaseAtCreature(Creature* creature);
|
||||
bool LoadPetFromDB(Player* owner, uint32 petentry = 0, uint32 petnumber = 0, bool current = false);
|
||||
void SavePetToDB(PetSaveMode mode);
|
||||
void Unsummon(PetSaveMode mode, Unit* owner = NULL);
|
||||
static void DeleteFromDB(uint32 guidlow, bool separate_transaction = true);
|
||||
|
||||
void SetDeathState(DeathState s) override; // overwrite virtual Creature::SetDeathState and Unit::SetDeathState
|
||||
void Update(uint32 update_diff, uint32 diff) override; // overwrite virtual Creature::Update and Unit::Update
|
||||
|
||||
uint8 GetPetAutoSpellSize() const { return m_autospells.size(); }
|
||||
uint32 GetPetAutoSpellOnPos(uint8 pos) const override
|
||||
{
|
||||
if (pos >= m_autospells.size())
|
||||
return 0;
|
||||
else
|
||||
return m_autospells[pos];
|
||||
}
|
||||
|
||||
bool CanSwim() const override
|
||||
{
|
||||
Unit const* owner = GetOwner();
|
||||
if (owner)
|
||||
return owner->GetTypeId() == TYPEID_PLAYER ? true : ((Creature const*)owner)->CanSwim();
|
||||
else
|
||||
return Creature::CanSwim();
|
||||
}
|
||||
|
||||
void RegenerateAll(uint32 update_diff) override; // overwrite Creature::RegenerateAll
|
||||
void Regenerate(Powers power);
|
||||
void GivePetXP(uint32 xp);
|
||||
void GivePetLevel(uint32 level);
|
||||
void SynchronizeLevelWithOwner();
|
||||
bool InitStatsForLevel(uint32 level, Unit* owner = NULL);
|
||||
bool HaveInDiet(ItemPrototype const* item) const;
|
||||
uint32 GetCurrentFoodBenefitLevel(uint32 itemlevel);
|
||||
void SetDuration(int32 dur) { m_duration = dur; }
|
||||
|
||||
int32 GetBonusDamage() { return m_bonusdamage; }
|
||||
void SetBonusDamage(int32 damage) { m_bonusdamage = damage; }
|
||||
|
||||
bool UpdateStats(Stats stat) override;
|
||||
bool UpdateAllStats() override;
|
||||
void UpdateResistances(uint32 school) override;
|
||||
void UpdateArmor() override;
|
||||
void UpdateMaxHealth() override;
|
||||
void UpdateMaxPower(Powers power) override;
|
||||
void UpdateAttackPowerAndDamage(bool ranged = false) override;
|
||||
void UpdateDamagePhysical(WeaponAttackType attType) override;
|
||||
|
||||
bool CanTakeMoreActiveSpells(uint32 SpellIconID);
|
||||
void ToggleAutocast(uint32 spellid, bool apply);
|
||||
|
||||
void ApplyModeFlags(PetModeFlags mode, bool apply);
|
||||
PetModeFlags GetModeFlags() const { return m_petModeFlags; }
|
||||
|
||||
bool HasSpell(uint32 spell) const override;
|
||||
|
||||
void LearnPetPassives();
|
||||
void CastPetAuras(bool current);
|
||||
void CastPetAura(PetAura const* aura);
|
||||
|
||||
void _LoadSpellCooldowns();
|
||||
void _SaveSpellCooldowns();
|
||||
void _LoadAuras(uint32 timediff);
|
||||
void _SaveAuras();
|
||||
void _LoadSpells();
|
||||
void _SaveSpells();
|
||||
|
||||
bool addSpell(uint32 spell_id, ActiveStates active = ACT_DECIDE, PetSpellState state = PETSPELL_NEW, PetSpellType type = PETSPELL_NORMAL);
|
||||
bool learnSpell(uint32 spell_id);
|
||||
void learnSpellHighRank(uint32 spellid);
|
||||
void InitLevelupSpellsForLevel();
|
||||
bool unlearnSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true);
|
||||
bool removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true);
|
||||
void CleanupActionBar();
|
||||
|
||||
PetSpellMap m_spells;
|
||||
AutoSpellList m_autospells;
|
||||
|
||||
void InitPetCreateSpells();
|
||||
|
||||
bool resetTalents(bool no_cost = false);
|
||||
static void resetTalentsForAllPetsOf(Player* owner, Pet* online_pet = NULL);
|
||||
uint32 resetTalentsCost() const;
|
||||
void InitTalentForLevel();
|
||||
|
||||
uint8 GetMaxTalentPointsForLevel(uint32 level);
|
||||
uint8 GetFreeTalentPoints() { return GetByteValue(UNIT_FIELD_BYTES_1, 1); }
|
||||
void SetFreeTalentPoints(uint8 points) { SetByteValue(UNIT_FIELD_BYTES_1, 1, points); }
|
||||
void UpdateFreeTalentPoints(bool resetIfNeed = true);
|
||||
|
||||
uint32 m_resetTalentsCost;
|
||||
time_t m_resetTalentsTime;
|
||||
uint32 m_usedTalentCount;
|
||||
|
||||
const uint64& GetAuraUpdateMask() const { return m_auraUpdateMask; }
|
||||
void SetAuraUpdateMask(uint8 slot) { m_auraUpdateMask |= (uint64(1) << slot); }
|
||||
void ResetAuraUpdateMask() { m_auraUpdateMask = 0; }
|
||||
|
||||
// overwrite Creature function for name localization back to WorldObject version without localization
|
||||
const char* GetNameForLocaleIdx(int32 locale_idx) const { return WorldObject::GetNameForLocaleIdx(locale_idx); }
|
||||
|
||||
DeclinedName const* GetDeclinedNames() const { return m_declinedname; }
|
||||
|
||||
bool m_removed; // prevent overwrite pet state in DB at next Pet::Update if pet already removed(saved)
|
||||
protected:
|
||||
PetType m_petType;
|
||||
int32 m_duration; // time until unsummon (used mostly for summoned guardians and not used for controlled pets)
|
||||
int32 m_bonusdamage;
|
||||
uint64 m_auraUpdateMask;
|
||||
bool m_loading;
|
||||
|
||||
DeclinedName* m_declinedname;
|
||||
|
||||
private:
|
||||
PetModeFlags m_petModeFlags;
|
||||
|
||||
void SaveToDB(uint32, uint8, uint32) override // overwrite of Creature::SaveToDB - don't must be called
|
||||
{
|
||||
MANGOS_ASSERT(false);
|
||||
}
|
||||
void DeleteFromDB() override // overwrite of Creature::DeleteFromDB - don't must be called
|
||||
{
|
||||
MANGOS_ASSERT(false);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
361
src/game/Object/PetAI.cpp
Normal file
361
src/game/Object/PetAI.cpp
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include "PetAI.h"
|
||||
#include "Errors.h"
|
||||
#include "Pet.h"
|
||||
#include "Player.h"
|
||||
#include "DBCStores.h"
|
||||
#include "Spell.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "SpellMgr.h"
|
||||
#include "Creature.h"
|
||||
#include "World.h"
|
||||
#include "Util.h"
|
||||
|
||||
int PetAI::Permissible(const Creature* creature)
|
||||
{
|
||||
if (creature->IsPet())
|
||||
return PERMIT_BASE_SPECIAL;
|
||||
|
||||
return PERMIT_BASE_NO;
|
||||
}
|
||||
|
||||
PetAI::PetAI(Creature* c) : CreatureAI(c), i_tracker(TIME_INTERVAL_LOOK), inCombat(false)
|
||||
{
|
||||
m_AllySet.clear();
|
||||
UpdateAllies();
|
||||
}
|
||||
|
||||
void PetAI::MoveInLineOfSight(Unit* u)
|
||||
{
|
||||
if (m_creature->getVictim())
|
||||
return;
|
||||
|
||||
if (m_creature->IsPet() && ((Pet*)m_creature)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS)
|
||||
return;
|
||||
|
||||
if (!m_creature->GetCharmInfo() || !m_creature->GetCharmInfo()->HasReactState(REACT_AGGRESSIVE))
|
||||
return;
|
||||
|
||||
if (u->isTargetableForAttack() && m_creature->IsHostileTo(u) &&
|
||||
u->isInAccessablePlaceFor(m_creature))
|
||||
{
|
||||
float attackRadius = m_creature->GetAttackDistance(u);
|
||||
if (m_creature->IsWithinDistInMap(u, attackRadius) && m_creature->GetDistanceZ(u) <= CREATURE_Z_ATTACK_RANGE)
|
||||
{
|
||||
if (m_creature->IsWithinLOSInMap(u))
|
||||
{
|
||||
AttackStart(u);
|
||||
u->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PetAI::AttackStart(Unit* u)
|
||||
{
|
||||
if (!u || (m_creature->IsPet() && ((Pet*)m_creature)->getPetType() == MINI_PET))
|
||||
return;
|
||||
|
||||
if (m_creature->Attack(u, true))
|
||||
{
|
||||
// TMGs call CreatureRelocation which via MoveInLineOfSight can call this function
|
||||
// thus with the following clear the original TMG gets invalidated and crash, doh
|
||||
// hope it doesn't start to leak memory without this :-/
|
||||
// i_pet->Clear();
|
||||
HandleMovementOnAttackStart(u);
|
||||
inCombat = true;
|
||||
}
|
||||
}
|
||||
|
||||
void PetAI::EnterEvadeMode()
|
||||
{
|
||||
}
|
||||
|
||||
bool PetAI::IsVisible(Unit* pl) const
|
||||
{
|
||||
return _isVisible(pl);
|
||||
}
|
||||
|
||||
bool PetAI::_needToStop() const
|
||||
{
|
||||
// This is needed for charmed creatures, as once their target was reset other effects can trigger threat
|
||||
if (m_creature->isCharmed() && m_creature->getVictim() == m_creature->GetCharmer())
|
||||
return true;
|
||||
|
||||
return !m_creature->getVictim()->isTargetableForAttack();
|
||||
}
|
||||
|
||||
void PetAI::_stopAttack()
|
||||
{
|
||||
inCombat = false;
|
||||
|
||||
Unit* owner = m_creature->GetCharmerOrOwner();
|
||||
|
||||
if (owner && m_creature->GetCharmInfo() && m_creature->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
|
||||
{
|
||||
m_creature->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_creature->GetMotionMaster()->Clear(false);
|
||||
m_creature->GetMotionMaster()->MoveIdle();
|
||||
}
|
||||
m_creature->AttackStop();
|
||||
}
|
||||
|
||||
void PetAI::UpdateAI(const uint32 diff)
|
||||
{
|
||||
if (!m_creature->isAlive())
|
||||
return;
|
||||
|
||||
Unit* owner = m_creature->GetCharmerOrOwner();
|
||||
|
||||
if (m_updateAlliesTimer <= diff)
|
||||
// UpdateAllies self set update timer
|
||||
UpdateAllies();
|
||||
else
|
||||
m_updateAlliesTimer -= diff;
|
||||
|
||||
if (inCombat && (!m_creature->getVictim() || (m_creature->IsPet() && ((Pet*)m_creature)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS)))
|
||||
_stopAttack();
|
||||
|
||||
// i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc.
|
||||
if (m_creature->getVictim())
|
||||
{
|
||||
if (_needToStop())
|
||||
{
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "PetAI (guid = %u) is stopping attack.", m_creature->GetGUIDLow());
|
||||
_stopAttack();
|
||||
return;
|
||||
}
|
||||
|
||||
bool meleeReach = m_creature->CanReachWithMeleeAttack(m_creature->getVictim());
|
||||
|
||||
if (m_creature->IsStopped() || meleeReach)
|
||||
{
|
||||
// required to be stopped cases
|
||||
if (m_creature->IsStopped() && m_creature->IsNonMeleeSpellCasted(false))
|
||||
{
|
||||
if (m_creature->hasUnitState(UNIT_STAT_FOLLOW_MOVE))
|
||||
m_creature->InterruptNonMeleeSpells(false);
|
||||
else
|
||||
return;
|
||||
}
|
||||
// not required to be stopped case
|
||||
else if (DoMeleeAttackIfReady())
|
||||
{
|
||||
if (!m_creature->getVictim())
|
||||
return;
|
||||
|
||||
// if pet misses its target, it will also be the first in threat list
|
||||
m_creature->getVictim()->AddThreat(m_creature);
|
||||
|
||||
if (_needToStop())
|
||||
_stopAttack();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (owner && m_creature->GetCharmInfo())
|
||||
{
|
||||
if (owner->isInCombat() && !(m_creature->GetCharmInfo()->HasReactState(REACT_PASSIVE) || m_creature->GetCharmInfo()->HasCommandState(COMMAND_STAY)))
|
||||
{
|
||||
AttackStart(owner->getAttackerForHelper());
|
||||
}
|
||||
else if (m_creature->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
|
||||
{
|
||||
if (!m_creature->hasUnitState(UNIT_STAT_FOLLOW))
|
||||
{
|
||||
m_creature->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Autocast (casted only in combat or persistent spells in any state)
|
||||
if (!m_creature->IsNonMeleeSpellCasted(false))
|
||||
{
|
||||
typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList;
|
||||
TargetSpellList targetSpellStore;
|
||||
|
||||
for (uint8 i = 0; i < m_creature->GetPetAutoSpellSize(); ++i)
|
||||
{
|
||||
uint32 spellID = m_creature->GetPetAutoSpellOnPos(i);
|
||||
if (!spellID)
|
||||
continue;
|
||||
|
||||
SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellID);
|
||||
if (!spellInfo)
|
||||
continue;
|
||||
|
||||
if (m_creature->GetCharmInfo() && m_creature->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo))
|
||||
continue;
|
||||
|
||||
// ignore some combinations of combat state and combat/noncombat spells
|
||||
if (!inCombat)
|
||||
{
|
||||
// ignore attacking spells, and allow only self/around spells
|
||||
if (!IsPositiveSpell(spellInfo->Id))
|
||||
continue;
|
||||
|
||||
// non combat spells allowed
|
||||
// only pet spells have IsNonCombatSpell and not fit this reqs:
|
||||
// Consume Shadows, Lesser Invisibility, so ignore checks for its
|
||||
if (!IsNonCombatSpell(spellInfo))
|
||||
{
|
||||
// allow only spell without spell cost or with spell cost but not duration limit
|
||||
int32 duration = GetSpellDuration(spellInfo);
|
||||
SpellPowerEntry const* spellPower = spellInfo->GetSpellPower();
|
||||
if (spellPower && (spellPower->manaCost || spellPower->ManaCostPercentage || spellPower->manaPerSecond) && duration > 0)
|
||||
continue;
|
||||
|
||||
// allow only spell without cooldown > duration
|
||||
int32 cooldown = GetSpellRecoveryTime(spellInfo);
|
||||
if (cooldown >= 0 && duration >= 0 && cooldown > duration)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// just ignore non-combat spells
|
||||
if (IsNonCombatSpell(spellInfo))
|
||||
continue;
|
||||
}
|
||||
|
||||
Spell* spell = new Spell(m_creature, spellInfo, false);
|
||||
|
||||
if (inCombat && !m_creature->hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(m_creature->getVictim()))
|
||||
{
|
||||
targetSpellStore.push_back(TargetSpellList::value_type(m_creature->getVictim(), spell));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool spellUsed = false;
|
||||
for (GuidSet::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar)
|
||||
{
|
||||
Unit* Target = m_creature->GetMap()->GetUnit(*tar);
|
||||
|
||||
// only buff targets that are in combat, unless the spell can only be cast while out of combat
|
||||
if (!Target)
|
||||
continue;
|
||||
|
||||
if (spell->CanAutoCast(Target))
|
||||
{
|
||||
targetSpellStore.push_back(TargetSpellList::value_type(Target, spell));
|
||||
spellUsed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!spellUsed)
|
||||
delete spell;
|
||||
}
|
||||
}
|
||||
|
||||
// found units to cast on to
|
||||
if (!targetSpellStore.empty())
|
||||
{
|
||||
uint32 index = urand(0, targetSpellStore.size() - 1);
|
||||
|
||||
Spell* spell = targetSpellStore[index].second;
|
||||
Unit* target = targetSpellStore[index].first;
|
||||
|
||||
targetSpellStore.erase(targetSpellStore.begin() + index);
|
||||
|
||||
SpellCastTargets targets;
|
||||
targets.setUnitTarget(target);
|
||||
|
||||
if (!m_creature->HasInArc(M_PI_F, target))
|
||||
{
|
||||
m_creature->SetInFront(target);
|
||||
if (target->GetTypeId() == TYPEID_PLAYER)
|
||||
m_creature->SendCreateUpdateToPlayer((Player*)target);
|
||||
|
||||
if (owner && owner->GetTypeId() == TYPEID_PLAYER)
|
||||
m_creature->SendCreateUpdateToPlayer((Player*)owner);
|
||||
}
|
||||
|
||||
m_creature->AddCreatureSpellCooldown(spell->m_spellInfo->Id);
|
||||
|
||||
spell->prepare(&targets);
|
||||
}
|
||||
|
||||
// deleted cached Spell objects
|
||||
for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr)
|
||||
delete itr->second;
|
||||
}
|
||||
}
|
||||
|
||||
bool PetAI::_isVisible(Unit* u) const
|
||||
{
|
||||
return m_creature->IsWithinDist(u, sWorld.getConfig(CONFIG_FLOAT_SIGHT_GUARDER))
|
||||
&& u->isVisibleForOrDetect(m_creature, m_creature, true);
|
||||
}
|
||||
|
||||
void PetAI::UpdateAllies()
|
||||
{
|
||||
Unit* owner = m_creature->GetCharmerOrOwner();
|
||||
Group* pGroup = NULL;
|
||||
|
||||
m_updateAlliesTimer = 10 * IN_MILLISECONDS; // update friendly targets every 10 seconds, lesser checks increase performance
|
||||
|
||||
if (!owner)
|
||||
return;
|
||||
else if (owner->GetTypeId() == TYPEID_PLAYER)
|
||||
pGroup = ((Player*)owner)->GetGroup();
|
||||
|
||||
// only pet and owner/not in group->ok
|
||||
if (m_AllySet.size() == 2 && !pGroup)
|
||||
return;
|
||||
// owner is in group; group members filled in already (no raid -> subgroupcount = whole count)
|
||||
if (pGroup && !pGroup->isRaidGroup() && m_AllySet.size() == (pGroup->GetMembersCount() + 2))
|
||||
return;
|
||||
|
||||
m_AllySet.clear();
|
||||
m_AllySet.insert(m_creature->GetObjectGuid());
|
||||
if (pGroup) // add group
|
||||
{
|
||||
for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
|
||||
{
|
||||
Player* target = itr->getSource();
|
||||
if (!target || !pGroup->SameSubGroup((Player*)owner, target))
|
||||
continue;
|
||||
|
||||
if (target->GetObjectGuid() == owner->GetObjectGuid())
|
||||
continue;
|
||||
|
||||
m_AllySet.insert(target->GetObjectGuid());
|
||||
}
|
||||
}
|
||||
else // remove group
|
||||
m_AllySet.insert(owner->GetObjectGuid());
|
||||
}
|
||||
|
||||
void PetAI::AttackedBy(Unit* attacker)
|
||||
{
|
||||
// when attacked, fight back in case 1)no victim already AND 2)not set to passive AND 3)not set to stay, unless can it can reach attacker with melee attack anyway
|
||||
if (!m_creature->getVictim() && m_creature->GetCharmInfo() && !m_creature->GetCharmInfo()->HasReactState(REACT_PASSIVE) &&
|
||||
(!m_creature->GetCharmInfo()->HasCommandState(COMMAND_STAY) || m_creature->CanReachWithMeleeAttack(attacker)))
|
||||
AttackStart(attacker);
|
||||
}
|
||||
63
src/game/Object/PetAI.h
Normal file
63
src/game/Object/PetAI.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOS_PETAI_H
|
||||
#define MANGOS_PETAI_H
|
||||
|
||||
#include "CreatureAI.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Timer.h"
|
||||
|
||||
class Creature;
|
||||
class Spell;
|
||||
|
||||
class MANGOS_DLL_DECL PetAI : public CreatureAI
|
||||
{
|
||||
public:
|
||||
|
||||
explicit PetAI(Creature* c);
|
||||
|
||||
void MoveInLineOfSight(Unit*) override;
|
||||
void AttackStart(Unit*) override;
|
||||
void EnterEvadeMode() override;
|
||||
void AttackedBy(Unit*) override;
|
||||
bool IsVisible(Unit*) const override;
|
||||
|
||||
void UpdateAI(const uint32) override;
|
||||
static int Permissible(const Creature*);
|
||||
|
||||
private:
|
||||
bool _isVisible(Unit*) const;
|
||||
bool _needToStop(void) const;
|
||||
void _stopAttack(void);
|
||||
|
||||
void UpdateAllies();
|
||||
|
||||
TimeTracker i_tracker;
|
||||
bool inCombat;
|
||||
|
||||
GuidSet m_AllySet;
|
||||
uint32 m_updateAlliesTimer;
|
||||
};
|
||||
#endif
|
||||
24407
src/game/Object/Player.cpp
Normal file
24407
src/game/Object/Player.cpp
Normal file
File diff suppressed because it is too large
Load diff
2701
src/game/Object/Player.h
Normal file
2701
src/game/Object/Player.h
Normal file
File diff suppressed because it is too large
Load diff
127
src/game/Object/ReactorAI.cpp
Normal file
127
src/game/Object/ReactorAI.cpp
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include "ByteBuffer.h"
|
||||
#include "ReactorAI.h"
|
||||
#include "Errors.h"
|
||||
#include "Creature.h"
|
||||
#include "Map.h"
|
||||
#include "Log.h"
|
||||
|
||||
#define REACTOR_VISIBLE_RANGE (26.46f)
|
||||
|
||||
int
|
||||
ReactorAI::Permissible(const Creature* creature)
|
||||
{
|
||||
if (creature->IsCivilian() || creature->IsNeutralToAll())
|
||||
return PERMIT_BASE_REACTIVE;
|
||||
|
||||
return PERMIT_BASE_NO;
|
||||
}
|
||||
|
||||
void
|
||||
ReactorAI::MoveInLineOfSight(Unit*)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ReactorAI::AttackStart(Unit* p)
|
||||
{
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
if (m_creature->Attack(p, true))
|
||||
{
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Tag unit GUID: %u (TypeId: %u) as a victim", p->GetGUIDLow(), p->GetTypeId());
|
||||
i_victimGuid = p->GetObjectGuid();
|
||||
m_creature->AddThreat(p);
|
||||
|
||||
m_creature->SetInCombatWith(p);
|
||||
p->SetInCombatWith(m_creature);
|
||||
|
||||
HandleMovementOnAttackStart(p);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ReactorAI::IsVisible(Unit*) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
ReactorAI::UpdateAI(const uint32 /*time_diff*/)
|
||||
{
|
||||
// update i_victimGuid if i_creature.getVictim() !=0 and changed
|
||||
if (!m_creature->SelectHostileTarget() || !m_creature->getVictim())
|
||||
return;
|
||||
|
||||
i_victimGuid = m_creature->getVictim()->GetObjectGuid();
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
|
||||
void
|
||||
ReactorAI::EnterEvadeMode()
|
||||
{
|
||||
if (!m_creature->isAlive())
|
||||
{
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature stopped attacking, he is dead [guid=%u]", m_creature->GetGUIDLow());
|
||||
m_creature->GetMotionMaster()->MovementExpired();
|
||||
m_creature->GetMotionMaster()->MoveIdle();
|
||||
i_victimGuid.Clear();
|
||||
m_creature->CombatStop(true);
|
||||
m_creature->DeleteThreatList();
|
||||
return;
|
||||
}
|
||||
|
||||
Unit* victim = m_creature->GetMap()->GetUnit(i_victimGuid);
|
||||
|
||||
if (!victim)
|
||||
{
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature stopped attacking, no victim [guid=%u]", m_creature->GetGUIDLow());
|
||||
}
|
||||
else if (victim->HasStealthAura())
|
||||
{
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature stopped attacking, victim is in stealth [guid=%u]", m_creature->GetGUIDLow());
|
||||
}
|
||||
else if (victim->IsTaxiFlying())
|
||||
{
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature stopped attacking, victim is in flight [guid=%u]", m_creature->GetGUIDLow());
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature stopped attacking, victim %s [guid=%u]", victim->isAlive() ? "out run him" : "is dead", m_creature->GetGUIDLow());
|
||||
}
|
||||
|
||||
m_creature->RemoveAllAurasOnEvade();
|
||||
m_creature->DeleteThreatList();
|
||||
i_victimGuid.Clear();
|
||||
m_creature->CombatStop(true);
|
||||
m_creature->SetLootRecipient(NULL);
|
||||
|
||||
// Remove ChaseMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead
|
||||
if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
|
||||
m_creature->GetMotionMaster()->MoveTargetedHome();
|
||||
}
|
||||
50
src/game/Object/ReactorAI.h
Normal file
50
src/game/Object/ReactorAI.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOS_REACTORAI_H
|
||||
#define MANGOS_REACTORAI_H
|
||||
|
||||
#include "CreatureAI.h"
|
||||
#include "ObjectGuid.h"
|
||||
|
||||
class Unit;
|
||||
|
||||
class MANGOS_DLL_DECL ReactorAI : public CreatureAI
|
||||
{
|
||||
public:
|
||||
|
||||
explicit ReactorAI(Creature* c) : CreatureAI(c) {}
|
||||
|
||||
void MoveInLineOfSight(Unit*) override;
|
||||
void AttackStart(Unit*) override;
|
||||
void EnterEvadeMode() override;
|
||||
bool IsVisible(Unit*) const override;
|
||||
|
||||
void UpdateAI(const uint32) override;
|
||||
static int Permissible(const Creature*);
|
||||
|
||||
private:
|
||||
ObjectGuid i_victimGuid;
|
||||
};
|
||||
#endif
|
||||
559
src/game/Object/ReputationMgr.cpp
Normal file
559
src/game/Object/ReputationMgr.cpp
Normal file
|
|
@ -0,0 +1,559 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include "ReputationMgr.h"
|
||||
#include "DBCStores.h"
|
||||
#include "Player.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "ObjectMgr.h"
|
||||
|
||||
const int32 ReputationMgr::PointsInRank[MAX_REPUTATION_RANK] = {36000, 3000, 3000, 3000, 6000, 12000, 21000, 1000};
|
||||
|
||||
ReputationRank ReputationMgr::ReputationToRank(int32 standing)
|
||||
{
|
||||
int32 limit = Reputation_Cap + 1;
|
||||
for (int i = MAX_REPUTATION_RANK - 1; i >= MIN_REPUTATION_RANK; --i)
|
||||
{
|
||||
limit -= PointsInRank[i];
|
||||
if (standing >= limit)
|
||||
return ReputationRank(i);
|
||||
}
|
||||
return MIN_REPUTATION_RANK;
|
||||
}
|
||||
|
||||
int32 ReputationMgr::GetReputation(uint32 faction_id) const
|
||||
{
|
||||
FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id);
|
||||
|
||||
if (!factionEntry)
|
||||
{
|
||||
sLog.outError("ReputationMgr::GetReputation: Can't get reputation of %s for unknown faction (faction id) #%u.", m_player->GetName(), faction_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return GetReputation(factionEntry);
|
||||
}
|
||||
|
||||
int32 ReputationMgr::GetBaseReputation(FactionEntry const* factionEntry) const
|
||||
{
|
||||
if (!factionEntry)
|
||||
return 0;
|
||||
|
||||
uint32 raceMask = m_player->getRaceMask();
|
||||
uint32 classMask = m_player->getClassMask();
|
||||
|
||||
int idx = factionEntry->GetIndexFitTo(raceMask, classMask);
|
||||
|
||||
return idx >= 0 ? factionEntry->BaseRepValue[idx] : 0;
|
||||
}
|
||||
|
||||
int32 ReputationMgr::GetReputation(FactionEntry const* factionEntry) const
|
||||
{
|
||||
// Faction without recorded reputation. Just ignore.
|
||||
if (!factionEntry)
|
||||
return 0;
|
||||
|
||||
if (FactionState const* state = GetState(factionEntry))
|
||||
return GetBaseReputation(factionEntry) + state->Standing;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ReputationRank ReputationMgr::GetRank(FactionEntry const* factionEntry) const
|
||||
{
|
||||
int32 reputation = GetReputation(factionEntry);
|
||||
return ReputationToRank(reputation);
|
||||
}
|
||||
|
||||
ReputationRank ReputationMgr::GetBaseRank(FactionEntry const* factionEntry) const
|
||||
{
|
||||
int32 reputation = GetBaseReputation(factionEntry);
|
||||
return ReputationToRank(reputation);
|
||||
}
|
||||
|
||||
void ReputationMgr::ApplyForceReaction(uint32 faction_id, ReputationRank rank, bool apply)
|
||||
{
|
||||
if (apply)
|
||||
m_forcedReactions[faction_id] = rank;
|
||||
else
|
||||
m_forcedReactions.erase(faction_id);
|
||||
}
|
||||
|
||||
uint32 ReputationMgr::GetDefaultStateFlags(FactionEntry const* factionEntry) const
|
||||
{
|
||||
if (!factionEntry)
|
||||
return 0;
|
||||
|
||||
uint32 raceMask = m_player->getRaceMask();
|
||||
uint32 classMask = m_player->getClassMask();
|
||||
|
||||
int idx = factionEntry->GetIndexFitTo(raceMask, classMask);
|
||||
|
||||
return idx >= 0 ? factionEntry->ReputationFlags[idx] : 0;
|
||||
}
|
||||
|
||||
void ReputationMgr::SendForceReactions()
|
||||
{
|
||||
WorldPacket data;
|
||||
data.Initialize(SMSG_SET_FORCED_REACTIONS, 4 + m_forcedReactions.size() * (4 + 4));
|
||||
data << uint32(m_forcedReactions.size());
|
||||
for (ForcedReactions::const_iterator itr = m_forcedReactions.begin(); itr != m_forcedReactions.end(); ++itr)
|
||||
{
|
||||
data << uint32(itr->first); // faction_id (Faction.dbc)
|
||||
data << uint32(itr->second); // reputation rank
|
||||
}
|
||||
m_player->SendDirectMessage(&data);
|
||||
}
|
||||
|
||||
void ReputationMgr::SendState(FactionState const* faction, bool anyRankIncreased)
|
||||
{
|
||||
uint32 count = 1;
|
||||
|
||||
WorldPacket data(SMSG_SET_FACTION_STANDING, 17);
|
||||
data << float(0); // refer-a-friend bonus reputation
|
||||
data << uint8(anyRankIncreased ? 1 : 0); // display visual effect
|
||||
|
||||
size_t p_count = data.wpos();
|
||||
data << uint32(count); // placeholder
|
||||
|
||||
data << uint32(faction->ReputationListID);
|
||||
data << uint32(faction->Standing);
|
||||
|
||||
for (FactionStateList::iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr)
|
||||
{
|
||||
if (itr->second.needSend)
|
||||
{
|
||||
itr->second.needSend = false;
|
||||
if (itr->second.ReputationListID != faction->ReputationListID)
|
||||
{
|
||||
data << uint32(itr->second.ReputationListID);
|
||||
data << uint32(itr->second.Standing);
|
||||
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data.put<uint32>(p_count, count);
|
||||
m_player->SendDirectMessage(&data);
|
||||
}
|
||||
|
||||
void ReputationMgr::SendInitialReputations()
|
||||
{
|
||||
WorldPacket data(SMSG_INITIALIZE_FACTIONS, (4 + 128 * 5));
|
||||
data << uint32 (0x00000100);
|
||||
|
||||
RepListID a = 0;
|
||||
|
||||
for (FactionStateList::iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr)
|
||||
{
|
||||
// fill in absent fields
|
||||
for (; a != itr->first; ++a)
|
||||
{
|
||||
data << uint8(0x00);
|
||||
data << uint32(0x00000000);
|
||||
}
|
||||
|
||||
// fill in encountered data
|
||||
data << uint8(itr->second.Flags);
|
||||
data << uint32(itr->second.Standing);
|
||||
|
||||
itr->second.needSend = false;
|
||||
|
||||
++a;
|
||||
}
|
||||
|
||||
// fill in absent fields
|
||||
for (; a != 256; ++a)
|
||||
{
|
||||
data << uint8(0x00);
|
||||
data << uint32(0x00000000);
|
||||
}
|
||||
|
||||
m_player->SendDirectMessage(&data);
|
||||
}
|
||||
|
||||
void ReputationMgr::SendVisible(FactionState const* faction) const
|
||||
{
|
||||
if (m_player->GetSession()->PlayerLoading())
|
||||
return;
|
||||
|
||||
// make faction visible in reputation list at client
|
||||
WorldPacket data(SMSG_SET_FACTION_VISIBLE, 4);
|
||||
data << faction->ReputationListID;
|
||||
m_player->SendDirectMessage(&data);
|
||||
}
|
||||
|
||||
void ReputationMgr::Initialize()
|
||||
{
|
||||
m_factions.clear();
|
||||
m_visibleFactionCount = 0;
|
||||
m_honoredFactionCount = 0;
|
||||
m_reveredFactionCount = 0;
|
||||
m_exaltedFactionCount = 0;
|
||||
|
||||
for (unsigned int i = 1; i < sFactionStore.GetNumRows(); ++i)
|
||||
{
|
||||
FactionEntry const* factionEntry = sFactionStore.LookupEntry(i);
|
||||
|
||||
if (factionEntry && (factionEntry->reputationListID >= 0))
|
||||
{
|
||||
FactionState newFaction;
|
||||
newFaction.ID = factionEntry->ID;
|
||||
newFaction.ReputationListID = factionEntry->reputationListID;
|
||||
newFaction.Standing = 0;
|
||||
newFaction.Flags = GetDefaultStateFlags(factionEntry);
|
||||
newFaction.needSend = true;
|
||||
newFaction.needSave = true;
|
||||
|
||||
if (newFaction.Flags & FACTION_FLAG_VISIBLE)
|
||||
++m_visibleFactionCount;
|
||||
|
||||
UpdateRankCounters(REP_HOSTILE, GetBaseRank(factionEntry));
|
||||
|
||||
m_factions[newFaction.ReputationListID] = newFaction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReputationMgr::SetReputation(FactionEntry const* factionEntry, int32 standing, bool incremental)
|
||||
{
|
||||
bool anyRankIncreased = false;
|
||||
|
||||
// if spillover definition exists in DB, override DBC
|
||||
if (const RepSpilloverTemplate* repTemplate = sObjectMgr.GetRepSpilloverTemplate(factionEntry->ID))
|
||||
{
|
||||
for (uint32 i = 0; i < MAX_SPILLOVER_FACTIONS; ++i)
|
||||
{
|
||||
if (repTemplate->faction[i])
|
||||
{
|
||||
if (m_player->GetReputationRank(repTemplate->faction[i]) <= ReputationRank(repTemplate->faction_rank[i]))
|
||||
{
|
||||
// bonuses are already given, so just modify standing by rate
|
||||
int32 spilloverRep = standing * repTemplate->faction_rate[i];
|
||||
if (SetOneFactionReputation(sFactionStore.LookupEntry(repTemplate->faction[i]), spilloverRep, incremental))
|
||||
anyRankIncreased = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float spillOverRepOut = standing;
|
||||
// check for sub-factions that receive spillover
|
||||
SimpleFactionsList const* flist = GetFactionTeamList(factionEntry->ID);
|
||||
// if has no sub-factions, check for factions with same parent
|
||||
if (!flist && factionEntry->team && factionEntry->spilloverRateOut != 0.0f)
|
||||
{
|
||||
spillOverRepOut *= factionEntry->spilloverRateOut;
|
||||
if (FactionEntry const* parent = sFactionStore.LookupEntry(factionEntry->team))
|
||||
{
|
||||
FactionStateList::iterator parentState = m_factions.find(parent->reputationListID);
|
||||
// some team factions have own reputation standing, in this case do not spill to other sub-factions
|
||||
if (parentState != m_factions.end() && (parentState->second.Flags & FACTION_FLAG_TEAM_REPUTATION))
|
||||
{
|
||||
if (SetOneFactionReputation(parent, int32(spillOverRepOut), incremental))
|
||||
anyRankIncreased = true;
|
||||
}
|
||||
else // spill to "sister" factions
|
||||
{
|
||||
flist = GetFactionTeamList(factionEntry->team);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (flist)
|
||||
{
|
||||
// Spillover to affiliated factions
|
||||
for (SimpleFactionsList::const_iterator itr = flist->begin(); itr != flist->end(); ++itr)
|
||||
{
|
||||
if (FactionEntry const* factionEntryCalc = sFactionStore.LookupEntry(*itr))
|
||||
{
|
||||
if (factionEntryCalc == factionEntry || GetRank(factionEntryCalc) > ReputationRank(factionEntryCalc->spilloverMaxRankIn))
|
||||
continue;
|
||||
|
||||
int32 spilloverRep = int32(spillOverRepOut * factionEntryCalc->spilloverRateIn);
|
||||
if (spilloverRep != 0 || !incremental)
|
||||
if (SetOneFactionReputation(factionEntryCalc, spilloverRep, incremental))
|
||||
anyRankIncreased = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// spillover done, update faction itself
|
||||
FactionStateList::iterator faction = m_factions.find(factionEntry->reputationListID);
|
||||
if (faction != m_factions.end())
|
||||
{
|
||||
if (SetOneFactionReputation(factionEntry, standing, incremental))
|
||||
anyRankIncreased = true;
|
||||
|
||||
// only this faction gets reported to client, even if it has no own visible standing
|
||||
SendState(&faction->second, anyRankIncreased);
|
||||
}
|
||||
}
|
||||
|
||||
bool ReputationMgr::SetOneFactionReputation(FactionEntry const* factionEntry, int32 standing, bool incremental)
|
||||
{
|
||||
FactionStateList::iterator itr = m_factions.find(factionEntry->reputationListID);
|
||||
if (itr != m_factions.end())
|
||||
{
|
||||
int32 BaseRep = GetBaseReputation(factionEntry);
|
||||
|
||||
if (incremental)
|
||||
standing += itr->second.Standing + BaseRep;
|
||||
|
||||
if (standing > Reputation_Cap)
|
||||
standing = Reputation_Cap;
|
||||
else if (standing < Reputation_Bottom)
|
||||
standing = Reputation_Bottom;
|
||||
|
||||
ReputationRank old_rank = ReputationToRank(itr->second.Standing + BaseRep);
|
||||
ReputationRank new_rank = ReputationToRank(standing);
|
||||
|
||||
itr->second.Standing = standing - BaseRep;
|
||||
itr->second.needSend = true;
|
||||
itr->second.needSave = true;
|
||||
|
||||
SetVisible(&itr->second);
|
||||
|
||||
if (new_rank <= REP_HOSTILE)
|
||||
SetAtWar(&itr->second, true);
|
||||
|
||||
UpdateRankCounters(old_rank, new_rank);
|
||||
|
||||
m_player->ReputationChanged(factionEntry);
|
||||
m_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS, factionEntry->ID);
|
||||
m_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION, factionEntry->ID);
|
||||
m_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION, factionEntry->ID);
|
||||
m_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION, factionEntry->ID);
|
||||
m_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION, factionEntry->ID);
|
||||
|
||||
if (new_rank > old_rank)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ReputationMgr::SetVisible(FactionTemplateEntry const* factionTemplateEntry)
|
||||
{
|
||||
if (!factionTemplateEntry->faction)
|
||||
return;
|
||||
|
||||
if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplateEntry->faction))
|
||||
SetVisible(factionEntry);
|
||||
}
|
||||
|
||||
void ReputationMgr::SetVisible(FactionEntry const* factionEntry)
|
||||
{
|
||||
if (factionEntry->reputationListID < 0)
|
||||
return;
|
||||
|
||||
FactionStateList::iterator itr = m_factions.find(factionEntry->reputationListID);
|
||||
if (itr == m_factions.end())
|
||||
return;
|
||||
|
||||
SetVisible(&itr->second);
|
||||
}
|
||||
|
||||
void ReputationMgr::SetVisible(FactionState* faction)
|
||||
{
|
||||
// always invisible or hidden faction can't be make visible
|
||||
if (faction->Flags & (FACTION_FLAG_INVISIBLE_FORCED | FACTION_FLAG_HIDDEN))
|
||||
return;
|
||||
|
||||
// already set
|
||||
if (faction->Flags & FACTION_FLAG_VISIBLE)
|
||||
return;
|
||||
|
||||
faction->Flags |= FACTION_FLAG_VISIBLE;
|
||||
faction->needSend = true;
|
||||
faction->needSave = true;
|
||||
|
||||
++m_visibleFactionCount;
|
||||
|
||||
SendVisible(faction);
|
||||
}
|
||||
|
||||
void ReputationMgr::SetAtWar(RepListID repListID, bool on)
|
||||
{
|
||||
FactionStateList::iterator itr = m_factions.find(repListID);
|
||||
if (itr == m_factions.end())
|
||||
return;
|
||||
|
||||
// always invisible or hidden faction can't change war state
|
||||
if (itr->second.Flags & (FACTION_FLAG_INVISIBLE_FORCED | FACTION_FLAG_HIDDEN))
|
||||
return;
|
||||
|
||||
SetAtWar(&itr->second, on);
|
||||
}
|
||||
|
||||
void ReputationMgr::SetAtWar(FactionState* faction, bool atWar)
|
||||
{
|
||||
// not allow declare war to faction unless already hated or less
|
||||
if (atWar && (faction->Flags & FACTION_FLAG_PEACE_FORCED) && ReputationToRank(faction->Standing) > REP_HATED)
|
||||
return;
|
||||
|
||||
// already set
|
||||
if (((faction->Flags & FACTION_FLAG_AT_WAR) != 0) == atWar)
|
||||
return;
|
||||
|
||||
if (atWar)
|
||||
faction->Flags |= FACTION_FLAG_AT_WAR;
|
||||
else
|
||||
faction->Flags &= ~FACTION_FLAG_AT_WAR;
|
||||
|
||||
faction->needSend = true;
|
||||
faction->needSave = true;
|
||||
}
|
||||
|
||||
void ReputationMgr::SetInactive(RepListID repListID, bool on)
|
||||
{
|
||||
FactionStateList::iterator itr = m_factions.find(repListID);
|
||||
if (itr == m_factions.end())
|
||||
return;
|
||||
|
||||
SetInactive(&itr->second, on);
|
||||
}
|
||||
|
||||
void ReputationMgr::SetInactive(FactionState* faction, bool inactive)
|
||||
{
|
||||
// always invisible or hidden faction can't be inactive
|
||||
if (inactive && ((faction->Flags & (FACTION_FLAG_INVISIBLE_FORCED | FACTION_FLAG_HIDDEN)) || !(faction->Flags & FACTION_FLAG_VISIBLE)))
|
||||
return;
|
||||
|
||||
// already set
|
||||
if (((faction->Flags & FACTION_FLAG_INACTIVE) != 0) == inactive)
|
||||
return;
|
||||
|
||||
if (inactive)
|
||||
faction->Flags |= FACTION_FLAG_INACTIVE;
|
||||
else
|
||||
faction->Flags &= ~FACTION_FLAG_INACTIVE;
|
||||
|
||||
faction->needSend = true;
|
||||
faction->needSave = true;
|
||||
}
|
||||
|
||||
void ReputationMgr::LoadFromDB(QueryResult* result)
|
||||
{
|
||||
// Set initial reputations (so everything is nifty before DB data load)
|
||||
Initialize();
|
||||
|
||||
// QueryResult *result = CharacterDatabase.PQuery("SELECT faction,standing,flags FROM character_reputation WHERE guid = '%u'",GetGUIDLow());
|
||||
|
||||
if (result)
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
FactionEntry const* factionEntry = sFactionStore.LookupEntry(fields[0].GetUInt32());
|
||||
if (factionEntry && (factionEntry->reputationListID >= 0))
|
||||
{
|
||||
FactionState* faction = &m_factions[factionEntry->reputationListID];
|
||||
|
||||
// update standing to current
|
||||
faction->Standing = int32(fields[1].GetUInt32());
|
||||
|
||||
// update counters
|
||||
int32 BaseRep = GetBaseReputation(factionEntry);
|
||||
ReputationRank old_rank = ReputationToRank(BaseRep);
|
||||
ReputationRank new_rank = ReputationToRank(BaseRep + faction->Standing);
|
||||
UpdateRankCounters(old_rank, new_rank);
|
||||
|
||||
uint32 dbFactionFlags = fields[2].GetUInt32();
|
||||
|
||||
if (dbFactionFlags & FACTION_FLAG_VISIBLE)
|
||||
SetVisible(faction); // have internal checks for forced invisibility
|
||||
|
||||
if (dbFactionFlags & FACTION_FLAG_INACTIVE)
|
||||
SetInactive(faction, true); // have internal checks for visibility requirement
|
||||
|
||||
if (dbFactionFlags & FACTION_FLAG_AT_WAR) // DB at war
|
||||
SetAtWar(faction, true); // have internal checks for FACTION_FLAG_PEACE_FORCED
|
||||
else // DB not at war
|
||||
{
|
||||
// allow remove if visible (and then not FACTION_FLAG_INVISIBLE_FORCED or FACTION_FLAG_HIDDEN)
|
||||
if (faction->Flags & FACTION_FLAG_VISIBLE)
|
||||
SetAtWar(faction, false); // have internal checks for FACTION_FLAG_PEACE_FORCED
|
||||
}
|
||||
|
||||
// set atWar for hostile
|
||||
ForcedReactions::const_iterator forceItr = m_forcedReactions.find(factionEntry->ID);
|
||||
if (forceItr != m_forcedReactions.end())
|
||||
{
|
||||
if (forceItr->second <= REP_HOSTILE)
|
||||
SetAtWar(faction, true);
|
||||
}
|
||||
else if (GetRank(factionEntry) <= REP_HOSTILE)
|
||||
SetAtWar(faction, true);
|
||||
|
||||
// reset changed flag if values similar to saved in DB
|
||||
if (faction->Flags == dbFactionFlags)
|
||||
{
|
||||
faction->needSend = false;
|
||||
faction->needSave = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (result->NextRow());
|
||||
|
||||
delete result;
|
||||
}
|
||||
}
|
||||
|
||||
void ReputationMgr::SaveToDB()
|
||||
{
|
||||
static SqlStatementID delRep ;
|
||||
static SqlStatementID insRep ;
|
||||
|
||||
SqlStatement stmtDel = CharacterDatabase.CreateStatement(delRep, "DELETE FROM character_reputation WHERE guid = ? AND faction=?");
|
||||
SqlStatement stmtIns = CharacterDatabase.CreateStatement(insRep, "INSERT INTO character_reputation (guid,faction,standing,flags) VALUES (?, ?, ?, ?)");
|
||||
|
||||
for (FactionStateList::iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr)
|
||||
{
|
||||
if (itr->second.needSave)
|
||||
{
|
||||
stmtDel.PExecute(m_player->GetGUIDLow(), itr->second.ID);
|
||||
stmtIns.PExecute(m_player->GetGUIDLow(), itr->second.ID, itr->second.Standing, itr->second.Flags);
|
||||
itr->second.needSave = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReputationMgr::UpdateRankCounters(ReputationRank old_rank, ReputationRank new_rank)
|
||||
{
|
||||
if (old_rank >= REP_EXALTED)
|
||||
--m_exaltedFactionCount;
|
||||
if (old_rank >= REP_REVERED)
|
||||
--m_reveredFactionCount;
|
||||
if (old_rank >= REP_HONORED)
|
||||
--m_honoredFactionCount;
|
||||
|
||||
if (new_rank >= REP_EXALTED)
|
||||
++m_exaltedFactionCount;
|
||||
if (new_rank >= REP_REVERED)
|
||||
++m_reveredFactionCount;
|
||||
if (new_rank >= REP_HONORED)
|
||||
++m_honoredFactionCount;
|
||||
}
|
||||
153
src/game/Object/ReputationMgr.h
Normal file
153
src/game/Object/ReputationMgr.h
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef __MANGOS_REPUTATION_MGR_H
|
||||
#define __MANGOS_REPUTATION_MGR_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "DBCStructure.h"
|
||||
#include <map>
|
||||
|
||||
enum FactionFlags
|
||||
{
|
||||
FACTION_FLAG_VISIBLE = 0x01, // makes visible in client (set or can be set at interaction with target of this faction)
|
||||
FACTION_FLAG_AT_WAR = 0x02, // enable AtWar-button in client. player controlled (except opposition team always war state), Flag only set on initial creation
|
||||
FACTION_FLAG_HIDDEN = 0x04, // hidden faction from reputation pane in client (player can gain reputation, but this update not sent to client)
|
||||
FACTION_FLAG_INVISIBLE_FORCED = 0x08, // always overwrite FACTION_FLAG_VISIBLE and hide faction in rep.list, used for hide opposite team factions
|
||||
FACTION_FLAG_PEACE_FORCED = 0x10, // always overwrite FACTION_FLAG_AT_WAR, used for prevent war with own team factions
|
||||
FACTION_FLAG_INACTIVE = 0x20, // player controlled, state stored in characters.data ( CMSG_SET_FACTION_INACTIVE )
|
||||
FACTION_FLAG_RIVAL = 0x40, // flag for the two competing outland factions
|
||||
FACTION_FLAG_TEAM_REPUTATION = 0x80 // faction has own reputation standing despite teaming up sub-factions; spillover from subfactions will go this instead of other subfactions
|
||||
};
|
||||
|
||||
typedef uint32 RepListID;
|
||||
struct FactionState
|
||||
{
|
||||
uint32 ID;
|
||||
RepListID ReputationListID;
|
||||
uint32 Flags;
|
||||
int32 Standing;
|
||||
bool needSend;
|
||||
bool needSave;
|
||||
};
|
||||
|
||||
typedef std::map<RepListID, FactionState> FactionStateList;
|
||||
typedef std::pair<FactionStateList::const_iterator, FactionStateList::const_iterator> FactionStateListPair;
|
||||
|
||||
typedef std::map<uint32, ReputationRank> ForcedReactions;
|
||||
|
||||
class Player;
|
||||
class QueryResult;
|
||||
|
||||
class ReputationMgr
|
||||
{
|
||||
public: // constructors and global modifiers
|
||||
explicit ReputationMgr(Player* owner) : m_player(owner),
|
||||
m_visibleFactionCount(0), m_honoredFactionCount(0), m_reveredFactionCount(0), m_exaltedFactionCount(0) {}
|
||||
~ReputationMgr() {}
|
||||
|
||||
void SaveToDB();
|
||||
void LoadFromDB(QueryResult* result);
|
||||
public: // statics
|
||||
static const int32 PointsInRank[MAX_REPUTATION_RANK];
|
||||
static const int32 Reputation_Cap = 42999;
|
||||
static const int32 Reputation_Bottom = -42000;
|
||||
|
||||
static ReputationRank ReputationToRank(int32 standing);
|
||||
public: // accessors
|
||||
uint8 GetVisibleFactionCount() const { return m_visibleFactionCount; }
|
||||
uint8 GetHonoredFactionCount() const { return m_honoredFactionCount; }
|
||||
uint8 GetReveredFactionCount() const { return m_reveredFactionCount; }
|
||||
uint8 GetExaltedFactionCount() const { return m_exaltedFactionCount; }
|
||||
|
||||
FactionStateList const& GetStateList() const { return m_factions; }
|
||||
|
||||
FactionState const* GetState(FactionEntry const* factionEntry) const
|
||||
{
|
||||
return factionEntry->reputationListID >= 0 ? GetState(factionEntry->reputationListID) : NULL;
|
||||
}
|
||||
|
||||
FactionState const* GetState(RepListID id) const
|
||||
{
|
||||
FactionStateList::const_iterator repItr = m_factions.find(id);
|
||||
return repItr != m_factions.end() ? &repItr->second : NULL;
|
||||
}
|
||||
|
||||
int32 GetReputation(uint32 faction_id) const;
|
||||
int32 GetReputation(FactionEntry const* factionEntry) const;
|
||||
int32 GetBaseReputation(FactionEntry const* factionEntry) const;
|
||||
|
||||
ReputationRank GetRank(FactionEntry const* factionEntry) const;
|
||||
ReputationRank GetBaseRank(FactionEntry const* factionEntry) const;
|
||||
|
||||
ReputationRank const* GetForcedRankIfAny(FactionTemplateEntry const* factionTemplateEntry) const
|
||||
{
|
||||
ForcedReactions::const_iterator forceItr = m_forcedReactions.find(factionTemplateEntry->faction);
|
||||
return forceItr != m_forcedReactions.end() ? &forceItr->second : NULL;
|
||||
}
|
||||
|
||||
public: // modifiers
|
||||
void SetReputation(FactionEntry const* factionEntry, int32 standing)
|
||||
{
|
||||
SetReputation(factionEntry, standing, false);
|
||||
}
|
||||
void ModifyReputation(FactionEntry const* factionEntry, int32 standing)
|
||||
{
|
||||
SetReputation(factionEntry, standing, true);
|
||||
}
|
||||
|
||||
void SetVisible(FactionTemplateEntry const* factionTemplateEntry);
|
||||
void SetVisible(FactionEntry const* factionEntry);
|
||||
void SetAtWar(RepListID repListID, bool on);
|
||||
void SetInactive(RepListID repListID, bool on);
|
||||
|
||||
void ApplyForceReaction(uint32 faction_id, ReputationRank rank, bool apply);
|
||||
|
||||
public: // senders
|
||||
void SendInitialReputations();
|
||||
void SendForceReactions();
|
||||
void SendState(FactionState const* faction, bool anyRankIncreased);
|
||||
|
||||
private: // internal helper functions
|
||||
void Initialize();
|
||||
uint32 GetDefaultStateFlags(const FactionEntry* factionEntry) const;
|
||||
void SetReputation(FactionEntry const* factionEntry, int32 standing, bool incremental);
|
||||
bool SetOneFactionReputation(FactionEntry const* factionEntry, int32 standing, bool incremental);
|
||||
void SetVisible(FactionState* faction);
|
||||
void SetAtWar(FactionState* faction, bool atWar);
|
||||
void SetInactive(FactionState* faction, bool inactive);
|
||||
void SendVisible(FactionState const* faction) const;
|
||||
void UpdateRankCounters(ReputationRank old_rank, ReputationRank new_rank);
|
||||
private:
|
||||
Player* m_player;
|
||||
FactionStateList m_factions;
|
||||
ForcedReactions m_forcedReactions;
|
||||
uint8 m_visibleFactionCount : 8;
|
||||
uint8 m_honoredFactionCount : 8;
|
||||
uint8 m_reveredFactionCount : 8;
|
||||
uint8 m_exaltedFactionCount : 8;
|
||||
};
|
||||
|
||||
#endif
|
||||
341
src/game/Object/SocialMgr.cpp
Normal file
341
src/game/Object/SocialMgr.cpp
Normal file
|
|
@ -0,0 +1,341 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include "SocialMgr.h"
|
||||
#include "Policies/Singleton.h"
|
||||
#include "Database/DatabaseEnv.h"
|
||||
#include "Opcodes.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "Player.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "World.h"
|
||||
#include "Util.h"
|
||||
|
||||
INSTANTIATE_SINGLETON_1(SocialMgr);
|
||||
|
||||
PlayerSocial::PlayerSocial()
|
||||
{
|
||||
}
|
||||
|
||||
PlayerSocial::~PlayerSocial()
|
||||
{
|
||||
m_playerSocialMap.clear();
|
||||
}
|
||||
|
||||
uint32 PlayerSocial::GetNumberOfSocialsWithFlag(SocialFlag flag)
|
||||
{
|
||||
uint32 counter = 0;
|
||||
for (PlayerSocialMap::const_iterator itr = m_playerSocialMap.begin(); itr != m_playerSocialMap.end(); ++itr)
|
||||
{
|
||||
if (itr->second.Flags & flag)
|
||||
++counter;
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
|
||||
bool PlayerSocial::AddToSocialList(ObjectGuid friend_guid, bool ignore)
|
||||
{
|
||||
// check client limits
|
||||
if (ignore)
|
||||
{
|
||||
if (GetNumberOfSocialsWithFlag(SOCIAL_FLAG_IGNORED) >= SOCIALMGR_IGNORE_LIMIT)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GetNumberOfSocialsWithFlag(SOCIAL_FLAG_FRIEND) >= SOCIALMGR_FRIEND_LIMIT)
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 flag = SOCIAL_FLAG_FRIEND;
|
||||
if (ignore)
|
||||
flag = SOCIAL_FLAG_IGNORED;
|
||||
|
||||
PlayerSocialMap::const_iterator itr = m_playerSocialMap.find(friend_guid.GetCounter());
|
||||
if (itr != m_playerSocialMap.end())
|
||||
{
|
||||
CharacterDatabase.PExecute("UPDATE character_social SET flags = (flags | %u) WHERE guid = '%u' AND friend = '%u'", flag, m_playerLowGuid, friend_guid.GetCounter());
|
||||
m_playerSocialMap[friend_guid.GetCounter()].Flags |= flag;
|
||||
}
|
||||
else
|
||||
{
|
||||
CharacterDatabase.PExecute("INSERT INTO character_social (guid, friend, flags) VALUES ('%u', '%u', '%u')", m_playerLowGuid, friend_guid.GetCounter(), flag);
|
||||
FriendInfo fi;
|
||||
fi.Flags |= flag;
|
||||
m_playerSocialMap[friend_guid.GetCounter()] = fi;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlayerSocial::RemoveFromSocialList(ObjectGuid friend_guid, bool ignore)
|
||||
{
|
||||
PlayerSocialMap::iterator itr = m_playerSocialMap.find(friend_guid.GetCounter());
|
||||
if (itr == m_playerSocialMap.end()) // not exist
|
||||
return;
|
||||
|
||||
uint32 flag = SOCIAL_FLAG_FRIEND;
|
||||
if (ignore)
|
||||
flag = SOCIAL_FLAG_IGNORED;
|
||||
|
||||
itr->second.Flags &= ~flag;
|
||||
if (itr->second.Flags == 0)
|
||||
{
|
||||
CharacterDatabase.PExecute("DELETE FROM character_social WHERE guid = '%u' AND friend = '%u'", m_playerLowGuid, friend_guid.GetCounter());
|
||||
m_playerSocialMap.erase(itr);
|
||||
}
|
||||
else
|
||||
{
|
||||
CharacterDatabase.PExecute("UPDATE character_social SET flags = (flags & ~%u) WHERE guid = '%u' AND friend = '%u'", flag, m_playerLowGuid, friend_guid.GetCounter());
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerSocial::SetFriendNote(ObjectGuid friend_guid, std::string note)
|
||||
{
|
||||
PlayerSocialMap::const_iterator itr = m_playerSocialMap.find(friend_guid.GetCounter());
|
||||
if (itr == m_playerSocialMap.end()) // not exist
|
||||
return;
|
||||
|
||||
utf8truncate(note, 48); // DB and client size limitation
|
||||
|
||||
std::string safe_note = note;
|
||||
CharacterDatabase.escape_string(safe_note);
|
||||
CharacterDatabase.PExecute("UPDATE character_social SET note = '%s' WHERE guid = '%u' AND friend = '%u'", safe_note.c_str(), m_playerLowGuid, friend_guid.GetCounter());
|
||||
m_playerSocialMap[friend_guid.GetCounter()].Note = note;
|
||||
}
|
||||
|
||||
void PlayerSocial::SendSocialList()
|
||||
{
|
||||
Player* plr = sObjectMgr.GetPlayer(ObjectGuid(HIGHGUID_PLAYER, m_playerLowGuid));
|
||||
if (!plr)
|
||||
return;
|
||||
|
||||
uint32 size = m_playerSocialMap.size();
|
||||
|
||||
WorldPacket data(SMSG_CONTACT_LIST, (4 + 4 + size * 25)); // just can guess size
|
||||
data << uint32(7); // unk flag (0x1, 0x2, 0x4), 0x7 if it include ignore list
|
||||
data << uint32(size); // friends count
|
||||
|
||||
for (PlayerSocialMap::iterator itr = m_playerSocialMap.begin(); itr != m_playerSocialMap.end(); ++itr)
|
||||
{
|
||||
sSocialMgr.GetFriendInfo(plr, itr->first, itr->second);
|
||||
|
||||
data << ObjectGuid(HIGHGUID_PLAYER, itr->first); // player guid
|
||||
data << uint32(itr->second.Flags); // player flag (0x1-friend?, 0x2-ignored?, 0x4-muted?)
|
||||
data << itr->second.Note; // string note
|
||||
if (itr->second.Flags & SOCIAL_FLAG_FRIEND) // if IsFriend()
|
||||
{
|
||||
data << uint8(itr->second.Status); // online/offline/etc?
|
||||
if (itr->second.Status) // if online
|
||||
{
|
||||
data << uint32(itr->second.Area); // player area
|
||||
data << uint32(itr->second.Level); // player level
|
||||
data << uint32(itr->second.Class); // player class
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plr->GetSession()->SendPacket(&data);
|
||||
DEBUG_LOG("WORLD: Sent SMSG_CONTACT_LIST");
|
||||
}
|
||||
|
||||
bool PlayerSocial::HasFriend(ObjectGuid friend_guid)
|
||||
{
|
||||
PlayerSocialMap::const_iterator itr = m_playerSocialMap.find(friend_guid.GetCounter());
|
||||
if (itr != m_playerSocialMap.end())
|
||||
return itr->second.Flags & SOCIAL_FLAG_FRIEND;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PlayerSocial::HasIgnore(ObjectGuid ignore_guid)
|
||||
{
|
||||
PlayerSocialMap::const_iterator itr = m_playerSocialMap.find(ignore_guid.GetCounter());
|
||||
if (itr != m_playerSocialMap.end())
|
||||
return itr->second.Flags & SOCIAL_FLAG_IGNORED;
|
||||
return false;
|
||||
}
|
||||
|
||||
SocialMgr::SocialMgr()
|
||||
{
|
||||
}
|
||||
|
||||
SocialMgr::~SocialMgr()
|
||||
{
|
||||
}
|
||||
|
||||
void SocialMgr::GetFriendInfo(Player* player, uint32 friend_lowguid, FriendInfo& friendInfo)
|
||||
{
|
||||
if (!player)
|
||||
return;
|
||||
|
||||
Player* pFriend = ObjectAccessor::FindPlayer(ObjectGuid(HIGHGUID_PLAYER, friend_lowguid));
|
||||
|
||||
Team team = player->GetTeam();
|
||||
AccountTypes security = player->GetSession()->GetSecurity();
|
||||
bool allowTwoSideWhoList = sWorld.getConfig(CONFIG_BOOL_ALLOW_TWO_SIDE_WHO_LIST);
|
||||
AccountTypes gmLevelInWhoList = AccountTypes(sWorld.getConfig(CONFIG_UINT32_GM_LEVEL_IN_WHO_LIST));
|
||||
|
||||
PlayerSocialMap::iterator itr = player->GetSocial()->m_playerSocialMap.find(friend_lowguid);
|
||||
if (itr != player->GetSocial()->m_playerSocialMap.end())
|
||||
friendInfo.Note = itr->second.Note;
|
||||
|
||||
// PLAYER see his team only and PLAYER can't see MODERATOR, GAME MASTER, ADMINISTRATOR characters
|
||||
// MODERATOR, GAME MASTER, ADMINISTRATOR can see all
|
||||
if (pFriend && pFriend->GetName() &&
|
||||
(security > SEC_PLAYER ||
|
||||
((pFriend->GetTeam() == team || allowTwoSideWhoList) && (pFriend->GetSession()->GetSecurity() <= gmLevelInWhoList))) &&
|
||||
pFriend->IsVisibleGloballyFor(player))
|
||||
{
|
||||
friendInfo.Status = FRIEND_STATUS_ONLINE;
|
||||
if (pFriend->isAFK())
|
||||
friendInfo.Status = FRIEND_STATUS_AFK;
|
||||
if (pFriend->isDND())
|
||||
friendInfo.Status = FRIEND_STATUS_DND;
|
||||
friendInfo.Area = pFriend->GetZoneId();
|
||||
friendInfo.Level = pFriend->getLevel();
|
||||
friendInfo.Class = pFriend->getClass();
|
||||
}
|
||||
else
|
||||
{
|
||||
friendInfo.Status = FRIEND_STATUS_OFFLINE;
|
||||
friendInfo.Area = 0;
|
||||
friendInfo.Level = 0;
|
||||
friendInfo.Class = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void SocialMgr::MakeFriendStatusPacket(FriendsResult result, uint32 guid, WorldPacket* data)
|
||||
{
|
||||
data->Initialize(SMSG_FRIEND_STATUS, 5);
|
||||
*data << uint8(result);
|
||||
*data << ObjectGuid(HIGHGUID_PLAYER, guid);
|
||||
}
|
||||
|
||||
void SocialMgr::SendFriendStatus(Player* player, FriendsResult result, ObjectGuid friend_guid, bool broadcast)
|
||||
{
|
||||
uint32 friend_lowguid = friend_guid.GetCounter();
|
||||
|
||||
FriendInfo fi;
|
||||
|
||||
WorldPacket data;
|
||||
MakeFriendStatusPacket(result, friend_lowguid, &data);
|
||||
GetFriendInfo(player, friend_lowguid, fi);
|
||||
switch (result)
|
||||
{
|
||||
case FRIEND_ADDED_OFFLINE:
|
||||
case FRIEND_ADDED_ONLINE:
|
||||
data << fi.Note;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case FRIEND_ADDED_ONLINE:
|
||||
case FRIEND_ONLINE:
|
||||
data << uint8(fi.Status);
|
||||
data << uint32(fi.Area);
|
||||
data << uint32(fi.Level);
|
||||
data << uint32(fi.Class);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (broadcast)
|
||||
BroadcastToFriendListers(player, &data);
|
||||
else
|
||||
player->GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
void SocialMgr::BroadcastToFriendListers(Player* player, WorldPacket* packet)
|
||||
{
|
||||
if (!player)
|
||||
return;
|
||||
|
||||
Team team = player->GetTeam();
|
||||
AccountTypes security = player->GetSession()->GetSecurity();
|
||||
uint32 guid = player->GetGUIDLow();
|
||||
AccountTypes gmLevelInWhoList = AccountTypes(sWorld.getConfig(CONFIG_UINT32_GM_LEVEL_IN_WHO_LIST));
|
||||
bool allowTwoSideWhoList = sWorld.getConfig(CONFIG_BOOL_ALLOW_TWO_SIDE_WHO_LIST);
|
||||
|
||||
for (SocialMap::const_iterator itr = m_socialMap.begin(); itr != m_socialMap.end(); ++itr)
|
||||
{
|
||||
PlayerSocialMap::const_iterator itr2 = itr->second.m_playerSocialMap.find(guid);
|
||||
if (itr2 != itr->second.m_playerSocialMap.end() && (itr2->second.Flags & SOCIAL_FLAG_FRIEND))
|
||||
{
|
||||
Player* pFriend = ObjectAccessor::FindPlayer(ObjectGuid(HIGHGUID_PLAYER, itr->first));
|
||||
|
||||
// PLAYER see his team only and PLAYER can't see MODERATOR, GAME MASTER, ADMINISTRATOR characters
|
||||
// MODERATOR, GAME MASTER, ADMINISTRATOR can see all
|
||||
if (pFriend && pFriend->IsInWorld() &&
|
||||
(pFriend->GetSession()->GetSecurity() > SEC_PLAYER ||
|
||||
((pFriend->GetTeam() == team || allowTwoSideWhoList) && security <= gmLevelInWhoList)) &&
|
||||
player->IsVisibleGloballyFor(pFriend))
|
||||
{
|
||||
pFriend->GetSession()->SendPacket(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PlayerSocial* SocialMgr::LoadFromDB(QueryResult* result, ObjectGuid guid)
|
||||
{
|
||||
PlayerSocial* social = &m_socialMap[guid.GetCounter()];
|
||||
social->SetPlayerGuid(guid);
|
||||
|
||||
if (!result)
|
||||
return social;
|
||||
|
||||
uint32 friend_guid = 0;
|
||||
uint32 flags = 0;
|
||||
std::string note = "";
|
||||
|
||||
// used to speed up check below. Using GetNumberOfSocialsWithFlag will cause unneeded iteration
|
||||
uint32 friendCounter = 0, ignoreCounter = 0;
|
||||
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
friend_guid = fields[0].GetUInt32();
|
||||
flags = fields[1].GetUInt32();
|
||||
note = fields[2].GetCppString();
|
||||
|
||||
if ((flags & SOCIAL_FLAG_IGNORED) && ignoreCounter >= SOCIALMGR_IGNORE_LIMIT)
|
||||
continue;
|
||||
if ((flags & SOCIAL_FLAG_FRIEND) && friendCounter >= SOCIALMGR_FRIEND_LIMIT)
|
||||
continue;
|
||||
|
||||
social->m_playerSocialMap[friend_guid] = FriendInfo(flags, note);
|
||||
|
||||
if (flags & SOCIAL_FLAG_IGNORED)
|
||||
++ignoreCounter;
|
||||
else
|
||||
++friendCounter;
|
||||
}
|
||||
while (result->NextRow());
|
||||
delete result;
|
||||
return social;
|
||||
}
|
||||
161
src/game/Object/SocialMgr.h
Normal file
161
src/game/Object/SocialMgr.h
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef __MANGOS_SOCIALMGR_H
|
||||
#define __MANGOS_SOCIALMGR_H
|
||||
|
||||
#include "Policies/Singleton.h"
|
||||
#include "Database/DatabaseEnv.h"
|
||||
#include "ObjectGuid.h"
|
||||
|
||||
class SocialMgr;
|
||||
class PlayerSocial;
|
||||
class Player;
|
||||
class WorldPacket;
|
||||
|
||||
enum FriendStatus
|
||||
{
|
||||
FRIEND_STATUS_OFFLINE = 0,
|
||||
FRIEND_STATUS_ONLINE = 1,
|
||||
FRIEND_STATUS_AFK = 2,
|
||||
FRIEND_STATUS_UNK3 = 3,
|
||||
FRIEND_STATUS_DND = 4
|
||||
};
|
||||
|
||||
enum SocialFlag
|
||||
{
|
||||
SOCIAL_FLAG_FRIEND = 0x01,
|
||||
SOCIAL_FLAG_IGNORED = 0x02,
|
||||
SOCIAL_FLAG_MUTED = 0x04, // guessed
|
||||
SOCIAL_FLAG_RAF = 0x08 // Recruit-A-Friend
|
||||
};
|
||||
|
||||
struct FriendInfo
|
||||
{
|
||||
FriendStatus Status;
|
||||
uint32 Flags;
|
||||
uint32 Area;
|
||||
uint32 Level;
|
||||
uint32 Class;
|
||||
std::string Note;
|
||||
|
||||
FriendInfo() :
|
||||
Status(FRIEND_STATUS_OFFLINE),
|
||||
Flags(0),
|
||||
Area(0),
|
||||
Level(0),
|
||||
Class(0)
|
||||
{}
|
||||
|
||||
FriendInfo(uint32 flags, const std::string& note) :
|
||||
Status(FRIEND_STATUS_OFFLINE),
|
||||
Flags(flags),
|
||||
Area(0),
|
||||
Level(0),
|
||||
Class(0),
|
||||
Note(note)
|
||||
{}
|
||||
};
|
||||
|
||||
typedef std::map<uint32, FriendInfo> PlayerSocialMap;
|
||||
typedef std::map<uint32, PlayerSocial> SocialMap;
|
||||
|
||||
/// Results of friend related commands
|
||||
enum FriendsResult
|
||||
{
|
||||
FRIEND_DB_ERROR = 0x00, // ERR_FRIEND_NOT_FOUND
|
||||
FRIEND_LIST_FULL = 0x01,
|
||||
FRIEND_ONLINE = 0x02,
|
||||
FRIEND_OFFLINE = 0x03,
|
||||
FRIEND_NOT_FOUND = 0x04, // ERR_FRIEND_NOT_FOUND
|
||||
FRIEND_REMOVED = 0x05,
|
||||
FRIEND_ADDED_ONLINE = 0x06, // ERR_FRIEND_ADDED_S
|
||||
FRIEND_ADDED_OFFLINE = 0x07,
|
||||
FRIEND_ALREADY = 0x08,
|
||||
FRIEND_SELF = 0x09,
|
||||
FRIEND_ENEMY = 0x0A,
|
||||
FRIEND_IGNORE_FULL = 0x0B,
|
||||
FRIEND_IGNORE_SELF = 0x0C,
|
||||
FRIEND_IGNORE_NOT_FOUND = 0x0D,
|
||||
FRIEND_IGNORE_ALREADY = 0x0E,
|
||||
FRIEND_IGNORE_ADDED = 0x0F,
|
||||
FRIEND_IGNORE_REMOVED = 0x10,
|
||||
FRIEND_IGNORE_AMBIGUOUS = 0x11, // That name is ambiguous, type more of the player's server name
|
||||
FRIEND_MUTE_FULL = 0x12,
|
||||
FRIEND_MUTE_SELF = 0x13,
|
||||
FRIEND_MUTE_NOT_FOUND = 0x14,
|
||||
FRIEND_MUTE_ALREADY = 0x15,
|
||||
FRIEND_MUTE_ADDED = 0x16,
|
||||
FRIEND_MUTE_REMOVED = 0x17,
|
||||
FRIEND_MUTE_AMBIGUOUS = 0x18, // ERR_VOICE_IGNORE_AMBIGUOUS
|
||||
FRIEND_UNK7 = 0x19, // ERR_MAX_VALUE (nothing is showed)
|
||||
FRIEND_UNKNOWN = 0x1A // Unknown friend response from server
|
||||
};
|
||||
|
||||
#define SOCIALMGR_FRIEND_LIMIT 50
|
||||
#define SOCIALMGR_IGNORE_LIMIT 50
|
||||
|
||||
class PlayerSocial
|
||||
{
|
||||
friend class SocialMgr;
|
||||
public:
|
||||
PlayerSocial();
|
||||
~PlayerSocial();
|
||||
// adding/removing
|
||||
bool AddToSocialList(ObjectGuid friend_guid, bool ignore);
|
||||
void RemoveFromSocialList(ObjectGuid friend_guid, bool ignore);
|
||||
void SetFriendNote(ObjectGuid friend_guid, std::string note);
|
||||
// Packet send's
|
||||
void SendSocialList();
|
||||
// Misc
|
||||
bool HasFriend(ObjectGuid friend_guid);
|
||||
bool HasIgnore(ObjectGuid ignore_guid);
|
||||
void SetPlayerGuid(ObjectGuid guid) { m_playerLowGuid = guid.GetCounter(); }
|
||||
uint32 GetNumberOfSocialsWithFlag(SocialFlag flag);
|
||||
private:
|
||||
PlayerSocialMap m_playerSocialMap;
|
||||
uint32 m_playerLowGuid;
|
||||
};
|
||||
|
||||
class SocialMgr
|
||||
{
|
||||
public:
|
||||
SocialMgr();
|
||||
~SocialMgr();
|
||||
// Misc
|
||||
void RemovePlayerSocial(uint32 guid) { m_socialMap.erase(guid); }
|
||||
|
||||
void GetFriendInfo(Player* player, uint32 friendGUID, FriendInfo& friendInfo);
|
||||
// Packet management
|
||||
void MakeFriendStatusPacket(FriendsResult result, uint32 friend_guid, WorldPacket* data);
|
||||
void SendFriendStatus(Player* player, FriendsResult result, ObjectGuid friend_guid, bool broadcast);
|
||||
void BroadcastToFriendListers(Player* player, WorldPacket* packet);
|
||||
// Loading
|
||||
PlayerSocial* LoadFromDB(QueryResult* result, ObjectGuid guid);
|
||||
private:
|
||||
SocialMap m_socialMap;
|
||||
};
|
||||
|
||||
#define sSocialMgr MaNGOS::Singleton<SocialMgr>::Instance()
|
||||
#endif
|
||||
4911
src/game/Object/SpellMgr.cpp
Normal file
4911
src/game/Object/SpellMgr.cpp
Normal file
File diff suppressed because it is too large
Load diff
1302
src/game/Object/SpellMgr.h
Normal file
1302
src/game/Object/SpellMgr.h
Normal file
File diff suppressed because it is too large
Load diff
1109
src/game/Object/StatSystem.cpp
Normal file
1109
src/game/Object/StatSystem.cpp
Normal file
File diff suppressed because it is too large
Load diff
219
src/game/Object/TemporarySummon.cpp
Normal file
219
src/game/Object/TemporarySummon.cpp
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include "TemporarySummon.h"
|
||||
#include "Log.h"
|
||||
#include "CreatureAI.h"
|
||||
|
||||
TemporarySummon::TemporarySummon(ObjectGuid summoner) :
|
||||
Creature(CREATURE_SUBTYPE_TEMPORARY_SUMMON), m_type(TEMPSUMMON_TIMED_OOC_OR_CORPSE_DESPAWN), m_timer(0), m_lifetime(0), m_summoner(summoner)
|
||||
{
|
||||
}
|
||||
|
||||
void TemporarySummon::Update(uint32 update_diff, uint32 diff)
|
||||
{
|
||||
switch (m_type)
|
||||
{
|
||||
case TEMPSUMMON_MANUAL_DESPAWN:
|
||||
break;
|
||||
case TEMPSUMMON_TIMED_DESPAWN:
|
||||
{
|
||||
if (m_timer <= update_diff)
|
||||
{
|
||||
UnSummon();
|
||||
return;
|
||||
}
|
||||
|
||||
m_timer -= update_diff;
|
||||
break;
|
||||
}
|
||||
case TEMPSUMMON_TIMED_OOC_DESPAWN:
|
||||
{
|
||||
if (!isInCombat())
|
||||
{
|
||||
if (m_timer <= update_diff)
|
||||
{
|
||||
UnSummon();
|
||||
return;
|
||||
}
|
||||
|
||||
m_timer -= update_diff;
|
||||
}
|
||||
else if (m_timer != m_lifetime)
|
||||
m_timer = m_lifetime;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case TEMPSUMMON_CORPSE_TIMED_DESPAWN:
|
||||
{
|
||||
if (IsCorpse())
|
||||
{
|
||||
if (m_timer <= update_diff)
|
||||
{
|
||||
UnSummon();
|
||||
return;
|
||||
}
|
||||
|
||||
m_timer -= update_diff;
|
||||
}
|
||||
if (IsDespawned())
|
||||
{
|
||||
UnSummon();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TEMPSUMMON_CORPSE_DESPAWN:
|
||||
{
|
||||
// if m_deathState is DEAD, CORPSE was skipped
|
||||
if (isDead())
|
||||
{
|
||||
UnSummon();
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case TEMPSUMMON_DEAD_DESPAWN:
|
||||
{
|
||||
if (IsDespawned())
|
||||
{
|
||||
UnSummon();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TEMPSUMMON_TIMED_OOC_OR_CORPSE_DESPAWN:
|
||||
{
|
||||
// if m_deathState is DEAD, CORPSE was skipped
|
||||
if (isDead())
|
||||
{
|
||||
UnSummon();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isInCombat())
|
||||
{
|
||||
if (m_timer <= update_diff)
|
||||
{
|
||||
UnSummon();
|
||||
return;
|
||||
}
|
||||
else
|
||||
m_timer -= update_diff;
|
||||
}
|
||||
else if (m_timer != m_lifetime)
|
||||
m_timer = m_lifetime;
|
||||
break;
|
||||
}
|
||||
case TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN:
|
||||
{
|
||||
// if m_deathState is DEAD, CORPSE was skipped
|
||||
if (IsDespawned())
|
||||
{
|
||||
UnSummon();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isInCombat() && isAlive())
|
||||
{
|
||||
if (m_timer <= update_diff)
|
||||
{
|
||||
UnSummon();
|
||||
return;
|
||||
}
|
||||
else
|
||||
m_timer -= update_diff;
|
||||
}
|
||||
else if (m_timer != m_lifetime)
|
||||
m_timer = m_lifetime;
|
||||
break;
|
||||
}
|
||||
case TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN:
|
||||
{
|
||||
// if m_deathState is DEAD, CORPSE was skipped
|
||||
if (isDead())
|
||||
{
|
||||
UnSummon();
|
||||
return;
|
||||
}
|
||||
if (m_timer <= update_diff)
|
||||
{
|
||||
UnSummon();
|
||||
return;
|
||||
}
|
||||
m_timer -= update_diff;
|
||||
break;
|
||||
}
|
||||
case TEMPSUMMON_TIMED_OR_DEAD_DESPAWN:
|
||||
{
|
||||
// if m_deathState is DEAD, CORPSE was skipped
|
||||
if (IsDespawned())
|
||||
{
|
||||
UnSummon();
|
||||
return;
|
||||
}
|
||||
if (m_timer <= update_diff)
|
||||
{
|
||||
UnSummon();
|
||||
return;
|
||||
}
|
||||
m_timer -= update_diff;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UnSummon();
|
||||
sLog.outError("Temporary summoned creature (entry: %u) have unknown type %u of ", GetEntry(), m_type);
|
||||
break;
|
||||
}
|
||||
|
||||
Creature::Update(update_diff, diff);
|
||||
}
|
||||
|
||||
void TemporarySummon::Summon(TempSummonType type, uint32 lifetime)
|
||||
{
|
||||
m_type = type;
|
||||
m_timer = lifetime;
|
||||
m_lifetime = lifetime;
|
||||
|
||||
AIM_Initialize();
|
||||
GetMap()->Add((Creature*)this);
|
||||
}
|
||||
|
||||
void TemporarySummon::UnSummon()
|
||||
{
|
||||
CombatStop();
|
||||
|
||||
if (GetSummonerGuid().IsCreatureOrVehicle())
|
||||
if (Creature* sum = GetMap()->GetCreature(GetSummonerGuid()))
|
||||
if (sum->AI())
|
||||
sum->AI()->SummonedCreatureDespawn(this);
|
||||
|
||||
AddObjectToRemoveList();
|
||||
}
|
||||
|
||||
void TemporarySummon::SaveToDB()
|
||||
{
|
||||
}
|
||||
58
src/game/Object/TemporarySummon.h
Normal file
58
src/game/Object/TemporarySummon.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOSSERVER_TEMPSUMMON_H
|
||||
#define MANGOSSERVER_TEMPSUMMON_H
|
||||
|
||||
#include "Creature.h"
|
||||
#include "ObjectAccessor.h"
|
||||
|
||||
class TemporarySummon : public Creature
|
||||
{
|
||||
public:
|
||||
explicit TemporarySummon(ObjectGuid summoner = ObjectGuid());
|
||||
virtual ~TemporarySummon() {};
|
||||
|
||||
void Update(uint32 update_diff, uint32 time) override;
|
||||
void Summon(TempSummonType type, uint32 lifetime);
|
||||
void MANGOS_DLL_SPEC UnSummon();
|
||||
void SaveToDB();
|
||||
ObjectGuid const& GetSummonerGuid() const { return m_summoner ; }
|
||||
Unit* GetSummoner() const { return ObjectAccessor::GetUnit(*this, m_summoner); }
|
||||
private:
|
||||
void SaveToDB(uint32, uint8, uint32) override // overwrited of Creature::SaveToDB - don't must be called
|
||||
{
|
||||
MANGOS_ASSERT(false);
|
||||
}
|
||||
void DeleteFromDB() override // overwrited of Creature::DeleteFromDB - don't must be called
|
||||
{
|
||||
MANGOS_ASSERT(false);
|
||||
}
|
||||
|
||||
TempSummonType m_type;
|
||||
uint32 m_timer;
|
||||
uint32 m_lifetime;
|
||||
ObjectGuid m_summoner;
|
||||
};
|
||||
#endif
|
||||
217
src/game/Object/Totem.cpp
Normal file
217
src/game/Object/Totem.cpp
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include "Totem.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "Log.h"
|
||||
#include "Group.h"
|
||||
#include "Player.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "SpellMgr.h"
|
||||
#include "CreatureAI.h"
|
||||
#include "InstanceData.h"
|
||||
|
||||
Totem::Totem() : Creature(CREATURE_SUBTYPE_TOTEM)
|
||||
{
|
||||
m_duration = 0;
|
||||
m_type = TOTEM_PASSIVE;
|
||||
}
|
||||
|
||||
bool Totem::Create(uint32 guidlow, CreatureCreatePos& cPos, CreatureInfo const* cinfo, Unit* owner)
|
||||
{
|
||||
SetMap(cPos.GetMap());
|
||||
SetPhaseMask(cPos.GetPhaseMask(), false);
|
||||
|
||||
Team team = owner->GetTypeId() == TYPEID_PLAYER ? ((Player*)owner)->GetTeam() : TEAM_NONE;
|
||||
|
||||
if (!CreateFromProto(guidlow, cinfo, team))
|
||||
return false;
|
||||
|
||||
// special model selection case for totems
|
||||
if (owner->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
if (uint32 modelid_race = sObjectMgr.GetModelForRace(GetNativeDisplayId(), owner->getRaceMask()))
|
||||
SetDisplayId(modelid_race);
|
||||
}
|
||||
|
||||
cPos.SelectFinalPoint(this);
|
||||
|
||||
// totem must be at same Z in case swimming caster and etc.
|
||||
if (fabs(cPos.m_pos.z - owner->GetPositionZ()) > 5.0f)
|
||||
cPos.m_pos.z = owner->GetPositionZ();
|
||||
|
||||
if (!cPos.Relocate(this))
|
||||
return false;
|
||||
|
||||
// Notify the map's instance data.
|
||||
// Only works if you create the object in it, not if it is moves to that map.
|
||||
// Normally non-players do not teleport to other maps.
|
||||
if (InstanceData* iData = GetMap()->GetInstanceData())
|
||||
iData->OnCreatureCreate(this);
|
||||
|
||||
LoadCreatureAddon(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Totem::Update(uint32 update_diff, uint32 time)
|
||||
{
|
||||
Unit* owner = GetOwner();
|
||||
if (!owner || !owner->isAlive() || !isAlive())
|
||||
{
|
||||
UnSummon(); // remove self
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_duration <= update_diff)
|
||||
{
|
||||
UnSummon(); // remove self
|
||||
return;
|
||||
}
|
||||
else
|
||||
m_duration -= update_diff;
|
||||
|
||||
Creature::Update(update_diff, time);
|
||||
}
|
||||
|
||||
void Totem::Summon(Unit* owner)
|
||||
{
|
||||
AIM_Initialize();
|
||||
owner->GetMap()->Add((Creature*)this);
|
||||
|
||||
if (owner->GetTypeId() == TYPEID_UNIT && ((Creature*)owner)->AI())
|
||||
((Creature*)owner)->AI()->JustSummoned((Creature*)this);
|
||||
|
||||
// there are some totems, which exist just for their visual appeareance
|
||||
if (!GetSpell())
|
||||
return;
|
||||
|
||||
switch (m_type)
|
||||
{
|
||||
case TOTEM_PASSIVE:
|
||||
CastSpell(this, GetSpell(), true);
|
||||
break;
|
||||
case TOTEM_STATUE:
|
||||
CastSpell(GetOwner(), GetSpell(), true);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void Totem::UnSummon()
|
||||
{
|
||||
CombatStop();
|
||||
RemoveAurasDueToSpell(GetSpell());
|
||||
|
||||
if (Unit* owner = GetOwner())
|
||||
{
|
||||
owner->_RemoveTotem(this);
|
||||
owner->RemoveAurasDueToSpell(GetSpell());
|
||||
|
||||
// remove aura all party members too
|
||||
if (owner->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
((Player*)owner)->SendAutoRepeatCancel(this);
|
||||
|
||||
// Not only the player can summon the totem (scripted AI)
|
||||
if (Group* pGroup = ((Player*)owner)->GetGroup())
|
||||
{
|
||||
for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
|
||||
{
|
||||
Player* Target = itr->getSource();
|
||||
if (Target && pGroup->SameSubGroup((Player*)owner, Target))
|
||||
Target->RemoveAurasDueToSpell(GetSpell());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (owner->GetTypeId() == TYPEID_UNIT && ((Creature*)owner)->AI())
|
||||
((Creature*)owner)->AI()->SummonedCreatureDespawn((Creature*)this);
|
||||
}
|
||||
|
||||
// any totem unsummon look like as totem kill, req. for proper animation
|
||||
if (isAlive())
|
||||
SetDeathState(DEAD);
|
||||
|
||||
AddObjectToRemoveList();
|
||||
}
|
||||
|
||||
void Totem::SetOwner(Unit* owner)
|
||||
{
|
||||
SetCreatorGuid(owner->GetObjectGuid());
|
||||
SetOwnerGuid(owner->GetObjectGuid());
|
||||
setFaction(owner->getFaction());
|
||||
SetLevel(owner->getLevel());
|
||||
}
|
||||
|
||||
Unit* Totem::GetOwner()
|
||||
{
|
||||
if (ObjectGuid ownerGuid = GetOwnerGuid())
|
||||
return ObjectAccessor::GetUnit(*this, ownerGuid);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Totem::SetTypeBySummonSpell(SpellEntry const* spellProto)
|
||||
{
|
||||
// Get spell casted by totem
|
||||
SpellEntry const* totemSpell = sSpellStore.LookupEntry(GetSpell());
|
||||
if (totemSpell)
|
||||
{
|
||||
// If spell have cast time -> so its active totem
|
||||
if (GetSpellCastTime(totemSpell))
|
||||
m_type = TOTEM_ACTIVE;
|
||||
}
|
||||
if (spellProto->SpellIconID == 2056)
|
||||
m_type = TOTEM_STATUE; // Jewelery statue
|
||||
}
|
||||
|
||||
bool Totem::IsImmuneToSpellEffect(SpellEntry const* spellInfo, SpellEffectIndex index, bool castOnSelf) const
|
||||
{
|
||||
SpellEffectEntry const* spellEffect = spellInfo->GetSpellEffect(index);
|
||||
if(spellEffect)
|
||||
{
|
||||
// TODO: possibly all negative auras immune?
|
||||
switch(spellEffect->Effect)
|
||||
{
|
||||
case SPELL_EFFECT_ATTACK_ME:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch(spellEffect->EffectApplyAuraName)
|
||||
{
|
||||
case SPELL_AURA_PERIODIC_DAMAGE:
|
||||
case SPELL_AURA_PERIODIC_LEECH:
|
||||
case SPELL_AURA_MOD_FEAR:
|
||||
case SPELL_AURA_TRANSFORM:
|
||||
case SPELL_AURA_MOD_TAUNT:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Creature::IsImmuneToSpellEffect(spellInfo, index, castOnSelf);
|
||||
}
|
||||
69
src/game/Object/Totem.h
Normal file
69
src/game/Object/Totem.h
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOSSERVER_TOTEM_H
|
||||
#define MANGOSSERVER_TOTEM_H
|
||||
|
||||
#include "Creature.h"
|
||||
|
||||
enum TotemType
|
||||
{
|
||||
TOTEM_PASSIVE = 0,
|
||||
TOTEM_ACTIVE = 1,
|
||||
TOTEM_STATUE = 2
|
||||
};
|
||||
|
||||
class Totem : public Creature
|
||||
{
|
||||
public:
|
||||
explicit Totem();
|
||||
virtual ~Totem() {};
|
||||
bool Create(uint32 guidlow, CreatureCreatePos& cPos, CreatureInfo const* cinfo, Unit* owner);
|
||||
void Update(uint32 update_diff, uint32 time) override;
|
||||
void Summon(Unit* owner);
|
||||
void UnSummon();
|
||||
uint32 GetSpell() const { return m_spells[0]; }
|
||||
uint32 GetTotemDuration() const { return m_duration; }
|
||||
Unit* GetOwner();
|
||||
TotemType GetTotemType() const { return m_type; }
|
||||
void SetTypeBySummonSpell(SpellEntry const* spellProto);
|
||||
void SetDuration(uint32 dur) { m_duration = dur; }
|
||||
void SetOwner(Unit* owner);
|
||||
|
||||
bool UpdateStats(Stats /*stat*/) override { return true; }
|
||||
bool UpdateAllStats() override { return true; }
|
||||
void UpdateResistances(uint32 /*school*/) override {}
|
||||
void UpdateArmor() override {}
|
||||
void UpdateMaxHealth() override {}
|
||||
void UpdateMaxPower(Powers /*power*/) override {}
|
||||
void UpdateAttackPowerAndDamage(bool /*ranged*/) override {}
|
||||
void UpdateDamagePhysical(WeaponAttackType /*attType*/) override {}
|
||||
|
||||
bool IsImmuneToSpellEffect(SpellEntry const* spellInfo, SpellEffectIndex index, bool castOnSelf) const override;
|
||||
|
||||
protected:
|
||||
TotemType m_type;
|
||||
uint32 m_duration;
|
||||
};
|
||||
#endif
|
||||
121
src/game/Object/TotemAI.cpp
Normal file
121
src/game/Object/TotemAI.cpp
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include "TotemAI.h"
|
||||
#include "Totem.h"
|
||||
#include "Creature.h"
|
||||
#include "DBCStores.h"
|
||||
#include "SpellMgr.h"
|
||||
|
||||
#include "GridNotifiers.h"
|
||||
#include "GridNotifiersImpl.h"
|
||||
#include "CellImpl.h"
|
||||
|
||||
int
|
||||
TotemAI::Permissible(const Creature* creature)
|
||||
{
|
||||
if (creature->IsTotem())
|
||||
return PERMIT_BASE_PROACTIVE;
|
||||
|
||||
return PERMIT_BASE_NO;
|
||||
}
|
||||
|
||||
TotemAI::TotemAI(Creature* c) : CreatureAI(c)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TotemAI::MoveInLineOfSight(Unit*)
|
||||
{
|
||||
}
|
||||
|
||||
void TotemAI::EnterEvadeMode()
|
||||
{
|
||||
m_creature->CombatStop(true);
|
||||
}
|
||||
|
||||
void
|
||||
TotemAI::UpdateAI(const uint32 /*diff*/)
|
||||
{
|
||||
if (getTotem().GetTotemType() != TOTEM_ACTIVE)
|
||||
return;
|
||||
|
||||
if (!m_creature->isAlive() || m_creature->IsNonMeleeSpellCasted(false))
|
||||
return;
|
||||
|
||||
// Search spell
|
||||
SpellEntry const* spellInfo = sSpellStore.LookupEntry(getTotem().GetSpell());
|
||||
if (!spellInfo)
|
||||
return;
|
||||
|
||||
// Get spell rangy
|
||||
SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex);
|
||||
float max_range = GetSpellMaxRange(srange);
|
||||
|
||||
// SPELLMOD_RANGE not applied in this place just because nonexistent range mods for attacking totems
|
||||
|
||||
// pointer to appropriate target if found any
|
||||
Unit* victim = m_creature->GetMap()->GetUnit(i_victimGuid);
|
||||
|
||||
// Search victim if no, not attackable, or out of range, or friendly (possible in case duel end)
|
||||
if (!victim ||
|
||||
!victim->isTargetableForAttack() || !m_creature->IsWithinDistInMap(victim, max_range) ||
|
||||
m_creature->IsFriendlyTo(victim) || !victim->isVisibleForOrDetect(m_creature, m_creature, false))
|
||||
{
|
||||
victim = NULL;
|
||||
|
||||
MaNGOS::NearestAttackableUnitInObjectRangeCheck u_check(m_creature, m_creature, max_range);
|
||||
MaNGOS::UnitLastSearcher<MaNGOS::NearestAttackableUnitInObjectRangeCheck> checker(victim, u_check);
|
||||
Cell::VisitAllObjects(m_creature, checker, max_range);
|
||||
}
|
||||
|
||||
// If have target
|
||||
if (victim)
|
||||
{
|
||||
// remember
|
||||
i_victimGuid = victim->GetObjectGuid();
|
||||
|
||||
// attack
|
||||
m_creature->SetInFront(victim); // client change orientation by self
|
||||
m_creature->CastSpell(victim, getTotem().GetSpell(), false);
|
||||
}
|
||||
else
|
||||
i_victimGuid.Clear();
|
||||
}
|
||||
|
||||
bool
|
||||
TotemAI::IsVisible(Unit*) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
TotemAI::AttackStart(Unit*)
|
||||
{
|
||||
}
|
||||
|
||||
Totem& TotemAI::getTotem()
|
||||
{
|
||||
return static_cast<Totem&>(*m_creature);
|
||||
}
|
||||
54
src/game/Object/TotemAI.h
Normal file
54
src/game/Object/TotemAI.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef MANGOS_TOTEMAI_H
|
||||
#define MANGOS_TOTEMAI_H
|
||||
|
||||
#include "CreatureAI.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Timer.h"
|
||||
|
||||
class Creature;
|
||||
class Totem;
|
||||
|
||||
class MANGOS_DLL_DECL TotemAI : public CreatureAI
|
||||
{
|
||||
public:
|
||||
|
||||
explicit TotemAI(Creature* c);
|
||||
|
||||
void MoveInLineOfSight(Unit*) override;
|
||||
void AttackStart(Unit*) override;
|
||||
void EnterEvadeMode() override;
|
||||
bool IsVisible(Unit*) const override;
|
||||
|
||||
void UpdateAI(const uint32) override;
|
||||
static int Permissible(const Creature*);
|
||||
protected:
|
||||
Totem& getTotem();
|
||||
|
||||
private:
|
||||
ObjectGuid i_victimGuid;
|
||||
};
|
||||
#endif
|
||||
11959
src/game/Object/Unit.cpp
Normal file
11959
src/game/Object/Unit.cpp
Normal file
File diff suppressed because it is too large
Load diff
3474
src/game/Object/Unit.h
Normal file
3474
src/game/Object/Unit.h
Normal file
File diff suppressed because it is too large
Load diff
135
src/game/Object/UnitEvents.h
Normal file
135
src/game/Object/UnitEvents.h
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef _UNITEVENTS
|
||||
#define _UNITEVENTS
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
class ThreatContainer;
|
||||
class ThreatManager;
|
||||
class HostileReference;
|
||||
|
||||
//==============================================================
|
||||
//==============================================================
|
||||
|
||||
enum UnitThreatEventType
|
||||
{
|
||||
// Player/Pet changed on/offline status
|
||||
UEV_THREAT_REF_ONLINE_STATUS = 1 << 0,
|
||||
|
||||
// Threat for Player/Pet changed
|
||||
UEV_THREAT_REF_THREAT_CHANGE = 1 << 1,
|
||||
|
||||
// Player/Pet will be removed from list (dead) [for internal use]
|
||||
UEV_THREAT_REF_REMOVE_FROM_LIST = 1 << 2,
|
||||
|
||||
// Player/Pet entered/left water or some other place where it is/was not accessible for the creature
|
||||
UEV_THREAT_REF_ASSECCIBLE_STATUS = 1 << 3,
|
||||
|
||||
// Threat list is going to be sorted (if dirty flag is set)
|
||||
UEV_THREAT_SORT_LIST = 1 << 4,
|
||||
|
||||
// New target should be fetched, could tbe the current target as well
|
||||
UEV_THREAT_SET_NEXT_TARGET = 1 << 5,
|
||||
|
||||
// A new victim (target) was set. Could be NULL
|
||||
UEV_THREAT_VICTIM_CHANGED = 1 << 6,
|
||||
};
|
||||
|
||||
#define UEV_THREAT_REF_EVENT_MASK ( UEV_THREAT_REF_ONLINE_STATUS | UEV_THREAT_REF_THREAT_CHANGE | UEV_THREAT_REF_REMOVE_FROM_LIST | UEV_THREAT_REF_ASSECCIBLE_STATUS)
|
||||
#define UEV_THREAT_MANAGER_EVENT_MASK (UEV_THREAT_SORT_LIST | UEV_THREAT_SET_NEXT_TARGET | UEV_THREAT_VICTIM_CHANGED)
|
||||
#define UEV_ALL_EVENT_MASK (0xffffffff)
|
||||
|
||||
// Future use
|
||||
//#define UEV_UNIT_EVENT_MASK (UEV_UNIT_KILLED | UEV_UNIT_HEALTH_CHANGE)
|
||||
|
||||
//==============================================================
|
||||
|
||||
class MANGOS_DLL_SPEC UnitBaseEvent
|
||||
{
|
||||
private:
|
||||
uint32 iType;
|
||||
public:
|
||||
UnitBaseEvent(uint32 pType) { iType = pType; }
|
||||
uint32 getType() const { return iType; }
|
||||
bool matchesTypeMask(uint32 pMask) const { return iType & pMask; }
|
||||
|
||||
void setType(uint32 pType) { iType = pType; }
|
||||
};
|
||||
|
||||
//==============================================================
|
||||
|
||||
class MANGOS_DLL_SPEC ThreatRefStatusChangeEvent : public UnitBaseEvent
|
||||
{
|
||||
private:
|
||||
HostileReference* iHostileReference;
|
||||
union
|
||||
{
|
||||
float iFValue;
|
||||
int32 iIValue;
|
||||
bool iBValue;
|
||||
};
|
||||
ThreatManager* iThreatManager;
|
||||
public:
|
||||
ThreatRefStatusChangeEvent(uint32 pType) : UnitBaseEvent(pType) { iHostileReference = NULL; }
|
||||
|
||||
ThreatRefStatusChangeEvent(uint32 pType, HostileReference* pHostileReference) : UnitBaseEvent(pType) { iHostileReference = pHostileReference; }
|
||||
|
||||
ThreatRefStatusChangeEvent(uint32 pType, HostileReference* pHostileReference, float pValue) : UnitBaseEvent(pType) { iHostileReference = pHostileReference; iFValue = pValue; }
|
||||
|
||||
ThreatRefStatusChangeEvent(uint32 pType, HostileReference* pHostileReference, bool pValue) : UnitBaseEvent(pType) { iHostileReference = pHostileReference; iBValue = pValue; }
|
||||
|
||||
int32 getIValue() const { return iIValue; }
|
||||
|
||||
float getFValue() const { return iFValue; }
|
||||
|
||||
bool getBValue() const { return iBValue; }
|
||||
|
||||
void setBValue(bool pValue) { iBValue = pValue; }
|
||||
|
||||
HostileReference* getReference() const { return iHostileReference; }
|
||||
|
||||
void setThreatManager(ThreatManager* pThreatManager) { iThreatManager = pThreatManager; }
|
||||
|
||||
ThreatManager* getThreatManager() const { return iThreatManager; }
|
||||
};
|
||||
|
||||
//==============================================================
|
||||
|
||||
class MANGOS_DLL_SPEC ThreatManagerEvent : public ThreatRefStatusChangeEvent
|
||||
{
|
||||
private:
|
||||
ThreatContainer* iThreatContainer;
|
||||
public:
|
||||
ThreatManagerEvent(uint32 pType) : ThreatRefStatusChangeEvent(pType) {}
|
||||
ThreatManagerEvent(uint32 pType, HostileReference* pHostileReference) : ThreatRefStatusChangeEvent(pType, pHostileReference) {}
|
||||
|
||||
void setThreatContainer(ThreatContainer* pThreatContainer) { iThreatContainer = pThreatContainer; }
|
||||
|
||||
ThreatContainer* getThreatContainer() const { return iThreatContainer; }
|
||||
};
|
||||
|
||||
//==============================================================
|
||||
#endif
|
||||
571
src/game/Object/UpdateFields.h
Normal file
571
src/game/Object/UpdateFields.h
Normal file
|
|
@ -0,0 +1,571 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef _UPDATEFIELDS_AUTO_H
|
||||
#define _UPDATEFIELDS_AUTO_H
|
||||
|
||||
// Auto generated for version 4, 3, 4, 15595
|
||||
|
||||
enum EObjectFields
|
||||
{
|
||||
OBJECT_FIELD_GUID = 0x0,
|
||||
OBJECT_FIELD_DATA = 0x2,
|
||||
OBJECT_FIELD_TYPE = 0x4,
|
||||
OBJECT_FIELD_ENTRY = 0x5,
|
||||
OBJECT_FIELD_SCALE_X = 0x6,
|
||||
OBJECT_FIELD_PADDING = 0x7,
|
||||
OBJECT_END = 0x8
|
||||
};
|
||||
|
||||
enum EUnitFields
|
||||
{
|
||||
UNIT_FIELD_CHARM = OBJECT_END + 0x0,
|
||||
UNIT_FIELD_SUMMON = OBJECT_END + 0x2,
|
||||
UNIT_FIELD_CRITTER = OBJECT_END + 0x4,
|
||||
UNIT_FIELD_CHARMEDBY = OBJECT_END + 0x6,
|
||||
UNIT_FIELD_SUMMONEDBY = OBJECT_END + 0x8,
|
||||
UNIT_FIELD_CREATEDBY = OBJECT_END + 0xA,
|
||||
UNIT_FIELD_TARGET = OBJECT_END + 0xC,
|
||||
UNIT_FIELD_CHANNEL_OBJECT = OBJECT_END + 0xE,
|
||||
UNIT_CHANNEL_SPELL = OBJECT_END + 0x10,
|
||||
UNIT_FIELD_BYTES_0 = OBJECT_END + 0x11,
|
||||
UNIT_FIELD_HEALTH = OBJECT_END + 0x12,
|
||||
UNIT_FIELD_POWER1 = OBJECT_END + 0x13,
|
||||
UNIT_FIELD_POWER2 = OBJECT_END + 0x14,
|
||||
UNIT_FIELD_POWER3 = OBJECT_END + 0x15,
|
||||
UNIT_FIELD_POWER4 = OBJECT_END + 0x16,
|
||||
UNIT_FIELD_POWER5 = OBJECT_END + 0x17,
|
||||
UNIT_FIELD_MAXHEALTH = OBJECT_END + 0x18,
|
||||
UNIT_FIELD_MAXPOWER1 = OBJECT_END + 0x19,
|
||||
UNIT_FIELD_MAXPOWER2 = OBJECT_END + 0x1A,
|
||||
UNIT_FIELD_MAXPOWER3 = OBJECT_END + 0x1B,
|
||||
UNIT_FIELD_MAXPOWER4 = OBJECT_END + 0x1C,
|
||||
UNIT_FIELD_MAXPOWER5 = OBJECT_END + 0x1D,
|
||||
UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER = OBJECT_END + 0x1E,
|
||||
UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER = OBJECT_END + 0x23,
|
||||
UNIT_FIELD_LEVEL = OBJECT_END + 0x28,
|
||||
UNIT_FIELD_FACTIONTEMPLATE = OBJECT_END + 0x29,
|
||||
UNIT_VIRTUAL_ITEM_SLOT_ID = OBJECT_END + 0x2A,
|
||||
UNIT_FIELD_FLAGS = OBJECT_END + 0x2D,
|
||||
UNIT_FIELD_FLAGS_2 = OBJECT_END + 0x2E,
|
||||
UNIT_FIELD_AURASTATE = OBJECT_END + 0x2F,
|
||||
UNIT_FIELD_BASEATTACKTIME = OBJECT_END + 0x30,
|
||||
UNIT_FIELD_RANGEDATTACKTIME = OBJECT_END + 0x32,
|
||||
UNIT_FIELD_BOUNDINGRADIUS = OBJECT_END + 0x33,
|
||||
UNIT_FIELD_COMBATREACH = OBJECT_END + 0x34,
|
||||
UNIT_FIELD_DISPLAYID = OBJECT_END + 0x35,
|
||||
UNIT_FIELD_NATIVEDISPLAYID = OBJECT_END + 0x36,
|
||||
UNIT_FIELD_MOUNTDISPLAYID = OBJECT_END + 0x37,
|
||||
UNIT_FIELD_MINDAMAGE = OBJECT_END + 0x38,
|
||||
UNIT_FIELD_MAXDAMAGE = OBJECT_END + 0x39,
|
||||
UNIT_FIELD_MINOFFHANDDAMAGE = OBJECT_END + 0x3A,
|
||||
UNIT_FIELD_MAXOFFHANDDAMAGE = OBJECT_END + 0x3B,
|
||||
UNIT_FIELD_BYTES_1 = OBJECT_END + 0x3C,
|
||||
UNIT_FIELD_PETNUMBER = OBJECT_END + 0x3D,
|
||||
UNIT_FIELD_PET_NAME_TIMESTAMP = OBJECT_END + 0x3E,
|
||||
UNIT_FIELD_PETEXPERIENCE = OBJECT_END + 0x3F,
|
||||
UNIT_FIELD_PETNEXTLEVELEXP = OBJECT_END + 0x40,
|
||||
UNIT_DYNAMIC_FLAGS = OBJECT_END + 0x41,
|
||||
UNIT_MOD_CAST_SPEED = OBJECT_END + 0x42,
|
||||
UNIT_MOD_CAST_HASTE = OBJECT_END + 0x43,
|
||||
UNIT_CREATED_BY_SPELL = OBJECT_END + 0x44,
|
||||
UNIT_NPC_FLAGS = OBJECT_END + 0x45,
|
||||
UNIT_NPC_EMOTESTATE = OBJECT_END + 0x46,
|
||||
UNIT_FIELD_STAT0 = OBJECT_END + 0x47,
|
||||
UNIT_FIELD_STAT1 = OBJECT_END + 0x48,
|
||||
UNIT_FIELD_STAT2 = OBJECT_END + 0x49,
|
||||
UNIT_FIELD_STAT3 = OBJECT_END + 0x4A,
|
||||
UNIT_FIELD_STAT4 = OBJECT_END + 0x4B,
|
||||
UNIT_FIELD_POSSTAT0 = OBJECT_END + 0x4C,
|
||||
UNIT_FIELD_POSSTAT1 = OBJECT_END + 0x4D,
|
||||
UNIT_FIELD_POSSTAT2 = OBJECT_END + 0x4E,
|
||||
UNIT_FIELD_POSSTAT3 = OBJECT_END + 0x4F,
|
||||
UNIT_FIELD_POSSTAT4 = OBJECT_END + 0x50,
|
||||
UNIT_FIELD_NEGSTAT0 = OBJECT_END + 0x51,
|
||||
UNIT_FIELD_NEGSTAT1 = OBJECT_END + 0x52,
|
||||
UNIT_FIELD_NEGSTAT2 = OBJECT_END + 0x53,
|
||||
UNIT_FIELD_NEGSTAT3 = OBJECT_END + 0x54,
|
||||
UNIT_FIELD_NEGSTAT4 = OBJECT_END + 0x55,
|
||||
UNIT_FIELD_RESISTANCES = OBJECT_END + 0x56,
|
||||
UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE = OBJECT_END + 0x5D,
|
||||
UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE = OBJECT_END + 0x64,
|
||||
UNIT_FIELD_BASE_MANA = OBJECT_END + 0x6B,
|
||||
UNIT_FIELD_BASE_HEALTH = OBJECT_END + 0x6C,
|
||||
UNIT_FIELD_BYTES_2 = OBJECT_END + 0x6D,
|
||||
UNIT_FIELD_ATTACK_POWER = OBJECT_END + 0x6E,
|
||||
UNIT_FIELD_ATTACK_POWER_MOD_POS = OBJECT_END + 0x6F,
|
||||
UNIT_FIELD_ATTACK_POWER_MOD_NEG = OBJECT_END + 0x70,
|
||||
UNIT_FIELD_ATTACK_POWER_MULTIPLIER = OBJECT_END + 0x71,
|
||||
UNIT_FIELD_RANGED_ATTACK_POWER = OBJECT_END + 0x72,
|
||||
UNIT_FIELD_RANGED_ATTACK_POWER_MOD_POS = OBJECT_END + 0x73,
|
||||
UNIT_FIELD_RANGED_ATTACK_POWER_MOD_NEG = OBJECT_END + 0x74,
|
||||
UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER = OBJECT_END + 0x75,
|
||||
UNIT_FIELD_MINRANGEDDAMAGE = OBJECT_END + 0x76,
|
||||
UNIT_FIELD_MAXRANGEDDAMAGE = OBJECT_END + 0x77,
|
||||
UNIT_FIELD_POWER_COST_MODIFIER = OBJECT_END + 0x78,
|
||||
UNIT_FIELD_POWER_COST_MULTIPLIER = OBJECT_END + 0x7F,
|
||||
UNIT_FIELD_MAXHEALTHMODIFIER = OBJECT_END + 0x86,
|
||||
UNIT_FIELD_HOVERHEIGHT = OBJECT_END + 0x87,
|
||||
UNIT_FIELD_MAXITEMLEVEL = OBJECT_END + 0x88,
|
||||
UNIT_FIELD_PADDING = OBJECT_END + 0x89,
|
||||
UNIT_END = OBJECT_END + 0x8A
|
||||
};
|
||||
|
||||
enum EItemFields
|
||||
{
|
||||
ITEM_FIELD_OWNER = OBJECT_END + 0x0,
|
||||
ITEM_FIELD_CONTAINED = OBJECT_END + 0x2,
|
||||
ITEM_FIELD_CREATOR = OBJECT_END + 0x4,
|
||||
ITEM_FIELD_GIFTCREATOR = OBJECT_END + 0x6,
|
||||
ITEM_FIELD_STACK_COUNT = OBJECT_END + 0x8,
|
||||
ITEM_FIELD_DURATION = OBJECT_END + 0x9,
|
||||
ITEM_FIELD_SPELL_CHARGES = OBJECT_END + 0xA,
|
||||
ITEM_FIELD_FLAGS = OBJECT_END + 0xF,
|
||||
ITEM_FIELD_ENCHANTMENT_1_1 = OBJECT_END + 0x10,
|
||||
ITEM_FIELD_ENCHANTMENT_1_3 = OBJECT_END + 0x12,
|
||||
ITEM_FIELD_ENCHANTMENT_2_1 = OBJECT_END + 0x13,
|
||||
ITEM_FIELD_ENCHANTMENT_2_3 = OBJECT_END + 0x15,
|
||||
ITEM_FIELD_ENCHANTMENT_3_1 = OBJECT_END + 0x16,
|
||||
ITEM_FIELD_ENCHANTMENT_3_3 = OBJECT_END + 0x18,
|
||||
ITEM_FIELD_ENCHANTMENT_4_1 = OBJECT_END + 0x19,
|
||||
ITEM_FIELD_ENCHANTMENT_4_3 = OBJECT_END + 0x1B,
|
||||
ITEM_FIELD_ENCHANTMENT_5_1 = OBJECT_END + 0x1C,
|
||||
ITEM_FIELD_ENCHANTMENT_5_3 = OBJECT_END + 0x1E,
|
||||
ITEM_FIELD_ENCHANTMENT_6_1 = OBJECT_END + 0x1F,
|
||||
ITEM_FIELD_ENCHANTMENT_6_3 = OBJECT_END + 0x21,
|
||||
ITEM_FIELD_ENCHANTMENT_7_1 = OBJECT_END + 0x22,
|
||||
ITEM_FIELD_ENCHANTMENT_7_3 = OBJECT_END + 0x24,
|
||||
ITEM_FIELD_ENCHANTMENT_8_1 = OBJECT_END + 0x25,
|
||||
ITEM_FIELD_ENCHANTMENT_8_3 = OBJECT_END + 0x27,
|
||||
ITEM_FIELD_ENCHANTMENT_9_1 = OBJECT_END + 0x28,
|
||||
ITEM_FIELD_ENCHANTMENT_9_3 = OBJECT_END + 0x2A,
|
||||
ITEM_FIELD_ENCHANTMENT_10_1 = OBJECT_END + 0x2B,
|
||||
ITEM_FIELD_ENCHANTMENT_10_3 = OBJECT_END + 0x2D,
|
||||
ITEM_FIELD_ENCHANTMENT_11_1 = OBJECT_END + 0x2E,
|
||||
ITEM_FIELD_ENCHANTMENT_11_3 = OBJECT_END + 0x30,
|
||||
ITEM_FIELD_ENCHANTMENT_12_1 = OBJECT_END + 0x31,
|
||||
ITEM_FIELD_ENCHANTMENT_12_3 = OBJECT_END + 0x33,
|
||||
ITEM_FIELD_ENCHANTMENT_13_1 = OBJECT_END + 0x34,
|
||||
ITEM_FIELD_ENCHANTMENT_13_3 = OBJECT_END + 0x36,
|
||||
ITEM_FIELD_ENCHANTMENT_14_1 = OBJECT_END + 0x37,
|
||||
ITEM_FIELD_ENCHANTMENT_14_3 = OBJECT_END + 0x39,
|
||||
ITEM_FIELD_ENCHANTMENT_15_1 = OBJECT_END + 0x3A,
|
||||
ITEM_FIELD_ENCHANTMENT_15_3 = OBJECT_END + 0x3C,
|
||||
ITEM_FIELD_PROPERTY_SEED = OBJECT_END + 0x3D,
|
||||
ITEM_FIELD_RANDOM_PROPERTIES_ID = OBJECT_END + 0x3E,
|
||||
ITEM_FIELD_DURABILITY = OBJECT_END + 0x3F,
|
||||
ITEM_FIELD_MAXDURABILITY = OBJECT_END + 0x40,
|
||||
ITEM_FIELD_CREATE_PLAYED_TIME = OBJECT_END + 0x41,
|
||||
ITEM_END = OBJECT_END + 0x42
|
||||
};
|
||||
|
||||
enum EPlayerFields
|
||||
{
|
||||
PLAYER_DUEL_ARBITER = UNIT_END + 0x0,
|
||||
PLAYER_FLAGS = UNIT_END + 0x2,
|
||||
PLAYER_GUILDRANK = UNIT_END + 0x3,
|
||||
PLAYER_GUILDDELETE_DATE = UNIT_END + 0x4,
|
||||
PLAYER_GUILDLEVEL = UNIT_END + 0x5,
|
||||
PLAYER_BYTES = UNIT_END + 0x6,
|
||||
PLAYER_BYTES_2 = UNIT_END + 0x7,
|
||||
PLAYER_BYTES_3 = UNIT_END + 0x8,
|
||||
PLAYER_DUEL_TEAM = UNIT_END + 0x9,
|
||||
PLAYER_GUILD_TIMESTAMP = UNIT_END + 0xA,
|
||||
PLAYER_QUEST_LOG_1_1 = UNIT_END + 0xB,
|
||||
PLAYER_QUEST_LOG_1_2 = UNIT_END + 0xC,
|
||||
PLAYER_QUEST_LOG_1_3 = UNIT_END + 0xD,
|
||||
PLAYER_QUEST_LOG_1_4 = UNIT_END + 0xF,
|
||||
PLAYER_QUEST_LOG_2_1 = UNIT_END + 0x10,
|
||||
PLAYER_QUEST_LOG_2_2 = UNIT_END + 0x11,
|
||||
PLAYER_QUEST_LOG_2_3 = UNIT_END + 0x12,
|
||||
PLAYER_QUEST_LOG_2_5 = UNIT_END + 0x14,
|
||||
PLAYER_QUEST_LOG_3_1 = UNIT_END + 0x15,
|
||||
PLAYER_QUEST_LOG_3_2 = UNIT_END + 0x16,
|
||||
PLAYER_QUEST_LOG_3_3 = UNIT_END + 0x17,
|
||||
PLAYER_QUEST_LOG_3_5 = UNIT_END + 0x19,
|
||||
PLAYER_QUEST_LOG_4_1 = UNIT_END + 0x1A,
|
||||
PLAYER_QUEST_LOG_4_2 = UNIT_END + 0x1B,
|
||||
PLAYER_QUEST_LOG_4_3 = UNIT_END + 0x1C,
|
||||
PLAYER_QUEST_LOG_4_5 = UNIT_END + 0x1E,
|
||||
PLAYER_QUEST_LOG_5_1 = UNIT_END + 0x1F,
|
||||
PLAYER_QUEST_LOG_5_2 = UNIT_END + 0x20,
|
||||
PLAYER_QUEST_LOG_5_3 = UNIT_END + 0x21,
|
||||
PLAYER_QUEST_LOG_5_5 = UNIT_END + 0x23,
|
||||
PLAYER_QUEST_LOG_6_1 = UNIT_END + 0x24,
|
||||
PLAYER_QUEST_LOG_6_2 = UNIT_END + 0x25,
|
||||
PLAYER_QUEST_LOG_6_3 = UNIT_END + 0x26,
|
||||
PLAYER_QUEST_LOG_6_5 = UNIT_END + 0x28,
|
||||
PLAYER_QUEST_LOG_7_1 = UNIT_END + 0x29,
|
||||
PLAYER_QUEST_LOG_7_2 = UNIT_END + 0x2A,
|
||||
PLAYER_QUEST_LOG_7_3 = UNIT_END + 0x2B,
|
||||
PLAYER_QUEST_LOG_7_5 = UNIT_END + 0x2D,
|
||||
PLAYER_QUEST_LOG_8_1 = UNIT_END + 0x2E,
|
||||
PLAYER_QUEST_LOG_8_2 = UNIT_END + 0x2F,
|
||||
PLAYER_QUEST_LOG_8_3 = UNIT_END + 0x30,
|
||||
PLAYER_QUEST_LOG_8_5 = UNIT_END + 0x32,
|
||||
PLAYER_QUEST_LOG_9_1 = UNIT_END + 0x33,
|
||||
PLAYER_QUEST_LOG_9_2 = UNIT_END + 0x34,
|
||||
PLAYER_QUEST_LOG_9_3 = UNIT_END + 0x35,
|
||||
PLAYER_QUEST_LOG_9_5 = UNIT_END + 0x37,
|
||||
PLAYER_QUEST_LOG_10_1 = UNIT_END + 0x38,
|
||||
PLAYER_QUEST_LOG_10_2 = UNIT_END + 0x39,
|
||||
PLAYER_QUEST_LOG_10_3 = UNIT_END + 0x3A,
|
||||
PLAYER_QUEST_LOG_10_5 = UNIT_END + 0x3C,
|
||||
PLAYER_QUEST_LOG_11_1 = UNIT_END + 0x3D,
|
||||
PLAYER_QUEST_LOG_11_2 = UNIT_END + 0x3E,
|
||||
PLAYER_QUEST_LOG_11_3 = UNIT_END + 0x3F,
|
||||
PLAYER_QUEST_LOG_11_5 = UNIT_END + 0x41,
|
||||
PLAYER_QUEST_LOG_12_1 = UNIT_END + 0x42,
|
||||
PLAYER_QUEST_LOG_12_2 = UNIT_END + 0x43,
|
||||
PLAYER_QUEST_LOG_12_3 = UNIT_END + 0x44,
|
||||
PLAYER_QUEST_LOG_12_5 = UNIT_END + 0x46,
|
||||
PLAYER_QUEST_LOG_13_1 = UNIT_END + 0x47,
|
||||
PLAYER_QUEST_LOG_13_2 = UNIT_END + 0x48,
|
||||
PLAYER_QUEST_LOG_13_3 = UNIT_END + 0x49,
|
||||
PLAYER_QUEST_LOG_13_5 = UNIT_END + 0x4B,
|
||||
PLAYER_QUEST_LOG_14_1 = UNIT_END + 0x4C,
|
||||
PLAYER_QUEST_LOG_14_2 = UNIT_END + 0x4D,
|
||||
PLAYER_QUEST_LOG_14_3 = UNIT_END + 0x4E,
|
||||
PLAYER_QUEST_LOG_14_5 = UNIT_END + 0x50,
|
||||
PLAYER_QUEST_LOG_15_1 = UNIT_END + 0x51,
|
||||
PLAYER_QUEST_LOG_15_2 = UNIT_END + 0x52,
|
||||
PLAYER_QUEST_LOG_15_3 = UNIT_END + 0x53,
|
||||
PLAYER_QUEST_LOG_15_5 = UNIT_END + 0x55,
|
||||
PLAYER_QUEST_LOG_16_1 = UNIT_END + 0x56,
|
||||
PLAYER_QUEST_LOG_16_2 = UNIT_END + 0x57,
|
||||
PLAYER_QUEST_LOG_16_3 = UNIT_END + 0x58,
|
||||
PLAYER_QUEST_LOG_16_5 = UNIT_END + 0x5A,
|
||||
PLAYER_QUEST_LOG_17_1 = UNIT_END + 0x5B,
|
||||
PLAYER_QUEST_LOG_17_2 = UNIT_END + 0x5C,
|
||||
PLAYER_QUEST_LOG_17_3 = UNIT_END + 0x5D,
|
||||
PLAYER_QUEST_LOG_17_5 = UNIT_END + 0x5F,
|
||||
PLAYER_QUEST_LOG_18_1 = UNIT_END + 0x60,
|
||||
PLAYER_QUEST_LOG_18_2 = UNIT_END + 0x61,
|
||||
PLAYER_QUEST_LOG_18_3 = UNIT_END + 0x62,
|
||||
PLAYER_QUEST_LOG_18_5 = UNIT_END + 0x64,
|
||||
PLAYER_QUEST_LOG_19_1 = UNIT_END + 0x65,
|
||||
PLAYER_QUEST_LOG_19_2 = UNIT_END + 0x66,
|
||||
PLAYER_QUEST_LOG_19_3 = UNIT_END + 0x67,
|
||||
PLAYER_QUEST_LOG_19_5 = UNIT_END + 0x69,
|
||||
PLAYER_QUEST_LOG_20_1 = UNIT_END + 0x6A,
|
||||
PLAYER_QUEST_LOG_20_2 = UNIT_END + 0x6B,
|
||||
PLAYER_QUEST_LOG_20_3 = UNIT_END + 0x6C,
|
||||
PLAYER_QUEST_LOG_20_5 = UNIT_END + 0x6E,
|
||||
PLAYER_QUEST_LOG_21_1 = UNIT_END + 0x6F,
|
||||
PLAYER_QUEST_LOG_21_2 = UNIT_END + 0x70,
|
||||
PLAYER_QUEST_LOG_21_3 = UNIT_END + 0x71,
|
||||
PLAYER_QUEST_LOG_21_5 = UNIT_END + 0x73,
|
||||
PLAYER_QUEST_LOG_22_1 = UNIT_END + 0x74,
|
||||
PLAYER_QUEST_LOG_22_2 = UNIT_END + 0x75,
|
||||
PLAYER_QUEST_LOG_22_3 = UNIT_END + 0x76,
|
||||
PLAYER_QUEST_LOG_22_5 = UNIT_END + 0x78,
|
||||
PLAYER_QUEST_LOG_23_1 = UNIT_END + 0x79,
|
||||
PLAYER_QUEST_LOG_23_2 = UNIT_END + 0x7A,
|
||||
PLAYER_QUEST_LOG_23_3 = UNIT_END + 0x7B,
|
||||
PLAYER_QUEST_LOG_23_5 = UNIT_END + 0x7D,
|
||||
PLAYER_QUEST_LOG_24_1 = UNIT_END + 0x7E,
|
||||
PLAYER_QUEST_LOG_24_2 = UNIT_END + 0x7F,
|
||||
PLAYER_QUEST_LOG_24_3 = UNIT_END + 0x80,
|
||||
PLAYER_QUEST_LOG_24_5 = UNIT_END + 0x82,
|
||||
PLAYER_QUEST_LOG_25_1 = UNIT_END + 0x83,
|
||||
PLAYER_QUEST_LOG_25_2 = UNIT_END + 0x84,
|
||||
PLAYER_QUEST_LOG_25_3 = UNIT_END + 0x85,
|
||||
PLAYER_QUEST_LOG_25_5 = UNIT_END + 0x87,
|
||||
PLAYER_QUEST_LOG_26_1 = UNIT_END + 0x88,
|
||||
PLAYER_QUEST_LOG_26_2 = UNIT_END + 0x89,
|
||||
PLAYER_QUEST_LOG_26_3 = UNIT_END + 0x8A,
|
||||
PLAYER_QUEST_LOG_26_5 = UNIT_END + 0x8C,
|
||||
PLAYER_QUEST_LOG_27_1 = UNIT_END + 0x8D,
|
||||
PLAYER_QUEST_LOG_27_2 = UNIT_END + 0x8E,
|
||||
PLAYER_QUEST_LOG_27_3 = UNIT_END + 0x8F,
|
||||
PLAYER_QUEST_LOG_27_5 = UNIT_END + 0x91,
|
||||
PLAYER_QUEST_LOG_28_1 = UNIT_END + 0x92,
|
||||
PLAYER_QUEST_LOG_28_2 = UNIT_END + 0x93,
|
||||
PLAYER_QUEST_LOG_28_3 = UNIT_END + 0x94,
|
||||
PLAYER_QUEST_LOG_28_5 = UNIT_END + 0x96,
|
||||
PLAYER_QUEST_LOG_29_1 = UNIT_END + 0x97,
|
||||
PLAYER_QUEST_LOG_29_2 = UNIT_END + 0x98,
|
||||
PLAYER_QUEST_LOG_29_3 = UNIT_END + 0x99,
|
||||
PLAYER_QUEST_LOG_29_5 = UNIT_END + 0x9B,
|
||||
PLAYER_QUEST_LOG_30_1 = UNIT_END + 0x9C,
|
||||
PLAYER_QUEST_LOG_30_2 = UNIT_END + 0x9D,
|
||||
PLAYER_QUEST_LOG_30_3 = UNIT_END + 0x9E,
|
||||
PLAYER_QUEST_LOG_30_5 = UNIT_END + 0xA0,
|
||||
PLAYER_QUEST_LOG_31_1 = UNIT_END + 0xA1,
|
||||
PLAYER_QUEST_LOG_31_2 = UNIT_END + 0xA2,
|
||||
PLAYER_QUEST_LOG_31_3 = UNIT_END + 0xA3,
|
||||
PLAYER_QUEST_LOG_31_5 = UNIT_END + 0xA5,
|
||||
PLAYER_QUEST_LOG_32_1 = UNIT_END + 0xA6,
|
||||
PLAYER_QUEST_LOG_32_2 = UNIT_END + 0xA7,
|
||||
PLAYER_QUEST_LOG_32_3 = UNIT_END + 0xA8,
|
||||
PLAYER_QUEST_LOG_32_5 = UNIT_END + 0xAA,
|
||||
PLAYER_QUEST_LOG_33_1 = UNIT_END + 0xAB,
|
||||
PLAYER_QUEST_LOG_33_2 = UNIT_END + 0xAC,
|
||||
PLAYER_QUEST_LOG_33_3 = UNIT_END + 0xAD,
|
||||
PLAYER_QUEST_LOG_33_5 = UNIT_END + 0xAF,
|
||||
PLAYER_QUEST_LOG_34_1 = UNIT_END + 0xB0,
|
||||
PLAYER_QUEST_LOG_34_2 = UNIT_END + 0xB1,
|
||||
PLAYER_QUEST_LOG_34_3 = UNIT_END + 0xB2,
|
||||
PLAYER_QUEST_LOG_34_5 = UNIT_END + 0xB4,
|
||||
PLAYER_QUEST_LOG_35_1 = UNIT_END + 0xB5,
|
||||
PLAYER_QUEST_LOG_35_2 = UNIT_END + 0xB6,
|
||||
PLAYER_QUEST_LOG_35_3 = UNIT_END + 0xB7,
|
||||
PLAYER_QUEST_LOG_35_5 = UNIT_END + 0xB9,
|
||||
PLAYER_QUEST_LOG_36_1 = UNIT_END + 0xBA,
|
||||
PLAYER_QUEST_LOG_36_2 = UNIT_END + 0xBB,
|
||||
PLAYER_QUEST_LOG_36_3 = UNIT_END + 0xBC,
|
||||
PLAYER_QUEST_LOG_36_5 = UNIT_END + 0xBE,
|
||||
PLAYER_QUEST_LOG_37_1 = UNIT_END + 0xBF,
|
||||
PLAYER_QUEST_LOG_37_2 = UNIT_END + 0xC0,
|
||||
PLAYER_QUEST_LOG_37_3 = UNIT_END + 0xC1,
|
||||
PLAYER_QUEST_LOG_37_5 = UNIT_END + 0xC3,
|
||||
PLAYER_QUEST_LOG_38_1 = UNIT_END + 0xC4,
|
||||
PLAYER_QUEST_LOG_38_2 = UNIT_END + 0xC5,
|
||||
PLAYER_QUEST_LOG_38_3 = UNIT_END + 0xC6,
|
||||
PLAYER_QUEST_LOG_38_5 = UNIT_END + 0xC8,
|
||||
PLAYER_QUEST_LOG_39_1 = UNIT_END + 0xC9,
|
||||
PLAYER_QUEST_LOG_39_2 = UNIT_END + 0xCA,
|
||||
PLAYER_QUEST_LOG_39_3 = UNIT_END + 0xCB,
|
||||
PLAYER_QUEST_LOG_39_5 = UNIT_END + 0xCD,
|
||||
PLAYER_QUEST_LOG_40_1 = UNIT_END + 0xCE,
|
||||
PLAYER_QUEST_LOG_40_2 = UNIT_END + 0xCF,
|
||||
PLAYER_QUEST_LOG_40_3 = UNIT_END + 0xD0,
|
||||
PLAYER_QUEST_LOG_40_5 = UNIT_END + 0xD2,
|
||||
PLAYER_QUEST_LOG_41_1 = UNIT_END + 0xD3,
|
||||
PLAYER_QUEST_LOG_41_2 = UNIT_END + 0xD4,
|
||||
PLAYER_QUEST_LOG_41_3 = UNIT_END + 0xD5,
|
||||
PLAYER_QUEST_LOG_41_5 = UNIT_END + 0xD7,
|
||||
PLAYER_QUEST_LOG_42_1 = UNIT_END + 0xD8,
|
||||
PLAYER_QUEST_LOG_42_2 = UNIT_END + 0xD9,
|
||||
PLAYER_QUEST_LOG_42_3 = UNIT_END + 0xDA,
|
||||
PLAYER_QUEST_LOG_42_5 = UNIT_END + 0xDC,
|
||||
PLAYER_QUEST_LOG_43_1 = UNIT_END + 0xDD,
|
||||
PLAYER_QUEST_LOG_43_2 = UNIT_END + 0xDE,
|
||||
PLAYER_QUEST_LOG_43_3 = UNIT_END + 0xDF,
|
||||
PLAYER_QUEST_LOG_43_5 = UNIT_END + 0xE1,
|
||||
PLAYER_QUEST_LOG_44_1 = UNIT_END + 0xE2,
|
||||
PLAYER_QUEST_LOG_44_2 = UNIT_END + 0xE3,
|
||||
PLAYER_QUEST_LOG_44_3 = UNIT_END + 0xE4,
|
||||
PLAYER_QUEST_LOG_44_5 = UNIT_END + 0xE6,
|
||||
PLAYER_QUEST_LOG_45_1 = UNIT_END + 0xE7,
|
||||
PLAYER_QUEST_LOG_45_2 = UNIT_END + 0xE8,
|
||||
PLAYER_QUEST_LOG_45_3 = UNIT_END + 0xE9,
|
||||
PLAYER_QUEST_LOG_45_5 = UNIT_END + 0xEB,
|
||||
PLAYER_QUEST_LOG_46_1 = UNIT_END + 0xEC,
|
||||
PLAYER_QUEST_LOG_46_2 = UNIT_END + 0xED,
|
||||
PLAYER_QUEST_LOG_46_3 = UNIT_END + 0xEE,
|
||||
PLAYER_QUEST_LOG_46_5 = UNIT_END + 0xF0,
|
||||
PLAYER_QUEST_LOG_47_1 = UNIT_END + 0xF1,
|
||||
PLAYER_QUEST_LOG_47_2 = UNIT_END + 0xF2,
|
||||
PLAYER_QUEST_LOG_47_3 = UNIT_END + 0xF3,
|
||||
PLAYER_QUEST_LOG_47_5 = UNIT_END + 0xF5,
|
||||
PLAYER_QUEST_LOG_48_1 = UNIT_END + 0xF6,
|
||||
PLAYER_QUEST_LOG_48_2 = UNIT_END + 0xF7,
|
||||
PLAYER_QUEST_LOG_48_3 = UNIT_END + 0xF8,
|
||||
PLAYER_QUEST_LOG_48_5 = UNIT_END + 0xFA,
|
||||
PLAYER_QUEST_LOG_49_1 = UNIT_END + 0xFB,
|
||||
PLAYER_QUEST_LOG_49_2 = UNIT_END + 0xFC,
|
||||
PLAYER_QUEST_LOG_49_3 = UNIT_END + 0xFD,
|
||||
PLAYER_QUEST_LOG_49_5 = UNIT_END + 0xFF,
|
||||
PLAYER_QUEST_LOG_50_1 = UNIT_END + 0x100,
|
||||
PLAYER_QUEST_LOG_50_2 = UNIT_END + 0x101,
|
||||
PLAYER_QUEST_LOG_50_3 = UNIT_END + 0x102,
|
||||
PLAYER_QUEST_LOG_50_5 = UNIT_END + 0x104,
|
||||
PLAYER_VISIBLE_ITEM_1_ENTRYID = UNIT_END + 0x105,
|
||||
PLAYER_VISIBLE_ITEM_1_ENCHANTMENT = UNIT_END + 0x106,
|
||||
PLAYER_VISIBLE_ITEM_2_ENTRYID = UNIT_END + 0x107,
|
||||
PLAYER_VISIBLE_ITEM_2_ENCHANTMENT = UNIT_END + 0x108,
|
||||
PLAYER_VISIBLE_ITEM_3_ENTRYID = UNIT_END + 0x109,
|
||||
PLAYER_VISIBLE_ITEM_3_ENCHANTMENT = UNIT_END + 0x10A,
|
||||
PLAYER_VISIBLE_ITEM_4_ENTRYID = UNIT_END + 0x10B,
|
||||
PLAYER_VISIBLE_ITEM_4_ENCHANTMENT = UNIT_END + 0x10C,
|
||||
PLAYER_VISIBLE_ITEM_5_ENTRYID = UNIT_END + 0x10D,
|
||||
PLAYER_VISIBLE_ITEM_5_ENCHANTMENT = UNIT_END + 0x10E,
|
||||
PLAYER_VISIBLE_ITEM_6_ENTRYID = UNIT_END + 0x10F,
|
||||
PLAYER_VISIBLE_ITEM_6_ENCHANTMENT = UNIT_END + 0x110,
|
||||
PLAYER_VISIBLE_ITEM_7_ENTRYID = UNIT_END + 0x111,
|
||||
PLAYER_VISIBLE_ITEM_7_ENCHANTMENT = UNIT_END + 0x112,
|
||||
PLAYER_VISIBLE_ITEM_8_ENTRYID = UNIT_END + 0x113,
|
||||
PLAYER_VISIBLE_ITEM_8_ENCHANTMENT = UNIT_END + 0x114,
|
||||
PLAYER_VISIBLE_ITEM_9_ENTRYID = UNIT_END + 0x115,
|
||||
PLAYER_VISIBLE_ITEM_9_ENCHANTMENT = UNIT_END + 0x116,
|
||||
PLAYER_VISIBLE_ITEM_10_ENTRYID = UNIT_END + 0x117,
|
||||
PLAYER_VISIBLE_ITEM_10_ENCHANTMENT = UNIT_END + 0x118,
|
||||
PLAYER_VISIBLE_ITEM_11_ENTRYID = UNIT_END + 0x119,
|
||||
PLAYER_VISIBLE_ITEM_11_ENCHANTMENT = UNIT_END + 0x11A,
|
||||
PLAYER_VISIBLE_ITEM_12_ENTRYID = UNIT_END + 0x11B,
|
||||
PLAYER_VISIBLE_ITEM_12_ENCHANTMENT = UNIT_END + 0x11C,
|
||||
PLAYER_VISIBLE_ITEM_13_ENTRYID = UNIT_END + 0x11D,
|
||||
PLAYER_VISIBLE_ITEM_13_ENCHANTMENT = UNIT_END + 0x11E,
|
||||
PLAYER_VISIBLE_ITEM_14_ENTRYID = UNIT_END + 0x11F,
|
||||
PLAYER_VISIBLE_ITEM_14_ENCHANTMENT = UNIT_END + 0x120,
|
||||
PLAYER_VISIBLE_ITEM_15_ENTRYID = UNIT_END + 0x121,
|
||||
PLAYER_VISIBLE_ITEM_15_ENCHANTMENT = UNIT_END + 0x122,
|
||||
PLAYER_VISIBLE_ITEM_16_ENTRYID = UNIT_END + 0x123,
|
||||
PLAYER_VISIBLE_ITEM_16_ENCHANTMENT = UNIT_END + 0x124,
|
||||
PLAYER_VISIBLE_ITEM_17_ENTRYID = UNIT_END + 0x125,
|
||||
PLAYER_VISIBLE_ITEM_17_ENCHANTMENT = UNIT_END + 0x126,
|
||||
PLAYER_VISIBLE_ITEM_18_ENTRYID = UNIT_END + 0x127,
|
||||
PLAYER_VISIBLE_ITEM_18_ENCHANTMENT = UNIT_END + 0x128,
|
||||
PLAYER_VISIBLE_ITEM_19_ENTRYID = UNIT_END + 0x129,
|
||||
PLAYER_VISIBLE_ITEM_19_ENCHANTMENT = UNIT_END + 0x12A,
|
||||
PLAYER_CHOSEN_TITLE = UNIT_END + 0x12B,
|
||||
PLAYER_FAKE_INEBRIATION = UNIT_END + 0x12C,
|
||||
PLAYER_FIELD_PAD_0 = UNIT_END + 0x12D,
|
||||
PLAYER_FIELD_INV_SLOT_HEAD = UNIT_END + 0x12E,
|
||||
PLAYER_FIELD_PACK_SLOT_1 = UNIT_END + 0x15C,
|
||||
PLAYER_FIELD_BANK_SLOT_1 = UNIT_END + 0x17C,
|
||||
PLAYER_FIELD_BANKBAG_SLOT_1 = UNIT_END + 0x1B4,
|
||||
PLAYER_FIELD_VENDORBUYBACK_SLOT_1 = UNIT_END + 0x1C2,
|
||||
PLAYER_FARSIGHT = UNIT_END + 0x1DA,
|
||||
PLAYER__FIELD_KNOWN_TITLES = UNIT_END + 0x1DC,
|
||||
PLAYER__FIELD_KNOWN_TITLES1 = UNIT_END + 0x1DE,
|
||||
PLAYER__FIELD_KNOWN_TITLES2 = UNIT_END + 0x1E0,
|
||||
PLAYER__FIELD_KNOWN_TITLES3 = UNIT_END + 0x1E2,
|
||||
PLAYER_XP = UNIT_END + 0x1E4,
|
||||
PLAYER_NEXT_LEVEL_XP = UNIT_END + 0x1E5,
|
||||
PLAYER_SKILL_LINEID_0 = UNIT_END + 0x1E6,
|
||||
PLAYER_SKILL_STEP_0 = UNIT_END + 0x226,
|
||||
PLAYER_SKILL_RANK_0 = UNIT_END + 0x266,
|
||||
PLAYER_SKILL_MAX_RANK_0 = UNIT_END + 0x2A6,
|
||||
PLAYER_SKILL_MODIFIER_0 = UNIT_END + 0x2E6,
|
||||
PLAYER_SKILL_TALENT_0 = UNIT_END + 0x326,
|
||||
PLAYER_CHARACTER_POINTS = UNIT_END + 0x366,
|
||||
PLAYER_TRACK_CREATURES = UNIT_END + 0x367,
|
||||
PLAYER_TRACK_RESOURCES = UNIT_END + 0x368,
|
||||
PLAYER_EXPERTISE = UNIT_END + 0x369,
|
||||
PLAYER_OFFHAND_EXPERTISE = UNIT_END + 0x36A,
|
||||
PLAYER_BLOCK_PERCENTAGE = UNIT_END + 0x36B,
|
||||
PLAYER_DODGE_PERCENTAGE = UNIT_END + 0x36C,
|
||||
PLAYER_PARRY_PERCENTAGE = UNIT_END + 0x36D,
|
||||
PLAYER_CRIT_PERCENTAGE = UNIT_END + 0x36E,
|
||||
PLAYER_RANGED_CRIT_PERCENTAGE = UNIT_END + 0x36F,
|
||||
PLAYER_OFFHAND_CRIT_PERCENTAGE = UNIT_END + 0x370,
|
||||
PLAYER_SPELL_CRIT_PERCENTAGE1 = UNIT_END + 0x371,
|
||||
PLAYER_SHIELD_BLOCK = UNIT_END + 0x378,
|
||||
PLAYER_SHIELD_BLOCK_CRIT_PERCENTAGE = UNIT_END + 0x379,
|
||||
PLAYER_MASTERY = UNIT_END + 0x37A,
|
||||
PLAYER_EXPLORED_ZONES_1 = UNIT_END + 0x37B,
|
||||
PLAYER_REST_STATE_EXPERIENCE = UNIT_END + 0x417,
|
||||
PLAYER_FIELD_COINAGE = UNIT_END + 0x418,
|
||||
PLAYER_FIELD_MOD_DAMAGE_DONE_POS = UNIT_END + 0x41A,
|
||||
PLAYER_FIELD_MOD_DAMAGE_DONE_NEG = UNIT_END + 0x421,
|
||||
PLAYER_FIELD_MOD_DAMAGE_DONE_PCT = UNIT_END + 0x428,
|
||||
PLAYER_FIELD_MOD_HEALING_DONE_POS = UNIT_END + 0x42F,
|
||||
PLAYER_FIELD_MOD_HEALING_PCT = UNIT_END + 0x430,
|
||||
PLAYER_FIELD_MOD_HEALING_DONE_PCT = UNIT_END + 0x431,
|
||||
PLAYER_FIELD_WEAPON_DMG_MULTIPLIERS = UNIT_END + 0x432,
|
||||
PLAYER_FIELD_MOD_SPELL_POWER_PCT = UNIT_END + 0x435,
|
||||
PLAYER_FIELD_OVERRIDE_SPELL_POWER_BY_AP_PCT = UNIT_END + 0x436,
|
||||
PLAYER_FIELD_MOD_TARGET_RESISTANCE = UNIT_END + 0x437,
|
||||
PLAYER_FIELD_MOD_TARGET_PHYSICAL_RESISTANCE = UNIT_END + 0x438,
|
||||
PLAYER_FIELD_BYTES = UNIT_END + 0x439,
|
||||
PLAYER_SELF_RES_SPELL = UNIT_END + 0x43A,
|
||||
PLAYER_FIELD_PVP_MEDALS = UNIT_END + 0x43B,
|
||||
PLAYER_FIELD_BUYBACK_PRICE_1 = UNIT_END + 0x43C,
|
||||
PLAYER_FIELD_BUYBACK_TIMESTAMP_1 = UNIT_END + 0x448,
|
||||
PLAYER_FIELD_KILLS = UNIT_END + 0x454,
|
||||
PLAYER_FIELD_LIFETIME_HONORBALE_KILLS = UNIT_END + 0x455,
|
||||
PLAYER_FIELD_BYTES2 = UNIT_END + 0x456,
|
||||
PLAYER_FIELD_WATCHED_FACTION_INDEX = UNIT_END + 0x457,
|
||||
PLAYER_FIELD_COMBAT_RATING_1 = UNIT_END + 0x458,
|
||||
PLAYER_FIELD_ARENA_TEAM_INFO_1_1 = UNIT_END + 0x472,
|
||||
PLAYER_FIELD_BATTLEGROUND_RATING = UNIT_END + 0x487,
|
||||
PLAYER_FIELD_MAX_LEVEL = UNIT_END + 0x488,
|
||||
PLAYER_FIELD_DAILY_QUESTS_1 = UNIT_END + 0x489,
|
||||
PLAYER_RUNE_REGEN_1 = UNIT_END + 0x4A2,
|
||||
PLAYER_NO_REAGENT_COST_1 = UNIT_END + 0x4A6,
|
||||
PLAYER_FIELD_GLYPH_SLOTS_1 = UNIT_END + 0x4A9,
|
||||
PLAYER_FIELD_GLYPHS_1 = UNIT_END + 0x4B2,
|
||||
PLAYER_GLYPHS_ENABLED = UNIT_END + 0x4BB,
|
||||
PLAYER_PET_SPELL_POWER = UNIT_END + 0x4BC,
|
||||
PLAYER_FIELD_RESEARCHING_1 = UNIT_END + 0x4BD,
|
||||
PLAYER_FIELD_RESERACH_SITE_1 = UNIT_END + 0x4C5,
|
||||
PLAYER_PROFESSION_SKILL_LINE_1 = UNIT_END + 0x4CD,
|
||||
PLAYER_FIELD_UI_HIT_MODIFIER = UNIT_END + 0x4CF,
|
||||
PLAYER_FIELD_UI_SPELL_HIT_MODIFIER = UNIT_END + 0x4D0,
|
||||
PLAYER_FIELD_HOME_REALM_TIME_OFFSET = UNIT_END + 0x4D1,
|
||||
PLAYER_FIELD_MOD_HASTE = UNIT_END + 0x4D2,
|
||||
PLAYER_FIELD_MOD_RANGED_HASTE = UNIT_END + 0x4D3,
|
||||
PLAYER_FIELD_MOD_PET_HASTE = UNIT_END + 0x4D4,
|
||||
PLAYER_FIELD_MOD_HASTE_REGEN = UNIT_END + 0x4D5,
|
||||
PLAYER_END = UNIT_END + 0x4D6
|
||||
};
|
||||
|
||||
enum EContainerFields
|
||||
{
|
||||
CONTAINER_FIELD_NUM_SLOTS = ITEM_END + 0x0,
|
||||
CONTAINER_ALIGN_PAD = ITEM_END + 0x1,
|
||||
CONTAINER_FIELD_SLOT_1 = ITEM_END + 0x2,
|
||||
CONTAINER_END = ITEM_END + 0x4A
|
||||
};
|
||||
|
||||
enum EGameObjectFields
|
||||
{
|
||||
OBJECT_FIELD_CREATED_BY = OBJECT_END + 0x0,
|
||||
GAMEOBJECT_DISPLAYID = OBJECT_END + 0x2,
|
||||
GAMEOBJECT_FLAGS = OBJECT_END + 0x3,
|
||||
GAMEOBJECT_PARENTROTATION = OBJECT_END + 0x4,
|
||||
GAMEOBJECT_DYNAMIC = OBJECT_END + 0x8,
|
||||
GAMEOBJECT_FACTION = OBJECT_END + 0x9,
|
||||
GAMEOBJECT_LEVEL = OBJECT_END + 0xA,
|
||||
GAMEOBJECT_BYTES_1 = OBJECT_END + 0xB,
|
||||
GAMEOBJECT_END = OBJECT_END + 0xC
|
||||
};
|
||||
|
||||
enum EDynamicObjectFields
|
||||
{
|
||||
DYNAMICOBJECT_CASTER = OBJECT_END + 0x0,
|
||||
DYNAMICOBJECT_BYTES = OBJECT_END + 0x2,
|
||||
DYNAMICOBJECT_SPELLID = OBJECT_END + 0x3,
|
||||
DYNAMICOBJECT_RADIUS = OBJECT_END + 0x4,
|
||||
DYNAMICOBJECT_CASTTIME = OBJECT_END + 0x5,
|
||||
DYNAMICOBJECT_END = OBJECT_END + 0x6
|
||||
};
|
||||
|
||||
enum ECorpseFields
|
||||
{
|
||||
CORPSE_FIELD_OWNER = OBJECT_END + 0x0,
|
||||
CORPSE_FIELD_PARTY = OBJECT_END + 0x2,
|
||||
CORPSE_FIELD_DISPLAY_ID = OBJECT_END + 0x4,
|
||||
CORPSE_FIELD_ITEM = OBJECT_END + 0x5,
|
||||
CORPSE_FIELD_BYTES_1 = OBJECT_END + 0x18,
|
||||
CORPSE_FIELD_BYTES_2 = OBJECT_END + 0x19,
|
||||
CORPSE_FIELD_FLAGS = OBJECT_END + 0x1A,
|
||||
CORPSE_FIELD_DYNAMIC_FLAGS = OBJECT_END + 0x1B,
|
||||
CORPSE_END = OBJECT_END + 0x1C
|
||||
};
|
||||
|
||||
enum EAreaTriggerFields
|
||||
{
|
||||
AREATRIGGER_SPELLID = OBJECT_END + 0x0,
|
||||
AREATRIGGER_SPELLVISUALID = OBJECT_END + 0x1,
|
||||
AREATRIGGER_DURATION = OBJECT_END + 0x2,
|
||||
AREATRIGGER_FINAL_POS = OBJECT_END + 0x3,
|
||||
AREATRIGGER_END = OBJECT_END + 0x6
|
||||
};
|
||||
|
||||
|
||||
#define PLAYER_END_NOT_SELF PLAYER_FIELD_INV_SLOT_HEAD
|
||||
#endif
|
||||
128
src/game/Object/UpdateMask.h
Normal file
128
src/game/Object/UpdateMask.h
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
/**
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2014 MaNGOS project <http://getmangos.eu>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#ifndef __UPDATEMASK_H
|
||||
#define __UPDATEMASK_H
|
||||
|
||||
#include "UpdateFields.h"
|
||||
#include "Errors.h"
|
||||
|
||||
class UpdateMask
|
||||
{
|
||||
public:
|
||||
UpdateMask() : mCount(0), mBlocks(0), mUpdateMask(0) { }
|
||||
UpdateMask(const UpdateMask& mask) : mUpdateMask(0) { *this = mask; }
|
||||
|
||||
~UpdateMask()
|
||||
{
|
||||
delete[] mUpdateMask;
|
||||
}
|
||||
|
||||
void SetBit(uint32 index)
|
||||
{
|
||||
((uint8*)mUpdateMask)[ index >> 3 ] |= 1 << (index & 0x7);
|
||||
}
|
||||
|
||||
void UnsetBit(uint32 index)
|
||||
{
|
||||
((uint8*)mUpdateMask)[ index >> 3 ] &= (0xff ^ (1 << (index & 0x7)));
|
||||
}
|
||||
|
||||
bool GetBit(uint32 index) const
|
||||
{
|
||||
return (((uint8*)mUpdateMask)[ index >> 3 ] & (1 << (index & 0x7))) != 0;
|
||||
}
|
||||
|
||||
uint32 GetBlockCount() const { return mBlocks; }
|
||||
uint32 GetLength() const { return mBlocks << 2; }
|
||||
uint32 GetCount() const { return mCount; }
|
||||
uint8* GetMask() { return (uint8*)mUpdateMask; }
|
||||
|
||||
void SetCount(uint32 valuesCount)
|
||||
{
|
||||
delete[] mUpdateMask;
|
||||
|
||||
mCount = valuesCount;
|
||||
mBlocks = (valuesCount + 31) / 32;
|
||||
|
||||
mUpdateMask = new uint32[mBlocks];
|
||||
memset(mUpdateMask, 0, mBlocks << 2);
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
if (mUpdateMask)
|
||||
memset(mUpdateMask, 0, mBlocks << 2);
|
||||
}
|
||||
|
||||
UpdateMask& operator = (const UpdateMask& mask)
|
||||
{
|
||||
SetCount(mask.mCount);
|
||||
memcpy(mUpdateMask, mask.mUpdateMask, mBlocks << 2);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void operator &= (const UpdateMask& mask)
|
||||
{
|
||||
MANGOS_ASSERT(mask.mCount <= mCount);
|
||||
for (uint32 i = 0; i < mBlocks; ++i)
|
||||
mUpdateMask[i] &= mask.mUpdateMask[i];
|
||||
}
|
||||
|
||||
void operator |= (const UpdateMask& mask)
|
||||
{
|
||||
MANGOS_ASSERT(mask.mCount <= mCount);
|
||||
for (uint32 i = 0; i < mBlocks; ++i)
|
||||
mUpdateMask[i] |= mask.mUpdateMask[i];
|
||||
}
|
||||
|
||||
UpdateMask operator & (const UpdateMask& mask) const
|
||||
{
|
||||
MANGOS_ASSERT(mask.mCount <= mCount);
|
||||
|
||||
UpdateMask newmask;
|
||||
newmask = *this;
|
||||
newmask &= mask;
|
||||
|
||||
return newmask;
|
||||
}
|
||||
|
||||
UpdateMask operator | (const UpdateMask& mask) const
|
||||
{
|
||||
MANGOS_ASSERT(mask.mCount <= mCount);
|
||||
|
||||
UpdateMask newmask;
|
||||
newmask = *this;
|
||||
newmask |= mask;
|
||||
|
||||
return newmask;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32 mCount;
|
||||
uint32 mBlocks;
|
||||
uint32* mUpdateMask;
|
||||
};
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue