* Even more restructuring of the game library.

--HG--
branch : trunk
This commit is contained in:
XTZGZoReX
2010-06-06 22:55:56 +02:00
parent 840d4ab99d
commit 62200e8d02
169 changed files with 0 additions and 0 deletions
@@ -1,869 +0,0 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 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 "Common.h"
#include "Language.h"
#include "Database/DatabaseEnv.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Opcodes.h"
#include "Log.h"
#include "ObjectMgr.h"
#include "SpellMgr.h"
#include "Player.h"
#include "GossipDef.h"
#include "UpdateMask.h"
#include "ObjectAccessor.h"
#include "Creature.h"
#include "Pet.h"
#include "BattleGroundMgr.h"
#include "BattleGround.h"
#include "Guild.h"
#include "ScriptMgr.h"
void WorldSession::HandleTabardVendorActivateOpcode(WorldPacket & recv_data)
{
uint64 guid;
recv_data >> guid;
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(guid,UNIT_NPC_FLAG_TABARDDESIGNER);
if (!unit)
{
sLog.outDebug("WORLD: HandleTabardVendorActivateOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
return;
}
// remove fake death
if (GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
SendTabardVendorActivate(guid);
}
void WorldSession::SendTabardVendorActivate(uint64 guid)
{
WorldPacket data(MSG_TABARDVENDOR_ACTIVATE, 8);
data << guid;
SendPacket(&data);
}
void WorldSession::HandleBankerActivateOpcode(WorldPacket & recv_data)
{
uint64 guid;
sLog.outDebug("WORLD: Received CMSG_BANKER_ACTIVATE");
recv_data >> guid;
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(guid,UNIT_NPC_FLAG_BANKER);
if (!unit)
{
sLog.outDebug("WORLD: HandleBankerActivateOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
return;
}
// remove fake death
if (GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
SendShowBank(guid);
}
void WorldSession::SendShowBank(uint64 guid)
{
WorldPacket data(SMSG_SHOW_BANK, 8);
data << guid;
SendPacket(&data);
}
void WorldSession::HandleTrainerListOpcode(WorldPacket & recv_data)
{
uint64 guid;
recv_data >> guid;
SendTrainerList(guid);
}
void WorldSession::SendTrainerList(uint64 guid)
{
std::string str = GetTrinityString(LANG_NPC_TAINER_HELLO);
SendTrainerList(guid, str);
}
void WorldSession::SendTrainerList(uint64 guid, const std::string& strTitle)
{
sLog.outDebug("WORLD: SendTrainerList");
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(guid,UNIT_NPC_FLAG_TRAINER);
if (!unit)
{
sLog.outDebug("WORLD: SendTrainerList - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
return;
}
// remove fake death
if (GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
// trainer list loaded at check;
if (!unit->isCanTrainingOf(_player,true))
return;
CreatureInfo const *ci = unit->GetCreatureInfo();
if (!ci)
{
sLog.outDebug("WORLD: SendTrainerList - (GUID: %u) NO CREATUREINFO!",GUID_LOPART(guid));
return;
}
TrainerSpellData const* trainer_spells = unit->GetTrainerSpells();
if (!trainer_spells)
{
sLog.outDebug("WORLD: SendTrainerList - Training spells not found for creature (GUID: %u Entry: %u)",
GUID_LOPART(guid), unit->GetEntry());
return;
}
WorldPacket data(SMSG_TRAINER_LIST, 8+4+4+trainer_spells->spellList.size()*38 + strTitle.size()+1);
data << guid;
data << uint32(trainer_spells->trainerType);
size_t count_pos = data.wpos();
data << uint32(trainer_spells->spellList.size());
// reputation discount
float fDiscountMod = _player->GetReputationPriceDiscount(unit);
bool can_learn_primary_prof = GetPlayer()->GetFreePrimaryProfessionPoints() > 0;
uint32 count = 0;
for (TrainerSpellMap::const_iterator itr = trainer_spells->spellList.begin(); itr != trainer_spells->spellList.end(); ++itr)
{
TrainerSpell const* tSpell = &itr->second;
bool valid = true;
bool primary_prof_first_rank = false;
for (uint8 i = 0; i < MAX_SPELL_EFFECTS ; ++i)
{
if (!tSpell->learnedSpell[i])
continue;
if (!_player->IsSpellFitByClassAndRace(tSpell->learnedSpell[i]))
{
valid = false;
break;
}
if (spellmgr.IsPrimaryProfessionFirstRankSpell(tSpell->learnedSpell[i]))
primary_prof_first_rank = true;
}
if (!valid)
continue;
TrainerSpellState state = _player->GetTrainerSpellState(tSpell);
data << uint32(tSpell->spell); // learned spell (or cast-spell in profession case)
data << uint8(state == TRAINER_SPELL_GREEN_DISABLED ? TRAINER_SPELL_GREEN : state);
data << uint32(floor(tSpell->spellCost * fDiscountMod));
data << uint32(primary_prof_first_rank && can_learn_primary_prof ? 1 : 0);
// primary prof. learn confirmation dialog
data << uint32(primary_prof_first_rank ? 1 : 0); // must be equal prev. field to have learn button in enabled state
data << uint8(tSpell->reqLevel);
data << uint32(tSpell->reqSkill);
data << uint32(tSpell->reqSkillValue);
//prev + req or req + 0
uint8 maxReq = 0;
for (uint8 i = 0; i < MAX_SPELL_EFFECTS ; ++i)
{
if (!tSpell->learnedSpell[i])
continue;
if (SpellChainNode const* chain_node = spellmgr.GetSpellChainNode(tSpell->learnedSpell[i]))
{
if (chain_node->prev)
{
data << uint32(chain_node->prev);
++maxReq;
}
}
if (maxReq == 3)
break;
SpellsRequiringSpellMapBounds spellsRequired = spellmgr.GetSpellsRequiredForSpellBounds(tSpell->learnedSpell[i]);
for (SpellsRequiringSpellMap::const_iterator itr2 = spellsRequired.first; itr2 != spellsRequired.second && maxReq < 3; ++itr2)
{
data << uint32(itr2->second);
++maxReq;
}
if (maxReq == 3)
break;
}
while (maxReq < 3)
{
data << uint32(0);
++maxReq;
}
++count;
}
data << strTitle;
data.put<uint32>(count_pos,count);
SendPacket(&data);
}
void WorldSession::HandleTrainerBuySpellOpcode(WorldPacket & recv_data)
{
uint64 guid;
uint32 spellId = 0;
recv_data >> guid >> spellId;
sLog.outDebug("WORLD: Received CMSG_TRAINER_BUY_SPELL NpcGUID=%u, learn spell id is: %u",uint32(GUID_LOPART(guid)), spellId);
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TRAINER);
if (!unit)
{
sLog.outDebug("WORLD: HandleTrainerBuySpellOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
return;
}
// remove fake death
if (GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
if (!unit->isCanTrainingOf(_player,true))
return;
// check present spell in trainer spell list
TrainerSpellData const* trainer_spells = unit->GetTrainerSpells();
if (!trainer_spells)
return;
// not found, cheat?
TrainerSpell const* trainer_spell = trainer_spells->Find(spellId);
if (!trainer_spell)
return;
// can't be learn, cheat? Or double learn with lags...
if (_player->GetTrainerSpellState(trainer_spell) != TRAINER_SPELL_GREEN)
return;
// apply reputation discount
uint32 nSpellCost = uint32(floor(trainer_spell->spellCost * _player->GetReputationPriceDiscount(unit)));
// check money requirement
if (_player->GetMoney() < nSpellCost)
return;
_player->ModifyMoney(-int32(nSpellCost));
WorldPacket data(SMSG_PLAY_SPELL_VISUAL, 12); // visual effect on trainer
data << uint64(guid);
data << uint32(0xB3); // index from SpellVisualKit.dbc
SendPacket(&data);
data.Initialize(SMSG_PLAY_SPELL_IMPACT, 12); // visual effect on player
data << uint64(_player->GetGUID());
data << uint32(0x016A); // index from SpellVisualKit.dbc
SendPacket(&data);
// learn explicitly or cast explicitly
if (trainer_spell->IsCastable())
_player->CastSpell(_player,trainer_spell->spell,true);
else
_player->learnSpell(spellId, false);
data.Initialize(SMSG_TRAINER_BUY_SUCCEEDED, 12);
data << uint64(guid);
data << uint32(spellId); // should be same as in packet from client
SendPacket(&data);
}
void WorldSession::HandleGossipHelloOpcode(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: Received CMSG_GOSSIP_HELLO");
uint64 guid;
recv_data >> guid;
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE);
if (!unit)
{
sLog.outDebug("WORLD: HandleGossipHelloOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
return;
}
GetPlayer()->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TALK);
// remove fake death
//if (GetPlayer()->hasUnitState(UNIT_STAT_DIED))
// GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
if (unit->isArmorer() || unit->isCivilian() || unit->isQuestGiver() || unit->isServiceProvider())
{
unit->StopMoving();
}
// If spiritguide, no need for gossip menu, just put player into resurrect queue
if (unit->isSpiritGuide())
{
BattleGround *bg = _player->GetBattleGround();
if (bg)
{
bg->AddPlayerToResurrectQueue(unit->GetGUID(), _player->GetGUID());
sBattleGroundMgr.SendAreaSpiritHealerQueryOpcode(_player, bg, unit->GetGUID());
return;
}
}
if (!sScriptMgr.GossipHello(_player, unit))
{
// _player->TalkedToCreature(unit->GetEntry(), unit->GetGUID());
_player->PrepareGossipMenu(unit, unit->GetCreatureInfo()->GossipMenuId, true);
_player->SendPreparedGossip(unit);
}
}
/*void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: CMSG_GOSSIP_SELECT_OPTION");
uint32 option;
uint32 unk;
uint64 guid;
std::string code = "";
recv_data >> guid >> unk >> option;
if (_player->PlayerTalkClass->GossipOptionCoded(option))
{
sLog.outDebug("reading string");
recv_data >> code;
sLog.outDebug("string read: %s", code.c_str());
}
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE);
if (!unit)
{
sLog.outDebug("WORLD: HandleGossipSelectOptionOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
return;
}
// remove fake death
if (GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
if (!code.empty())
{
if (!Script->GossipSelectWithCode(_player, unit, _player->PlayerTalkClass->GossipOptionSender (option), _player->PlayerTalkClass->GossipOptionAction(option), code.c_str()))
unit->OnGossipSelect (_player, option);
}
else
{
if (!Script->GossipSelect (_player, unit, _player->PlayerTalkClass->GossipOptionSender (option), _player->PlayerTalkClass->GossipOptionAction (option)))
unit->OnGossipSelect (_player, option);
}
}*/
void WorldSession::HandleSpiritHealerActivateOpcode(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: CMSG_SPIRIT_HEALER_ACTIVATE");
uint64 guid;
recv_data >> guid;
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_SPIRITHEALER);
if (!unit)
{
sLog.outDebug("WORLD: HandleSpiritHealerActivateOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(guid)));
return;
}
// remove fake death
if (GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
SendSpiritResurrect();
}
void WorldSession::SendSpiritResurrect()
{
_player->ResurrectPlayer(0.5f, true);
_player->DurabilityLossAll(0.25f,true);
// get corpse nearest graveyard
WorldSafeLocsEntry const *corpseGrave = NULL;
Corpse *corpse = _player->GetCorpse();
if (corpse)
corpseGrave = objmgr.GetClosestGraveYard(
corpse->GetPositionX(), corpse->GetPositionY(), corpse->GetPositionZ(), corpse->GetMapId(), _player->GetTeam());
// now can spawn bones
_player->SpawnCorpseBones();
// teleport to nearest from corpse graveyard, if different from nearest to player ghost
if (corpseGrave)
{
WorldSafeLocsEntry const *ghostGrave = objmgr.GetClosestGraveYard(
_player->GetPositionX(), _player->GetPositionY(), _player->GetPositionZ(), _player->GetMapId(), _player->GetTeam());
if (corpseGrave != ghostGrave)
_player->TeleportTo(corpseGrave->map_id, corpseGrave->x, corpseGrave->y, corpseGrave->z, _player->GetOrientation());
// or update at original position
else
_player->UpdateObjectVisibility();
}
// or update at original position
else
_player->UpdateObjectVisibility();
}
void WorldSession::HandleBinderActivateOpcode(WorldPacket & recv_data)
{
uint64 npcGUID;
recv_data >> npcGUID;
if (!GetPlayer()->IsInWorld() || !GetPlayer()->isAlive())
return;
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(npcGUID,UNIT_NPC_FLAG_INNKEEPER);
if (!unit)
{
sLog.outDebug("WORLD: HandleBinderActivateOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(npcGUID)));
return;
}
// remove fake death
if (GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
SendBindPoint(unit);
}
void WorldSession::SendBindPoint(Creature *npc)
{
// prevent set homebind to instances in any case
if (GetPlayer()->GetMap()->Instanceable())
return;
uint32 bindspell = 3286;
// update sql homebind
CharacterDatabase.PExecute("UPDATE character_homebind SET map = '%u', zone = '%u', position_x = '%f', position_y = '%f', position_z = '%f' WHERE guid = '%u'",
_player->GetMapId(), _player->GetAreaId(), _player->GetPositionX(), _player->GetPositionY(), _player->GetPositionZ(), _player->GetGUIDLow());
_player->m_homebindMapId = _player->GetMapId();
_player->m_homebindAreaId = _player->GetAreaId();
_player->m_homebindX = _player->GetPositionX();
_player->m_homebindY = _player->GetPositionY();
_player->m_homebindZ = _player->GetPositionZ();
// send spell for homebinding (3286)
npc->CastSpell(_player, bindspell, true);
WorldPacket data(SMSG_TRAINER_BUY_SUCCEEDED, (8+4));
data << uint64(npc->GetGUID());
data << uint32(bindspell);
SendPacket(&data);
_player->PlayerTalkClass->CloseGossip();
}
void WorldSession::HandleListStabledPetsOpcode(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: Recv MSG_LIST_STABLED_PETS");
uint64 npcGUID;
recv_data >> npcGUID;
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
if (!unit)
{
sLog.outDebug("WORLD: HandleListStabledPetsOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(npcGUID)));
return;
}
// remove fake death
if (GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
// remove mounts this fix bug where getting pet from stable while mounted deletes pet.
if (GetPlayer()->IsMounted())
GetPlayer()->RemoveAurasByType(SPELL_AURA_MOUNTED);
SendStablePet(npcGUID);
}
void WorldSession::SendStablePet(uint64 guid)
{
sLog.outDebug("WORLD: Recv MSG_LIST_STABLED_PETS Send.");
WorldPacket data(MSG_LIST_STABLED_PETS, 200); // guess size
data << uint64 (guid);
Pet *pet = _player->GetPet();
size_t wpos = data.wpos();
data << uint8(0); // place holder for slot show number
data << uint8(GetPlayer()->m_stableSlots);
uint8 num = 0; // counter for place holder
// not let move dead pet in slot
if (pet && pet->isAlive() && pet->getPetType() == HUNTER_PET)
{
data << uint32(pet->GetCharmInfo()->GetPetNumber());
data << uint32(pet->GetEntry());
data << uint32(pet->getLevel());
data << pet->GetName(); // petname
data << uint8(1); // 1 = current, 2/3 = in stable (any from 4,5,... create problems with proper show)
++num;
}
// 0 1 2 3 4
QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT owner, id, entry, level, name FROM character_pet WHERE owner = '%u' AND slot >= '%u' AND slot <= '%u' ORDER BY slot",
_player->GetGUIDLow(),PET_SAVE_FIRST_STABLE_SLOT,PET_SAVE_LAST_STABLE_SLOT);
if (result)
{
do
{
Field *fields = result->Fetch();
data << uint32(fields[1].GetUInt32()); // petnumber
data << uint32(fields[2].GetUInt32()); // creature entry
data << uint32(fields[3].GetUInt32()); // level
data << fields[4].GetString(); // name
data << uint8(2); // 1 = current, 2/3 = in stable (any from 4,5,... create problems with proper show)
++num;
}while (result->NextRow());
}
data.put<uint8>(wpos, num); // set real data to placeholder
SendPacket(&data);
}
void WorldSession::HandleStablePet(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: Recv CMSG_STABLE_PET");
uint64 npcGUID;
recv_data >> npcGUID;
if (!GetPlayer()->isAlive())
return;
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
if (!unit)
{
sLog.outDebug("WORLD: HandleStablePet - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(npcGUID)));
return;
}
// remove fake death
if (GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
Pet *pet = _player->GetPet();
// can't place in stable dead pet
if (!pet||!pet->isAlive()||pet->getPetType() != HUNTER_PET)
{
WorldPacket data(SMSG_STABLE_RESULT, 1);
data << uint8(0x06);
SendPacket(&data);
return;
}
uint32 free_slot = 1;
QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT owner,slot,id FROM character_pet WHERE owner = '%u' AND slot >= '%u' AND slot <= '%u' ORDER BY slot ",
_player->GetGUIDLow(),PET_SAVE_FIRST_STABLE_SLOT,PET_SAVE_LAST_STABLE_SLOT);
if (result)
{
do
{
Field *fields = result->Fetch();
uint32 slot = fields[1].GetUInt32();
// slots ordered in query, and if not equal then free
if (slot != free_slot)
break;
// this slot not free, skip
++free_slot;
}while (result->NextRow());
}
WorldPacket data(SMSG_STABLE_RESULT, 1);
if (free_slot > 0 && free_slot <= GetPlayer()->m_stableSlots)
{
_player->RemovePet(pet,PetSaveMode(free_slot));
data << uint8(0x08);
}
else
data << uint8(0x06);
SendPacket(&data);
}
void WorldSession::HandleUnstablePet(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: Recv CMSG_UNSTABLE_PET.");
uint64 npcGUID;
uint32 petnumber;
recv_data >> npcGUID >> petnumber;
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
if (!unit)
{
sLog.outDebug("WORLD: HandleUnstablePet - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(npcGUID)));
return;
}
// remove fake death
if (GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
uint32 creature_id = 0;
{
QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT entry FROM character_pet WHERE owner = '%u' AND id = '%u' AND slot >='%u' AND slot <= '%u'",
_player->GetGUIDLow(),petnumber,PET_SAVE_FIRST_STABLE_SLOT,PET_SAVE_LAST_STABLE_SLOT);
if (result)
{
Field *fields = result->Fetch();
creature_id = fields[0].GetUInt32();
}
}
if (!creature_id)
{
WorldPacket data(SMSG_STABLE_RESULT, 1);
data << uint8(0x06);
SendPacket(&data);
return;
}
CreatureInfo const* creatureInfo = objmgr.GetCreatureTemplate(creature_id);
if (!creatureInfo || !creatureInfo->isTameable(_player->CanTameExoticPets()))
{
WorldPacket data(SMSG_STABLE_RESULT, 1);
data << uint8(0x06);
SendPacket(&data);
return;
}
Pet* pet = _player->GetPet();
if (pet && pet->isAlive())
{
WorldPacket data(SMSG_STABLE_RESULT, 1);
data << uint8(0x06);
SendPacket(&data);
return;
}
// delete dead pet
if (pet)
_player->RemovePet(pet,PET_SAVE_AS_DELETED);
Pet *newpet = new Pet(_player, HUNTER_PET);
if (!newpet->LoadPetFromDB(_player,creature_id,petnumber))
{
delete newpet;
newpet = NULL;
WorldPacket data(SMSG_STABLE_RESULT, 1);
data << uint8(0x06);
SendPacket(&data);
return;
}
WorldPacket data(SMSG_STABLE_RESULT, 1);
data << uint8(0x09);
SendPacket(&data);
}
void WorldSession::HandleBuyStableSlot(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: Recv CMSG_BUY_STABLE_SLOT.");
uint64 npcGUID;
recv_data >> npcGUID;
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
if (!unit)
{
sLog.outDebug("WORLD: HandleBuyStableSlot - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(npcGUID)));
return;
}
// remove fake death
if (GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
WorldPacket data(SMSG_STABLE_RESULT, 200);
if (GetPlayer()->m_stableSlots < MAX_PET_STABLES)
{
StableSlotPricesEntry const *SlotPrice = sStableSlotPricesStore.LookupEntry(GetPlayer()->m_stableSlots+1);
if (_player->GetMoney() >= SlotPrice->Price)
{
++GetPlayer()->m_stableSlots;
_player->ModifyMoney(-int32(SlotPrice->Price));
data << uint8(0x0A); // success buy
}
else
data << uint8(0x06);
}
else
data << uint8(0x06);
SendPacket(&data);
}
void WorldSession::HandleStableRevivePet(WorldPacket &/* recv_data */)
{
sLog.outDebug("HandleStableRevivePet: Not implemented");
}
void WorldSession::HandleStableSwapPet(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: Recv CMSG_STABLE_SWAP_PET.");
uint64 npcGUID;
uint32 pet_number;
recv_data >> npcGUID >> pet_number;
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
if (!unit)
{
sLog.outDebug("WORLD: HandleStableSwapPet - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(npcGUID)));
return;
}
// remove fake death
if (GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
WorldPacket data(SMSG_STABLE_RESULT, 200); // guess size
Pet* pet = _player->GetPet();
if (!pet || pet->getPetType() != HUNTER_PET)
return;
// find swapped pet slot in stable
QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT slot,entry FROM character_pet WHERE owner = '%u' AND id = '%u'",
_player->GetGUIDLow(),pet_number);
if (!result)
return;
Field *fields = result->Fetch();
uint32 slot = fields[0].GetUInt32();
uint32 creature_id = fields[1].GetUInt32();
if (!creature_id)
{
WorldPacket data(SMSG_STABLE_RESULT, 1);
data << uint8(0x06);
SendPacket(&data);
return;
}
CreatureInfo const* creatureInfo = objmgr.GetCreatureTemplate(creature_id);
if (!creatureInfo || !creatureInfo->isTameable(_player->CanTameExoticPets()))
{
WorldPacket data(SMSG_STABLE_RESULT, 1);
data << uint8(0x06);
SendPacket(&data);
return;
}
// move alive pet to slot or delete dead pet
_player->RemovePet(pet,pet->isAlive() ? PetSaveMode(slot) : PET_SAVE_AS_DELETED);
// summon unstabled pet
Pet *newpet = new Pet(_player);
if (!newpet->LoadPetFromDB(_player,creature_id,pet_number))
{
delete newpet;
data << uint8(0x06);
}
else
data << uint8(0x09);
SendPacket(&data);
}
void WorldSession::HandleRepairItemOpcode(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: CMSG_REPAIR_ITEM");
uint64 npcGUID, itemGUID;
uint8 guildBank; // new in 2.3.2, bool that means from guild bank money
recv_data >> npcGUID >> itemGUID >> guildBank;
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(npcGUID, UNIT_NPC_FLAG_REPAIR);
if (!unit)
{
sLog.outDebug("WORLD: HandleRepairItemOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(npcGUID)));
return;
}
// remove fake death
if (GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
// reputation discount
float discountMod = _player->GetReputationPriceDiscount(unit);
uint32 TotalCost = 0;
if (itemGUID)
{
sLog.outDebug("ITEM: Repair item, itemGUID = %u, npcGUID = %u", GUID_LOPART(itemGUID), GUID_LOPART(npcGUID));
Item* item = _player->GetItemByGuid(itemGUID);
if (item)
TotalCost= _player->DurabilityRepair(item->GetPos(),true,discountMod,guildBank>0?true:false);
}
else
{
sLog.outDebug("ITEM: Repair all items, npcGUID = %u", GUID_LOPART(npcGUID));
TotalCost = _player->DurabilityRepairAll(true,discountMod,guildBank>0?true:false);
}
if (guildBank)
{
uint32 GuildId = _player->GetGuildId();
if (!GuildId)
return;
Guild *pGuild = objmgr.GetGuildById(GuildId);
if (!pGuild)
return;
pGuild->LogBankEvent(GUILD_BANK_LOG_REPAIR_MONEY, 0, _player->GetGUIDLow(), TotalCost);
pGuild->SendMoneyInfo(this, _player->GetGUIDLow());
}
}
@@ -1,79 +0,0 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 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
*/
#ifndef __NPCHANDLER_H
#define __NPCHANDLER_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
struct PageText
{
uint32 Page_ID;
char * Text;
uint32 Next_Page;
};
// 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 QEmote
{
uint32 _Emote;
uint32 _Delay;
};
struct GossipTextOption
{
std::string Text_0;
std::string Text_1;
uint32 Language;
float Probability;
QEmote Emotes[3];
};
struct GossipText
{
GossipTextOption Options[8];
};
struct PageTextLocale
{
std::vector<std::string> Text;
};
struct NpcTextLocale
{
NpcTextLocale() { Text_0.resize(8); Text_1.resize(8); }
std::vector<std::vector<std::string> > Text_0;
std::vector<std::vector<std::string> > Text_1;
};
#endif
File diff suppressed because it is too large Load Diff
@@ -1,375 +0,0 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 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 "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "Policies/SingletonImp.h"
#include "Player.h"
#include "Creature.h"
#include "GameObject.h"
#include "DynamicObject.h"
#include "Vehicle.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 "Opcodes.h"
#include "ObjectDefines.h"
#include "MapInstanced.h"
#include "World.h"
#include <cmath>
#define CLASS_LOCK Trinity::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;
}
}
Creature* ObjectAccessor::GetCreatureOrPetOrVehicle(WorldObject const& u, uint64 guid)
{
if (IS_PLAYER_GUID(guid))
return NULL;
if (IS_PET_GUID(guid))
return GetPet(guid);
return u.IsInWorld() ? u.GetMap()->GetCreature(guid) : NULL;
}
Corpse* ObjectAccessor::GetCorpse(WorldObject const& u, uint64 guid)
{
Corpse* ret = GetObjectInWorld(guid, (Corpse*)NULL);
if (!ret)
return NULL;
if (ret->GetMapId() != u.GetMapId())
return NULL;
if (ret->GetInstanceId() != u.GetInstanceId())
return NULL;
return ret;
}
WorldObject* ObjectAccessor::GetWorldObject(WorldObject const& p, uint64 guid)
{
switch (GUID_HIPART(guid))
{
case HIGHGUID_PLAYER: return FindPlayer(guid);
case HIGHGUID_GAMEOBJECT: return p.GetMap()->GetGameObject(guid);
case HIGHGUID_VEHICLE:
case HIGHGUID_UNIT: return p.GetMap()->GetCreature(guid);
case HIGHGUID_PET: return GetPet(guid);
case HIGHGUID_DYNAMICOBJECT: return p.GetMap()->GetDynamicObject(guid);
case HIGHGUID_TRANSPORT: return NULL;
case HIGHGUID_CORPSE: return GetCorpse(p,guid);
case HIGHGUID_MO_TRANSPORT: return NULL;
default: return NULL;
}
}
Object* ObjectAccessor::GetObjectByTypeMask(WorldObject const& p, uint64 guid, uint32 typemask)
{
switch (GUID_HIPART(guid))
{
case HIGHGUID_ITEM:
if (typemask & TYPEMASK_ITEM && p.GetTypeId() == TYPEID_PLAYER)
return ((Player const&)p).GetItemByGuid(guid);
break;
case HIGHGUID_PLAYER:
if (typemask & TYPEMASK_PLAYER)
return FindPlayer(guid);
break;
case HIGHGUID_GAMEOBJECT:
if (typemask & TYPEMASK_GAMEOBJECT)
return p.GetMap()->GetGameObject(guid);
break;
case HIGHGUID_UNIT:
case HIGHGUID_VEHICLE:
if (typemask & TYPEMASK_UNIT)
return p.GetMap()->GetCreature(guid);
break;
case HIGHGUID_PET:
if (typemask & TYPEMASK_UNIT)
return GetPet(guid);
break;
case HIGHGUID_DYNAMICOBJECT:
if (typemask & TYPEMASK_DYNAMICOBJECT)
return p.GetMap()->GetDynamicObject(guid);
break;
case HIGHGUID_TRANSPORT:
case HIGHGUID_CORPSE:
case HIGHGUID_MO_TRANSPORT:
break;
}
return NULL;
}
Player* ObjectAccessor::FindPlayer(uint64 guid)
{
Player* plr = GetObjectInWorld(guid, (Player*)NULL);
if (!plr || !plr->IsInWorld())
return NULL;
return plr;
}
Player* ObjectAccessor::FindPlayerByName(const char* name)
{
Guard guard(*HashMapHolder<Player>::GetLock());
HashMapHolder<Player>::MapType& m = HashMapHolder<Player>::GetContainer();
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()
{
Guard guard(*HashMapHolder<Player>::GetLock());
HashMapHolder<Player>::MapType& m = HashMapHolder<Player>::GetContainer();
for (HashMapHolder<Player>::MapType::iterator itr = m.begin(); itr != m.end(); ++itr)
itr->second->SaveToDB();
}
Pet* ObjectAccessor::GetPet(uint64 guid)
{
return GetObjectInWorld(guid, (Pet*)NULL);
}
Corpse* ObjectAccessor::GetCorpseForPlayerGUID(uint64 guid)
{
Guard guard(i_corpseGuard);
Player2CorpsesMapType::iterator iter = i_player2corpse.find(guid);
if (iter == i_player2corpse.end())
return NULL;
assert(iter->second->GetType() != CORPSE_BONES);
return iter->second;
}
void ObjectAccessor::RemoveCorpse(Corpse* corpse)
{
assert(corpse && corpse->GetType() != CORPSE_BONES);
if (corpse->FindMap())
corpse->FindMap()->Remove(corpse, false);
else
corpse->RemoveFromWorld();
// Critical section
{
Guard guard(i_corpseGuard);
Player2CorpsesMapType::iterator iter = i_player2corpse.find(corpse->GetOwnerGUID());
if (iter == i_player2corpse.end()) // TODO: Fix this
return;
// build mapid*cellid -> guid_set map
CellPair cell_pair = Trinity::ComputeCellPair(corpse->GetPositionX(), corpse->GetPositionY());
uint32 cell_id = (cell_pair.y_coord * TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
objmgr.DeleteCorpseCellData(corpse->GetMapId(), cell_id, corpse->GetOwnerGUID());
i_player2corpse.erase(iter);
}
}
void ObjectAccessor::AddCorpse(Corpse* corpse)
{
assert(corpse && corpse->GetType() != CORPSE_BONES);
// Critical section
{
Guard guard(i_corpseGuard);
assert(i_player2corpse.find(corpse->GetOwnerGUID()) == i_player2corpse.end());
i_player2corpse[corpse->GetOwnerGUID()] = corpse;
// build mapid*cellid -> guid_set map
CellPair cell_pair = Trinity::ComputeCellPair(corpse->GetPositionX(), corpse->GetPositionY());
uint32 cell_id = (cell_pair.y_coord * TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
objmgr.AddCorpseCellData(corpse->GetMapId(), cell_id, corpse->GetOwnerGUID(), 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(uint64 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
// TODO: really, now...
//sLog.outError("Try remove corpse that not in map for GUID %ul", player_guid);
return NULL;
}
DEBUG_LOG("Deleting Corpse and spawned bones.");
//Map* map = corpse->FindMap();
// remove corpse from player_guid -> corpse map
RemoveCorpse(corpse);
// done in removecorpse
// remove resurrectable corpse from grid object registry (loaded state checked into call)
// do not load the map if it's not loaded
//Map *map = MapManager::Instance().FindMap(corpse->GetMapId(), corpse->GetInstanceId());
//if (map)
// map->Remove(corpse, false);
// remove corpse from DB
corpse->DeleteFromDB();
// we don't want bones to save some cpu.. :)
delete corpse;
return NULL;
/*
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_DEATH_BONES_BG_OR_ARENA) : sWorld.getConfig(CONFIG_DEATH_BONES_WORLD))) &&
!map->IsRemovalGrid(corpse->GetPositionX(), corpse->GetPositionY()))
{
// Create bones, don't change Corpse
bones = new Corpse;
bones->Create(corpse->GetGUIDLow(), map);
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 in-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->SetUInt64Value(CORPSE_FIELD_OWNER, 0);
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::Update(uint32 /*diff*/)
{
UpdateDataMapType update_players;
// Critical section
{
Guard guard(i_updateGuard);
while (!i_objects.empty())
{
Object* obj = *i_objects.begin();
assert(obj && obj->IsInWorld());
i_objects.erase(i_objects.begin());
obj->BuildUpdate(update_players);
}
}
WorldPacket packet; // here we allocate a std::vector with a size of 0x10000
for (UpdateDataMapType::iterator iter = update_players.begin(); iter != update_players.end(); ++iter)
{
iter->second.BuildPacket(&packet);
iter->first->GetSession()->SendPacket(&packet);
packet.clear(); // clean the string
}
}
/// Define the static members of HashMapHolder
template <class T> UNORDERED_MAP< uint64, T* > HashMapHolder<T>::m_objectMap;
template <class T> ACE_Thread_Mutex HashMapHolder<T>::i_lock;
/// Global definitions for the hashmap storage
template class HashMapHolder<Player>;
template class HashMapHolder<Pet>;
template class HashMapHolder<GameObject>;
template class HashMapHolder<DynamicObject>;
template class HashMapHolder<Creature>;
template class HashMapHolder<Corpse>;
template Player* ObjectAccessor::GetObjectInWorld<Player>(uint32 mapid, float x, float y, uint64 guid, Player* /*fake*/);
template Pet* ObjectAccessor::GetObjectInWorld<Pet>(uint32 mapid, float x, float y, uint64 guid, Pet* /*fake*/);
template Creature* ObjectAccessor::GetObjectInWorld<Creature>(uint32 mapid, float x, float y, uint64 guid, Creature* /*fake*/);
template Corpse* ObjectAccessor::GetObjectInWorld<Corpse>(uint32 mapid, float x, float y, uint64 guid, Corpse* /*fake*/);
template GameObject* ObjectAccessor::GetObjectInWorld<GameObject>(uint32 mapid, float x, float y, uint64 guid, GameObject* /*fake*/);
template DynamicObject* ObjectAccessor::GetObjectInWorld<DynamicObject>(uint32 mapid, float x, float y, uint64 guid, DynamicObject* /*fake*/);
@@ -1,257 +0,0 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 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
*/
#ifndef TRINITY_OBJECTACCESSOR_H
#define TRINITY_OBJECTACCESSOR_H
#include "Platform/Define.h"
#include "Policies/Singleton.h"
#include <ace/Thread_Mutex.h>
#include "Utilities/UnorderedMap.h"
#include "Policies/ThreadingModel.h"
#include "UpdateData.h"
#include "GridDefines.h"
#include "Object.h"
#include "Player.h"
#include <set>
class Creature;
class Corpse;
class Unit;
class GameObject;
class DynamicObject;
class WorldObject;
class Map;
template <class T>
class HashMapHolder
{
public:
typedef UNORDERED_MAP<uint64, T*> MapType;
typedef ACE_Thread_Mutex LockType;
typedef Trinity::GeneralLock<LockType> Guard;
static void Insert(T* o)
{
Guard guard(i_lock);
m_objectMap[o->GetGUID()] = o;
}
static void Remove(T* o)
{
Guard guard(i_lock);
m_objectMap.erase(o->GetGUID());
}
static T* Find(uint64 guid)
{
Guard 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 ObjectAccessor : public Trinity::Singleton<ObjectAccessor, Trinity::ClassLevelLockable<ObjectAccessor, ACE_Thread_Mutex> >
{
friend class Trinity::OperatorNew<ObjectAccessor>;
ObjectAccessor();
~ObjectAccessor();
ObjectAccessor(const ObjectAccessor&);
ObjectAccessor& operator=(const ObjectAccessor&);
public:
typedef UNORDERED_MAP<uint64, Corpse*> Player2CorpsesMapType;
typedef UNORDERED_MAP<Player*, UpdateData>::value_type UpdateDataValueType;
template<class T> static T* GetObjectInWorld(uint64 guid, T* /*fake*/)
{
return HashMapHolder<T>::Find(guid);
}
static Unit* GetObjectInWorld(uint64 guid, Unit* /*fake*/)
{
if (!guid)
return NULL;
if (IS_PLAYER_GUID(guid))
{
Unit* u = (Unit*)HashMapHolder<Player>::Find(guid);
if (!u || !u->IsInWorld())
return NULL;
return u;
}
if (IS_PET_GUID(guid))
return (Unit*)HashMapHolder<Pet>::Find(guid);
return (Unit*)HashMapHolder<Creature>::Find(guid);
}
static Unit* GetUnitInOrOutOfWorld(uint64 guid, Unit* /*fake*/)
{
if (!guid)
return NULL;
if (IS_PLAYER_GUID(guid))
{
Unit* u = (Unit*)HashMapHolder<Player>::Find(guid);
if (!u)
return NULL;
return u;
}
// Other object types than player are unloaded while out of world
return GetObjectInWorld(guid, (Unit*)NULL);
}
template<class T> static T* GetObjectInWorld(uint32 mapid, float x, float y, uint64 guid, T* /*fake*/)
{
T* obj = HashMapHolder<T>::Find(guid);
if (!obj || obj->GetMapId() != mapid)
return NULL;
CellPair p = Trinity::ComputeCellPair(x, y);
if (p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP)
{
sLog.outError("ObjectAccessor::GetObjectInWorld: invalid coordinates supplied X:%f Y:%f grid cell [%u:%u]", x, y, p.x_coord, p.y_coord);
return NULL;
}
CellPair q = Trinity::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
if (q.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || q.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP)
{
sLog.outError("ObjectAccessor::GetObjecInWorld: object (GUID: %u TypeId: %u) has invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUIDLow(), obj->GetTypeId(), obj->GetPositionX(), obj->GetPositionY(), q.x_coord, q.y_coord);
return NULL;
}
int32 dx = int32(p.x_coord) - int32(q.x_coord);
int32 dy = int32(p.y_coord) - int32(q.y_coord);
if (dx > -2 && dx < 2 && dy > -2 && dy < 2)
return obj;
else
return NULL;
}
static WorldObject* GetWorldObject(WorldObject const&, uint64);
static Object* GetObjectByTypeMask(WorldObject const&, uint64, uint32 typemask);
static Creature* GetCreatureOrPetOrVehicle(WorldObject const&, uint64);
static Unit* GetUnit(WorldObject const&, uint64 guid) { return GetObjectInWorld(guid, (Unit*)NULL); }
static Unit* GetUnitInOrOutOfWorld(WorldObject const&, uint64 guid) { return GetUnitInOrOutOfWorld(guid, (Unit*)NULL); }
static Pet* GetPet(Unit const&, uint64 guid) { return GetPet(guid); }
static Player* GetPlayer(Unit const&, uint64 guid) { return FindPlayer(guid); }
static Corpse* GetCorpse(WorldObject const& u, uint64 guid);
static Pet* GetPet(uint64 guid);
static Player* FindPlayer(uint64);
Player* FindPlayerByName(const char* name) ;
// when using this, you must use the hashmapholder's lock
HashMapHolder<Player>::MapType& GetPlayers()
{
return HashMapHolder<Player>::GetContainer();
}
// when using this, you must use the hashmapholder's lock
HashMapHolder<Creature>::MapType& GetCreatures()
{
return HashMapHolder<Creature>::GetContainer();
}
// when using this, you must use the hashmapholder's lock
HashMapHolder<GameObject>::MapType& GetGameObjects()
{
return HashMapHolder<GameObject>::GetContainer();
}
template<class T> void AddObject(T* object)
{
HashMapHolder<T>::Insert(object);
}
template<class T> void RemoveObject(T* object)
{
HashMapHolder<T>::Remove(object);
}
void RemoveObject(Player* pl)
{
HashMapHolder<Player>::Remove(pl);
RemoveUpdateObject((Object*)pl);
}
void SaveAllPlayers();
void AddUpdateObject(Object* obj)
{
Guard guard(i_updateGuard);
i_objects.insert(obj);
}
void RemoveUpdateObject(Object* obj)
{
Guard guard(i_updateGuard);
i_objects.erase(obj);
}
void Update(uint32 diff);
Corpse* GetCorpseForPlayerGUID(uint64 guid);
void RemoveCorpse(Corpse* corpse);
void AddCorpse(Corpse* corpse);
void AddCorpsesToGrid(GridPair const& gridpair, GridType& grid, Map* map);
Corpse* ConvertCorpseForPlayer(uint64 player_guid, bool insignia = false);
typedef ACE_Thread_Mutex LockType;
typedef Trinity::GeneralLock<LockType> Guard;
private:
Player2CorpsesMapType i_player2corpse;
static void _buildChangeObjectForPlayer(WorldObject*, UpdateDataMapType&);
static void _buildPacket(Player*, Object*, UpdateDataMapType&);
void _update();
std::set<Object*> i_objects;
LockType i_updateGuard;
LockType i_corpseGuard;
};
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,157 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* 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 "ObjectPosSelector.h"
ObjectPosSelector::ObjectPosSelector(float x,float y,float size,float dist)
: m_center_x(x),m_center_y(y),m_size(size),m_dist(dist)
{
m_anglestep = acos(m_dist/(m_dist+2*m_size));
m_nextUsedPos[USED_POS_PLUS] = m_UsedPosLists[USED_POS_PLUS].end();
m_nextUsedPos[USED_POS_MINUS] = m_UsedPosLists[USED_POS_MINUS].end();
m_smallStepAngle[USED_POS_PLUS] = 0;
m_smallStepAngle[USED_POS_MINUS] = 0;
m_smallStepOk[USED_POS_PLUS] = false;
m_smallStepOk[USED_POS_MINUS] = false;
m_smallStepNextUsedPos[USED_POS_PLUS] = NULL;
m_smallStepNextUsedPos[USED_POS_MINUS] = NULL;
}
ObjectPosSelector::UsedPosList::value_type const* ObjectPosSelector::nextUsedPos(UsedPosType uptype)
{
UsedPosList::const_iterator itr = m_nextUsedPos[uptype];
if(itr!=m_UsedPosLists[uptype].end())
++itr;
if(itr==m_UsedPosLists[uptype].end())
{
if(!m_UsedPosLists[~uptype].empty())
return &*m_UsedPosLists[~uptype].rbegin();
else
return NULL;
}
else
return &*itr;
}
void ObjectPosSelector::AddUsedPos(float size,float angle,float dist)
{
if(angle>=0)
m_UsedPosLists[USED_POS_PLUS].insert(UsedPosList::value_type(angle,UsedPos(1.0,size,dist)));
else
m_UsedPosLists[USED_POS_MINUS].insert(UsedPosList::value_type(-angle,UsedPos(-1.0,size,dist)));
}
void ObjectPosSelector::InitializeAngle()
{
m_nextUsedPos[USED_POS_PLUS] = m_UsedPosLists[USED_POS_PLUS].begin();
m_nextUsedPos[USED_POS_MINUS] = m_UsedPosLists[USED_POS_MINUS].begin();
m_smallStepAngle[USED_POS_PLUS] = 0;
m_smallStepAngle[USED_POS_MINUS] = 0;
m_smallStepOk[USED_POS_PLUS] = true;
m_smallStepOk[USED_POS_MINUS] = true;
}
bool ObjectPosSelector::FirstAngle(float& angle)
{
if(m_UsedPosLists[USED_POS_PLUS].empty() && !m_UsedPosLists[USED_POS_MINUS].empty() )
return NextAngleFor(*m_UsedPosLists[USED_POS_MINUS].begin(),1.0,USED_POS_PLUS,angle);
else if(m_UsedPosLists[USED_POS_MINUS].empty() && !m_UsedPosLists[USED_POS_PLUS].empty() )
return NextAngleFor(*m_UsedPosLists[USED_POS_PLUS].begin(),-1.0,USED_POS_MINUS,angle);
return false;
}
bool ObjectPosSelector::NextAngle(float& angle)
{
while(m_nextUsedPos[USED_POS_PLUS]!=m_UsedPosLists[USED_POS_PLUS].end() ||
m_nextUsedPos[USED_POS_MINUS]!=m_UsedPosLists[USED_POS_MINUS].end() ||
m_smallStepOk[USED_POS_PLUS] || m_smallStepOk[USED_POS_MINUS] )
{
// calculate next possible angle
if(NextPosibleAngle(angle))
return true;
}
return false;
}
bool ObjectPosSelector::NextUsedAngle(float& angle)
{
while(m_nextUsedPos[USED_POS_PLUS]!=m_UsedPosLists[USED_POS_PLUS].end() ||
m_nextUsedPos[USED_POS_MINUS]!=m_UsedPosLists[USED_POS_MINUS].end() )
{
// calculate next possible angle
if(!NextPosibleAngle(angle))
return true;
}
return false;
}
bool ObjectPosSelector::NextPosibleAngle( float& angle )
{
// ++ direction less updated
if( m_nextUsedPos[USED_POS_PLUS]!=m_UsedPosLists[USED_POS_PLUS].end() &&
(m_nextUsedPos[USED_POS_MINUS]==m_UsedPosLists[USED_POS_MINUS].end() || m_nextUsedPos[USED_POS_PLUS]->first <= m_nextUsedPos[USED_POS_MINUS]->first) )
{
bool ok;
if(m_smallStepOk[USED_POS_PLUS])
ok = NextSmallStepAngle(1.0,USED_POS_PLUS,angle);
else
ok = NextAngleFor(*m_nextUsedPos[USED_POS_PLUS],1.0,USED_POS_PLUS,angle);
if(!ok)
++m_nextUsedPos[USED_POS_PLUS]; // increase. only at fail (original or checked)
return ok;
}
// -- direction less updated
else if( m_nextUsedPos[USED_POS_MINUS]!=m_UsedPosLists[USED_POS_MINUS].end())
{
bool ok;
if(m_smallStepOk[USED_POS_MINUS])
ok = NextSmallStepAngle(-1.0,USED_POS_MINUS,angle);
else
ok = NextAngleFor(*m_nextUsedPos[USED_POS_MINUS],-1.0,USED_POS_MINUS,angle);
if(!ok)
++m_nextUsedPos[USED_POS_MINUS];
return ok;
}
else // both list empty
{
if( m_smallStepOk[USED_POS_PLUS] && (!m_smallStepOk[USED_POS_MINUS] || m_smallStepAngle[USED_POS_PLUS] <= m_smallStepAngle[USED_POS_MINUS]) )
{
return NextSmallStepAngle(1.0,USED_POS_PLUS,angle);
}
// -- direction less updated
else if( m_smallStepOk[USED_POS_MINUS] )
{
return NextSmallStepAngle(-1.0,USED_POS_MINUS,angle);
}
}
// no angles
return false;
}
@@ -0,0 +1,155 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* 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
*/
#ifndef _OBJECT_POS_SELECTOR_H
#define _OBJECT_POS_SELECTOR_H
#include<Common.h>
#include<map>
enum UsedPosType { USED_POS_PLUS, USED_POS_MINUS };
inline UsedPosType operator ~(UsedPosType uptype)
{
return uptype==USED_POS_PLUS ? USED_POS_MINUS : USED_POS_PLUS;
}
struct ObjectPosSelector
{
struct UsedPos
{
UsedPos(float sign_, float size_,float dist_) : sign(sign_), size(size_),dist(dist_) {}
float sign;
float size; // size of point
float dist; // dist to central point (including central point size)
};
typedef std::multimap<float,UsedPos> UsedPosList; // abs(angle)->Node
ObjectPosSelector(float x,float y,float size,float dist);
void AddUsedPos(float size,float angle,float dist);
void InitializeAngle();
bool FirstAngle(float& angle);
bool NextAngle(float& angle);
bool NextUsedAngle(float& angle);
bool NextPosibleAngle( float& angle );
bool CheckAngle(UsedPosList::value_type const& nextUsedPos, float sign, float angle ) const
{
float angle_step2 = GetAngle(nextUsedPos.second);
float next_angle = nextUsedPos.first;
if(nextUsedPos.second.sign * sign < 0) // last node from diff. list (-pi+alpha)
next_angle = 2*M_PI-next_angle; // move to positive
return fabs(angle)+angle_step2 <= next_angle;
}
bool CheckOriginal() const
{
return (m_UsedPosLists[USED_POS_PLUS].empty() || CheckAngle( *m_UsedPosLists[USED_POS_PLUS].begin(),1.0,0)) &&
(m_UsedPosLists[USED_POS_MINUS].empty() || CheckAngle( *m_UsedPosLists[USED_POS_MINUS].begin(),-1.0,0));
}
bool IsNonBalanced() const { return m_UsedPosLists[USED_POS_PLUS].empty() != m_UsedPosLists[USED_POS_MINUS].empty(); }
bool NextAngleFor( UsedPosList::value_type const& usedPos, float sign, UsedPosType uptype, float &angle )
{
float angle_step = GetAngle(usedPos.second);
// next possible angle
angle = usedPos.first * usedPos.second.sign + angle_step * sign;
UsedPosList::value_type const* nextNode = nextUsedPos(uptype);
if(nextNode)
{
// if next node permit use selected angle, then do it
if(!CheckAngle(*nextNode, sign, angle))
{
m_smallStepOk[uptype] = false;
return false;
}
}
// possible more points
m_smallStepOk[uptype] = true;
m_smallStepAngle[uptype] = angle;
m_smallStepNextUsedPos[uptype] = nextNode;
return true;
}
bool NextSmallStepAngle( float sign, UsedPosType uptype, float &angle )
{
// next possible angle
angle = m_smallStepAngle[uptype] + m_anglestep * sign;
if(fabs(angle) > M_PI)
{
m_smallStepOk[uptype] = false;
return false;
}
if(m_smallStepNextUsedPos[uptype])
{
if(fabs(angle) >= m_smallStepNextUsedPos[uptype]->first)
{
m_smallStepOk[uptype] = false;
return false;
}
// if next node permit use selected angle, then do it
if(!CheckAngle(*m_smallStepNextUsedPos[uptype], sign, angle))
{
m_smallStepOk[uptype] = false;
return false;
}
}
// possible more points
m_smallStepAngle[uptype] = angle;
return true;
}
// next used post for m_nextUsedPos[uptype]
UsedPosList::value_type const* nextUsedPos(UsedPosType uptype);
// angle from used pos to next possible free pos
float GetAngle(UsedPos const& usedPos) const { return acos(m_dist/(usedPos.dist+usedPos.size+m_size)); }
float m_center_x;
float m_center_y;
float m_size; // size of object in center
float m_dist; // distance for searching pos (including central object size)
float m_anglestep;
UsedPosList m_UsedPosLists[2];
UsedPosList::const_iterator m_nextUsedPos[2];
// field for small step from first after next used pos until next pos
float m_smallStepAngle[2];
bool m_smallStepOk[2];
UsedPosList::value_type const* m_smallStepNextUsedPos[2];
};
#endif
-813
View File
@@ -1,813 +0,0 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 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 "Common.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "ObjectMgr.h"
#include "SpellMgr.h"
#include "Log.h"
#include "Opcodes.h"
#include "Spell.h"
#include "ObjectAccessor.h"
#include "CreatureAI.h"
#include "Util.h"
#include "Pet.h"
#include "World.h"
void WorldSession::HandleDismissCritter(WorldPacket &recv_data)
{
uint64 guid;
recv_data >> guid;
sLog.outDebug("WORLD: Received CMSG_DISMISS_CRITTER for GUID %u", guid);
Unit* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
if (!pet)
{
sLog.outError("Vanitypet %u does not exist", uint32(GUID_LOPART(guid)));
return;
}
if (_player->GetCritterGUID() == pet->GetGUID())
{
if (pet->GetTypeId() == TYPEID_UNIT && pet->ToCreature()->isSummon())
pet->ToTempSummon()->UnSummon();
}
}
void WorldSession::HandlePetAction(WorldPacket & recv_data)
{
uint64 guid1;
uint32 data;
uint64 guid2;
recv_data >> guid1; //pet guid
recv_data >> data;
recv_data >> guid2; //tag guid
uint32 spellid = UNIT_ACTION_BUTTON_ACTION(data);
uint8 flag = UNIT_ACTION_BUTTON_TYPE(data); //delete = 0x07 CastSpell = C1
// used also for charmed creature
Unit* pet= ObjectAccessor::GetUnit(*_player, guid1);
sLog.outDetail("HandlePetAction.Pet %u flag is %u, spellid is %u, target %u.", uint32(GUID_LOPART(guid1)), uint32(flag), spellid, uint32(GUID_LOPART(guid2)));
if (!pet)
{
sLog.outError("Pet %u not exist.", uint32(GUID_LOPART(guid1)));
return;
}
if (pet != GetPlayer()->GetFirstControlled())
{
sLog.outError("HandlePetAction.Pet %u isn't pet of player %s.", uint32(GUID_LOPART(guid1)), GetPlayer()->GetName());
return;
}
if (!pet->isAlive())
return;
//TODO: allow control charmed player?
if (pet->GetTypeId() == TYPEID_PLAYER && !(flag == ACT_COMMAND && spellid == COMMAND_ATTACK))
return;
if (GetPlayer()->m_Controlled.size() == 1)
HandlePetActionHelper(pet, guid1, spellid, flag, guid2);
else
{
//If a pet is dismissed, m_Controlled will change
std::vector<Unit*> controlled;
for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr)
if ((*itr)->GetEntry() == pet->GetEntry() && (*itr)->isAlive())
controlled.push_back(*itr);
for (std::vector<Unit*>::iterator itr = controlled.begin(); itr != controlled.end(); ++itr)
HandlePetActionHelper(*itr, guid1, spellid, flag, guid2);
}
}
void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid, uint16 flag, uint64 guid2)
{
CharmInfo *charmInfo = pet->GetCharmInfo();
if (!charmInfo)
{
sLog.outError("WorldSession::HandlePetAction: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId());
return;
}
switch(flag)
{
case ACT_COMMAND: //0x07
switch(spellid)
{
case COMMAND_STAY: //flat=1792 //STAY
pet->AttackStop();
pet->InterruptNonMeleeSpells(false);
pet->GetMotionMaster()->MoveIdle();
charmInfo->SetCommandState(COMMAND_STAY);
charmInfo->SetIsCommandAttack(false);
charmInfo->SetIsAtStay(true);
charmInfo->SetIsFollowing(false);
charmInfo->SetIsReturning(false);
charmInfo->SaveStayPosition();
break;
case COMMAND_FOLLOW: //spellid=1792 //FOLLOW
pet->AttackStop();
pet->InterruptNonMeleeSpells(false);
pet->GetMotionMaster()->MoveFollow(_player,PET_FOLLOW_DIST,pet->GetFollowAngle());
charmInfo->SetCommandState(COMMAND_FOLLOW);
charmInfo->SetIsCommandAttack(false);
charmInfo->SetIsAtStay(false);
charmInfo->SetIsReturning(true);
charmInfo->SetIsFollowing(false);
break;
case COMMAND_ATTACK: //spellid=1792 //ATTACK
{
// Can't attack if owner is pacified
if (_player->HasAuraType(SPELL_AURA_MOD_PACIFY))
{
//pet->SendPetCastFail(spellid, SPELL_FAILED_PACIFIED);
//TODO: Send proper error message to client
return;
}
// only place where pet can be player
Unit *TargetUnit = ObjectAccessor::GetUnit(*_player, guid2);
if (!TargetUnit)
return;
if (!pet->canAttack(TargetUnit))
return;
// Not let attack through obstructions
if (sWorld.getConfig(CONFIG_PET_LOS))
{
if (!pet->IsWithinLOSInMap(TargetUnit))
return;
}
pet->clearUnitState(UNIT_STAT_FOLLOW);
// This is true if pet has no target or has target but targets differs.
if (pet->getVictim() != TargetUnit || (pet->getVictim() == TargetUnit && !pet->GetCharmInfo()->IsCommandAttack()))
{
if (pet->getVictim())
pet->AttackStop();
if (pet->GetTypeId() != TYPEID_PLAYER && pet->ToCreature()->IsAIEnabled)
{
charmInfo->SetIsCommandAttack(true);
charmInfo->SetIsAtStay(false);
charmInfo->SetIsFollowing(false);
charmInfo->SetIsReturning(false);
pet->ToCreature()->AI()->AttackStart(TargetUnit);
//10% chance to play special pet attack talk, else growl
if (pet->ToCreature()->isPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != TargetUnit && urand(0, 100) < 10)
pet->SendPetTalk((uint32)PET_TALK_ATTACK);
else
{
// 90% chance for pet and 100% chance for charmed creature
pet->SendPetAIReaction(guid1);
}
}
else // charmed player
{
if (pet->getVictim() && pet->getVictim() != TargetUnit)
pet->AttackStop();
charmInfo->SetIsCommandAttack(true);
charmInfo->SetIsAtStay(false);
charmInfo->SetIsFollowing(false);
charmInfo->SetIsReturning(false);
pet->Attack(TargetUnit,true);
pet->SendPetAIReaction(guid1);
}
}
break;
}
case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet)
if (pet->GetCharmerGUID() == GetPlayer()->GetGUID())
_player->StopCastingCharm();
else if (pet->GetOwnerGUID() == GetPlayer()->GetGUID())
{
assert(pet->GetTypeId() == TYPEID_UNIT);
if (pet->isPet())
{
if (((Pet*)pet)->getPetType() == HUNTER_PET)
GetPlayer()->RemovePet((Pet*)pet, PET_SAVE_AS_DELETED);
else
//dismissing a summoned pet is like killing them (this prevents returning a soulshard...)
pet->setDeathState(CORPSE);
}
else if (pet->HasUnitTypeMask(UNIT_MASK_MINION))
{
((Minion*)pet)->UnSummon();
}
}
break;
default:
sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid);
}
break;
case ACT_REACTION: // 0x6
switch(spellid)
{
case REACT_PASSIVE: //passive
pet->AttackStop();
case REACT_DEFENSIVE: //recovery
case REACT_AGGRESSIVE: //activete
if (pet->GetTypeId() == TYPEID_UNIT)
pet->ToCreature()->SetReactState(ReactStates(spellid));
break;
}
break;
case ACT_DISABLED: // 0x81 spell (disabled), ignore
case ACT_PASSIVE: // 0x01
case ACT_ENABLED: // 0xC1 spell
{
Unit* unit_target = NULL;
if (guid2)
unit_target = ObjectAccessor::GetUnit(*_player,guid2);
// do not cast unknown spells
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid);
if (!spellInfo)
{
sLog.outError("WORLD: unknown PET spell id %i", spellid);
return;
}
if (spellInfo->StartRecoveryCategory > 0)
if (pet->ToCreature()->GetGlobalCooldown() > 0)
return;
for (uint32 i = 0; i < 3; ++i)
{
if (spellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_AREA_ENEMY_SRC || spellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_AREA_ENEMY_DST || spellInfo->EffectImplicitTargetA[i] == TARGET_DEST_DYNOBJ_ENEMY)
return;
}
// do not cast not learned spells
if (!pet->HasSpell(spellid) || IsPassiveSpell(spellid))
return;
// Clear the flags as if owner clicked 'attack'. AI will reset them
// after AttackStart, even if spell failed
if (pet->GetCharmInfo())
{
pet->GetCharmInfo()->SetIsAtStay(false);
pet->GetCharmInfo()->SetIsCommandAttack(true);
pet->GetCharmInfo()->SetIsReturning(false);
pet->GetCharmInfo()->SetIsFollowing(false);
}
Spell *spell = new Spell(pet, spellInfo, false);
SpellCastResult result = spell->CheckPetCast(unit_target);
//auto turn to target unless possessed
if (result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->isPossessed() && !pet->IsVehicle())
{
if (unit_target)
{
pet->SetInFront(unit_target);
if (unit_target->GetTypeId() == TYPEID_PLAYER)
pet->SendUpdateToPlayer((Player*)unit_target);
}
else if (Unit *unit_target2 = spell->m_targets.getUnitTarget())
{
pet->SetInFront(unit_target2);
if (unit_target2->GetTypeId() == TYPEID_PLAYER)
pet->SendUpdateToPlayer((Player*)unit_target2);
}
if (Unit* powner = pet->GetCharmerOrOwner())
if (powner->GetTypeId() == TYPEID_PLAYER)
pet->SendUpdateToPlayer(powner->ToPlayer());
result = SPELL_CAST_OK;
}
if (result == SPELL_CAST_OK)
{
pet->ToCreature()->AddCreatureSpellCooldown(spellid);
unit_target = spell->m_targets.getUnitTarget();
//10% chance to play special pet attack talk, else growl
//actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
if (pet->ToCreature()->isPet() && (((Pet*)pet)->getPetType() == SUMMON_PET) && (pet != unit_target) && (urand(0, 100) < 10))
pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL);
else
{
pet->SendPetAIReaction(guid1);
}
if (unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->isPossessed() && !pet->IsVehicle())
{
// This is true if pet has no target or has target but targets differs.
if (pet->getVictim() != unit_target)
{
if (pet->getVictim())
pet->AttackStop();
pet->GetMotionMaster()->Clear();
if (pet->ToCreature()->IsAIEnabled)
pet->ToCreature()->AI()->AttackStart(unit_target);
}
}
spell->prepare(&(spell->m_targets));
}
else
{
if (pet->isPossessed() || pet->IsVehicle())
Spell::SendCastResult(GetPlayer(),spellInfo,0,result);
else
pet->SendPetCastFail(spellid, result);
if (!pet->ToCreature()->HasSpellCooldown(spellid))
GetPlayer()->SendClearCooldown(spellid, pet);
spell->finish(false);
delete spell;
// reset specific flags in case of spell fail. AI will reset other flags
if (pet->GetCharmInfo())
pet->GetCharmInfo()->SetIsCommandAttack(false);
}
break;
}
default:
sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid);
}
}
void WorldSession::HandlePetNameQuery(WorldPacket & recv_data)
{
sLog.outDetail("HandlePetNameQuery. CMSG_PET_NAME_QUERY");
uint32 petnumber;
uint64 petguid;
recv_data >> petnumber;
recv_data >> petguid;
SendPetNameQuery(petguid,petnumber);
}
void WorldSession::SendPetNameQuery(uint64 petguid, uint32 petnumber)
{
Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, petguid);
if (!pet)
{
WorldPacket data(SMSG_PET_NAME_QUERY_RESPONSE, (4+4+7+1));
data << uint32(petnumber);
data << "Unknown";
data << uint32(0);
data << uint8(0);
_player->GetSession()->SendPacket(&data);
return;
}
std::string name = pet->GetName();
WorldPacket data(SMSG_PET_NAME_QUERY_RESPONSE, (4+4+name.size()+1));
data << uint32(petnumber);
data << name.c_str();
data << uint32(pet->GetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP));
if (pet->isPet() && ((Pet*)pet)->GetDeclinedNames())
{
data << uint8(1);
for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
data << ((Pet*)pet)->GetDeclinedNames()->name[i];
}
else
data << uint8(0);
_player->GetSession()->SendPacket(&data);
}
void WorldSession::HandlePetSetAction(WorldPacket & recv_data)
{
sLog.outDetail("HandlePetSetAction. CMSG_PET_SET_ACTION");
uint64 petguid;
uint8 count;
recv_data >> petguid;
Unit* pet = ObjectAccessor::GetUnit(*_player, petguid);
if (!pet || pet != _player->GetFirstControlled())
{
sLog.outError("HandlePetSetAction: Unknown pet or pet owner.");
return;
}
CharmInfo *charmInfo = pet->GetCharmInfo();
if (!charmInfo)
{
sLog.outError("WorldSession::HandlePetSetAction: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId());
return;
}
count = (recv_data.size() == 24) ? 2 : 1;
uint32 position[2];
uint32 data[2];
bool move_command = false;
for (uint8 i = 0; i < count; ++i)
{
recv_data >> position[i];
recv_data >> data[i];
uint8 act_state = UNIT_ACTION_BUTTON_TYPE(data[i]);
//ignore invalid position
if (position[i] >= MAX_UNIT_ACTION_BAR_INDEX)
return;
// in the normal case, command and reaction buttons can only be moved, not removed
// at moving count == 2, at removing count == 1
// ignore attempt to remove command|reaction buttons (not possible at normal case)
if (act_state == ACT_COMMAND || act_state == ACT_REACTION)
{
if (count == 1)
return;
move_command = true;
}
}
// check swap (at command->spell swap client remove spell first in another packet, so check only command move correctness)
if (move_command)
{
uint8 act_state_0 = UNIT_ACTION_BUTTON_TYPE(data[0]);
if (act_state_0 == ACT_COMMAND || act_state_0 == ACT_REACTION)
{
uint32 spell_id_0 = UNIT_ACTION_BUTTON_ACTION(data[0]);
UnitActionBarEntry const* actionEntry_1 = charmInfo->GetActionBarEntry(position[1]);
if (!actionEntry_1 || spell_id_0 != actionEntry_1->GetAction() ||
act_state_0 != actionEntry_1->GetType())
return;
}
uint8 act_state_1 = UNIT_ACTION_BUTTON_TYPE(data[1]);
if (act_state_1 == ACT_COMMAND || act_state_1 == ACT_REACTION)
{
uint32 spell_id_1 = UNIT_ACTION_BUTTON_ACTION(data[1]);
UnitActionBarEntry const* actionEntry_0 = charmInfo->GetActionBarEntry(position[0]);
if (!actionEntry_0 || spell_id_1 != actionEntry_0->GetAction() ||
act_state_1 != actionEntry_0->GetType())
return;
}
}
for (uint8 i = 0; i < count; ++i)
{
uint32 spell_id = UNIT_ACTION_BUTTON_ACTION(data[i]);
uint8 act_state = UNIT_ACTION_BUTTON_TYPE(data[i]);
sLog.outDetail("Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X", _player->GetName(), position[i], spell_id, uint32(act_state));
//if it's act for spell (en/disable/cast) and there is a spell given (0 = remove spell) which pet doesn't know, don't add
if (!((act_state == ACT_ENABLED || act_state == ACT_DISABLED || act_state == ACT_PASSIVE) && spell_id && !pet->HasSpell(spell_id)))
{
//sign for autocast
if (act_state == ACT_ENABLED && spell_id)
{
if (pet->GetTypeId() == TYPEID_UNIT && pet->ToCreature()->isPet())
((Pet*)pet)->ToggleAutocast(spell_id, true);
else
charmInfo->ToggleCreatureAutocast(spell_id, true);
}
//sign for no/turn off autocast
else if (act_state == ACT_DISABLED && spell_id)
{
if (pet->GetTypeId() == TYPEID_UNIT && pet->ToCreature()->isPet())
((Pet*)pet)->ToggleAutocast(spell_id, false);
else
charmInfo->ToggleCreatureAutocast(spell_id, false);
}
charmInfo->SetActionBar(position[i],spell_id,ActiveStates(act_state));
}
}
}
void WorldSession::HandlePetRename(WorldPacket & recv_data)
{
sLog.outDetail("HandlePetRename. CMSG_PET_RENAME");
uint64 petguid;
uint8 isdeclined;
std::string name;
DeclinedName declinedname;
recv_data >> petguid;
recv_data >> name;
recv_data >> isdeclined;
Pet* pet = ObjectAccessor::GetPet(petguid);
// check it!
if (!pet || !pet->isPet() || ((Pet*)pet)->getPetType()!= HUNTER_PET ||
!pet->HasByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED) ||
pet->GetOwnerGUID() != _player->GetGUID() || !pet->GetCharmInfo())
return;
PetNameInvalidReason res = ObjectMgr::CheckPetName(name);
if (res != PET_NAME_SUCCESS)
{
SendPetNameInvalid(res, name, NULL);
return;
}
if (objmgr.IsReservedName(name))
{
SendPetNameInvalid(PET_NAME_RESERVED, name, NULL);
return;
}
pet->SetName(name);
Unit *owner = pet->GetOwner();
if (owner && (owner->GetTypeId() == TYPEID_PLAYER) && owner->ToPlayer()->GetGroup())
owner->ToPlayer()->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_NAME);
pet->RemoveByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED);
if (isdeclined)
{
for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
{
recv_data >> declinedname.name[i];
}
std::wstring wname;
Utf8toWStr(name, wname);
if (!ObjectMgr::CheckDeclinedNames(GetMainPartOfName(wname,0),declinedname))
{
SendPetNameInvalid(PET_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME, name, &declinedname);
return;
}
}
CharacterDatabase.BeginTransaction();
if (isdeclined)
{
for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
CharacterDatabase.escape_string(declinedname.name[i]);
CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE owner = '%u' AND id = '%u'", _player->GetGUIDLow(), pet->GetCharmInfo()->GetPetNumber());
CharacterDatabase.PExecute("INSERT INTO character_pet_declinedname (id, owner, genitive, dative, accusative, instrumental, prepositional) VALUES ('%u','%u','%s','%s','%s','%s','%s')",
pet->GetCharmInfo()->GetPetNumber(), _player->GetGUIDLow(), declinedname.name[0].c_str(), declinedname.name[1].c_str(), declinedname.name[2].c_str(), declinedname.name[3].c_str(), declinedname.name[4].c_str());
}
CharacterDatabase.escape_string(name);
CharacterDatabase.PExecute("UPDATE character_pet SET name = '%s', renamed = '1' WHERE owner = '%u' AND id = '%u'", name.c_str(), _player->GetGUIDLow(), pet->GetCharmInfo()->GetPetNumber());
CharacterDatabase.CommitTransaction();
pet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL));
}
void WorldSession::HandlePetAbandon(WorldPacket & recv_data)
{
uint64 guid;
recv_data >> guid; //pet guid
sLog.outDetail("HandlePetAbandon. CMSG_PET_ABANDON pet guid is %u", GUID_LOPART(guid));
if (!_player->IsInWorld())
return;
// pet/charmed
Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
if (pet)
{
if (pet->isPet())
{
if (pet->GetGUID() == _player->GetPetGUID())
{
uint32 feelty = pet->GetPower(POWER_HAPPINESS);
pet->SetPower(POWER_HAPPINESS ,(feelty-50000) > 0 ?(feelty-50000) : 0);
}
_player->RemovePet((Pet*)pet,PET_SAVE_AS_DELETED);
}
else if (pet->GetGUID() == _player->GetCharmGUID())
_player->StopCastingCharm();
}
}
void WorldSession::HandlePetSpellAutocastOpcode(WorldPacket& recvPacket)
{
sLog.outDetail("CMSG_PET_SPELL_AUTOCAST");
uint64 guid;
uint32 spellid;
uint8 state; //1 for on, 0 for off
recvPacket >> guid >> spellid >> state;
if (!_player->GetGuardianPet() && !_player->GetCharm())
return;
if (ObjectAccessor::FindPlayer(guid))
return;
Creature* pet=ObjectAccessor::GetCreatureOrPetOrVehicle(*_player,guid);
if (!pet || (pet != _player->GetGuardianPet() && pet != _player->GetCharm()))
{
sLog.outError("HandlePetSpellAutocastOpcode.Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid)),GetPlayer()->GetName());
return;
}
// do not add not learned spells/ passive spells
if (!pet->HasSpell(spellid) || IsAutocastableSpell(spellid))
return;
CharmInfo *charmInfo = pet->GetCharmInfo();
if (!charmInfo)
{
sLog.outError("WorldSession::HandlePetSpellAutocastOpcod: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId());
return;
}
if (pet->isPet())
((Pet*)pet)->ToggleAutocast(spellid, state);
else
pet->GetCharmInfo()->ToggleCreatureAutocast(spellid, state);
charmInfo->SetSpellAutocast(spellid,state);
}
void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket)
{
sLog.outDetail("WORLD: CMSG_PET_CAST_SPELL");
uint64 guid;
uint32 spellid;
uint8 cast_count;
uint8 unk_flags; // flags (if 0x02 - some additional data are received)
recvPacket >> guid >> cast_count >> spellid >> unk_flags;
sLog.outDebug("WORLD: CMSG_PET_CAST_SPELL, cast_count: %u, spellid %u, unk_flags %u", cast_count, spellid, unk_flags);
// This opcode is also sent from charmed and possessed units (players and creatures)
if (!_player->GetGuardianPet() && !_player->GetCharm())
return;
Unit* caster = ObjectAccessor::GetUnit(*_player, guid);
if (!caster || (caster != _player->GetGuardianPet() && caster != _player->GetCharm()))
{
sLog.outError("HandlePetCastSpellOpcode: Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid)),GetPlayer()->GetName());
return;
}
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid);
if (!spellInfo)
{
sLog.outError("WORLD: unknown PET spell id %i", spellid);
return;
}
if (spellInfo->StartRecoveryCategory > 0) //Check if spell is affected by GCD
if (caster->GetTypeId() == TYPEID_UNIT && caster->ToCreature()->GetGlobalCooldown() > 0)
{
caster->SendPetCastFail(spellid, SPELL_FAILED_NOT_READY);
return;
}
// do not cast not learned spells
if (!caster->HasSpell(spellid) || IsPassiveSpell(spellid))
return;
SpellCastTargets targets;
if (!targets.read(&recvPacket,caster))
return;
caster->clearUnitState(UNIT_STAT_FOLLOW);
Spell *spell = new Spell(caster, spellInfo, spellid == 33395); // water elemental can cast freeze as triggered
spell->m_cast_count = spellid == 33395 ? 0 : cast_count; // probably pending spell cast
spell->m_targets = targets;
// TODO: need to check victim?
SpellCastResult result;
if (caster->m_movedPlayer)
result = spell->CheckPetCast(caster->m_movedPlayer->GetSelectedUnit());
else
result = spell->CheckPetCast(NULL);
if (result == SPELL_CAST_OK)
{
if (caster->GetTypeId() == TYPEID_UNIT)
{
Creature* pet = caster->ToCreature();
pet->AddCreatureSpellCooldown(spellid);
if (pet->isPet())
{
Pet* p = (Pet*)pet;
// 10% chance to play special pet attack talk, else growl
// actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell
if (p->getPetType() == SUMMON_PET && (urand(0, 100) < 10))
pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL);
else
pet->SendPetAIReaction(guid);
}
}
spell->prepare(&(spell->m_targets));
}
else
{
caster->SendPetCastFail(spellid, result);
if (caster->GetTypeId() == TYPEID_PLAYER)
{
if (!caster->ToPlayer()->HasSpellCooldown(spellid))
GetPlayer()->SendClearCooldown(spellid, caster);
}
else
{
if (!caster->ToCreature()->HasSpellCooldown(spellid))
GetPlayer()->SendClearCooldown(spellid, caster);
}
spell->finish(false);
delete spell;
}
}
void WorldSession::SendPetNameInvalid(uint32 error, const std::string& name, DeclinedName *declinedName)
{
WorldPacket data(SMSG_PET_NAME_INVALID, 4 + name.size() + 1 + 1);
data << uint32(error);
data << name;
if (declinedName)
{
data << uint8(1);
for (uint32 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
data << declinedName->name[i];
}
else
data << uint8(0);
SendPacket(&data);
}
void WorldSession::HandlePetLearnTalent(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: CMSG_PET_LEARN_TALENT");
uint64 guid;
uint32 talent_id, requested_rank;
recv_data >> guid >> talent_id >> requested_rank;
_player->LearnPetTalent(guid, talent_id, requested_rank);
_player->SendTalentsInfoData(true);
}
void WorldSession::HandleLearnPreviewTalentsPet(WorldPacket & recv_data)
{
sLog.outDebug("CMSG_LEARN_PREVIEW_TALENTS_PET");
uint64 guid;
recv_data >> guid;
uint32 talentsCount;
recv_data >> talentsCount;
uint32 talentId, talentRank;
for (uint32 i = 0; i < talentsCount; ++i)
{
recv_data >> talentId >> talentRank;
_player->LearnPetTalent(guid, talentId, talentRank);
}
_player->SendTalentsInfoData(true);
}
File diff suppressed because it is too large Load Diff
@@ -1,84 +0,0 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 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 "Common.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Log.h"
#include "Opcodes.h"
#include "UpdateData.h"
#include "Player.h"
void WorldSession::HandleDuelAcceptedOpcode(WorldPacket& recvPacket)
{
uint64 guid;
Player *pl;
Player *plTarget;
if (!GetPlayer()->duel) // ignore accept from duel-sender
return;
recvPacket >> guid;
pl = GetPlayer();
plTarget = pl->duel->opponent;
if (pl == pl->duel->initiator || !plTarget || pl == plTarget || pl->duel->startTime != 0 || plTarget->duel->startTime != 0)
return;
//sLog.outDebug("WORLD: received CMSG_DUEL_ACCEPTED");
DEBUG_LOG("Player 1 is: %u (%s)", pl->GetGUIDLow(),pl->GetName());
DEBUG_LOG("Player 2 is: %u (%s)", plTarget->GetGUIDLow(),plTarget->GetName());
time_t now = time(NULL);
pl->duel->startTimer = now;
plTarget->duel->startTimer = now;
pl->SendDuelCountdown(3000);
plTarget->SendDuelCountdown(3000);
}
void WorldSession::HandleDuelCancelledOpcode(WorldPacket& recvPacket)
{
//sLog.outDebug("WORLD: received CMSG_DUEL_CANCELLED");
// no duel requested
if (!GetPlayer()->duel)
return;
// player surrendered in a duel using /forfeit
if (GetPlayer()->duel->startTime != 0)
{
GetPlayer()->CombatStopWithPets(true);
if (GetPlayer()->duel->opponent)
GetPlayer()->duel->opponent->CombatStopWithPets(true);
GetPlayer()->CastSpell(GetPlayer(), 7267, true); // beg
GetPlayer()->DuelComplete(DUEL_WON);
return;
}
// player either discarded the duel using the "discard button"
// or used "/forfeit" before countdown reached 0
uint64 guid;
recvPacket >> guid;
GetPlayer()->DuelComplete(DUEL_INTERUPTED);
}
File diff suppressed because it is too large Load Diff
@@ -1,938 +0,0 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 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 "Common.h"
#include "Language.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "World.h"
#include "ObjectMgr.h"
#include "Log.h"
#include "Opcodes.h"
#include "Guild.h"
#include "ArenaTeam.h"
#include "GossipDef.h"
#include "SocialMgr.h"
/*enum PetitionType // dbc data
{
PETITION_TYPE_GUILD = 1,
PETITION_TYPE_ARENA_TEAM = 3
};*/
// Charters ID in item_template
#define GUILD_CHARTER 5863
#define GUILD_CHARTER_COST 1000 // 10 S
#define ARENA_TEAM_CHARTER_2v2 23560
#define ARENA_TEAM_CHARTER_2v2_COST 800000 // 80 G
#define ARENA_TEAM_CHARTER_3v3 23561
#define ARENA_TEAM_CHARTER_3v3_COST 1200000 // 120 G
#define ARENA_TEAM_CHARTER_5v5 23562
#define ARENA_TEAM_CHARTER_5v5_COST 2000000 // 200 G
void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data)
{
sLog.outDebug("Received opcode CMSG_PETITION_BUY");
recv_data.hexlike();
uint64 guidNPC;
uint32 clientIndex; // 1 for guild and arenaslot+1 for arenas in client
std::string name;
recv_data >> guidNPC; // NPC GUID
recv_data.read_skip<uint32>(); // 0
recv_data.read_skip<uint64>(); // 0
recv_data >> name; // name
recv_data.read_skip<std::string>(); // some string
recv_data.read_skip<uint32>(); // 0
recv_data.read_skip<uint32>(); // 0
recv_data.read_skip<uint32>(); // 0
recv_data.read_skip<uint32>(); // 0
recv_data.read_skip<uint32>(); // 0
recv_data.read_skip<uint32>(); // 0
recv_data.read_skip<uint32>(); // 0
recv_data.read_skip<uint16>(); // 0
recv_data.read_skip<uint32>(); // 0
recv_data.read_skip<uint32>(); // 0
recv_data.read_skip<uint32>(); // 0
for (int i = 0; i < 10; ++i)
recv_data.read_skip<std::string>();
recv_data >> clientIndex; // index
recv_data.read_skip<uint32>(); // 0
sLog.outDebug("Petitioner with GUID %u tried sell petition: name %s", GUID_LOPART(guidNPC), name.c_str());
// prevent cheating
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(guidNPC,UNIT_NPC_FLAG_PETITIONER);
if (!pCreature)
{
sLog.outDebug("WORLD: HandlePetitionBuyOpcode - Unit (GUID: %u) not found or you can't interact with him.", GUID_LOPART(guidNPC));
return;
}
// remove fake death
if (GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
uint32 charterid = 0;
uint32 cost = 0;
uint32 type = 0;
if (pCreature->isTabardDesigner())
{
// if tabard designer, then trying to buy a guild charter.
// do not let if already in guild.
if (_player->GetGuildId())
return;
charterid = GUILD_CHARTER;
cost = GUILD_CHARTER_COST;
type = 9;
}
else
{
// TODO: find correct opcode
if (_player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
{
SendNotification(LANG_ARENA_ONE_TOOLOW, sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL));
return;
}
switch(clientIndex) // arenaSlot+1 as received from client (1 from 3 case)
{
case 1:
charterid = ARENA_TEAM_CHARTER_2v2;
cost = ARENA_TEAM_CHARTER_2v2_COST;
type = 2; // 2v2
break;
case 2:
charterid = ARENA_TEAM_CHARTER_3v3;
cost = ARENA_TEAM_CHARTER_3v3_COST;
type = 3; // 3v3
break;
case 3:
charterid = ARENA_TEAM_CHARTER_5v5;
cost = ARENA_TEAM_CHARTER_5v5_COST;
type = 5; // 5v5
break;
default:
sLog.outDebug("unknown selection at buy arena petition: %u", clientIndex);
return;
}
if (_player->GetArenaTeamId(clientIndex - 1)) // arenaSlot+1 as received from client
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ALREADY_IN_ARENA_TEAM);
return;
}
}
if (type == 9)
{
if (objmgr.GetGuildByName(name))
{
SendGuildCommandResult(GUILD_CREATE_S, name, ERR_GUILD_NAME_EXISTS_S);
return;
}
if (objmgr.IsReservedName(name) || !ObjectMgr::IsValidCharterName(name))
{
SendGuildCommandResult(GUILD_CREATE_S, name, ERR_GUILD_NAME_INVALID);
return;
}
}
else
{
if (objmgr.GetArenaTeamByName(name))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_EXISTS_S);
return;
}
if (objmgr.IsReservedName(name) || !ObjectMgr::IsValidCharterName(name))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_INVALID);
return;
}
}
ItemPrototype const *pProto = objmgr.GetItemPrototype(charterid);
if (!pProto)
{
_player->SendBuyError(BUY_ERR_CANT_FIND_ITEM, NULL, charterid, 0);
return;
}
if (_player->GetMoney() < cost)
{ //player hasn't got enough money
_player->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, charterid, 0);
return;
}
ItemPosCountVec dest;
uint8 msg = _player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, charterid, pProto->BuyCount);
if (msg != EQUIP_ERR_OK)
{
_player->SendBuyError(msg, pCreature, charterid, 0);
return;
}
_player->ModifyMoney(-(int32)cost);
Item *charter = _player->StoreNewItem(dest, charterid, true);
if (!charter)
return;
charter->SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1, charter->GetGUIDLow());
// ITEM_FIELD_ENCHANTMENT_1_1 is guild/arenateam id
// ITEM_FIELD_ENCHANTMENT_1_1+1 is current signatures count (showed on item)
charter->SetState(ITEM_CHANGED, _player);
_player->SendNewItem(charter, 1, true, false);
// a petition is invalid, if both the owner and the type matches
// we checked above, if this player is in an arenateam, so this must be
// datacorruption
QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT petitionguid FROM petition WHERE ownerguid = '%u' AND type = '%u'", _player->GetGUIDLow(), type);
std::ostringstream ssInvalidPetitionGUIDs;
if (result)
{
do
{
Field *fields = result->Fetch();
ssInvalidPetitionGUIDs << "'" << fields[0].GetUInt32() << "' , ";
} while (result->NextRow());
}
// delete petitions with the same guid as this one
ssInvalidPetitionGUIDs << "'" << charter->GetGUIDLow() << "'";
sLog.outDebug("Invalid petition GUIDs: %s", ssInvalidPetitionGUIDs.str().c_str());
CharacterDatabase.escape_string(name);
CharacterDatabase.BeginTransaction();
CharacterDatabase.PExecute("DELETE FROM petition WHERE petitionguid IN (%s)", ssInvalidPetitionGUIDs.str().c_str());
CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE petitionguid IN (%s)", ssInvalidPetitionGUIDs.str().c_str());
CharacterDatabase.PExecute("INSERT INTO petition (ownerguid, petitionguid, name, type) VALUES ('%u', '%u', '%s', '%u')",
_player->GetGUIDLow(), charter->GetGUIDLow(), name.c_str(), type);
CharacterDatabase.CommitTransaction();
}
void WorldSession::HandlePetitionShowSignOpcode(WorldPacket & recv_data)
{
// ok
sLog.outDebug("Received opcode CMSG_PETITION_SHOW_SIGNATURES");
//recv_data.hexlike();
uint8 signs = 0;
uint64 petitionguid;
recv_data >> petitionguid; // petition guid
// solve (possible) some strange compile problems with explicit use GUID_LOPART(petitionguid) at some GCC versions (wrong code optimization in compiler?)
uint32 petitionguid_low = GUID_LOPART(petitionguid);
QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", petitionguid_low);
if (!result)
{
sLog.outError("Petition %u is not found for player %u %s", GUID_LOPART(petitionguid), GetPlayer()->GetGUIDLow(), GetPlayer()->GetName());
return;
}
Field *fields = result->Fetch();
uint32 type = fields[0].GetUInt32();
// if guild petition and has guild => error, return;
if (type == 9 && _player->GetGuildId())
return;
result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", petitionguid_low);
// result == NULL also correct in case no sign yet
if (result)
signs = result->GetRowCount();
sLog.outDebug("CMSG_PETITION_SHOW_SIGNATURES petition entry: '%u'", petitionguid_low);
WorldPacket data(SMSG_PETITION_SHOW_SIGNATURES, (8+8+4+1+signs*12));
data << uint64(petitionguid); // petition guid
data << uint64(_player->GetGUID()); // owner guid
data << uint32(petitionguid_low); // guild guid (in mangos always same as GUID_LOPART(petitionguid)
data << uint8(signs); // sign's count
for (uint8 i = 1; i <= signs; ++i)
{
Field *fields2 = result->Fetch();
uint64 plguid = fields2[0].GetUInt64();
data << uint64(plguid); // Player GUID
data << (uint32)0; // there 0 ...
result->NextRow();
}
SendPacket(&data);
}
void WorldSession::HandlePetitionQueryOpcode(WorldPacket & recv_data)
{
sLog.outDebug("Received opcode CMSG_PETITION_QUERY"); // ok
//recv_data.hexlike();
uint32 guildguid;
uint64 petitionguid;
recv_data >> guildguid; // in Trinity always same as GUID_LOPART(petitionguid)
recv_data >> petitionguid; // petition guid
sLog.outDebug("CMSG_PETITION_QUERY Petition GUID %u Guild GUID %u", GUID_LOPART(petitionguid), guildguid);
SendPetitionQueryOpcode(petitionguid);
}
void WorldSession::SendPetitionQueryOpcode(uint64 petitionguid)
{
uint64 ownerguid = 0;
uint32 type;
std::string name = "NO_NAME_FOR_GUID";
uint8 signs = 0;
QueryResult_AutoPtr result = CharacterDatabase.PQuery(
"SELECT ownerguid, name, "
" (SELECT COUNT(playerguid) FROM petition_sign WHERE petition_sign.petitionguid = '%u') AS signs, "
" type "
"FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid), GUID_LOPART(petitionguid));
if (result)
{
Field* fields = result->Fetch();
ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
name = fields[1].GetCppString();
signs = fields[2].GetUInt8();
type = fields[3].GetUInt32();
}
else
{
sLog.outDebug("CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid));
return;
}
WorldPacket data(SMSG_PETITION_QUERY_RESPONSE, (4+8+name.size()+1+1+4*12+2+10));
data << uint32(GUID_LOPART(petitionguid)); // guild/team guid (in Trinity always same as GUID_LOPART(petition guid)
data << uint64(ownerguid); // charter owner guid
data << name; // name (guild/arena team)
data << uint8(0); // some string
if (type == 9)
{
data << uint32(9);
data << uint32(9);
data << uint32(0); // bypass client - side limitation, a different value is needed here for each petition
}
else
{
data << uint32(type-1);
data << uint32(type-1);
data << uint32(type); // bypass client - side limitation, a different value is needed here for each petition
}
data << uint32(0); // 5
data << uint32(0); // 6
data << uint32(0); // 7
data << uint32(0); // 8
data << uint16(0); // 9 2 bytes field
data << uint32(0); // 10
data << uint32(0); // 11
data << uint32(0); // 13 count of next strings?
for (int i = 0; i < 10; ++i)
data << uint8(0); // some string
data << uint32(0); // 14
if (type == 9)
data << uint32(0); // 15 0 - guild, 1 - arena team
else
data << uint32(1);
SendPacket(&data);
}
void WorldSession::HandlePetitionRenameOpcode(WorldPacket & recv_data)
{
sLog.outDebug("Received opcode MSG_PETITION_RENAME"); // ok
//recv_data.hexlike();
uint64 petitionguid;
uint32 type;
std::string newname;
recv_data >> petitionguid; // guid
recv_data >> newname; // new name
Item *item = _player->GetItemByGuid(petitionguid);
if (!item)
return;
QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
if (result)
{
Field* fields = result->Fetch();
type = fields[0].GetUInt32();
}
else
{
sLog.outDebug("CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid));
return;
}
if (type == 9)
{
if (objmgr.GetGuildByName(newname))
{
SendGuildCommandResult(GUILD_CREATE_S, newname, ERR_GUILD_NAME_EXISTS_S);
return;
}
if (objmgr.IsReservedName(newname) || !ObjectMgr::IsValidCharterName(newname))
{
SendGuildCommandResult(GUILD_CREATE_S, newname, ERR_GUILD_NAME_INVALID);
return;
}
}
else
{
if (objmgr.GetArenaTeamByName(newname))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, newname, "", ERR_ARENA_TEAM_NAME_EXISTS_S);
return;
}
if (objmgr.IsReservedName(newname) || !ObjectMgr::IsValidCharterName(newname))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, newname, "", ERR_ARENA_TEAM_NAME_INVALID);
return;
}
}
std::string db_newname = newname;
CharacterDatabase.escape_string(db_newname);
CharacterDatabase.PExecute("UPDATE petition SET name = '%s' WHERE petitionguid = '%u'",
db_newname.c_str(), GUID_LOPART(petitionguid));
sLog.outDebug("Petition (GUID: %u) renamed to '%s'", GUID_LOPART(petitionguid), newname.c_str());
WorldPacket data(MSG_PETITION_RENAME, (8+newname.size()+1));
data << uint64(petitionguid);
data << newname;
SendPacket(&data);
}
void WorldSession::HandlePetitionSignOpcode(WorldPacket & recv_data)
{
sLog.outDebug("Received opcode CMSG_PETITION_SIGN"); // ok
//recv_data.hexlike();
Field *fields;
uint64 petitionguid;
uint8 unk;
recv_data >> petitionguid; // petition guid
recv_data >> unk;
QueryResult_AutoPtr result = CharacterDatabase.PQuery(
"SELECT ownerguid, "
" (SELECT COUNT(playerguid) FROM petition_sign WHERE petition_sign.petitionguid = '%u') AS signs, "
" type "
"FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid), GUID_LOPART(petitionguid));
if (!result)
{
sLog.outError("Petition %u is not found for player %u %s", GUID_LOPART(petitionguid), GetPlayer()->GetGUIDLow(), GetPlayer()->GetName());
return;
}
fields = result->Fetch();
uint64 ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
uint8 signs = fields[1].GetUInt8();
uint32 type = fields[2].GetUInt32();
uint32 plguidlo = _player->GetGUIDLow();
if (GUID_LOPART(ownerguid) == plguidlo)
return;
// not let enemies sign guild charter
if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && GetPlayer()->GetTeam() != objmgr.GetPlayerTeamByGUID(ownerguid))
{
if (type != 9)
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
else
SendGuildCommandResult(GUILD_CREATE_S, "", ERR_GUILD_NOT_ALLIED);
return;
}
if (type != 9)
{
if (_player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", _player->GetName(), ERR_ARENA_TEAM_TARGET_TOO_LOW_S);
return;
}
uint8 slot = ArenaTeam::GetSlotByType(type);
if (slot >= MAX_ARENA_SLOT)
return;
if (_player->GetArenaTeamId(slot))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName(), ERR_ALREADY_IN_ARENA_TEAM_S);
return;
}
if (_player->GetArenaTeamIdInvited())
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName(), ERR_ALREADY_INVITED_TO_ARENA_TEAM_S);
return;
}
}
else
{
if (_player->GetGuildId())
{
SendGuildCommandResult(GUILD_INVITE_S, _player->GetName(), ERR_ALREADY_IN_GUILD_S);
return;
}
if (_player->GetGuildIdInvited())
{
SendGuildCommandResult(GUILD_INVITE_S, _player->GetName(), ERR_ALREADY_INVITED_TO_GUILD_S);
return;
}
}
if (++signs > type) // client signs maximum
return;
//client doesn't allow to sign petition two times by one character, but not check sign by another character from same account
//not allow sign another player from already sign player account
result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE player_account = '%u' AND petitionguid = '%u'", GetAccountId(), GUID_LOPART(petitionguid));
if (result)
{
WorldPacket data(SMSG_PETITION_SIGN_RESULTS, (8+8+4));
data << uint64(petitionguid);
data << uint64(_player->GetGUID());
data << (uint32)PETITION_SIGN_ALREADY_SIGNED;
// close at signer side
SendPacket(&data);
// update for owner if online
if (Player *owner = objmgr.GetPlayer(ownerguid))
owner->GetSession()->SendPacket(&data);
return;
}
CharacterDatabase.PExecute("INSERT INTO petition_sign (ownerguid,petitionguid, playerguid, player_account) VALUES ('%u', '%u', '%u','%u')", GUID_LOPART(ownerguid),GUID_LOPART(petitionguid), plguidlo,GetAccountId());
sLog.outDebug("PETITION SIGN: GUID %u by player: %s (GUID: %u Account: %u)", GUID_LOPART(petitionguid), _player->GetName(),plguidlo,GetAccountId());
WorldPacket data(SMSG_PETITION_SIGN_RESULTS, (8+8+4));
data << uint64(petitionguid);
data << uint64(_player->GetGUID());
data << uint32(PETITION_SIGN_OK);
// close at signer side
SendPacket(&data);
// update signs count on charter, required testing...
//Item *item = _player->GetItemByGuid(petitionguid));
//if (item)
// item->SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1+1, signs);
// update for owner if online
if (Player *owner = objmgr.GetPlayer(ownerguid))
owner->GetSession()->SendPacket(&data);
}
void WorldSession::HandlePetitionDeclineOpcode(WorldPacket & recv_data)
{
sLog.outDebug("Received opcode MSG_PETITION_DECLINE"); // ok
//recv_data.hexlike();
uint64 petitionguid;
uint64 ownerguid;
recv_data >> petitionguid; // petition guid
sLog.outDebug("Petition %u declined by %u", GUID_LOPART(petitionguid), _player->GetGUIDLow());
QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT ownerguid FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
if (!result)
return;
Field *fields = result->Fetch();
ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
Player *owner = objmgr.GetPlayer(ownerguid);
if (owner) // petition owner online
{
WorldPacket data(MSG_PETITION_DECLINE, 8);
data << uint64(_player->GetGUID());
owner->GetSession()->SendPacket(&data);
}
}
void WorldSession::HandleOfferPetitionOpcode(WorldPacket & recv_data)
{
sLog.outDebug("Received opcode CMSG_OFFER_PETITION"); // ok
//recv_data.hexlike();
uint8 signs = 0;
uint64 petitionguid, plguid;
uint32 type, junk;
Player *player;
recv_data >> junk; // this is not petition type!
recv_data >> petitionguid; // petition guid
recv_data >> plguid; // player guid
player = ObjectAccessor::FindPlayer(plguid);
if (!player)
return;
QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
if (!result)
return;
Field *fields = result->Fetch();
type = fields[0].GetUInt32();
sLog.outDebug("OFFER PETITION: type %u, GUID1 %u, to player id: %u", type, GUID_LOPART(petitionguid), GUID_LOPART(plguid));
if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && GetPlayer()->GetTeam() != player->GetTeam())
{
if (type != 9)
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED);
else
SendGuildCommandResult(GUILD_CREATE_S, "", ERR_GUILD_NOT_ALLIED);
return;
}
if (type != 9)
{
if (player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
{
// player is too low level to join an arena team
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, player->GetName(), "", ERR_ARENA_TEAM_TARGET_TOO_LOW_S);
return;
}
uint8 slot = ArenaTeam::GetSlotByType(type);
if (slot >= MAX_ARENA_SLOT)
return;
if (player->GetArenaTeamId(slot))
{
// player is already in an arena team
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, player->GetName(), "", ERR_ALREADY_IN_ARENA_TEAM_S);
return;
}
if (player->GetArenaTeamIdInvited())
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName(), ERR_ALREADY_INVITED_TO_ARENA_TEAM_S);
return;
}
}
else
{
if (player->GetGuildId())
{
SendGuildCommandResult(GUILD_INVITE_S, _player->GetName(), ERR_ALREADY_IN_GUILD_S);
return;
}
if (player->GetGuildIdInvited())
{
SendGuildCommandResult(GUILD_INVITE_S, _player->GetName(), ERR_ALREADY_INVITED_TO_GUILD_S);
return;
}
}
result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
// result == NULL also correct charter without signs
if (result)
signs = result->GetRowCount();
WorldPacket data(SMSG_PETITION_SHOW_SIGNATURES, (8+8+4+signs+signs*12));
data << uint64(petitionguid); // petition guid
data << uint64(_player->GetGUID()); // owner guid
data << uint32(GUID_LOPART(petitionguid)); // guild guid (in mangos always same as GUID_LOPART(petition guid)
data << uint8(signs); // sign's count
for (uint8 i = 1; i <= signs; ++i)
{
Field *fields2 = result->Fetch();
plguid = fields2[0].GetUInt64();
data << uint64(plguid); // Player GUID
data << (uint32)0; // there 0 ...
result->NextRow();
}
player->GetSession()->SendPacket(&data);
}
void WorldSession::HandleTurnInPetitionOpcode(WorldPacket & recv_data)
{
sLog.outDebug("Received opcode CMSG_TURN_IN_PETITION"); // ok
//recv_data.hexlike();
WorldPacket data;
uint64 petitionguid;
uint32 ownerguidlo;
uint32 type;
std::string name;
recv_data >> petitionguid;
sLog.outDebug("Petition %u turned in by %u", GUID_LOPART(petitionguid), _player->GetGUIDLow());
// data
QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT ownerguid, name, type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
if (result)
{
Field *fields = result->Fetch();
ownerguidlo = fields[0].GetUInt32();
name = fields[1].GetCppString();
type = fields[2].GetUInt32();
}
else
{
sLog.outError("petition table has broken data!");
return;
}
if (type == 9)
{
if (_player->GetGuildId())
{
data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4);
data << (uint32)PETITION_TURN_ALREADY_IN_GUILD; // already in guild
_player->GetSession()->SendPacket(&data);
return;
}
}
else
{
uint8 slot = ArenaTeam::GetSlotByType(type);
if (slot >= MAX_ARENA_SLOT)
return;
if (_player->GetArenaTeamId(slot))
{
//data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4);
//data << (uint32)PETITION_TURN_ALREADY_IN_GUILD; // already in guild
//_player->GetSession()->SendPacket(&data);
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ALREADY_IN_ARENA_TEAM);
return;
}
}
if (_player->GetGUIDLow() != ownerguidlo)
return;
// signs
uint8 signs;
result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
if (result)
signs = result->GetRowCount();
else
signs = 0;
uint32 count;
//if (signs < sWorld.getConfig(CONFIG_MIN_PETITION_SIGNS))
if (type == 9)
count = sWorld.getConfig(CONFIG_MIN_PETITION_SIGNS);
else
count = type-1;
if (signs < count)
{
data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4);
data << (uint32)PETITION_TURN_NEED_MORE_SIGNATURES; // need more signatures...
SendPacket(&data);
return;
}
if (type == 9)
{
if (objmgr.GetGuildByName(name))
{
SendGuildCommandResult(GUILD_CREATE_S, name, ERR_GUILD_NAME_EXISTS_S);
return;
}
}
else
{
if (objmgr.GetArenaTeamByName(name))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_EXISTS_S);
return;
}
}
// and at last charter item check
Item *item = _player->GetItemByGuid(petitionguid);
if (!item)
return;
// OK!
// delete charter item
_player->DestroyItem(item->GetBagSlot(),item->GetSlot(), true);
if (type == 9) // create guild
{
Guild* guild = new Guild;
if (!guild->Create(_player, name))
{
delete guild;
return;
}
// register guild and add guildmaster
objmgr.AddGuild(guild);
// add members
for (uint8 i = 0; i < signs; ++i)
{
Field* fields = result->Fetch();
guild->AddMember(fields[0].GetUInt64(), guild->GetLowestRank());
result->NextRow();
}
}
else // or arena team
{
ArenaTeam* at = new ArenaTeam;
if (!at->Create(_player->GetGUID(), type, name))
{
sLog.outError("PetitionsHandler: arena team create failed.");
delete at;
return;
}
uint32 icon, iconcolor, border, bordercolor, backgroud;
recv_data >> backgroud >> icon >> iconcolor >> border >> bordercolor;
at->SetEmblem(backgroud, icon, iconcolor, border, bordercolor);
// register team and add captain
objmgr.AddArenaTeam(at);
sLog.outDebug("PetitonsHandler: arena team added to objmrg");
// add members
for (uint8 i = 0; i < signs; ++i)
{
Field* fields = result->Fetch();
uint64 memberGUID = fields[0].GetUInt64();
sLog.outDebug("PetitionsHandler: adding arena member %u", GUID_LOPART(memberGUID));
at->AddMember(memberGUID);
result->NextRow();
}
}
CharacterDatabase.BeginTransaction();
CharacterDatabase.PExecute("DELETE FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
CharacterDatabase.CommitTransaction();
// created
sLog.outDebug("TURN IN PETITION GUID %u", GUID_LOPART(petitionguid));
data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4);
data << (uint32)PETITION_TURN_OK;
SendPacket(&data);
}
void WorldSession::HandlePetitionShowListOpcode(WorldPacket & recv_data)
{
sLog.outDebug("Received CMSG_PETITION_SHOWLIST"); // ok
//recv_data.hexlike();
uint64 guid;
recv_data >> guid;
SendPetitionShowList(guid);
}
void WorldSession::SendPetitionShowList(uint64 guid)
{
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_PETITIONER);
if (!pCreature)
{
sLog.outDebug("WORLD: HandlePetitionShowListOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
return;
}
// remove fake death
if (GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
uint8 count = 0;
if (pCreature->isTabardDesigner())
count = 1;
else
count = 3;
WorldPacket data(SMSG_PETITION_SHOWLIST, 8+1+4*6);
data << guid; // npc guid
data << count; // count
if (count == 1)
{
data << uint32(1); // index
data << uint32(GUILD_CHARTER); // charter entry
data << uint32(16161); // charter display id
data << uint32(GUILD_CHARTER_COST); // charter cost
data << uint32(0); // unknown
data << uint32(9); // required signs?
}
else
{
// 2v2
data << uint32(1); // index
data << uint32(ARENA_TEAM_CHARTER_2v2); // charter entry
data << uint32(16161); // charter display id
data << uint32(ARENA_TEAM_CHARTER_2v2_COST); // charter cost
data << uint32(2); // unknown
data << uint32(2); // required signs?
// 3v3
data << uint32(2); // index
data << uint32(ARENA_TEAM_CHARTER_3v3); // charter entry
data << uint32(16161); // charter display id
data << uint32(ARENA_TEAM_CHARTER_3v3_COST); // charter cost
data << uint32(3); // unknown
data << uint32(3); // required signs?
// 5v5
data << uint32(3); // index
data << uint32(ARENA_TEAM_CHARTER_5v5); // charter entry
data << uint32(16161); // charter display id
data << uint32(ARENA_TEAM_CHARTER_5v5_COST); // charter cost
data << uint32(5); // unknown
data << uint32(5); // required signs?
}
//for (uint8 i = 0; i < count; ++i)
//{
// data << uint32(i); // index
// data << uint32(GUILD_CHARTER); // charter entry
// data << uint32(16161); // charter display id
// data << uint32(GUILD_CHARTER_COST+i); // charter cost
// data << uint32(0); // unknown
// data << uint32(9); // required signs?
//}
SendPacket(&data);
sLog.outDebug("Sent SMSG_PETITION_SHOWLIST");
}
@@ -1,161 +0,0 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 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 "Language.h"
#include "WorldPacket.h"
#include "Common.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "World.h"
void WorldSession::HandleGMTicketCreateOpcode(WorldPacket & recv_data)
{
if (GetPlayer()->getLevel() < sWorld.getConfig(CONFIG_TICKET_LEVEL_REQ))
{
SendNotification(GetTrinityString(LANG_TICKET_REQ), sWorld.getConfig(CONFIG_TICKET_LEVEL_REQ));
return;
}
if (GM_Ticket *ticket = objmgr.GetGMTicketByPlayer(GetPlayer()->GetGUID()))
{
WorldPacket data(SMSG_GMTICKET_CREATE, 4);
data << uint32(1); // 1 - You already have GM ticket
SendPacket(&data);
return;
}
uint32 map;
float x, y, z;
std::string ticketText, ticketText2;
SendQueryTimeResponse();
WorldPacket data(SMSG_GMTICKET_CREATE, 4);
recv_data >> map;
recv_data >> x;
recv_data >> y;
recv_data >> z;
recv_data >> ticketText;
recv_data >> ticketText2;
GM_Ticket *ticket = new GM_Ticket;
ticket->name = GetPlayer()->GetName();
ticket->guid = objmgr.GenerateGMTicketId();
ticket->playerGuid = GetPlayer()->GetGUID();
ticket->message = ticketText;
ticket->createtime = time(NULL);
ticket->map = map;
ticket->pos_x = x;
ticket->pos_y = y;
ticket->pos_z = z;
ticket->timestamp = time(NULL);
ticket->closed = 0;
ticket->assignedToGM = 0;
ticket->comment = "";
objmgr.AddOrUpdateGMTicket(*ticket, true);
data << uint32(2);
SendPacket(&data);
sWorld.SendGMText(LANG_COMMAND_TICKETNEW, GetPlayer()->GetName(), ticket->guid);
}
void WorldSession::HandleGMTicketUpdateOpcode(WorldPacket & recv_data)
{
WorldPacket data(SMSG_GMTICKET_UPDATETEXT, 4);
std::string message;
recv_data >> message;
GM_Ticket *ticket = objmgr.GetGMTicketByPlayer(GetPlayer()->GetGUID());
if (!ticket)
{
data << uint32(1);
SendPacket(&data);
return;
}
ticket->message = message;
ticket->timestamp = time(NULL);
objmgr.AddOrUpdateGMTicket(*ticket);
data << uint32(2);
SendPacket(&data);
sWorld.SendGMText(LANG_COMMAND_TICKETUPDATED, GetPlayer()->GetName(), ticket->guid);
}
void WorldSession::HandleGMTicketDeleteOpcode(WorldPacket & /*recv_data*/)
{
GM_Ticket* ticket = objmgr.GetGMTicketByPlayer(GetPlayer()->GetGUID());
if (ticket)
{
WorldPacket data(SMSG_GMTICKET_DELETETICKET, 4);
data << uint32(9);
SendPacket(&data);
sWorld.SendGMText(LANG_COMMAND_TICKETPLAYERABANDON, GetPlayer()->GetName(), ticket->guid);
objmgr.RemoveGMTicket(ticket, GetPlayer()->GetGUID(), false);
SendGMTicketGetTicket(0x0A, 0);
}
}
void WorldSession::HandleGMTicketGetTicketOpcode(WorldPacket & /*recv_data*/)
{
SendQueryTimeResponse();
GM_Ticket *ticket = objmgr.GetGMTicketByPlayer(GetPlayer()->GetGUID());
if (ticket)
SendGMTicketGetTicket(0x06, ticket->message.c_str());
else
SendGMTicketGetTicket(0x0A, 0);
}
void WorldSession::HandleGMTicketSystemStatusOpcode(WorldPacket & /*recv_data*/)
{
WorldPacket data(SMSG_GMTICKET_SYSTEMSTATUS, 4);
data << uint32(1);
SendPacket(&data);
}
void WorldSession::SendGMTicketGetTicket(uint32 status, char const* text)
{
int len = text ? strlen(text) : 0;
WorldPacket data(SMSG_GMTICKET_GETTICKET, (4+len+1+4+2+4+4));
data << uint32(status); // standard 0x0A, 0x06 if text present
data << uint32(1); // unk flags, if 0, can't edit the ticket
if (status == 6)
{
data << text; // ticket text
data << uint8(0x7); // ticket category
data << float(0); // tickets in queue?
data << float(0); // if > "tickets in queue" then "We are currently experiencing a high volume of petitions."
data << float(0); // 0 - "Your ticket will be serviced soon", 1 - "Wait time currently unavailable"
data << uint8(0); // if == 2 and next field == 1 then "Your ticket has been escalated"
data << uint8(0); // const
}
SendPacket(&data);
}
@@ -1,656 +0,0 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 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 "Common.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "World.h"
#include "ObjectAccessor.h"
#include "Log.h"
#include "Opcodes.h"
#include "Player.h"
#include "Item.h"
#include "SocialMgr.h"
#include "Language.h"
enum TradeStatus
{
TRADE_STATUS_BUSY = 0,
TRADE_STATUS_BEGIN_TRADE = 1,
TRADE_STATUS_OPEN_WINDOW = 2,
TRADE_STATUS_TRADE_CANCELED = 3,
TRADE_STATUS_TRADE_ACCEPT = 4,
TRADE_STATUS_BUSY_2 = 5,
TRADE_STATUS_NO_TARGET = 6,
TRADE_STATUS_BACK_TO_TRADE = 7,
TRADE_STATUS_TRADE_COMPLETE = 8,
// 9?
TRADE_STATUS_TARGET_TO_FAR = 10,
TRADE_STATUS_WRONG_FACTION = 11,
TRADE_STATUS_CLOSE_WINDOW = 12,
// 13?
TRADE_STATUS_IGNORE_YOU = 14,
TRADE_STATUS_YOU_STUNNED = 15,
TRADE_STATUS_TARGET_STUNNED = 16,
TRADE_STATUS_YOU_DEAD = 17,
TRADE_STATUS_TARGET_DEAD = 18,
TRADE_STATUS_YOU_LOGOUT = 19,
TRADE_STATUS_TARGET_LOGOUT = 20,
TRADE_STATUS_TRIAL_ACCOUNT = 21, // Trial accounts can not perform that action
TRADE_STATUS_ONLY_CONJURED = 22 // You can only trade conjured items... (cross realm BG related).
};
void WorldSession::SendTradeStatus(uint32 status)
{
WorldPacket data;
switch(status)
{
case TRADE_STATUS_BEGIN_TRADE:
data.Initialize(SMSG_TRADE_STATUS, 4+8);
data << uint32(status);
data << uint64(0);
break;
case TRADE_STATUS_OPEN_WINDOW:
data.Initialize(SMSG_TRADE_STATUS, 4+4);
data << uint32(status);
data << uint32(0); // added in 2.4.0
break;
case TRADE_STATUS_CLOSE_WINDOW:
data.Initialize(SMSG_TRADE_STATUS, 4+4+1+4);
data << uint32(status);
data << uint32(0);
data << uint8(0);
data << uint32(0);
break;
case TRADE_STATUS_ONLY_CONJURED:
data.Initialize(SMSG_TRADE_STATUS, 4+1);
data << uint32(status);
data << uint8(0);
break;
default:
data.Initialize(SMSG_TRADE_STATUS, 4);
data << uint32(status);
break;
}
SendPacket(&data);
}
void WorldSession::HandleIgnoreTradeOpcode(WorldPacket& /*recvPacket*/)
{
sLog.outDebug("WORLD: Ignore Trade %u",_player->GetGUIDLow());
// recvPacket.print_storage();
}
void WorldSession::HandleBusyTradeOpcode(WorldPacket& /*recvPacket*/)
{
sLog.outDebug("WORLD: Busy Trade %u",_player->GetGUIDLow());
// recvPacket.print_storage();
}
void WorldSession::SendUpdateTrade()
{
Item *item = NULL;
if (!_player || !_player->pTrader)
return;
// reset trade status
if (_player->acceptTrade)
{
_player->acceptTrade = false;
SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
}
if (_player->pTrader->acceptTrade)
{
_player->pTrader->acceptTrade = false;
_player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
}
WorldPacket data(SMSG_TRADE_STATUS_EXTENDED, (100)); // guess size
data << (uint8) 1; // can be different (only seen 0 and 1)
data << (uint32) 0; // added in 2.4.0, this value must be equal to value from TRADE_STATUS_OPEN_WINDOW status packet (different value for different players to block multiple trades?)
data << (uint32) TRADE_SLOT_COUNT; // trade slots count/number?, = next field in most cases
data << (uint32) TRADE_SLOT_COUNT; // trade slots count/number?, = prev field in most cases
data << (uint32) _player->pTrader->tradeGold; // trader gold
data << (uint32) 0; // spell casted on lowest slot item
for (uint8 i = 0; i < TRADE_SLOT_COUNT; ++i)
{
item = (_player->pTrader->tradeItems[i] != 0 ? _player->pTrader->GetItemByGuid(_player->pTrader->tradeItems[i]) : NULL);
data << (uint8) i; // trade slot number, if not specified, then end of packet
if (item)
{
data << (uint32) item->GetProto()->ItemId; // entry
// display id
data << (uint32) item->GetProto()->DisplayInfoID;
// stack count
data << (uint32) item->GetUInt32Value(ITEM_FIELD_STACK_COUNT);
data << (uint32) 0; // probably gift=1, created_by=0?
// gift creator
data << (uint64) item->GetUInt64Value(ITEM_FIELD_GIFTCREATOR);
data << (uint32) item->GetEnchantmentId(PERM_ENCHANTMENT_SLOT);
for (uint8 j = 0; j < 3; ++j)
data << (uint32) 0; // enchantment id (permanent/gems?)
// creator
data << (uint64) item->GetUInt64Value(ITEM_FIELD_CREATOR);
data << (uint32) item->GetSpellCharges(); // charges
data << (uint32) item->GetItemSuffixFactor(); // SuffixFactor
// random properties id
data << (int32) item->GetItemRandomPropertyId();
data << (uint32) item->GetProto()->LockID; // lock id
// max durability
data << (uint32) item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
// durability
data << (uint32) item->GetUInt32Value(ITEM_FIELD_DURABILITY);
}
else
{
for (uint8 j = 0; j < 18; j++)
data << uint32(0);
}
}
SendPacket(&data);
}
//==============================================================
// transfer the items to the players
void WorldSession::moveItems(Item* myItems[], Item* hisItems[])
{
for (int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i)
{
ItemPosCountVec traderDst;
ItemPosCountVec playerDst;
bool traderCanTrade = (myItems[i] == NULL || _player->pTrader->CanStoreItem(NULL_BAG, NULL_SLOT, traderDst, myItems[i], false) == EQUIP_ERR_OK);
bool playerCanTrade = (hisItems[i] == NULL || _player->CanStoreItem(NULL_BAG, NULL_SLOT, playerDst, hisItems[i], false) == EQUIP_ERR_OK);
if (traderCanTrade && playerCanTrade)
{
// Ok, if trade item exists and can be stored
// If we trade in both directions we had to check, if the trade will work before we actually do it
// A roll back is not possible after we stored it
if (myItems[i])
{
// logging
sLog.outDebug("partner storing: %u",myItems[i]->GetGUIDLow());
if (_player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
{
sLog.outCommand(_player->GetSession()->GetAccountId(),"GM %s (Account: %u) trade: %s (Entry: %d Count: %u) to player: %s (Account: %u)",
_player->GetName(),_player->GetSession()->GetAccountId(),
myItems[i]->GetProto()->Name1,myItems[i]->GetEntry(),myItems[i]->GetCount(),
_player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId());
}
// store
_player->pTrader->MoveItemToInventory(traderDst, myItems[i], true, true);
}
if (hisItems[i])
{
// logging
sLog.outDebug("player storing: %u",hisItems[i]->GetGUIDLow());
if (_player->pTrader->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
{
sLog.outCommand(_player->pTrader->GetSession()->GetAccountId(),"GM %s (Account: %u) trade: %s (Entry: %d Count: %u) to player: %s (Account: %u)",
_player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId(),
hisItems[i]->GetProto()->Name1,hisItems[i]->GetEntry(),hisItems[i]->GetCount(),
_player->GetName(),_player->GetSession()->GetAccountId());
}
// store
_player->MoveItemToInventory(playerDst, hisItems[i], true, true);
}
}
else
{
// in case of fatal error log error message
// return the already removed items to the original owner
if (myItems[i])
{
if (!traderCanTrade)
sLog.outError("trader can't store item: %u",myItems[i]->GetGUIDLow());
if (_player->CanStoreItem(NULL_BAG, NULL_SLOT, playerDst, myItems[i], false) == EQUIP_ERR_OK)
_player->MoveItemToInventory(playerDst, myItems[i], true, true);
else
sLog.outError("player can't take item back: %u",myItems[i]->GetGUIDLow());
}
// return the already removed items to the original owner
if (hisItems[i])
{
if (!playerCanTrade)
sLog.outError("player can't store item: %u",hisItems[i]->GetGUIDLow());
if (_player->pTrader->CanStoreItem(NULL_BAG, NULL_SLOT, traderDst, hisItems[i], false) == EQUIP_ERR_OK)
_player->pTrader->MoveItemToInventory(traderDst, hisItems[i], true, true);
else
sLog.outError("trader can't take item back: %u",hisItems[i]->GetGUIDLow());
}
}
}
}
//==============================================================
void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/)
{
Item *myItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL };
Item *hisItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL };
bool myCanCompleteTrade=true,hisCanCompleteTrade=true;
if (!GetPlayer()->pTrader)
return;
// not accept case incorrect money amount
if (_player->tradeGold > _player->GetMoney())
{
SendNotification(LANG_NOT_ENOUGH_GOLD);
_player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
_player->acceptTrade = false;
return;
}
// not accept case incorrect money amount
if (_player->pTrader->tradeGold > _player->pTrader->GetMoney())
{
_player->pTrader->GetSession()->SendNotification(LANG_NOT_ENOUGH_GOLD);
SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
_player->pTrader->acceptTrade = false;
return;
}
// not accept if some items now can't be trade (cheating)
for (int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i)
{
if (_player->tradeItems[i] != 0)
{
if (Item* item =_player->GetItemByGuid(_player->tradeItems[i]))
{
if (!item->CanBeTraded())
{
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
return;
}
}
}
if (_player->pTrader->tradeItems[i] != 0)
{
if (Item* item =_player->pTrader->GetItemByGuid(_player->pTrader->tradeItems[i]))
{
if (!item->CanBeTraded())
{
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
return;
}
}
}
}
_player->acceptTrade = true;
if (_player->pTrader->acceptTrade)
{
// inform partner client
_player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT);
// store items in local list and set 'in-trade' flag
for (int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i)
{
if (_player->tradeItems[i] != 0)
{
//Can return NULL
myItems[i] = _player->GetItemByGuid(_player->tradeItems[i]);
if (myItems[i])
{
myItems[i]->SetInTrade();
sLog.outDebug("Player trade item bag: %u slot: %u", myItems[i]->GetBagSlot(), myItems[i]->GetSlot());
}
}
if (_player->pTrader->tradeItems[i] != 0)
{
//Can return NULL
hisItems[i]=_player->pTrader->GetItemByGuid(_player->pTrader->tradeItems[i]);
if (hisItems[i])
{
hisItems[i]->SetInTrade();
sLog.outDebug("Player trade item bag: %u slot: %u", hisItems[i]->GetBagSlot(), hisItems[i]->GetSlot());
}
}
}
// test if item will fit in each inventory
hisCanCompleteTrade = (_player->pTrader->CanStoreItems(myItems,TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK);
myCanCompleteTrade = (_player->CanStoreItems(hisItems,TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK);
// clear 'in-trade' flag
for (int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i)
{
if (myItems[i]) myItems[i]->SetInTrade(false);
if (hisItems[i]) hisItems[i]->SetInTrade(false);
}
// in case of missing space report error
if (!myCanCompleteTrade)
{
SendNotification(LANG_NOT_FREE_TRADE_SLOTS);
GetPlayer()->pTrader->GetSession()->SendNotification(LANG_NOT_PARTNER_FREE_TRADE_SLOTS);
SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
_player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
return;
}
else if (!hisCanCompleteTrade)
{
SendNotification(LANG_NOT_PARTNER_FREE_TRADE_SLOTS);
GetPlayer()->pTrader->GetSession()->SendNotification(LANG_NOT_FREE_TRADE_SLOTS);
SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
_player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
return;
}
// execute trade: 1. remove
for (int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i)
{
Item* iPtr = NULL;
if (myItems[i])
{
myItems[i]->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, _player->GetGUID());
iPtr = _player->GetItemByGuid(_player->tradeItems[i]);
_player->MoveItemFromInventory(iPtr->GetBagSlot(), iPtr->GetSlot(), true);
}
if (hisItems[i])
{
hisItems[i]->SetUInt64Value(ITEM_FIELD_GIFTCREATOR,_player->pTrader->GetGUID());
iPtr = _player->pTrader->GetItemByGuid(_player->pTrader->tradeItems[i]);
_player->pTrader->MoveItemFromInventory(iPtr->GetBagSlot(), iPtr->GetSlot(), true);
}
}
// execute trade: 2. store
moveItems(myItems, hisItems);
// logging money
if (sWorld.getConfig(CONFIG_GM_LOG_TRADE))
{
if (_player->GetSession()->GetSecurity() > SEC_PLAYER && _player->tradeGold > 0)
{
sLog.outCommand(_player->GetSession()->GetAccountId(),"GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)",
_player->GetName(),_player->GetSession()->GetAccountId(),
_player->tradeGold,
_player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId());
}
if (_player->pTrader->GetSession()->GetSecurity() > SEC_PLAYER && _player->pTrader->tradeGold > 0)
{
sLog.outCommand(_player->pTrader->GetSession()->GetAccountId(),"GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)",
_player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId(),
_player->pTrader->tradeGold,
_player->GetName(),_player->GetSession()->GetAccountId());
}
}
// update money
_player->ModifyMoney(-int32(_player->tradeGold));
_player->ModifyMoney(_player->pTrader->tradeGold);
_player->pTrader->ModifyMoney(-int32(_player->pTrader->tradeGold));
_player->pTrader->ModifyMoney(_player->tradeGold);
_player->ClearTrade();
_player->pTrader->ClearTrade();
// desynchronized with the other saves here (SaveInventoryAndGoldToDB() not have own transaction guards)
CharacterDatabase.BeginTransaction();
_player->SaveInventoryAndGoldToDB();
_player->pTrader->SaveInventoryAndGoldToDB();
CharacterDatabase.CommitTransaction();
_player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE);
SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE);
_player->pTrader->pTrader = NULL;
_player->pTrader = NULL;
}
else
{
_player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT);
}
}
void WorldSession::HandleUnacceptTradeOpcode(WorldPacket& /*recvPacket*/)
{
if (!GetPlayer()->pTrader)
return;
_player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
_player->acceptTrade = false;
}
void WorldSession::HandleBeginTradeOpcode(WorldPacket& /*recvPacket*/)
{
if (!_player->pTrader)
return;
_player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_OPEN_WINDOW);
_player->pTrader->ClearTrade();
SendTradeStatus(TRADE_STATUS_OPEN_WINDOW);
_player->ClearTrade();
}
void WorldSession::SendCancelTrade()
{
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
}
void WorldSession::HandleCancelTradeOpcode(WorldPacket& /*recvPacket*/)
{
// sended also after LOGOUT COMPLETE
if (_player) // needed because STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT
_player->TradeCancel(true);
}
void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket)
{
if (GetPlayer()->pTrader)
return;
uint64 ID;
if (!GetPlayer()->isAlive())
{
SendTradeStatus(TRADE_STATUS_YOU_DEAD);
return;
}
if (GetPlayer()->hasUnitState(UNIT_STAT_STUNNED))
{
SendTradeStatus(TRADE_STATUS_YOU_STUNNED);
return;
}
if (isLogingOut())
{
SendTradeStatus(TRADE_STATUS_YOU_LOGOUT);
return;
}
if (GetPlayer()->isInFlight())
{
SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR);
return;
}
if (GetPlayer()->getLevel() < sWorld.getConfig(CONFIG_TRADE_LEVEL_REQ))
{
SendNotification(GetTrinityString(LANG_TRADE_REQ), sWorld.getConfig(CONFIG_TRADE_LEVEL_REQ));
return;
}
recvPacket >> ID;
Player* pOther = ObjectAccessor::FindPlayer(ID);
if (!pOther)
{
SendTradeStatus(TRADE_STATUS_NO_TARGET);
return;
}
if (pOther == GetPlayer() || pOther->pTrader)
{
SendTradeStatus(TRADE_STATUS_BUSY);
return;
}
if (!pOther->isAlive())
{
SendTradeStatus(TRADE_STATUS_TARGET_DEAD);
return;
}
if (pOther->isInFlight())
{
SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR);
return;
}
if (pOther->hasUnitState(UNIT_STAT_STUNNED))
{
SendTradeStatus(TRADE_STATUS_TARGET_STUNNED);
return;
}
if (pOther->GetSession()->isLogingOut())
{
SendTradeStatus(TRADE_STATUS_TARGET_LOGOUT);
return;
}
if (pOther->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow()))
{
SendTradeStatus(TRADE_STATUS_IGNORE_YOU);
return;
}
if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_TRADE) && pOther->GetTeam() !=_player->GetTeam())
{
SendTradeStatus(TRADE_STATUS_WRONG_FACTION);
return;
}
if (!pOther->IsWithinDistInMap(_player,10.0f,false))
{
SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR);
return;
}
if (pOther->getLevel() < sWorld.getConfig(CONFIG_TRADE_LEVEL_REQ))
{
SendNotification(GetTrinityString(LANG_TRADE_OTHER_REQ), sWorld.getConfig(CONFIG_TRADE_LEVEL_REQ));
return;
}
// OK start trade
_player->pTrader = pOther;
pOther->pTrader =_player;
WorldPacket data(SMSG_TRADE_STATUS, 12);
data << (uint32) TRADE_STATUS_BEGIN_TRADE;
data << (uint64)_player->GetGUID();
_player->pTrader->GetSession()->SendPacket(&data);
}
void WorldSession::HandleSetTradeGoldOpcode(WorldPacket& recvPacket)
{
if (!_player->pTrader)
return;
uint32 gold;
recvPacket >> gold;
// gold can be incorrect, but this is checked at trade finished.
_player->tradeGold = gold;
_player->pTrader->GetSession()->SendUpdateTrade();
}
void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket)
{
if (!_player->pTrader)
return;
// send update
uint8 tradeSlot;
uint8 bag;
uint8 slot;
recvPacket >> tradeSlot;
recvPacket >> bag;
recvPacket >> slot;
// invalid slot number
if (tradeSlot >= TRADE_SLOT_COUNT)
{
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
return;
}
// check cheating, can't fail with correct client operations
Item* item = _player->GetItemByPos(bag,slot);
if (!item || (tradeSlot != TRADE_SLOT_NONTRADED && !item->CanBeTraded()))
{
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
return;
}
uint64 iGUID = item->GetGUID();
// prevent place single item into many trade slots using cheating and client bugs
for (int i = 0; i < TRADE_SLOT_COUNT; ++i)
{
if (_player->tradeItems[i] == iGUID)
{
// cheating attempt
SendTradeStatus(TRADE_STATUS_TRADE_CANCELED);
return;
}
}
_player->tradeItems[tradeSlot] = iGUID;
_player->pTrader->GetSession()->SendUpdateTrade();
}
void WorldSession::HandleClearTradeItemOpcode(WorldPacket& recvPacket)
{
if (!_player->pTrader)
return;
uint8 tradeSlot;
recvPacket >> tradeSlot;
// invalid slot number
if (tradeSlot >= TRADE_SLOT_COUNT)
return;
_player->tradeItems[tradeSlot] = 0;
_player->pTrader->GetSession()->SendUpdateTrade();
}
@@ -1,50 +0,0 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 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 "Common.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Opcodes.h"
#include "Log.h"
void WorldSession::HandleVoiceSessionEnableOpcode(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: CMSG_VOICE_SESSION_ENABLE");
// uint8 isVoiceEnabled, uint8 isMicrophoneEnabled
recv_data.read_skip<uint8>();
recv_data.read_skip<uint8>();
recv_data.hexlike();
}
void WorldSession::HandleChannelVoiceOnOpcode(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: CMSG_CHANNEL_VOICE_ON");
// Enable Voice button in channel context menu
recv_data.hexlike();
}
void WorldSession::HandleSetActiveVoiceChannel(WorldPacket & recv_data)
{
sLog.outDebug("WORLD: CMSG_SET_ACTIVE_VOICE_CHANNEL");
recv_data.read_skip<uint32>();
recv_data.read_skip<char*>();
recv_data.hexlike();
}
@@ -0,0 +1,589 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 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 "Common.h"
#include "Transports.h"
#include "MapManager.h"
#include "ObjectMgr.h"
#include "Path.h"
#include "WorldPacket.h"
#include "DBCStores.h"
#include "ProgressBar.h"
#include "World.h"
void MapManager::LoadTransports()
{
QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry, name, period FROM transports");
uint32 count = 0;
if (!result)
{
barGoLink bar(1);
bar.step();
sLog.outString();
sLog.outString(">> Loaded %u transports", count);
return;
}
barGoLink bar(result->GetRowCount());
do
{
bar.step();
Transport *t = new Transport;
Field *fields = result->Fetch();
uint32 entry = fields[0].GetUInt32();
std::string name = fields[1].GetCppString();
t->m_period = fields[2].GetUInt32();
const GameObjectInfo *goinfo = objmgr.GetGameObjectInfo(entry);
if (!goinfo)
{
sLog.outErrorDb("Transport ID:%u, Name: %s, will not be loaded, gameobject_template missing", entry, name.c_str());
delete t;
continue;
}
if (goinfo->type != GAMEOBJECT_TYPE_MO_TRANSPORT)
{
sLog.outErrorDb("Transport ID:%u, Name: %s, will not be loaded, gameobject_template type wrong", entry, name.c_str());
delete t;
continue;
}
// sLog.outString("Loading transport %d between %s, %s", entry, name.c_str(), goinfo->name);
std::set<uint32> mapsUsed;
if (!t->GenerateWaypoints(goinfo->moTransport.taxiPathId, mapsUsed))
// skip transports with empty waypoints list
{
sLog.outErrorDb("Transport (path id %u) path size = 0. Transport ignored, check DBC files or transport GO data0 field.",goinfo->moTransport.taxiPathId);
delete t;
continue;
}
float x, y, z, o;
uint32 mapid;
x = t->m_WayPoints[0].x; y = t->m_WayPoints[0].y; z = t->m_WayPoints[0].z; mapid = t->m_WayPoints[0].mapid; o = 1;
// creates the Gameobject
if (!t->Create(entry, mapid, x, y, z, o, 100, 0))
{
delete t;
continue;
}
m_Transports.insert(t);
for (std::set<uint32>::const_iterator i = mapsUsed.begin(); i != mapsUsed.end(); ++i)
m_TransportsByMap[*i].insert(t);
//If we someday decide to use the grid to track transports, here:
t->SetMap(MapManager::Instance().CreateMap(mapid, t, 0));
//t->GetMap()->Add<GameObject>((GameObject *)t);
++count;
} while (result->NextRow());
sLog.outString();
sLog.outString(">> Loaded %u transports", count);
// check transport data DB integrity
result = WorldDatabase.Query("SELECT gameobject.guid,gameobject.id,transports.name FROM gameobject,transports WHERE gameobject.id = transports.entry");
if (result) // wrong data found
{
do
{
Field *fields = result->Fetch();
uint32 guid = fields[0].GetUInt32();
uint32 entry = fields[1].GetUInt32();
std::string name = fields[2].GetCppString();
sLog.outErrorDb("Transport %u '%s' have record (GUID: %u) in `gameobject`. Transports DON'T must have any records in `gameobject` or its behavior will be unpredictable/bugged.",entry,name.c_str(),guid);
}
while (result->NextRow());
}
}
Transport::Transport() : GameObject()
{
m_updateFlag = (UPDATEFLAG_TRANSPORT | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HAS_POSITION | UPDATEFLAG_ROTATION);
}
bool Transport::Create(uint32 guidlow, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress, uint32 dynflags)
{
Relocate(x,y,z,ang);
// instance id and phaseMask isn't set to values different from std.
if (!IsPositionValid())
{
sLog.outError("Transport (GUID: %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)",
guidlow,x,y);
return false;
}
Object::_Create(guidlow, 0, HIGHGUID_MO_TRANSPORT);
GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(guidlow);
if (!goinfo)
{
sLog.outErrorDb("Transport not created: entry in `gameobject_template` not found, guidlow: %u map: %u (X: %f Y: %f Z: %f) ang: %f",guidlow, mapid, x, y, z, ang);
return false;
}
m_goInfo = goinfo;
SetFloatValue(OBJECT_FIELD_SCALE_X, goinfo->size);
SetUInt32Value(GAMEOBJECT_FACTION, goinfo->faction);
//SetUInt32Value(GAMEOBJECT_FLAGS, goinfo->flags);
SetUInt32Value(GAMEOBJECT_FLAGS, MAKE_PAIR32(0x28, 0x64));
SetUInt32Value(GAMEOBJECT_LEVEL, m_period);
SetEntry(goinfo->id);
SetUInt32Value(GAMEOBJECT_DISPLAYID, goinfo->displayId);
SetGoState(GO_STATE_READY);
SetGoType(GameobjectTypes(goinfo->type));
SetGoAnimProgress(animprogress);
if (dynflags)
SetUInt32Value(GAMEOBJECT_DYNAMIC, MAKE_PAIR32(0, dynflags));
SetName(goinfo->name);
return true;
}
struct keyFrame
{
keyFrame(float _x, float _y, float _z, uint32 _mapid, int _actionflag, int _delay)
{
x = _x; y = _y; z = _z; mapid = _mapid; actionflag = _actionflag; delay = _delay; distFromPrev = -1; distSinceStop = -1; distUntilStop = -1;
tFrom = 0; tTo = 0;
}
float x;
float y;
float z;
uint32 mapid;
int actionflag;
int delay;
float distSinceStop;
float distUntilStop;
float distFromPrev;
float tFrom, tTo;
};
bool Transport::GenerateWaypoints(uint32 pathid, std::set<uint32> &mapids)
{
TransportPath path;
objmgr.GetTransportPathNodes(pathid, path);
if (path.Empty())
return false;
std::vector<keyFrame> keyFrames;
int mapChange = 0;
mapids.clear();
for (size_t i = 1; i < path.Size() - 1; ++i)
{
if (mapChange == 0)
{
if ((path[i].mapid == path[i+1].mapid))
{
keyFrame k(path[i].x, path[i].y, path[i].z, path[i].mapid, path[i].actionFlag, path[i].delay);
keyFrames.push_back(k);
mapids.insert(k.mapid);
}
else
{
mapChange = 1;
}
}
else
{
--mapChange;
}
}
int lastStop = -1;
int firstStop = -1;
// first cell is arrived at by teleportation :S
keyFrames[0].distFromPrev = 0;
if (keyFrames[0].actionflag == 2)
{
lastStop = 0;
}
// find the rest of the distances between key points
for (size_t i = 1; i < keyFrames.size(); ++i)
{
if ((keyFrames[i].actionflag == 1) || (keyFrames[i].mapid != keyFrames[i-1].mapid))
{
keyFrames[i].distFromPrev = 0;
}
else
{
keyFrames[i].distFromPrev =
sqrt(pow(keyFrames[i].x - keyFrames[i - 1].x, 2) +
pow(keyFrames[i].y - keyFrames[i - 1].y, 2) +
pow(keyFrames[i].z - keyFrames[i - 1].z, 2));
}
if (keyFrames[i].actionflag == 2)
{
// remember first stop frame
if (firstStop == -1)
firstStop = i;
lastStop = i;
}
}
float tmpDist = 0;
for (size_t i = 0; i < keyFrames.size(); ++i)
{
int j = (i + lastStop) % keyFrames.size();
if (keyFrames[j].actionflag == 2)
tmpDist = 0;
else
tmpDist += keyFrames[j].distFromPrev;
keyFrames[j].distSinceStop = tmpDist;
}
for (int i = int(keyFrames.size()) - 1; i >= 0; i--)
{
int j = (i + (firstStop+1)) % keyFrames.size();
tmpDist += keyFrames[(j + 1) % keyFrames.size()].distFromPrev;
keyFrames[j].distUntilStop = tmpDist;
if (keyFrames[j].actionflag == 2)
tmpDist = 0;
}
for (size_t i = 0; i < keyFrames.size(); ++i)
{
if (keyFrames[i].distSinceStop < (30 * 30 * 0.5f))
keyFrames[i].tFrom = sqrt(2 * keyFrames[i].distSinceStop);
else
keyFrames[i].tFrom = ((keyFrames[i].distSinceStop - (30 * 30 * 0.5f)) / 30) + 30;
if (keyFrames[i].distUntilStop < (30 * 30 * 0.5f))
keyFrames[i].tTo = sqrt(2 * keyFrames[i].distUntilStop);
else
keyFrames[i].tTo = ((keyFrames[i].distUntilStop - (30 * 30 * 0.5f)) / 30) + 30;
keyFrames[i].tFrom *= 1000;
keyFrames[i].tTo *= 1000;
}
// for (int i = 0; i < keyFrames.size(); ++i) {
// sLog.outString("%f, %f, %f, %f, %f, %f, %f", keyFrames[i].x, keyFrames[i].y, keyFrames[i].distUntilStop, keyFrames[i].distSinceStop, keyFrames[i].distFromPrev, keyFrames[i].tFrom, keyFrames[i].tTo);
// }
// Now we're completely set up; we can move along the length of each waypoint at 100 ms intervals
// speed = max(30, t) (remember x = 0.5s^2, and when accelerating, a = 1 unit/s^2
int t = 0;
bool teleport = false;
if (keyFrames[keyFrames.size() - 1].mapid != keyFrames[0].mapid)
teleport = true;
WayPoint pos(keyFrames[0].mapid, keyFrames[0].x, keyFrames[0].y, keyFrames[0].z, teleport, 0);
m_WayPoints[0] = pos;
t += keyFrames[0].delay * 1000;
uint32 cM = keyFrames[0].mapid;
for (size_t i = 0; i < keyFrames.size() - 1; ++i)
{
float d = 0;
float tFrom = keyFrames[i].tFrom;
float tTo = keyFrames[i].tTo;
// keep the generation of all these points; we use only a few now, but may need the others later
if (((d < keyFrames[i + 1].distFromPrev) && (tTo > 0)))
{
while ((d < keyFrames[i + 1].distFromPrev) && (tTo > 0))
{
tFrom += 100;
tTo -= 100;
if (d > 0)
{
float newX, newY, newZ;
newX = keyFrames[i].x + (keyFrames[i + 1].x - keyFrames[i].x) * d / keyFrames[i + 1].distFromPrev;
newY = keyFrames[i].y + (keyFrames[i + 1].y - keyFrames[i].y) * d / keyFrames[i + 1].distFromPrev;
newZ = keyFrames[i].z + (keyFrames[i + 1].z - keyFrames[i].z) * d / keyFrames[i + 1].distFromPrev;
bool teleport = false;
if (keyFrames[i].mapid != cM)
{
teleport = true;
cM = keyFrames[i].mapid;
}
// sLog.outString("T: %d, D: %f, x: %f, y: %f, z: %f", t, d, newX, newY, newZ);
WayPoint pos(keyFrames[i].mapid, newX, newY, newZ, teleport, i);
if (teleport)
m_WayPoints[t] = pos;
}
if (tFrom < tTo) // caught in tFrom dock's "gravitational pull"
{
if (tFrom <= 30000)
{
d = 0.5f * (tFrom / 1000) * (tFrom / 1000);
}
else
{
d = 0.5f * 30 * 30 + 30 * ((tFrom - 30000) / 1000);
}
d = d - keyFrames[i].distSinceStop;
}
else
{
if (tTo <= 30000)
{
d = 0.5f * (tTo / 1000) * (tTo / 1000);
}
else
{
d = 0.5f * 30 * 30 + 30 * ((tTo - 30000) / 1000);
}
d = keyFrames[i].distUntilStop - d;
}
t += 100;
}
t -= 100;
}
if (keyFrames[i + 1].tFrom > keyFrames[i + 1].tTo)
t += 100 - ((long)keyFrames[i + 1].tTo % 100);
else
t += (long)keyFrames[i + 1].tTo % 100;
bool teleport = false;
if ((keyFrames[i + 1].actionflag == 1) || (keyFrames[i + 1].mapid != keyFrames[i].mapid))
{
teleport = true;
cM = keyFrames[i + 1].mapid;
}
WayPoint pos(keyFrames[i + 1].mapid, keyFrames[i + 1].x, keyFrames[i + 1].y, keyFrames[i + 1].z, teleport, i);
// sLog.outString("T: %d, x: %f, y: %f, z: %f, t:%d", t, pos.x, pos.y, pos.z, teleport);
/*
if (keyFrames[i+1].delay > 5)
pos.delayed = true;
*/
//if (teleport)
m_WayPoints[t] = pos;
t += keyFrames[i + 1].delay * 1000;
// sLog.outString("------");
}
uint32 timer = t;
// sLog.outDetail(" Generated %lu waypoints, total time %u.", (unsigned long)m_WayPoints.size(), timer);
m_curr = m_WayPoints.begin();
m_curr = GetNextWayPoint();
m_next = GetNextWayPoint();
m_pathTime = timer;
m_nextNodeTime = m_curr->first;
return true;
}
Transport::WayPointMap::const_iterator Transport::GetNextWayPoint()
{
WayPointMap::const_iterator iter = m_curr;
++iter;
if (iter == m_WayPoints.end())
iter = m_WayPoints.begin();
return iter;
}
void Transport::TeleportTransport(uint32 newMapid, float x, float y, float z)
{
Map const* oldMap = GetMap();
Relocate(x, y, z);
for (PlayerSet::const_iterator itr = m_passengers.begin(); itr != m_passengers.end();)
{
Player *plr = *itr;
++itr;
if (plr->isDead() && !plr->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
{
plr->ResurrectPlayer(1.0);
}
plr->TeleportTo(newMapid, x, y, z, GetOrientation(), TELE_TO_NOT_LEAVE_TRANSPORT);
//WorldPacket data(SMSG_811, 4);
//data << uint32(0);
//plr->GetSession()->SendPacket(&data);
}
//we need to create and save new Map object with 'newMapid' because if not done -> lead to invalid Map object reference...
//player far teleport would try to create same instance, but we need it NOW for transport...
//correct me if I'm wrong O.o
//yes, you're right
ResetMap();
Map * newMap = MapManager::Instance().CreateMap(newMapid, this, 0);
SetMap(newMap);
assert (GetMap());
if (oldMap != newMap)
{
UpdateForMap(oldMap);
UpdateForMap(newMap);
}
}
bool Transport::AddPassenger(Player* passenger)
{
if (m_passengers.insert(passenger).second)
sLog.outDetail("Player %s boarded transport %s.", passenger->GetName(), GetName());
return true;
}
bool Transport::RemovePassenger(Player* passenger)
{
if (m_passengers.erase(passenger))
sLog.outDetail("Player %s removed from transport %s.", passenger->GetName(), GetName());
return true;
}
void Transport::CheckForEvent(uint32 entry, uint32 wp_id)
{
uint32 key = entry*100+wp_id;
if (objmgr.TransportEventMap.find(key) != objmgr.TransportEventMap.end())
GetMap()->ScriptsStart(sEventScripts, objmgr.TransportEventMap[key], this, NULL);
}
void Transport::Update(uint32 /*p_time*/)
{
if (m_WayPoints.size() <= 1)
return;
m_timer = getMSTime() % m_period;
while (((m_timer - m_curr->first) % m_pathTime) > ((m_next->first - m_curr->first) % m_pathTime))
{
m_curr = GetNextWayPoint();
m_next = GetNextWayPoint();
// first check help in case client-server transport coordinates de-synchronization
if (m_curr->second.mapid != GetMapId() || m_curr->second.teleport)
{
TeleportTransport(m_curr->second.mapid, m_curr->second.x, m_curr->second.y, m_curr->second.z);
}
else
{
Relocate(m_curr->second.x, m_curr->second.y, m_curr->second.z);
}
/*
if (m_curr->second.delayed)
{
switch (GetEntry())
{
case 176495:
case 164871:
case 175080:
SendPlaySound(11804, false); break; // ZeppelinDocked
case 20808:
case 181646:
case 176231:
case 176244:
case 176310:
case 177233:
SendPlaySound(5495, false);break; // BoatDockingWarning
default:
SendPlaySound(5154, false); break; // ShipDocked
}
}
*/
/*
for (PlayerSet::const_iterator itr = m_passengers.begin(); itr != m_passengers.end();)
{
PlayerSet::const_iterator it2 = itr;
++itr;
//(*it2)->SetPosition(m_curr->second.x + (*it2)->GetTransOffsetX(), m_curr->second.y + (*it2)->GetTransOffsetY(), m_curr->second.z + (*it2)->GetTransOffsetZ(), (*it2)->GetTransOffsetO());
}
*/
m_nextNodeTime = m_curr->first;
if (m_curr == m_WayPoints.begin() && (sLog.getLogFilter() & LOG_FILTER_TRANSPORT_MOVES) == 0)
sLog.outDetail(" ************ BEGIN ************** %s", this->m_name.c_str());
if ((sLog.getLogFilter() & LOG_FILTER_TRANSPORT_MOVES) == 0)
sLog.outDetail("%s moved to %d %f %f %f %d", this->m_name.c_str(), m_curr->second.id, m_curr->second.x, m_curr->second.y, m_curr->second.z, m_curr->second.mapid);
//Transport Event System
CheckForEvent(this->GetEntry(), m_curr->second.id);
}
}
void Transport::UpdateForMap(Map const* targetMap)
{
Map::PlayerList const& pl = targetMap->GetPlayers();
if (pl.isEmpty())
return;
if (GetMapId() == targetMap->GetId())
{
for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr)
{
if (this != itr->getSource()->GetTransport())
{
UpdateData transData;
BuildCreateUpdateBlockForPlayer(&transData, itr->getSource());
WorldPacket packet;
transData.BuildPacket(&packet);
itr->getSource()->SendDirectMessage(&packet);
}
}
}
else
{
UpdateData transData;
BuildOutOfRangeUpdateBlock(&transData);
WorldPacket out_packet;
transData.BuildPacket(&out_packet);
for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr)
if (this != itr->getSource()->GetTransport())
itr->getSource()->SendDirectMessage(&out_packet);
}
}
@@ -0,0 +1,108 @@
/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2010 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
*/
#ifndef TRANSPORTS_H
#define TRANSPORTS_H
#include "GameObject.h"
#include <map>
#include <set>
#include <string>
class TransportPath
{
public:
struct PathNode
{
uint32 mapid;
float x,y,z;
uint32 actionFlag;
uint32 delay;
};
void SetLength(const unsigned int sz)
{
i_nodes.resize(sz);
}
unsigned int Size(void) const { return i_nodes.size(); }
bool Empty(void) const { return i_nodes.empty(); }
void Resize(unsigned int sz) { i_nodes.resize(sz); }
void Clear(void) { i_nodes.clear(); }
PathNode* GetNodes(void) { return static_cast<PathNode *>(&i_nodes[0]); }
PathNode& operator[](const unsigned int idx) { return i_nodes[idx]; }
const PathNode& operator()(const unsigned int idx) const { return i_nodes[idx]; }
protected:
std::vector<PathNode> i_nodes;
};
class Transport : public GameObject
{
public:
explicit Transport();
bool Create(uint32 guidlow, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress, uint32 dynflags);
bool GenerateWaypoints(uint32 pathid, std::set<uint32> &mapids);
void Update(uint32 p_time);
bool AddPassenger(Player* passenger);
bool RemovePassenger(Player* passenger);
void CheckForEvent(uint32 entry, uint32 wp_id);
typedef std::set<Player*> PlayerSet;
PlayerSet const& GetPassengers() const { return m_passengers; }
private:
struct WayPoint
{
WayPoint() : mapid(0), x(0), y(0), z(0), teleport(false), id(0) {}
WayPoint(uint32 _mapid, float _x, float _y, float _z, bool _teleport, uint32 _id) :
mapid(_mapid), x(_x), y(_y), z(_z), teleport(_teleport), id(_id) {}
uint32 mapid;
float x;
float y;
float z;
bool teleport;
uint32 id;
};
typedef std::map<uint32, WayPoint> WayPointMap;
WayPointMap::const_iterator m_curr;
WayPointMap::const_iterator m_next;
uint32 m_pathTime;
uint32 m_timer;
PlayerSet m_passengers;
public:
WayPointMap m_WayPoints;
uint32 m_nextNodeTime;
uint32 m_period;
private:
void TeleportTransport(uint32 newMapid, float x, float y, float z);
void UpdateForMap(Map const* map);
WayPointMap::const_iterator GetNextWayPoint();
};
#endif