Initial project location adjustment

This commit is contained in:
Antz 2015-01-20 21:10:53 +00:00 committed by Antz
parent 11641a8bd7
commit 9d20fe2b32
244 changed files with 0 additions and 0 deletions

View 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);
}
}

View 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

View 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
View 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

File diff suppressed because it is too large Load diff

View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

799
src/game/Object/Creature.h Normal file
View 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

View 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);
}

View 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

View 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

View 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();
}
}

View 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

View 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));
}
}

View 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

File diff suppressed because it is too large Load diff

View 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

View 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.");
}
}

View 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

View 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;
}

View 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
View 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

View 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();
}

View 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

File diff suppressed because it is too large Load diff

View 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
View 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
View 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

File diff suppressed because it is too large Load diff

555
src/game/Object/Guild.h Normal file
View 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

File diff suppressed because it is too large Load diff

412
src/game/Object/Item.h Normal file
View 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

View 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;
}

View 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

View 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

File diff suppressed because it is too large Load diff

394
src/game/Object/LootMgr.h Normal file
View 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

View 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()
{
}

View 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

File diff suppressed because it is too large Load diff

655
src/game/Object/Object.h Normal file
View 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

View 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>;

View 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

View 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();

View 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

File diff suppressed because it is too large Load diff

1312
src/game/Object/ObjectMgr.h Normal file

File diff suppressed because it is too large Load diff

View 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;
}

View 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

File diff suppressed because it is too large Load diff

279
src/game/Object/Pet.h Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

2701
src/game/Object/Player.h Normal file

File diff suppressed because it is too large Load diff

View 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();
}

View 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

View 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;
}

View 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

View 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
View 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

File diff suppressed because it is too large Load diff

1302
src/game/Object/SpellMgr.h Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View 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()
{
}

View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

3474
src/game/Object/Unit.h Normal file

File diff suppressed because it is too large Load diff

View 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

View 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

View 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