Files
TrinityCore/src/game/OutdoorPvP.cpp
megamage 94ca55af86 *Fix a crash. Thanks to DaGNU.
--HG--
branch : trunk
2009-05-29 14:16:30 -05:00

639 lines
19 KiB
C++

/*
* Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "OutdoorPvP.h"
#include "OutdoorPvPImpl.h"
#include "OutdoorPvPMgr.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "Map.h"
#include "MapManager.h"
#include "Group.h"
#include "WorldPacket.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "CellImpl.h"
OPvPCapturePoint::OPvPCapturePoint(OutdoorPvP * pvp)
: m_PvP(pvp), m_ShiftPhase(0), m_ShiftMaxPhase(0), m_OldPhase(0),
m_State(0), m_OldState(0), m_CapturePointGUID(0), m_NeutralValue(0),
m_ShiftMaxCaptureSpeed(0), m_capturePoint(NULL)
{
}
bool OPvPCapturePoint::HandlePlayerEnter(Player * plr)
{
return m_activePlayers[plr->GetTeamId()].insert(plr).second;
}
void OPvPCapturePoint::HandlePlayerLeave(Player * plr)
{
m_activePlayers[plr->GetTeamId()].erase(plr);
}
bool OPvPCapturePoint::AddObject(uint32 type, uint32 entry, uint32 map, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3)
{
GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(entry);
if (!goinfo)
return false;
uint32 guid = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT);
GameObjectData& data = objmgr.NewGOData(guid);
data.id = entry;
data.mapid = map;
data.posX = x;
data.posY = y;
data.posZ = z;
data.orientation = o;
data.rotation0 = rotation0;
data.rotation1 = rotation1;
data.rotation2 = rotation2;
data.rotation3 = rotation3;
data.spawntimesecs = 0;
data.animprogress = 100;
data.spawnMask = 1;
data.go_state = GO_STATE_READY;
objmgr.AddGameobjectToGrid(guid, &data);
// 2 way registering
m_Objects[type] = MAKE_NEW_GUID(guid, entry, HIGHGUID_GAMEOBJECT);
m_ObjectTypes[m_Objects[type]]=type;
Map * pMap = MapManager::Instance().FindMap(map);
if(!pMap)
return true;
GameObject * go = new GameObject;
if(!go->Create(guid,entry, pMap,PHASEMASK_NORMAL,x,y,z,o,rotation0,rotation1,rotation2,rotation3,100,GO_STATE_READY))
{
sLog.outError("Gameobject template %u not found in database.", entry);
delete go;
return true;
}
go->SetRespawnTime(0);
objmgr.SaveGORespawnTime(go->GetDBTableGUIDLow(),0,0);
pMap->Add(go);
return true;
}
bool OPvPCapturePoint::AddCreature(uint32 type, uint32 entry, uint32 teamval, uint32 map, float x, float y, float z, float o, uint32 spawntimedelay)
{
CreatureInfo const *cinfo = objmgr.GetCreatureTemplate(entry);
if(!cinfo)
{
return false;
}
uint32 displayId = objmgr.ChooseDisplayId(teamval, cinfo, NULL);
CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(displayId);
if (!minfo)
{
return false;
}
else
displayId = minfo->modelid; // it can be different (for another gender)
uint32 guid = objmgr.GenerateLowGuid(HIGHGUID_UNIT);
CreatureData& data = objmgr.NewOrExistCreatureData(guid);
data.id = entry;
data.mapid = map;
data.displayid = displayId;
data.equipmentId = cinfo->equipmentId;
data.posX = x;
data.posY = y;
data.posZ = z;
data.orientation = o;
data.spawntimesecs = spawntimedelay;
data.spawndist = 0;
data.currentwaypoint = 0;
data.curhealth = cinfo->maxhealth;
data.curmana = cinfo->maxmana;
data.is_dead = false;
data.movementType = cinfo->MovementType;
data.spawnMask = 1;
objmgr.AddCreatureToGrid(guid, &data);
m_Creatures[type] = MAKE_NEW_GUID(guid, entry, HIGHGUID_UNIT);
m_CreatureTypes[m_Creatures[type]] = type;
Map * pMap = MapManager::Instance().FindMap(map);
if(!pMap)
return true;
Creature* pCreature = new Creature;
if (!pCreature->Create(guid, pMap, PHASEMASK_NORMAL, entry, teamval))
{
sLog.outError("Can't create creature entry: %u",entry);
delete pCreature;
return false;
}
pCreature->Relocate(x, y, z, o);
if(!pCreature->IsPositionValid())
{
sLog.outError("ERROR: Creature (guidlow %d, entry %d) not added to opvp. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY());
delete pCreature;
return false;
}
if(spawntimedelay)
pCreature->SetRespawnDelay(spawntimedelay);
pMap->Add(pCreature);
return true;
}
bool OPvPCapturePoint::AddCapturePoint(uint32 entry, uint32 map, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3)
{
sLog.outDebug("Creating capture point %u", entry);
// check info existence
GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(entry);
if(!goinfo || goinfo->type != GAMEOBJECT_TYPE_CAPTURE_POINT)
{
sLog.outError("OutdoorPvP: GO %u is not capture point!", goinfo->id);
return false;
}
// create capture point go
m_CapturePointGUID = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT);
// get the needed values from goinfo
m_ShiftMaxPhase = goinfo->capturePoint.maxTime;
m_ShiftMaxCaptureSpeed = m_ShiftMaxPhase / float(goinfo->capturePoint.minTime);
m_NeutralValue = goinfo->capturePoint.neutralPercent;
GameObjectData& data = objmgr.NewGOData(m_CapturePointGUID);
data.id = entry;
data.mapid = map;
data.posX = x;
data.posY = y;
data.posZ = z;
data.orientation = o;
data.rotation0 = rotation0;
data.rotation1 = rotation1;
data.rotation2 = rotation2;
data.rotation3 = rotation3;
data.spawntimesecs = 1;
data.animprogress = 100;
data.spawnMask = 1;
data.go_state = GO_STATE_READY;
data.phaseMask = PHASEMASK_NORMAL;
objmgr.AddGameobjectToGrid(m_CapturePointGUID, &data);
return true;
}
bool OPvPCapturePoint::DelCreature(uint32 type)
{
if(!m_Creatures[type])
{
sLog.outDebug("opvp creature type %u was already deleted",type);
return false;
}
Creature *cr = HashMapHolder<Creature>::Find(m_Creatures[type]);
if(!cr)
{
// can happen when closing the core
m_Creatures[type] = 0;
return false;
}
sLog.outDebug("deleting opvp creature type %u",type);
uint32 guid = cr->GetDBTableGUIDLow();
// Don't save respawn time
cr->SetRespawnTime(0);
cr->RemoveCorpse();
cr->CleanupsBeforeDelete();
// explicit removal from map
// beats me why this is needed, but with the recent removal "cleanup" some creatures stay in the map if "properly" deleted
// so this is a big fat workaround, if AddObjectToRemoveList and DoDelayedMovesAndRemoves worked correctly, this wouldn't be needed
//if(Map * map = MapManager::Instance().FindMap(cr->GetMapId()))
// map->Remove(cr,false);
// delete respawn time for this creature
WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE guid = '%u'", guid);
cr->AddObjectToRemoveList();
objmgr.DeleteCreatureData(guid);
m_CreatureTypes[m_Creatures[type]] = 0;
m_Creatures[type] = 0;
return true;
}
bool OPvPCapturePoint::DelObject(uint32 type)
{
if(!m_Objects[type])
return false;
GameObject *obj = HashMapHolder<GameObject>::Find(m_Objects[type]);
if(!obj)
{
m_Objects[type] = 0;
return false;
}
uint32 guid = obj->GetDBTableGUIDLow();
obj->SetRespawnTime(0); // not save respawn time
obj->Delete();
objmgr.DeleteGOData(guid);
m_ObjectTypes[m_Objects[type]] = 0;
m_Objects[type] = 0;
return true;
}
bool OPvPCapturePoint::DelCapturePoint()
{
objmgr.DeleteGOData(m_CapturePointGUID);
m_CapturePointGUID = 0;
if(m_capturePoint)
{
m_capturePoint->SetRespawnTime(0); // not save respawn time
m_capturePoint->Delete();
}
return true;
}
void OPvPCapturePoint::DeleteSpawns()
{
for(std::map<uint32,uint64>::iterator i = m_Objects.begin(); i != m_Objects.end(); ++i)
DelObject(i->first);
for(std::map<uint32,uint64>::iterator i = m_Creatures.begin(); i != m_Creatures.end(); ++i)
DelCreature(i->first);
DelCapturePoint();
}
void OutdoorPvP::DeleteSpawns()
{
for(OPvPCapturePointMap::iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
(*itr)->DeleteSpawns();
}
OutdoorPvP::OutdoorPvP()
{
}
OutdoorPvP::~OutdoorPvP()
{
DeleteSpawns();
}
void OutdoorPvP::HandlePlayerEnterZone(Player * plr, uint32 zone)
{
m_players[plr->GetTeamId()].insert(plr);
}
void OutdoorPvP::HandlePlayerLeaveZone(Player * plr, uint32 zone)
{
// inform the objectives of the leaving
for(OPvPCapturePointMap::iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
(*itr)->HandlePlayerLeave(plr);
// remove the world state information from the player (we can't keep everyone up to date, so leave out those who are not in the concerning zones)
if(!plr->GetSession()->PlayerLogout())
SendRemoveWorldStates(plr);
m_players[plr->GetTeamId()].erase(plr);
sLog.outDebug("Player %s left an outdoorpvp zone", plr->GetName());
}
bool OutdoorPvP::Update(uint32 diff)
{
bool objective_changed = false;
for(OPvPCapturePointMap::iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
objective_changed |= (*itr)->Update(diff);
return objective_changed;
}
bool OPvPCapturePoint::Update(uint32 diff)
{
if(!m_capturePoint)
return false;
float radius = m_capturePoint->GetGOInfo()->capturePoint.radius;
for(uint32 team = 0; team < 2; ++team)
{
for(PlayerSet::iterator itr = m_activePlayers[team].begin(); itr != m_activePlayers[team].end();)
{
Player *player = *itr;
++itr;
if(!m_capturePoint->IsWithinDistInMap(player, radius) || !player->IsOutdoorPvPActive())
HandlePlayerLeave(player);
}
}
std::list<Player*> players;
Trinity::AnyPlayerInObjectRangeCheck checker(m_capturePoint, radius);
Trinity::PlayerListSearcher<Trinity::AnyPlayerInObjectRangeCheck> searcher(m_capturePoint, players, checker);
m_capturePoint->VisitNearbyWorldObject(radius, searcher);
for(std::list<Player*>::iterator itr = players.begin(); itr != players.end(); ++itr)
{
if((*itr)->IsOutdoorPvPActive())
{
if(m_activePlayers[(*itr)->GetTeamId()].insert(*itr).second)
HandlePlayerEnter(*itr);
}
}
// get the difference of numbers
float fact_diff = (float)m_activePlayers[0].size() - (float)m_activePlayers[1].size();
if(!fact_diff)
return false;
uint32 Challenger = 0;
if(fact_diff < 0)
{
// horde is in majority, but it's already horde-controlled -> no change
if(m_State == OBJECTIVESTATE_HORDE && m_ShiftPhase <= -m_ShiftMaxPhase)
return false;
if(fact_diff < - m_ShiftMaxCaptureSpeed)
fact_diff = - m_ShiftMaxCaptureSpeed;
Challenger = HORDE;
}
else
{
// ally is in majority, but it's already ally-controlled -> no change
if(m_State == OBJECTIVESTATE_ALLIANCE && m_ShiftPhase >= m_ShiftMaxPhase)
return false;
if(fact_diff > m_ShiftMaxCaptureSpeed)
fact_diff = m_ShiftMaxCaptureSpeed;
Challenger = ALLIANCE;
}
m_OldPhase = m_ShiftPhase;
m_OldState = m_State;
m_ShiftPhase += fact_diff;
// check limits, these are over the grey part
if(m_ShiftPhase <= -m_ShiftMaxPhase * (float)(m_NeutralValue) / 100.0f)
{
if(m_ShiftPhase <= -m_ShiftMaxPhase)
m_ShiftPhase = -m_ShiftMaxPhase;
m_State = OBJECTIVESTATE_HORDE;
return true;
}
else if(m_ShiftPhase >= m_ShiftMaxPhase * (float)(m_NeutralValue) / 100.0f)
{
if(m_ShiftPhase >= m_ShiftMaxPhase)
m_ShiftPhase = m_ShiftMaxPhase;
m_State = OBJECTIVESTATE_ALLIANCE;
return true;
}
if(m_OldPhase*m_ShiftPhase <=0)
{
// gone through neutral
// if challenger is ally, then n->a challenge
if(Challenger == ALLIANCE)
m_State = OBJECTIVESTATE_NEUTRAL_ALLIANCE_CHALLENGE;
// if challenger is horde, then n->h challenge
else if(Challenger == HORDE)
m_State = OBJECTIVESTATE_NEUTRAL_HORDE_CHALLENGE;
}
else
{
// old phase and current are on the same side, so one team challenges the other
if(Challenger == ALLIANCE && (m_OldState == OBJECTIVESTATE_HORDE || m_OldState == OBJECTIVESTATE_NEUTRAL_HORDE_CHALLENGE))
m_State = OBJECTIVESTATE_HORDE_ALLIANCE_CHALLENGE;
else if(Challenger == HORDE && (m_OldState == OBJECTIVESTATE_ALLIANCE || m_OldState == OBJECTIVESTATE_NEUTRAL_ALLIANCE_CHALLENGE))
m_State = OBJECTIVESTATE_ALLIANCE_HORDE_CHALLENGE;
}
return true;
}
void OutdoorPvP::SendUpdateWorldState(uint32 field, uint32 value)
{
// send to both factions
for(int i = 0; i < 2; ++i)
{
// send to all players present in the area
for(PlayerSet::iterator itr = m_players[i].begin(); itr != m_players[i].end(); ++itr)
(*itr)->SendUpdateWorldState(field,value);
}
}
void OPvPCapturePoint::SendUpdateWorldState(uint32 field, uint32 value)
{
for(uint32 team = 0; team < 2; ++team)
{
// send to all players present in the area
for(PlayerSet::iterator itr = m_activePlayers[team].begin(); itr != m_activePlayers[team].end(); ++itr)
{
(*itr)->SendUpdateWorldState(field,value);
}
}
}
void OPvPCapturePoint::SendObjectiveComplete(uint32 id,uint64 guid)
{
uint32 team;
switch(m_State)
{
case OBJECTIVESTATE_ALLIANCE:
team = 0;
break;
case OBJECTIVESTATE_HORDE:
team = 1;
break;
default:
return;
}
// send to all players present in the area
for(PlayerSet::iterator itr = m_activePlayers[team].begin(); itr != m_activePlayers[team].end(); ++itr)
(*itr)->KilledMonster(id, guid);
}
void OutdoorPvP::HandleKill(Player *killer, Unit * killed)
{
if(Group * pGroup = killer->GetGroup())
{
for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player *pGroupGuy = itr->getSource();
if(!pGroupGuy)
continue;
// skip if too far away
if(!pGroupGuy->IsAtGroupRewardDistance(killed))
continue;
// creature kills must be notified, even if not inside objective / not outdoor pvp active
// player kills only count if active and inside objective
if(( pGroupGuy->IsOutdoorPvPActive() && IsInsideObjective(pGroupGuy) ) || killed->GetTypeId() == TYPEID_UNIT)
{
HandleKillImpl(pGroupGuy, killed);
}
}
}
else
{
// creature kills must be notified, even if not inside objective / not outdoor pvp active
if(killer && (( killer->IsOutdoorPvPActive() && IsInsideObjective(killer) ) || killed->GetTypeId() == TYPEID_UNIT))
{
HandleKillImpl(killer, killed);
}
}
}
bool OutdoorPvP::IsInsideObjective(Player *plr) const
{
for(OPvPCapturePointMap::const_iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
if((*itr)->IsInsideObjective(plr))
return true;
return false;
}
bool OPvPCapturePoint::IsInsideObjective(Player *plr) const
{
return m_activePlayers[plr->GetTeamId()].find(plr) != m_activePlayers[plr->GetTeamId()].end();
}
bool OutdoorPvP::HandleCustomSpell(Player *plr, uint32 spellId, GameObject * go)
{
for(OPvPCapturePointMap::iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
{
if((*itr)->HandleCustomSpell(plr,spellId,go))
return true;
}
return false;
}
bool OPvPCapturePoint::HandleCustomSpell(Player *plr, uint32 spellId, GameObject * go)
{
if(!plr->IsOutdoorPvPActive())
return false;
return false;
}
bool OutdoorPvP::HandleOpenGo(Player *plr, uint64 guid)
{
for(OPvPCapturePointMap::iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
{
if((*itr)->HandleOpenGo(plr,guid) >= 0)
return true;
}
return false;
}
bool OutdoorPvP::HandleGossipOption(Player * plr, uint64 guid, uint32 id)
{
for(OPvPCapturePointMap::iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
{
if((*itr)->HandleGossipOption(plr, guid, id))
return true;
}
return false;
}
bool OutdoorPvP::CanTalkTo(Player * plr, Creature * c, GossipOption &gso)
{
for(OPvPCapturePointMap::iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
{
if((*itr)->CanTalkTo(plr, c, gso))
return true;
}
return false;
}
bool OutdoorPvP::HandleDropFlag(Player * plr, uint32 id)
{
for(OPvPCapturePointMap::iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
{
if((*itr)->HandleDropFlag(plr, id))
return true;
}
return false;
}
bool OPvPCapturePoint::HandleGossipOption(Player * plr, uint64 guid, uint32 id)
{
return false;
}
bool OPvPCapturePoint::CanTalkTo(Player * plr, Creature * c, GossipOption &gso)
{
return false;
}
bool OPvPCapturePoint::HandleDropFlag(Player * plr, uint32 id)
{
return false;
}
int32 OPvPCapturePoint::HandleOpenGo(Player *plr, uint64 guid)
{
std::map<uint64,uint32>::iterator itr = m_ObjectTypes.find(guid);
if(itr != m_ObjectTypes.end())
{
return itr->second;
}
return -1;
}
bool OutdoorPvP::HandleAreaTrigger(Player *plr, uint32 trigger)
{
return false;
}
void OutdoorPvP::RegisterZone(uint32 zoneId)
{
sOutdoorPvPMgr.AddZone(zoneId, this);
}
bool OutdoorPvP::HasPlayer(Player *plr) const
{
return m_players[plr->GetTeamId()].find(plr) != m_players[plr->GetTeamId()].end();
}
void OutdoorPvP::TeamCastSpell(TeamId team, int32 spellId)
{
if(spellId > 0)
for(PlayerSet::iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr)
(*itr)->CastSpell(*itr, (uint32)spellId, true);
else
for(PlayerSet::iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr)
(*itr)->RemoveAura((uint32)-spellId); // by stack?
}
void OutdoorPvP::TeamApplyBuff(TeamId team, uint32 spellId, uint32 spellId2)
{
TeamCastSpell(team, spellId);
TeamCastSpell(OTHER_TEAM(team), spellId2 ? -(int32)spellId2 : -(int32)spellId);
}