Merge remote branch 'origin/master' into 330

Conflicts:
	src/game/ObjectMgr.h
This commit is contained in:
tomrus88 2009-12-11 21:50:24 +03:00
commit 7c6cae1af7
58 changed files with 1937 additions and 990 deletions

View file

@ -519,11 +519,13 @@ void BattleGround::SendPacketToAll(WorldPacket *packet)
{
for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
if (itr->second.OfflineRemoveTime)
continue;
Player *plr = sObjectMgr.GetPlayer(itr->first);
if (plr)
plr->GetSession()->SendPacket(packet);
else
sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
sLog.outError("BattleGround:SendPacketToAll: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
}
}
@ -531,11 +533,12 @@ void BattleGround::SendPacketToTeam(uint32 TeamID, WorldPacket *packet, Player *
{
for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
if (itr->second.OfflineRemoveTime)
continue;
Player *plr = sObjectMgr.GetPlayer(itr->first);
if (!plr)
{
sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
sLog.outError("BattleGround:SendPacketToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
continue;
}
@ -563,11 +566,13 @@ void BattleGround::PlaySoundToTeam(uint32 SoundID, uint32 TeamID)
for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
if (itr->second.OfflineRemoveTime)
continue;
Player *plr = sObjectMgr.GetPlayer(itr->first);
if (!plr)
{
sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
sLog.outError("BattleGround:PlaySoundToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
continue;
}
@ -586,11 +591,13 @@ void BattleGround::CastSpellOnTeam(uint32 SpellID, uint32 TeamID)
{
for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
if (itr->second.OfflineRemoveTime)
continue;
Player *plr = sObjectMgr.GetPlayer(itr->first);
if (!plr)
{
sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
sLog.outError("BattleGround:CastSpellOnTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
continue;
}
@ -606,11 +613,13 @@ void BattleGround::RewardHonorToTeam(uint32 Honor, uint32 TeamID)
{
for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
if (itr->second.OfflineRemoveTime)
continue;
Player *plr = sObjectMgr.GetPlayer(itr->first);
if (!plr)
{
sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
sLog.outError("BattleGround:RewardHonorToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
continue;
}
@ -631,11 +640,13 @@ void BattleGround::RewardReputationToTeam(uint32 faction_id, uint32 Reputation,
for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
if (itr->second.OfflineRemoveTime)
continue;
Player *plr = sObjectMgr.GetPlayer(itr->first);
if (!plr)
{
sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
sLog.outError("BattleGround:RewardReputationToTeam: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
continue;
}
@ -721,10 +732,9 @@ void BattleGround::EndBattleGround(uint32 winner)
for(BattleGroundPlayerMap::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
Player *plr = sObjectMgr.GetPlayer(itr->first);
uint32 team = itr->second.Team;
if (!plr)
if (itr->second.OfflineRemoveTime)
{
//if rated arena match - make member lost!
if (isArena() && isRated() && winner_arena_team && loser_arena_team)
@ -734,7 +744,12 @@ void BattleGround::EndBattleGround(uint32 winner)
else
loser_arena_team->OfflineMemberLost(itr->first, winner_rating);
}
sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
continue;
}
Player *plr = sObjectMgr.GetPlayer(itr->first);
if (!plr)
{
sLog.outError("BattleGround:EndBattleGround Player (GUID: %u) not found!", GUID_LOPART(itr->first));
continue;
}
@ -1285,14 +1300,13 @@ void BattleGround::EventPlayerLoggedOut(Player* player)
m_Players[player->GetGUID()].OfflineRemoveTime = sWorld.GetGameTime() + MAX_OFFLINE_TIME;
if (GetStatus() == STATUS_IN_PROGRESS)
{
if (isBattleGround())
EventPlayerDroppedFlag(player);
else
{
//1 player is logging out, if it is the last, then end arena!
// drop flag and handle other cleanups
RemovePlayer(player, player->GetGUID());
// 1 player is logging out, if it is the last, then end arena!
if (isArena())
if (GetAlivePlayersCountByTeam(player->GetTeam()) <= 1 && GetPlayersCountByTeam(GetOtherTeam(player->GetTeam())))
EndBattleGround(GetOtherTeam(player->GetTeam()));
}
}
}

View file

@ -134,7 +134,7 @@ void BattleGroundEY::CheckSomeoneJoinedPoint()
Player *plr = sObjectMgr.GetPlayer(m_PlayersNearPoint[BG_EY_NODES_MAX][j]);
if (!plr)
{
sLog.outError("BattleGroundEY: Player (GUID: %u) not found!", GUID_LOPART(m_PlayersNearPoint[BG_EY_NODES_MAX][j]));
sLog.outError("BattleGroundEY:CheckSomeoneJoinedPoint: Player (GUID: %u) not found!", GUID_LOPART(m_PlayersNearPoint[BG_EY_NODES_MAX][j]));
++j;
continue;
}
@ -170,7 +170,7 @@ void BattleGroundEY::CheckSomeoneLeftPoint()
Player *plr = sObjectMgr.GetPlayer(m_PlayersNearPoint[i][j]);
if (!plr)
{
sLog.outError("BattleGroundEY: Player (GUID: %u) not found!", GUID_LOPART(m_PlayersNearPoint[i][j]));
sLog.outError("BattleGroundEY:CheckSomeoneLeftPoint Player (GUID: %u) not found!", GUID_LOPART(m_PlayersNearPoint[i][j]));
//move not existed player to "free space" - this will cause many error showing in log, but it is a very important bug
m_PlayersNearPoint[BG_EY_NODES_MAX].push_back(m_PlayersNearPoint[i][j]);
m_PlayersNearPoint[i].erase(m_PlayersNearPoint[i].begin() + j);

View file

@ -366,7 +366,8 @@ void WorldSession::HandleBattleFieldPortOpcode( WorldPacket &recv_data )
//if player don't match battleground max level, then do not allow him to enter! (this might happen when player leveled up during his waiting in queue
if (_player->getLevel() > bg->GetMaxLevel())
{
sLog.outError("Battleground: Player %s (%u) has level higher than maxlevel of battleground! Do not port him to battleground!", _player->GetName(), _player->GetGUIDLow());
sLog.outError("Battleground: Player %s (%u) has level (%u) higher than maxlevel (%u) of battleground (%u)! Do not port him to battleground!",
_player->GetName(), _player->GetGUIDLow(), _player->getLevel(), bg->GetMaxLevel(), bg->GetTypeID());
action = 0;
}
}

View file

@ -698,7 +698,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder *holder)
if(at)
pCurrChar->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, pCurrChar->GetOrientation());
else
pCurrChar->TeleportTo(pCurrChar->m_homebindMapId, pCurrChar->m_homebindX, pCurrChar->m_homebindY, pCurrChar->m_homebindZ, pCurrChar->GetOrientation());
pCurrChar->TeleportToHomebind();
}
sObjectAccessor.AddObject(pCurrChar);

View file

@ -423,6 +423,8 @@ ChatCommand * ChatHandler::getCommandTable()
{ "gameobject_questrelation", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadGOQuestRelationsCommand, "", NULL },
{ "gameobject_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadGameObjectScriptsCommand, "", NULL },
{ "gameobject_battleground", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadBattleEventCommand, "", NULL },
{ "gossip_menu", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadGossipMenuCommand, "", NULL },
{ "gossip_menu_option", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadGossipMenuOptionCommand, "", NULL },
{ "item_enchantment_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadItemEnchantementsCommand, "", NULL },
{ "item_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesItemCommand, "", NULL },
{ "item_required_target", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadItemRequiredTragetCommand, "", NULL },
@ -439,7 +441,6 @@ ChatCommand * ChatHandler::getCommandTable()
{ "mangos_string", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadMangosStringCommand, "", NULL },
{ "milling_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesMillingCommand, "", NULL },
{ "npc_gossip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadNpcGossipCommand, "", NULL },
{ "npc_option", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadNpcOptionCommand, "", NULL },
{ "npc_spellclick_spells", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellClickSpellsCommand, "",NULL},
{ "npc_trainer", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadNpcTrainerCommand, "", NULL },
{ "npc_vendor", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadNpcVendorCommand, "", NULL },
@ -666,6 +667,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "flusharenapoints",SEC_ADMINISTRATOR, false, &ChatHandler::HandleFlushArenaPointsCommand, "", NULL },
{ "repairitems", SEC_GAMEMASTER, true, &ChatHandler::HandleRepairitemsCommand, "", NULL },
{ "waterwalk", SEC_GAMEMASTER, false, &ChatHandler::HandleWaterwalkCommand, "", NULL },
{ "quit", SEC_CONSOLE, true, &ChatHandler::HandleQuitCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};

View file

@ -335,6 +335,8 @@ class ChatHandler
bool HandleReloadGameGraveyardZoneCommand(const char* args);
bool HandleReloadGameObjectScriptsCommand(const char* args);
bool HandleReloadGameTeleCommand(const char* args);
bool HandleReloadGossipMenuCommand(const char* args);
bool HandleReloadGossipMenuOptionCommand(const char* args);
bool HandleReloadGOQuestRelationsCommand(const char* args);
bool HandleReloadGOQuestInvRelationsCommand(const char* args);
bool HandleReloadItemEnchantementsCommand(const char* args);
@ -362,7 +364,6 @@ class ChatHandler
bool HandleReloadMailLevelRewardCommand(const char* args);
bool HandleReloadMangosStringCommand(const char* args);
bool HandleReloadNpcGossipCommand(const char* args);
bool HandleReloadNpcOptionCommand(const char* args);
bool HandleReloadNpcTrainerCommand(const char* args);
bool HandleReloadNpcVendorCommand(const char* args);
bool HandleReloadPageTextsCommand(const char* args);
@ -497,6 +498,7 @@ class ChatHandler
bool HandleFlushArenaPointsCommand(const char *args);
bool HandleRepairitemsCommand(const char* args);
bool HandleWaterwalkCommand(const char* args);
bool HandleQuitCommand(const char* args);
//! Development Commands
bool HandleSaveAllCommand(const char* args);

View file

@ -110,7 +110,7 @@ Unit(), i_AI(NULL),
lootForPickPocketed(false), lootForBody(false), m_groupLootTimer(0), lootingGroupLeaderGUID(0),
m_lootMoney(0), m_lootRecipient(0),
m_deathTimer(0), m_respawnTime(0), m_respawnDelay(25), m_corpseDelay(60), m_respawnradius(0.0f),
m_gossipOptionLoaded(false), m_isPet(false), m_isVehicle(false), m_isTotem(false),
m_isPet(false), m_isVehicle(false), m_isTotem(false),
m_defaultMovementType(IDLE_MOTION_TYPE), m_DBTableGuid(0), m_equipmentId(0),
m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false),
m_regenHealth(true), m_AI_locked(false), m_isDeadByDefault(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL),
@ -744,21 +744,6 @@ bool Creature::isCanTrainingAndResetTalentsOf(Player* pPlayer) const
&& pPlayer->getClass() == GetCreatureInfo()->trainer_class;
}
void Creature::LoadGossipOptions()
{
if(m_gossipOptionLoaded)
return;
uint32 npcflags=GetUInt32Value(UNIT_NPC_FLAGS);
CacheNpcOptionList const& noList = sObjectMgr.GetNpcOptions ();
for (CacheNpcOptionList::const_iterator i = noList.begin (); i != noList.end (); ++i)
if(i->NpcFlag & npcflags)
addGossipOption(*i);
m_gossipOptionLoaded = true;
}
void Creature::AI_SendMoveToPacket(float x, float y, float z, uint32 time, MonsterMovementFlags flags, uint8 type)
{
/* uint32 timeElap = getMSTime();

View file

@ -36,102 +36,6 @@ class Quest;
class Player;
class WorldSession;
enum Gossip_Option
{
GOSSIP_OPTION_NONE = 0, //UNIT_NPC_FLAG_NONE = 0,
GOSSIP_OPTION_GOSSIP = 1, //UNIT_NPC_FLAG_GOSSIP = 1,
GOSSIP_OPTION_QUESTGIVER = 2, //UNIT_NPC_FLAG_QUESTGIVER = 2,
GOSSIP_OPTION_VENDOR = 3, //UNIT_NPC_FLAG_VENDOR = 4,
GOSSIP_OPTION_TAXIVENDOR = 4, //UNIT_NPC_FLAG_TAXIVENDOR = 8,
GOSSIP_OPTION_TRAINER = 5, //UNIT_NPC_FLAG_TRAINER = 16,
GOSSIP_OPTION_SPIRITHEALER = 6, //UNIT_NPC_FLAG_SPIRITHEALER = 32,
GOSSIP_OPTION_SPIRITGUIDE = 7, //UNIT_NPC_FLAG_SPIRITGUIDE = 64,
GOSSIP_OPTION_INNKEEPER = 8, //UNIT_NPC_FLAG_INNKEEPER = 128,
GOSSIP_OPTION_BANKER = 9, //UNIT_NPC_FLAG_BANKER = 256,
GOSSIP_OPTION_PETITIONER = 10, //UNIT_NPC_FLAG_PETITIONER = 512,
GOSSIP_OPTION_TABARDDESIGNER = 11, //UNIT_NPC_FLAG_TABARDDESIGNER = 1024,
GOSSIP_OPTION_BATTLEFIELD = 12, //UNIT_NPC_FLAG_BATTLEFIELDPERSON = 2048,
GOSSIP_OPTION_AUCTIONEER = 13, //UNIT_NPC_FLAG_AUCTIONEER = 4096,
GOSSIP_OPTION_STABLEPET = 14, //UNIT_NPC_FLAG_STABLE = 8192,
GOSSIP_OPTION_ARMORER = 15, //UNIT_NPC_FLAG_ARMORER = 16384,
GOSSIP_OPTION_UNLEARNTALENTS = 16, //UNIT_NPC_FLAG_TRAINER (bonus option for GOSSIP_OPTION_TRAINER)
GOSSIP_OPTION_UNLEARNPETSKILLS = 17 //UNIT_NPC_FLAG_TRAINER (bonus option for GOSSIP_OPTION_TRAINER)
};
enum Gossip_Guard
{
GOSSIP_GUARD_BANK = 32,
GOSSIP_GUARD_RIDE = 33,
GOSSIP_GUARD_GUILD = 34,
GOSSIP_GUARD_INN = 35,
GOSSIP_GUARD_MAIL = 36,
GOSSIP_GUARD_AUCTION = 37,
GOSSIP_GUARD_WEAPON = 38,
GOSSIP_GUARD_STABLE = 39,
GOSSIP_GUARD_BATTLE = 40,
GOSSIP_GUARD_SPELLTRAINER = 41,
GOSSIP_GUARD_SKILLTRAINER = 42
};
enum Gossip_Guard_Spell
{
GOSSIP_GUARD_SPELL_WARRIOR = 64,
GOSSIP_GUARD_SPELL_PALADIN = 65,
GOSSIP_GUARD_SPELL_HUNTER = 66,
GOSSIP_GUARD_SPELL_ROGUE = 67,
GOSSIP_GUARD_SPELL_PRIEST = 68,
GOSSIP_GUARD_SPELL_UNKNOWN1 = 69,
GOSSIP_GUARD_SPELL_SHAMAN = 70,
GOSSIP_GUARD_SPELL_MAGE = 71,
GOSSIP_GUARD_SPELL_WARLOCK = 72,
GOSSIP_GUARD_SPELL_UNKNOWN2 = 73,
GOSSIP_GUARD_SPELL_DRUID = 74
};
enum Gossip_Guard_Skill
{
GOSSIP_GUARD_SKILL_ALCHEMY = 80,
GOSSIP_GUARD_SKILL_BLACKSMITH = 81,
GOSSIP_GUARD_SKILL_COOKING = 82,
GOSSIP_GUARD_SKILL_ENCHANT = 83,
GOSSIP_GUARD_SKILL_FIRSTAID = 84,
GOSSIP_GUARD_SKILL_FISHING = 85,
GOSSIP_GUARD_SKILL_HERBALISM = 86,
GOSSIP_GUARD_SKILL_LEATHER = 87,
GOSSIP_GUARD_SKILL_MINING = 88,
GOSSIP_GUARD_SKILL_SKINNING = 89,
GOSSIP_GUARD_SKILL_TAILORING = 90,
GOSSIP_GUARD_SKILL_ENGINERING = 91
};
enum GossipOptionIcon
{
GOSSIP_ICON_CHAT = 0, //white chat bubble
GOSSIP_ICON_VENDOR = 1, //brown bag
GOSSIP_ICON_TAXI = 2, //flight
GOSSIP_ICON_TRAINER = 3, //book
GOSSIP_ICON_INTERACT_1 = 4, //interaction wheel
GOSSIP_ICON_INTERACT_2 = 5, //interaction wheel
GOSSIP_ICON_MONEY_BAG = 6, //brown bag with yellow dot
GOSSIP_ICON_TALK = 7, //white chat bubble with black dots
GOSSIP_ICON_TABARD = 8, //tabard
GOSSIP_ICON_BATTLE = 9, //two swords
GOSSIP_ICON_DOT = 10 //yellow dot
};
struct GossipOption
{
uint32 Id;
uint32 GossipId;
uint32 NpcFlag;
uint32 Icon;
uint32 Action;
uint32 BoxMoney;
bool Coded;
std::string OptionText;
std::string BoxText;
};
enum CreatureFlagsExtra
{
CREATURE_FLAG_EXTRA_INSTANCE_BIND = 0x00000001, // creature kill bind instance with killer and killer's group
@ -165,6 +69,7 @@ struct CreatureInfo
char* Name;
char* SubName;
char* IconName;
uint32 GossipMenuId;
uint32 minlevel;
uint32 maxlevel;
uint32 minhealth;
@ -259,7 +164,7 @@ struct CreatureLocale
std::vector<std::string> SubName;
};
struct NpcOptionLocale
struct GossipMenuItemsLocale
{
std::vector<std::string> OptionText;
std::vector<std::string> BoxText;
@ -449,8 +354,6 @@ struct TrainerSpellData
void Clear() { spellList.clear(); }
};
typedef std::list<GossipOption> GossipOptionList;
typedef std::map<uint32,time_t> CreatureSpellCooldowns;
// max different by z coordinate for creature aggro reaction
@ -576,10 +479,6 @@ class MANGOS_DLL_SPEC Creature : public Unit
std::string GetScriptName() const;
uint32 GetScriptId() const;
void LoadGossipOptions();
void addGossipOption(GossipOption const& gso) { m_goptions.push_back(gso); }
GossipOptionList &GetGossipOptionList() { return m_goptions; }
void Say(int32 textId, uint32 language, uint64 TargetGuid) { MonsterSay(textId,language,TargetGuid); }
void Yell(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYell(textId,language,TargetGuid); }
void TextEmote(int32 textId, uint64 TargetGuid, bool IsBossEmote = false) { MonsterTextEmote(textId,TargetGuid,IsBossEmote); }
@ -708,9 +607,6 @@ class MANGOS_DLL_SPEC Creature : public Unit
uint32 m_corpseDelay; // (secs) delay between death and corpse disappearance
float m_respawnradius;
bool m_gossipOptionLoaded;
GossipOptionList m_goptions;
bool m_isPet; // set only in Pet::Pet
bool m_isVehicle; // set only in Vehicle::Vehicle
bool m_isTotem; // set only in Totem::Totem

View file

@ -327,26 +327,26 @@ enum TotemCategoryType
// SummonProperties.dbc, col 1
enum SummonPropGroup
{
SUMMON_PROP_GROUP_UNKNOWN1 = 0, // 1160 spells in 3.0.3
SUMMON_PROP_GROUP_UNKNOWN2 = 1, // 861 spells in 3.0.3
SUMMON_PROP_GROUP_PETS = 2, // 52 spells in 3.0.3, pets mostly
SUMMON_PROP_GROUP_CONTROLLABLE = 3, // 13 spells in 3.0.3, mostly controllable
SUMMON_PROP_GROUP_UNKNOWN3 = 4 // 86 spells in 3.0.3, taxi/mounts
SUMMON_PROP_GROUP_WILD = 0,
SUMMON_PROP_GROUP_FRIENDLY = 1,
SUMMON_PROP_GROUP_PETS = 2,
SUMMON_PROP_GROUP_CONTROLLABLE = 3,
SUMMON_PROP_GROUP_VEHICLE = 4
};
// SummonProperties.dbc, col 3
enum SummonPropType
{
SUMMON_PROP_TYPE_UNKNOWN = 0, // different summons, 1330 spells in 3.0.3
SUMMON_PROP_TYPE_OTHER = 0, // different summons, 1330 spells in 3.0.3
SUMMON_PROP_TYPE_SUMMON = 1, // generic summons, 49 spells in 3.0.3
SUMMON_PROP_TYPE_GUARDIAN = 2, // summon guardian, 393 spells in 3.0.3
SUMMON_PROP_TYPE_ARMY = 3, // summon army, 5 spells in 3.0.3
SUMMON_PROP_TYPE_TOTEM = 4, // summon totem, 169 spells in 3.0.3
SUMMON_PROP_TYPE_CRITTER = 5, // critter/minipet, 195 spells in 3.0.3
SUMMON_PROP_TYPE_DK = 6, // summon DRW/Ghoul, 2 spells in 3.0.3
SUMMON_PROP_TYPE_BOMB = 7, // summon bot/bomb, 4 spells in 3.0.3
SUMMON_PROP_TYPE_PHASING = 8, // something todo with DK prequest line, 2 spells in 3.0.3
SUMMON_PROP_TYPE_SIEGE_VEH = 9, // summon different vehicles, 14 spells in 3.0.3
SUMMON_PROP_TYPE_DK = 6, // summon DRW/Ghoul, 2 spells in 3.0.3 "%s's Runeblade"
SUMMON_PROP_TYPE_CONSTRUCT = 7, // summon bot/bomb, 4 spells in 3.0.3 "%s's Construct"
SUMMON_PROP_TYPE_PHASING = 8, // something todo with DK prequest line, 2 spells in 3.0.3 "%s's Opponent"
SUMMON_PROP_TYPE_SIEGE_VEH = 9, // summon different vehicles, 14 spells in 3.0.3 "%s's Vehicle"
SUMMON_PROP_TYPE_DRAKE_VEH = 10, // summon drake (vehicle), 3 spells
SUMMON_PROP_TYPE_LIGHTWELL = 11 // summon lightwell, 6 spells in 3.0.3
};

View file

@ -129,7 +129,7 @@ DBCStorage <SpellRangeEntry> sSpellRangeStore(SpellRangefmt);
DBCStorage <SpellRuneCostEntry> sSpellRuneCostStore(SpellRuneCostfmt);
DBCStorage <SpellShapeshiftEntry> sSpellShapeshiftStore(SpellShapeshiftfmt);
DBCStorage <StableSlotPricesEntry> sStableSlotPricesStore(StableSlotPricesfmt);
//DBCStorage <SummonPropertiesEntry> sSummonPropertiesStore(SummonPropertiesfmt);
DBCStorage <SummonPropertiesEntry> sSummonPropertiesStore(SummonPropertiesfmt);
DBCStorage <TalentEntry> sTalentStore(TalentEntryfmt);
TalentSpellPosMap sTalentSpellPosMap;
DBCStorage <TalentTabEntry> sTalentTabStore(TalentTabEntryfmt);
@ -206,7 +206,7 @@ void LoadDBCStores(const std::string& dataPath)
{
std::string dbcPath = dataPath+"dbc/";
const uint32 DBCFilesCount = 80;
const uint32 DBCFilesCount = 81;
barGoLink bar( DBCFilesCount );
@ -360,7 +360,7 @@ void LoadDBCStores(const std::string& dataPath)
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellRuneCostStore, dbcPath,"SpellRuneCost.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellShapeshiftStore, dbcPath,"SpellShapeshiftForm.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sStableSlotPricesStore, dbcPath,"StableSlotPrices.dbc");
//LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSummonPropertiesStore, dbcPath,"SummonProperties.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSummonPropertiesStore, dbcPath,"SummonProperties.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTalentStore, dbcPath,"Talent.dbc");
// create talent spells set

View file

@ -136,7 +136,7 @@ extern DBCStorage <SpellRuneCostEntry> sSpellRuneCostStore;
extern DBCStorage <SpellShapeshiftEntry> sSpellShapeshiftStore;
extern DBCStorage <SpellEntry> sSpellStore;
extern DBCStorage <StableSlotPricesEntry> sStableSlotPricesStore;
//extern DBCStorage <SummonPropertiesEntry> sSummonPropertiesStore;
extern DBCStorage <SummonPropertiesEntry> sSummonPropertiesStore;
extern DBCStorage <TalentEntry> sTalentStore;
extern DBCStorage <TalentTabEntry> sTalentTabStore;
extern DBCStorage <TaxiNodesEntry> sTaxiNodesStore;

View file

@ -1540,17 +1540,15 @@ struct StableSlotPricesEntry
uint32 Price;
};
/* unused currently
struct SummonPropertiesEntry
{
uint32 Id; // 0
uint32 Group; // 1, enum SummonPropGroup, 0 - can't be controlled?, 1 - something guardian?, 2 - pet?, 3 - something controllable?, 4 - taxi/mount?
uint32 Group; // 1, enum SummonPropGroup
uint32 FactionId; // 2, 14 rows > 0
uint32 Type; // 3, enum SummonPropType
uint32 Slot; // 4, 0-6
uint32 Slot; // 4, if type = SUMMON_PROP_TYPE_TOTEM, its actual slot 0-6
uint32 Flags; // 5, enum SummonPropFlags
};
*/
#define MAX_TALENT_RANK 5
#define MAX_PET_TALENT_RANK 3 // use in calculations, expected <= MAX_TALENT_RANK

View file

@ -93,7 +93,7 @@ const char SpellRangefmt[]="nffffxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
const char SpellRuneCostfmt[]="niiii";
const char SpellShapeshiftfmt[]="nxxxxxxxxxxxxxxxxxxiixixxxxxxxxxxxx";
const char StableSlotPricesfmt[] = "ni";
//const char SummonPropertiesfmt[] = "niiiii";
const char SummonPropertiesfmt[] = "niiiii";
const char TalentEntryfmt[]="niiiiiiiixxxxixxixxxxxx";
const char TalentTabEntryfmt[]="nxxxxxxxxxxxxxxxxxxxiiix";
const char TaxiNodesEntryfmt[]="nifffssssssssssssssssxii";

View file

@ -872,13 +872,13 @@ void GameObject::Use(Unit* user)
case GAMEOBJECT_TYPE_QUESTGIVER: //2
{
if(user->GetTypeId()!=TYPEID_PLAYER)
if (user->GetTypeId() != TYPEID_PLAYER)
return;
Player* player = (Player*)user;
player->PrepareQuestMenu( GetGUID() );
player->SendPreparedQuest( GetGUID() );
player->PrepareGossipMenu(this, GetGOInfo()->questgiver.gossipID);
player->SendPreparedGossip(this);
return;
}
//Sitting: Wooden bench, chairs enzz
@ -952,12 +952,17 @@ void GameObject::Use(Unit* user)
Player* player = (Player*)user;
// show page
if(info->goober.pageId)
if (info->goober.pageId)
{
WorldPacket data(SMSG_GAMEOBJECT_PAGETEXT, 8);
data << GetGUID();
player->GetSession()->SendPacket(&data);
}
else if (info->questgiver.gossipID)
{
player->PrepareGossipMenu(this, info->goober.gossipID);
player->SendPreparedGossip(this);
}
// possible quest objective for active quests
player->CastedCreatureOrGO(info->id, GetGUID(), 0);

View file

@ -489,6 +489,16 @@ struct GameObjectInfo
default: return 0;
}
}
uint32 GetGossipMenuId() const
{
switch(type)
{
case GAMEOBJECT_TYPE_QUESTGIVER: return questgiver.gossipID;
case GAMEOBJECT_TYPE_GOOBER: return goober.gossipID;
default: return 0;
}
}
};
// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform

View file

@ -27,6 +27,7 @@
GossipMenu::GossipMenu()
{
m_gItems.reserve(16); // can be set for max from most often sizes to speedup push_back and less memory use
m_gMenuId = 0;
}
GossipMenu::~GossipMenu()
@ -44,13 +45,24 @@ void GossipMenu::AddMenuItem(uint8 Icon, const std::string& Message, uint32 dtSe
gItem.m_gMessage = Message;
gItem.m_gCoded = Coded;
gItem.m_gSender = dtSender;
gItem.m_gAction = dtAction;
gItem.m_gOptionId = dtAction;
gItem.m_gBoxMessage = BoxMessage;
gItem.m_gBoxMoney = BoxMoney;
m_gItems.push_back(gItem);
}
void GossipMenu::AddGossipMenuItemData(uint32 action_menu, uint32 action_poi, uint32 action_script)
{
GossipMenuItemData pItemData;
pItemData.m_gAction_menu = action_menu;
pItemData.m_gAction_poi = action_poi;
pItemData.m_gAction_script = action_script;
m_gItemsData.push_back(pItemData);
}
void GossipMenu::AddMenuItem(uint8 Icon, const std::string& Message, bool Coded)
{
AddMenuItem( Icon, Message, 0, 0, "", 0, Coded);
@ -77,7 +89,7 @@ uint32 GossipMenu::MenuItemAction( unsigned int ItemId )
{
if ( ItemId >= m_gItems.size() ) return 0;
return m_gItems[ ItemId ].m_gAction;
return m_gItems[ ItemId ].m_gOptionId;
}
bool GossipMenu::MenuItemCoded( unsigned int ItemId )
@ -90,6 +102,8 @@ bool GossipMenu::MenuItemCoded( unsigned int ItemId )
void GossipMenu::ClearMenu()
{
m_gItems.clear();
m_gItemsData.clear();
m_gMenuId = 0;
}
PlayerMenu::PlayerMenu( WorldSession *session ) : pSession(session)
@ -122,13 +136,13 @@ bool PlayerMenu::GossipOptionCoded( unsigned int Selection )
return mGossipMenu.MenuItemCoded( Selection );
}
void PlayerMenu::SendGossipMenu( uint32 TitleTextId, uint64 npcGUID )
void PlayerMenu::SendGossipMenu(uint32 TitleTextId, uint64 objectGUID)
{
WorldPacket data( SMSG_GOSSIP_MESSAGE, (100) ); // guess size
data << uint64(npcGUID);
data << uint32(0); // new 2.4.0
data << uint32( TitleTextId );
data << uint32( mGossipMenu.MenuItemCount() ); // max count 0x10
WorldPacket data(SMSG_GOSSIP_MESSAGE, (100)); // guess size
data << uint64(objectGUID);
data << uint32(mGossipMenu.GetMenuId()); // new 2.4.0
data << uint32(TitleTextId);
data << uint32(mGossipMenu.MenuItemCount()); // max count 0x10
for (uint32 iI = 0; iI < mGossipMenu.MenuItemCount(); ++iI )
{

View file

@ -28,6 +28,45 @@ class WorldSession;
#define GOSSIP_MAX_MENU_ITEMS 64 // client supported items unknown, but provided number must be enough
#define DEFAULT_GOSSIP_MESSAGE 0xffffff
enum Gossip_Option
{
GOSSIP_OPTION_NONE = 0, //UNIT_NPC_FLAG_NONE (0)
GOSSIP_OPTION_GOSSIP = 1, //UNIT_NPC_FLAG_GOSSIP (1)
GOSSIP_OPTION_QUESTGIVER = 2, //UNIT_NPC_FLAG_QUESTGIVER (2)
GOSSIP_OPTION_VENDOR = 3, //UNIT_NPC_FLAG_VENDOR (128)
GOSSIP_OPTION_TAXIVENDOR = 4, //UNIT_NPC_FLAG_TAXIVENDOR (8192)
GOSSIP_OPTION_TRAINER = 5, //UNIT_NPC_FLAG_TRAINER (16)
GOSSIP_OPTION_SPIRITHEALER = 6, //UNIT_NPC_FLAG_SPIRITHEALER (16384)
GOSSIP_OPTION_SPIRITGUIDE = 7, //UNIT_NPC_FLAG_SPIRITGUIDE (32768)
GOSSIP_OPTION_INNKEEPER = 8, //UNIT_NPC_FLAG_INNKEEPER (65536)
GOSSIP_OPTION_BANKER = 9, //UNIT_NPC_FLAG_BANKER (131072)
GOSSIP_OPTION_PETITIONER = 10, //UNIT_NPC_FLAG_PETITIONER (262144)
GOSSIP_OPTION_TABARDDESIGNER = 11, //UNIT_NPC_FLAG_TABARDDESIGNER (524288)
GOSSIP_OPTION_BATTLEFIELD = 12, //UNIT_NPC_FLAG_BATTLEFIELDPERSON (1048576)
GOSSIP_OPTION_AUCTIONEER = 13, //UNIT_NPC_FLAG_AUCTIONEER (2097152)
GOSSIP_OPTION_STABLEPET = 14, //UNIT_NPC_FLAG_STABLE (4194304)
GOSSIP_OPTION_ARMORER = 15, //UNIT_NPC_FLAG_ARMORER (4096)
GOSSIP_OPTION_UNLEARNTALENTS = 16, //UNIT_NPC_FLAG_TRAINER (16) (bonus option for GOSSIP_OPTION_TRAINER)
GOSSIP_OPTION_UNLEARNPETSKILLS = 17, //UNIT_NPC_FLAG_TRAINER (16) (bonus option for GOSSIP_OPTION_TRAINER)
GOSSIP_OPTION_MAX
};
enum GossipOptionIcon
{
GOSSIP_ICON_CHAT = 0, //white chat bubble
GOSSIP_ICON_VENDOR = 1, //brown bag
GOSSIP_ICON_TAXI = 2, //flight
GOSSIP_ICON_TRAINER = 3, //book
GOSSIP_ICON_INTERACT_1 = 4, //interaction wheel
GOSSIP_ICON_INTERACT_2 = 5, //interaction wheel
GOSSIP_ICON_MONEY_BAG = 6, //brown bag with yellow dot
GOSSIP_ICON_TALK = 7, //white chat bubble with black dots
GOSSIP_ICON_TABARD = 8, //tabard
GOSSIP_ICON_BATTLE = 9, //two swords
GOSSIP_ICON_DOT = 10, //yellow dot
GOSSIP_ICON_MAX
};
//POI icons. Many more exist, list not complete.
enum Poi_Icon
{
@ -80,13 +119,22 @@ struct GossipMenuItem
bool m_gCoded;
std::string m_gMessage;
uint32 m_gSender;
uint32 m_gAction;
uint32 m_gOptionId;
std::string m_gBoxMessage;
uint32 m_gBoxMoney;
};
typedef std::vector<GossipMenuItem> GossipMenuItemList;
struct GossipMenuItemData
{
uint32 m_gAction_menu;
uint32 m_gAction_poi;
uint32 m_gAction_script;
};
typedef std::vector<GossipMenuItemData> GossipMenuItemDataList;
struct QuestMenuItem
{
uint32 m_qId;
@ -108,6 +156,11 @@ class MANGOS_DLL_SPEC GossipMenu
void AddMenuItem(uint8 Icon, char const* Message, bool Coded = false);
void AddMenuItem(uint8 Icon, char const* Message, uint32 dtSender, uint32 dtAction, char const* BoxMessage, uint32 BoxMoney, bool Coded = false);
void SetMenuId(uint32 menu_id) { m_gMenuId = menu_id; }
uint32 GetMenuId() { return m_gMenuId; }
void AddGossipMenuItemData(uint32 action_menu, uint32 action_poi, uint32 action_script);
unsigned int MenuItemCount() const
{
return m_gItems.size();
@ -123,6 +176,11 @@ class MANGOS_DLL_SPEC GossipMenu
return m_gItems[ Id ];
}
GossipMenuItemData const& GetItemData(unsigned int indexId)
{
return m_gItemsData[indexId];
}
uint32 MenuItemSender( unsigned int ItemId );
uint32 MenuItemAction( unsigned int ItemId );
bool MenuItemCoded( unsigned int ItemId );
@ -130,7 +188,10 @@ class MANGOS_DLL_SPEC GossipMenu
void ClearMenu();
protected:
GossipMenuItemList m_gItems;
GossipMenuItemList m_gItems;
GossipMenuItemDataList m_gItemsData;
uint32 m_gMenuId;
};
class QuestMenu

View file

@ -780,7 +780,8 @@ enum MangosStrings
LANG_ACCOUNT_LIST_BAR = 1012,
LANG_ACCOUNT_LIST_LINE = 1013,
LANG_ACCOUNT_LIST_EMPTY = 1014,
// Room for more level 4 1015-1099 not used
LANG_QUIT_WRONG_USE_ERROR = 1015,
// Room for more level 4 1016-1099 not used
// Level 3 (continue)
LANG_ACCOUNT_SETADDON = 1100,

View file

@ -102,7 +102,6 @@ bool ChatHandler::HandleReloadAllLootCommand(const char*)
bool ChatHandler::HandleReloadAllNpcCommand(const char* /*args*/)
{
HandleReloadNpcGossipCommand("a");
HandleReloadNpcOptionCommand("a");
HandleReloadNpcTrainerCommand("a");
HandleReloadNpcVendorCommand("a");
HandleReloadPointsOfInterestCommand("a");
@ -252,6 +251,22 @@ bool ChatHandler::HandleReloadCreatureQuestInvRelationsCommand(const char*)
return true;
}
bool ChatHandler::HandleReloadGossipMenuCommand(const char*)
{
sLog.outString( "Re-Loading `gossip_menu` Table!" );
sObjectMgr.LoadGossipMenu();
SendGlobalSysMessage("DB table `gossip_menu` reloaded.");
return true;
}
bool ChatHandler::HandleReloadGossipMenuOptionCommand(const char*)
{
sLog.outString( "Re-Loading `gossip_menu_option` Table!" );
sObjectMgr.LoadGossipMenuItems();
SendGlobalSysMessage("DB table `gossip_menu_option` reloaded.");
return true;
}
bool ChatHandler::HandleReloadGOQuestRelationsCommand(const char*)
{
sLog.outString( "Loading Quests Relations... (`gameobject_questrelation`)" );
@ -404,14 +419,6 @@ bool ChatHandler::HandleReloadMangosStringCommand(const char*)
return true;
}
bool ChatHandler::HandleReloadNpcOptionCommand(const char*)
{
sLog.outString( "Re-Loading `npc_option` Table!" );
sObjectMgr.LoadNpcOptions();
SendGlobalSysMessage("DB table `npc_option` reloaded.");
return true;
}
bool ChatHandler::HandleReloadNpcGossipCommand(const char*)
{
sLog.outString( "Re-Loading `npc_gossip` Table!" );

View file

@ -2617,7 +2617,7 @@ void InstanceMap::UnloadAll(bool pForce)
for(MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
{
Player* plr = itr->getSource();
plr->TeleportTo(plr->m_homebindMapId, plr->m_homebindX, plr->m_homebindY, plr->m_homebindZ, plr->GetOrientation());
plr->TeleportToHomebind();
}
}

View file

@ -50,12 +50,12 @@ void WorldSession::HandleMoveWorldportAckOpcode()
// possible errors in the coordinate validity check
if(!MapManager::IsValidMapCoord(loc.mapid, loc.coord_x, loc.coord_y, loc.coord_z, loc.orientation))
{
sLog.outError("WorldSession::HandleMoveWorldportAckOpcode: player got's teleported far to a not valid location. (map:%u, x:%f, y:%f, z:%f) We log him out and don't save him..", loc.mapid, loc.coord_x, loc.coord_y, loc.coord_z);
// stop teleportation else we would try this again in the beginning of WorldSession::LogoutPlayer...
sLog.outError("WorldSession::HandleMoveWorldportAckOpcode: player %s (%d) was teleported far to a not valid location. (map:%u, x:%f, y:%f, "
"z:%f) We port him to his homebind instead..", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow(), loc.mapid, loc.coord_x, loc.coord_y, loc.coord_z);
// stop teleportation else we would try this again and again in LogoutPlayer...
GetPlayer()->SetSemaphoreTeleportFar(false);
// player don't gets saved - so his coords will stay at the point where
// he was last saved
LogoutPlayer(false);
// and teleport the player to a valid place
GetPlayer()->TeleportToHomebind();
return;
}
@ -81,14 +81,10 @@ void WorldSession::HandleMoveWorldportAckOpcode()
//if player wasn't added to map, reset his map pointer!
GetPlayer()->ResetMap();
sLog.outDebug("WORLD: teleport of player %s (%d) to location %d, %f, %f, %f, %f failed", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow(), loc.mapid, loc.coord_x, loc.coord_y, loc.coord_z, loc.orientation);
sLog.outError("WorldSession::HandleMoveWorldportAckOpcode: player %s (%d) was teleported far but couldn't be added to map. (map:%u, x:%f, y:%f, "
"z:%f) We port him to his homebind instead..", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow(), loc.mapid, loc.coord_x, loc.coord_y, loc.coord_z);
// teleport the player home
if(!GetPlayer()->TeleportTo(GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->GetOrientation()))
{
// the player must always be able to teleport home
sLog.outError("WORLD: failed to teleport player %s (%d) to homebind location %d, %f, %f, %f, %f!", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow(), GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->GetOrientation());
assert(false);
}
GetPlayer()->TeleportToHomebind();
return;
}
@ -242,7 +238,6 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data )
ReadMovementInfo(recv_data, &movementInfo);
/*----------------*/
recv_data.rpos(recv_data.wpos()); // prevent warnings spam
if (!MaNGOS::IsValidMapCoord(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o))
{
recv_data.rpos(recv_data.wpos()); // prevent warnings spam

View file

@ -277,7 +277,7 @@ void WorldSession::HandleGossipHelloOpcode(WorldPacket & recv_data)
if (!Script->GossipHello(_player, pCreature))
{
_player->TalkedToCreature(pCreature->GetEntry(), pCreature->GetGUID());
_player->PrepareGossipMenu(pCreature);
_player->PrepareGossipMenu(pCreature, pCreature->GetCreatureInfo()->GossipMenuId);
_player->SendPreparedGossip(pCreature);
}
}
@ -286,41 +286,58 @@ void WorldSession::HandleGossipSelectOptionOpcode( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: CMSG_GOSSIP_SELECT_OPTION");
uint32 option;
uint32 gossipListId;
uint32 menuId;
uint64 guid;
std::string code = "";
recv_data >> guid >> menuId >> option;
recv_data >> guid >> menuId >> gossipListId;
if (_player->PlayerTalkClass->GossipOptionCoded(option))
if (_player->PlayerTalkClass->GossipOptionCoded(gossipListId))
{
sLog.outBasic("reading string");
recv_data >> code;
sLog.outBasic("string read: %s", code.c_str());
}
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE);
if (!pCreature)
{
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()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
if (!code.empty())
// TODO: determine if scriptCall is needed for GO and also if scriptCall can be same as current, with modified argument WorldObject*
if (IS_CREATURE_GUID(guid))
{
if (!Script->GossipSelectWithCode(_player, pCreature, _player->PlayerTalkClass->GossipOptionSender(option), _player->PlayerTalkClass->GossipOptionAction(option), code.c_str()))
_player->OnGossipSelect(pCreature, option);
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE);
if (!pCreature)
{
sLog.outDebug("WORLD: HandleGossipSelectOptionOpcode - Creature (GUID: %u) not found or you can't interact with it.", uint32(GUID_LOPART(guid)));
return;
}
if (!code.empty())
{
if (!Script->GossipSelectWithCode(_player, pCreature, _player->PlayerTalkClass->GossipOptionSender(gossipListId), _player->PlayerTalkClass->GossipOptionAction(gossipListId), code.c_str()))
_player->OnGossipSelect(pCreature, gossipListId, menuId);
}
else
{
if (!Script->GossipSelect(_player, pCreature, _player->PlayerTalkClass->GossipOptionSender(gossipListId), _player->PlayerTalkClass->GossipOptionAction(gossipListId)))
_player->OnGossipSelect(pCreature, gossipListId, menuId);
}
}
else
else if (IS_GAMEOBJECT_GUID(guid))
{
if (!Script->GossipSelect(_player, pCreature, _player->PlayerTalkClass->GossipOptionSender(option), _player->PlayerTalkClass->GossipOptionAction(option)))
_player->OnGossipSelect(pCreature, option);
GameObject *pGo = GetPlayer()->GetGameObjectIfCanInteractWith(guid);
if (!pGo)
{
sLog.outDebug("WORLD: HandleGossipSelectOptionOpcode - GameObject (GUID: %u) not found or you can't interact with it.", uint32(GUID_LOPART(guid)));
return;
}
_player->OnGossipSelect(pGo, gossipListId, menuId);
}
}
@ -410,13 +427,7 @@ void WorldSession::SendBindPoint(Creature *npc)
uint32 bindspell = 3286;
uint32 zone_id = _player->GetZoneId();
// 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(), zone_id, _player->GetPositionX(), _player->GetPositionY(), _player->GetPositionZ(), _player->GetGUIDLow());
_player->m_homebindMapId = _player->GetMapId();
_player->m_homebindZoneId = zone_id;
_player->m_homebindX = _player->GetPositionX();
_player->m_homebindY = _player->GetPositionY();
_player->m_homebindZ = _player->GetPositionZ();
_player->SetHomebindToCurrentPos();
// send spell for bind 3286 bind magic
npc->CastSpell(_player, bindspell, true);

View file

@ -43,6 +43,7 @@
#include "SpellAuras.h"
#include "Util.h"
#include "WaypointManager.h"
#include "GossipDef.h"
INSTANTIATE_SINGLETON_1(ObjectMgr);
@ -51,6 +52,7 @@ ScriptMapMap sQuestStartScripts;
ScriptMapMap sSpellScripts;
ScriptMapMap sGameObjectScripts;
ScriptMapMap sEventScripts;
ScriptMapMap sGossipScripts;
bool normalizePlayerName(std::string& name)
{
@ -338,16 +340,16 @@ void ObjectMgr::LoadCreatureLocales()
sLog.outString( ">> Loaded %lu creature locale strings", (unsigned long)mCreatureLocaleMap.size() );
}
void ObjectMgr::LoadNpcOptionLocales()
void ObjectMgr::LoadGossipMenuItemsLocales()
{
mNpcOptionLocaleMap.clear(); // need for reload case
mGossipMenuItemsLocaleMap.clear(); // need for reload case
QueryResult *result = WorldDatabase.Query("SELECT entry,"
QueryResult *result = WorldDatabase.Query("SELECT menu_id,id,"
"option_text_loc1,box_text_loc1,option_text_loc2,box_text_loc2,"
"option_text_loc3,box_text_loc3,option_text_loc4,box_text_loc4,"
"option_text_loc5,box_text_loc5,option_text_loc6,box_text_loc6,"
"option_text_loc7,box_text_loc7,option_text_loc8,box_text_loc8 "
"FROM locales_npc_option");
"FROM locales_gossip_menu_option");
if(!result)
{
@ -356,7 +358,7 @@ void ObjectMgr::LoadNpcOptionLocales()
bar.step();
sLog.outString();
sLog.outString(">> Loaded 0 npc_option locale strings. DB table `locales_npc_option` is empty.");
sLog.outString(">> Loaded 0 gossip_menu_option locale strings. DB table `locales_gossip_menu_option` is empty.");
return;
}
@ -367,13 +369,14 @@ void ObjectMgr::LoadNpcOptionLocales()
Field *fields = result->Fetch();
bar.step();
uint32 entry = fields[0].GetUInt32();
uint16 menuId = fields[0].GetUInt16();
uint16 id = fields[1].GetUInt16();
NpcOptionLocale& data = mNpcOptionLocaleMap[entry];
GossipMenuItemsLocale& data = mGossipMenuItemsLocaleMap[MAKE_PAIR32(menuId,id)];
for(int i = 1; i < MAX_LOCALE; ++i)
{
std::string str = fields[1+2*(i-1)].GetCppString();
std::string str = fields[2+2*(i-1)].GetCppString();
if(!str.empty())
{
int idx = GetOrNewIndexForLocale(LocaleConstant(i));
@ -385,7 +388,7 @@ void ObjectMgr::LoadNpcOptionLocales()
data.OptionText[idx] = str;
}
}
str = fields[1+2*(i-1)+1].GetCppString();
str = fields[2+2*(i-1)+1].GetCppString();
if(!str.empty())
{
int idx = GetOrNewIndexForLocale(LocaleConstant(i));
@ -403,7 +406,7 @@ void ObjectMgr::LoadNpcOptionLocales()
delete result;
sLog.outString();
sLog.outString( ">> Loaded %lu npc_option locale strings", (unsigned long)mNpcOptionLocaleMap.size() );
sLog.outString( ">> Loaded %lu gossip_menu_option locale strings", (unsigned long)mGossipMenuItemsLocaleMap.size() );
}
void ObjectMgr::LoadPointOfInterestLocales()
@ -4441,6 +4444,13 @@ void ObjectMgr::LoadEventScripts()
}
}
void ObjectMgr::LoadGossipScripts()
{
LoadScripts(sGossipScripts, "gossip_scripts");
// checks are done in LoadGossipMenuItems
}
void ObjectMgr::LoadItemTexts()
{
QueryResult *result = CharacterDatabase.Query("SELECT id, text FROM item_text");
@ -7296,6 +7306,15 @@ bool PlayerCondition::Meets(Player const * player) const
return !player->HasAura(value1, value2);
case CONDITION_ACTIVE_EVENT:
return sGameEventMgr.IsActiveEvent(value1);
case CONDITION_AREA_FLAG:
{
if (AreaTableEntry const *pAreaEntry = GetAreaEntryByAreaID(player->GetAreaId()))
{
if ((!value1 || (pAreaEntry->flags & value1)) && (!value2 || !(pAreaEntry->flags & value2)))
return true;
}
return false;
}
default:
return false;
}
@ -7440,6 +7459,15 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val
}
break;
}
case CONDITION_AREA_FLAG:
{
if (!value1 && !value2)
{
sLog.outErrorDb("Area flag condition has both values like 0, skipped");
return false;
}
break;
}
case CONDITION_NONE:
break;
}
@ -7887,23 +7915,21 @@ void ObjectMgr::LoadNpcTextId()
sLog.outString( ">> Loaded %d NpcTextId ", count );
}
void ObjectMgr::LoadNpcOptions()
void ObjectMgr::LoadGossipMenu()
{
m_mCacheNpcOptionList.clear(); // For reload case
m_mGossipMenusMap.clear();
QueryResult *result = WorldDatabase.Query(
// 0 1 2 3 4 5 6 7 8
"SELECT id,gossip_id,npcflag,icon,action,box_money,coded,option_text,box_text "
"FROM npc_option");
QueryResult* result = WorldDatabase.Query("SELECT entry, text_id, "
"cond_1, cond_1_val_1, cond_1_val_2, cond_2, cond_2_val_1, cond_2_val_2 FROM gossip_menu");
if( !result )
if (!result)
{
barGoLink bar( 1 );
barGoLink bar(1);
bar.step();
sLog.outString();
sLog.outErrorDb(">> Loaded `npc_option`, table is empty!");
sLog.outErrorDb(">> Loaded gossip_menu, table is empty!");
return;
}
@ -7917,26 +7943,186 @@ void ObjectMgr::LoadNpcOptions()
Field* fields = result->Fetch();
GossipOption go;
go.Id = fields[0].GetUInt32();
go.GossipId = fields[1].GetUInt32();
go.NpcFlag = fields[2].GetUInt32();
go.Icon = fields[3].GetUInt32();
go.Action = fields[4].GetUInt32();
go.BoxMoney = fields[5].GetUInt32();
go.Coded = fields[6].GetUInt8()!=0;
go.OptionText = fields[7].GetCppString();
go.BoxText = fields[8].GetCppString();
GossipMenus gMenu;
m_mCacheNpcOptionList.push_back(go);
gMenu.entry = fields[0].GetUInt32();
gMenu.text_id = fields[1].GetUInt32();
ConditionType cond_1 = (ConditionType)fields[2].GetUInt32();
uint32 cond_1_val_1 = fields[3].GetUInt32();
uint32 cond_1_val_2 = fields[4].GetUInt32();
ConditionType cond_2 = (ConditionType)fields[5].GetUInt32();
uint32 cond_2_val_1 = fields[6].GetUInt32();
uint32 cond_2_val_2 = fields[7].GetUInt32();
if (!GetGossipText(gMenu.text_id))
{
sLog.outErrorDb("Table gossip_menu entry %u are using non-existing text_id %u", gMenu.entry, gMenu.text_id);
continue;
}
if (!PlayerCondition::IsValid(cond_1, cond_1_val_1, cond_1_val_2))
{
sLog.outErrorDb("Table gossip_menu entry %u, invalid condition 1 for id %u", gMenu.entry, gMenu.text_id);
continue;
}
if (!PlayerCondition::IsValid(cond_2, cond_2_val_1, cond_2_val_2))
{
sLog.outErrorDb("Table gossip_menu entry %u, invalid condition 2 for id %u", gMenu.entry, gMenu.text_id);
continue;
}
gMenu.cond_1 = GetConditionId(cond_1, cond_1_val_1, cond_1_val_2);
gMenu.cond_2 = GetConditionId(cond_2, cond_2_val_1, cond_2_val_2);
m_mGossipMenusMap.insert(GossipMenusMap::value_type(gMenu.entry, gMenu));
++count;
}
while(result->NextRow());
} while (result->NextRow());
delete result;
sLog.outString();
sLog.outString( ">> Loaded %d npc_option entries", count );
sLog.outString( ">> Loaded %u gossip_menu entries", count);
}
void ObjectMgr::LoadGossipMenuItems()
{
m_mGossipMenuItemsMap.clear();
QueryResult *result = WorldDatabase.Query(
"SELECT menu_id, id, option_icon, option_text, option_id, npc_option_npcflag, "
"action_menu_id, action_poi_id, action_script_id, box_coded, box_money, box_text, "
"cond_1, cond_1_val_1, cond_1_val_2, "
"cond_2, cond_2_val_1, cond_2_val_2, "
"cond_3, cond_3_val_1, cond_3_val_2 "
"FROM gossip_menu_option");
if (!result)
{
barGoLink bar(1);
bar.step();
sLog.outString();
sLog.outErrorDb(">> Loaded gossip_menu_option, table is empty!");
return;
}
barGoLink bar(result->GetRowCount());
uint32 count = 0;
std::set<uint32> gossipScriptSet;
for(ScriptMapMap::const_iterator itr = sGossipScripts.begin(); itr != sGossipScripts.end(); ++itr)
gossipScriptSet.insert(itr->first);
do
{
bar.step();
Field* fields = result->Fetch();
GossipMenuItems gMenuItem;
gMenuItem.menu_id = fields[0].GetUInt32();
gMenuItem.id = fields[1].GetUInt32();
gMenuItem.option_icon = fields[2].GetUInt8();
gMenuItem.option_text = fields[3].GetCppString();
gMenuItem.option_id = fields[4].GetUInt32();
gMenuItem.npc_option_npcflag = fields[5].GetUInt32();
gMenuItem.action_menu_id = fields[6].GetUInt32();
gMenuItem.action_poi_id = fields[7].GetUInt32();
gMenuItem.action_script_id = fields[8].GetUInt32();
gMenuItem.box_coded = fields[9].GetUInt8() != 0;
gMenuItem.box_money = fields[10].GetUInt32();
gMenuItem.box_text = fields[11].GetCppString();
ConditionType cond_1 = (ConditionType)fields[12].GetUInt32();
uint32 cond_1_val_1 = fields[13].GetUInt32();
uint32 cond_1_val_2 = fields[14].GetUInt32();
ConditionType cond_2 = (ConditionType)fields[15].GetUInt32();
uint32 cond_2_val_1 = fields[16].GetUInt32();
uint32 cond_2_val_2 = fields[17].GetUInt32();
ConditionType cond_3 = (ConditionType)fields[18].GetUInt32();
uint32 cond_3_val_1 = fields[19].GetUInt32();
uint32 cond_3_val_2 = fields[20].GetUInt32();
if (!PlayerCondition::IsValid(cond_1, cond_1_val_1, cond_1_val_2))
{
sLog.outErrorDb("Table gossip_menu_option menu %u, invalid condition 1 for id %u", gMenuItem.menu_id, gMenuItem.id);
continue;
}
if (!PlayerCondition::IsValid(cond_2, cond_2_val_1, cond_2_val_2))
{
sLog.outErrorDb("Table gossip_menu_option menu %u, invalid condition 2 for id %u", gMenuItem.menu_id, gMenuItem.id);
continue;
}
if (!PlayerCondition::IsValid(cond_3, cond_3_val_1, cond_3_val_2))
{
sLog.outErrorDb("Table gossip_menu_option menu %u, invalid condition 3 for id %u", gMenuItem.menu_id, gMenuItem.id);
continue;
}
if (gMenuItem.option_icon >= GOSSIP_ICON_MAX)
{
sLog.outErrorDb("Table gossip_menu_option for menu %u, id %u has unknown icon id %u. Replacing with GOSSIP_ICON_CHAT", gMenuItem.menu_id, gMenuItem.id, gMenuItem.option_icon);
gMenuItem.option_icon = GOSSIP_ICON_CHAT;
}
if (gMenuItem.option_id == GOSSIP_OPTION_NONE)
sLog.outErrorDb("Table gossip_menu_option for menu %u, id %u use option id GOSSIP_OPTION_NONE. Option will never be used", gMenuItem.menu_id, gMenuItem.id);
if (gMenuItem.option_id >= GOSSIP_OPTION_MAX)
sLog.outErrorDb("Table gossip_menu_option for menu %u, id %u has unknown option id %u. Option will not be used", gMenuItem.menu_id, gMenuItem.id, gMenuItem.option_id);
if (gMenuItem.action_poi_id && !GetPointOfInterest(gMenuItem.action_poi_id))
{
sLog.outErrorDb("Table gossip_menu_option for menu %u, id %u use non-existing action_poi_id %u, ignoring", gMenuItem.menu_id, gMenuItem.id, gMenuItem.action_poi_id);
gMenuItem.action_poi_id = 0;
}
if (gMenuItem.action_script_id)
{
if (gMenuItem.option_id != GOSSIP_OPTION_GOSSIP)
{
sLog.outErrorDb("Table gossip_menu_option for menu %u, id %u have action_script_id %u but option_id is not GOSSIP_OPTION_GOSSIP, ignoring", gMenuItem.menu_id, gMenuItem.id, gMenuItem.action_script_id);
continue;
}
if (sGossipScripts.find(gMenuItem.action_script_id) == sGossipScripts.end())
{
sLog.outErrorDb("Table gossip_menu_option for menu %u, id %u have action_script_id %u that does not exist in `gossip_scripts`, ignoring", gMenuItem.menu_id, gMenuItem.id, gMenuItem.action_script_id);
continue;
}
gossipScriptSet.erase(gMenuItem.action_script_id);
}
gMenuItem.cond_1 = GetConditionId(cond_1, cond_1_val_1, cond_1_val_2);
gMenuItem.cond_2 = GetConditionId(cond_2, cond_2_val_1, cond_2_val_2);
gMenuItem.cond_3 = GetConditionId(cond_3, cond_3_val_1, cond_3_val_2);
m_mGossipMenuItemsMap.insert(GossipMenuItemsMap::value_type(gMenuItem.menu_id, gMenuItem));
++count;
}
while(result->NextRow());
delete result;
if (!gossipScriptSet.empty())
{
for(std::set<uint32>::const_iterator itr = gossipScriptSet.begin(); itr != gossipScriptSet.end(); ++itr)
sLog.outErrorDb("Table `gossip_scripts` contain unused script, id %u.", *itr);
}
sLog.outString();
sLog.outString(">> Loaded %u gossip_menu_option entries", count);
}
void ObjectMgr::AddVendorItem( uint32 entry,uint32 item, uint32 maxcount, uint32 incrtime, uint32 extendedcost )

View file

@ -93,6 +93,7 @@ extern ScriptMapMap sQuestStartScripts;
extern ScriptMapMap sSpellScripts;
extern ScriptMapMap sGameObjectScripts;
extern ScriptMapMap sEventScripts;
extern ScriptMapMap sGossipScripts;
struct SpellClickInfo
{
@ -162,7 +163,7 @@ typedef UNORDERED_MAP<uint32,QuestLocale> QuestLocaleMap;
typedef UNORDERED_MAP<uint32,NpcTextLocale> NpcTextLocaleMap;
typedef UNORDERED_MAP<uint32,PageTextLocale> PageTextLocaleMap;
typedef UNORDERED_MAP<int32,MangosStringLocale> MangosStringLocaleMap;
typedef UNORDERED_MAP<uint32,NpcOptionLocale> NpcOptionLocaleMap;
typedef UNORDERED_MAP<uint32,GossipMenuItemsLocale> GossipMenuItemsLocaleMap;
typedef UNORDERED_MAP<uint32,PointOfInterestLocale> PointOfInterestLocaleMap;
typedef std::multimap<uint32,uint32> QuestRelations;
@ -216,6 +217,38 @@ struct PointOfInterest
std::string icon_name;
};
struct GossipMenuItems
{
uint32 menu_id;
uint32 id;
uint8 option_icon;
std::string option_text;
uint32 option_id;
uint32 npc_option_npcflag;
uint32 action_menu_id;
uint32 action_poi_id;
uint32 action_script_id;
bool box_coded;
uint32 box_money;
std::string box_text;
uint16 cond_1;
uint16 cond_2;
uint16 cond_3;
};
struct GossipMenus
{
uint32 entry;
uint32 text_id;
uint16 cond_1;
uint16 cond_2;
};
typedef std::multimap<uint32,GossipMenus> GossipMenusMap;
typedef std::pair<GossipMenusMap::const_iterator, GossipMenusMap::const_iterator> GossipMenusMapBounds;
typedef std::multimap<uint32,GossipMenuItems> GossipMenuItemsMap;
typedef std::pair<GossipMenuItemsMap::const_iterator, GossipMenuItemsMap::const_iterator> GossipMenuItemsMapBounds;
struct QuestPOIPoint
{
int32 x;
@ -223,6 +256,16 @@ struct QuestPOIPoint
QuestPOIPoint() : x(0), y(0) {}
QuestPOIPoint(int32 _x, int32 _y) : x(_x), y(_y) {}
uint32 npc_option_npcflag;
uint32 action_menu_id;
uint32 action_poi_id;
uint32 action_script_id;
bool box_coded;
uint32 box_money;
std::string box_text;
uint16 cond_1;
uint16 cond_2;
uint16 cond_3;
};
struct QuestPOI
@ -277,9 +320,10 @@ enum ConditionType
CONDITION_AD_COMMISSION_AURA = 10, // 0 0, for condition true while one from AD commission aura active
CONDITION_NO_AURA = 11, // spell_id effindex
CONDITION_ACTIVE_EVENT = 12, // event_id
CONDITION_AREA_FLAG = 13 // area_flag area_flag_not
};
#define MAX_CONDITION 13 // maximum value in ConditionType enum
#define MAX_CONDITION 14 // maximum value in ConditionType enum
struct PlayerCondition
{
@ -301,7 +345,6 @@ struct PlayerCondition
// NPC gossip text id
typedef UNORDERED_MAP<uint32, uint32> CacheNpcTextIdMap;
typedef std::list<GossipOption> CacheNpcOptionList;
typedef UNORDERED_MAP<uint32, VendorItemData> CacheVendorItemMap;
typedef UNORDERED_MAP<uint32, TrainerSpellData> CacheTrainerSpellMap;
@ -539,6 +582,7 @@ class ObjectMgr
void LoadQuestStartScripts();
void LoadEventScripts();
void LoadSpellScripts();
void LoadGossipScripts();
bool LoadMangosStrings(DatabaseType& db, char const* table, int32 min_value, int32 max_value);
bool LoadMangosStrings() { return LoadMangosStrings(WorldDatabase,"mangos_string",MIN_MANGOS_STRING_ID,MAX_MANGOS_STRING_ID); }
@ -559,7 +603,7 @@ class ObjectMgr
void LoadQuestLocales();
void LoadNpcTextLocales();
void LoadPageTextLocales();
void LoadNpcOptionLocales();
void LoadGossipMenuItemsLocales();
void LoadPointOfInterestLocales();
void LoadInstanceTemplate();
void LoadMailLevelRewards();
@ -592,8 +636,11 @@ class ObjectMgr
void LoadWeatherZoneChances();
void LoadGameTele();
void LoadNpcOptions();
void LoadNpcTextId();
void LoadGossipMenu();
void LoadGossipMenuItems();
void LoadVendors();
void LoadTrainerSpell();
@ -704,10 +751,10 @@ class ObjectMgr
if(itr==mPageTextLocaleMap.end()) return NULL;
return &itr->second;
}
NpcOptionLocale const* GetNpcOptionLocale(uint32 entry) const
GossipMenuItemsLocale const* GetGossipMenuItemsLocale(uint32 entry) const
{
NpcOptionLocaleMap::const_iterator itr = mNpcOptionLocaleMap.find(entry);
if(itr==mNpcOptionLocaleMap.end()) return NULL;
GossipMenuItemsLocaleMap::const_iterator itr = mGossipMenuItemsLocaleMap.find(entry);
if(itr==mGossipMenuItemsLocaleMap.end()) return NULL;
return &itr->second;
}
PointOfInterestLocale const* GetPointOfInterestLocale(uint32 poi_id) const
@ -786,8 +833,6 @@ class ObjectMgr
bool AddGameTele(GameTele& data);
bool DeleteGameTele(const std::string& name);
CacheNpcOptionList const& GetNpcOptions() const { return m_mCacheNpcOptionList; }
uint32 GetNpcGossip(uint32 entry) const
{
CacheNpcTextIdMap::const_iterator iter = m_mCacheNpcTextIdMap.find(entry);
@ -835,6 +880,16 @@ class ObjectMgr
return ItemRequiredTargetMapBounds(m_ItemRequiredTarget.lower_bound(uiItemEntry),m_ItemRequiredTarget.upper_bound(uiItemEntry));
}
GossipMenusMapBounds GetGossipMenusMapBounds(uint32 uiMenuId) const
{
return GossipMenusMapBounds(m_mGossipMenusMap.lower_bound(uiMenuId),m_mGossipMenusMap.upper_bound(uiMenuId));
}
GossipMenuItemsMapBounds GetGossipMenuItemsMapBounds(uint32 uiMenuId) const
{
return GossipMenuItemsMapBounds(m_mGossipMenuItemsMap.lower_bound(uiMenuId),m_mGossipMenuItemsMap.upper_bound(uiMenuId));
}
protected:
// first free id for selected id type
@ -876,7 +931,9 @@ class ObjectMgr
RepOnKillMap mRepOnKill;
PointOfInterestMap mPointsOfInterest;
GossipMenusMap m_mGossipMenusMap;
GossipMenuItemsMap m_mGossipMenuItemsMap;
PointOfInterestMap mPointsOfInterest;
QuestPOIMap mQuestPOIMap;
@ -942,7 +999,7 @@ class ObjectMgr
NpcTextLocaleMap mNpcTextLocaleMap;
PageTextLocaleMap mPageTextLocaleMap;
MangosStringLocaleMap mMangosStringLocaleMap;
NpcOptionLocaleMap mNpcOptionLocaleMap;
GossipMenuItemsLocaleMap mGossipMenuItemsLocaleMap;
PointOfInterestLocaleMap mPointOfInterestLocaleMap;
RespawnTimes mCreatureRespawnTimes;
RespawnTimes mGORespawnTimes;
@ -951,7 +1008,6 @@ class ObjectMgr
typedef std::vector<PlayerCondition> ConditionStore;
ConditionStore mConditions;
CacheNpcOptionList m_mCacheNpcOptionList;
CacheNpcTextIdMap m_mCacheNpcTextIdMap;
CacheVendorItemMap m_mCacheVendorItemMap;
CacheTrainerSpellMap m_mCacheTrainerSpellMap;

View file

@ -2117,14 +2117,14 @@ Creature* Player::GetNPCIfCanInteractWith(uint64 guid, uint32 npcflagmask)
return unit;
}
GameObject* Player::GetGameObjectIfCanInteractWith(uint64 guid, GameobjectTypes type) const
GameObject* Player::GetGameObjectIfCanInteractWith(uint64 guid, uint32 gameobject_type) const
{
if(GameObject *go = GetMap()->GetGameObject(guid))
if (GameObject *go = GetMap()->GetGameObject(guid))
{
if(go->GetGoType() == type)
if (uint32(go->GetGoType()) == gameobject_type || gameobject_type == MAX_GAMEOBJECT_TYPE)
{
float maxdist;
switch(type)
switch(go->GetGoType())
{
// TODO: find out how the client calculates the maximal usage distance to spellless working
// gameobjects like guildbanks and mailboxes - 10.0 is a just an abitrary choosen number
@ -2140,10 +2140,10 @@ GameObject* Player::GetGameObjectIfCanInteractWith(uint64 guid, GameobjectTypes
break;
}
if (go->IsWithinDistInMap(this, maxdist))
if (go->IsWithinDistInMap(this, maxdist) && go->isSpawned())
return go;
sLog.outError("IsGameObjectOfTypeInRange: GameObject '%s' [GUID: %u] is too far away from player %s [GUID: %u] to be used by him (distance=%f, maximal 10 is allowed)", go->GetGOInfo()->name,
sLog.outError("GetGameObjectIfCanInteractWith: GameObject '%s' [GUID: %u] is too far away from player %s [GUID: %u] to be used by him (distance=%f, maximal 10 is allowed)", go->GetGOInfo()->name,
go->GetGUIDLow(), GetName(), GetGUIDLow(), go->GetDistance(this));
}
}
@ -12178,126 +12178,151 @@ void Player::SendNewItem(Item *item, uint32 count, bool received, bool created,
/*** GOSSIP SYSTEM ***/
/*********************************************************/
void Player::PrepareGossipMenu(WorldObject *pSource, uint32 gossipid)
void Player::PrepareGossipMenu(WorldObject *pSource, uint32 menuId)
{
PlayerMenu* pMenu = PlayerTalkClass;
pMenu->ClearMenus();
if (pSource->GetTypeId() != TYPEID_UNIT)
return;
pMenu->GetGossipMenu().SetMenuId(menuId);
Creature *pCreature = (Creature*)pSource;
GossipMenuItemsMapBounds pMenuItemBounds = sObjectMgr.GetGossipMenuItemsMapBounds(menuId);
// lazy loading single time at use
pCreature->LoadGossipOptions();
// if default menuId and no menu options exist for this, use options from default options
if (pMenuItemBounds.first == pMenuItemBounds.second && menuId == GetDefaultGossipMenuForSource(pSource))
pMenuItemBounds = sObjectMgr.GetGossipMenuItemsMapBounds(0);
GossipOptionList &iOptList = pCreature->GetGossipOptionList();
for(GossipOptionList::iterator i = iOptList.begin( ); i != iOptList.end( ); ++i)
for(GossipMenuItemsMap::const_iterator itr = pMenuItemBounds.first; itr != pMenuItemBounds.second; ++itr)
{
GossipOption* gso = &*i;
bool bCanTalk = true;
if (gso->GossipId == gossipid)
if (itr->second.cond_1 && !sObjectMgr.IsPlayerMeetToCondition(this, itr->second.cond_1))
continue;
if (itr->second.cond_2 && !sObjectMgr.IsPlayerMeetToCondition(this, itr->second.cond_2))
continue;
if (itr->second.cond_3 && !sObjectMgr.IsPlayerMeetToCondition(this, itr->second.cond_3))
continue;
if (pSource->GetTypeId() == TYPEID_UNIT)
{
bool cantalking = true;
Creature *pCreature = (Creature*)pSource;
if (gso->Id == 1)
uint32 npcflags = pCreature->GetUInt32Value(UNIT_NPC_FLAGS);
if (!(itr->second.npc_option_npcflag & npcflags))
continue;
switch(itr->second.option_id)
{
uint32 textid = GetGossipTextId(pSource);
GossipText const* gossiptext = sObjectMgr.GetGossipText(textid);
if (!gossiptext)
cantalking = false;
}
else
{
switch(gso->Action)
case GOSSIP_OPTION_QUESTGIVER:
PrepareQuestMenu(pSource->GetGUID());
bCanTalk = false;
break;
case GOSSIP_OPTION_ARMORER:
bCanTalk = false; // added in special mode
break;
case GOSSIP_OPTION_SPIRITHEALER:
if (!isDead())
bCanTalk = false;
break;
case GOSSIP_OPTION_VENDOR:
{
case GOSSIP_OPTION_QUESTGIVER:
VendorItemData const* vItems = pCreature->GetVendorItems();
if (!vItems || vItems->Empty())
{
sLog.outErrorDb("Creature %u (Entry: %u) have UNIT_NPC_FLAG_VENDOR but have empty trading item list.", pCreature->GetGUIDLow(), pCreature->GetEntry());
bCanTalk = false;
}
break;
}
case GOSSIP_OPTION_TRAINER:
if (!pCreature->isCanTrainingOf(this, false))
bCanTalk = false;
break;
case GOSSIP_OPTION_UNLEARNTALENTS:
if (!pCreature->isCanTrainingAndResetTalentsOf(this))
bCanTalk = false;
break;
case GOSSIP_OPTION_UNLEARNPETSKILLS:
if (!GetPet() || GetPet()->getPetType() != HUNTER_PET || GetPet()->m_spells.size() <= 1 || pCreature->GetCreatureInfo()->trainer_type != TRAINER_TYPE_PETS || pCreature->GetCreatureInfo()->trainer_class != CLASS_HUNTER)
bCanTalk = false;
break;
case GOSSIP_OPTION_TAXIVENDOR:
if (GetSession()->SendLearnNewTaxiNode(pCreature))
return;
break;
case GOSSIP_OPTION_BATTLEFIELD:
if (!pCreature->isCanInteractWithBattleMaster(this, false))
bCanTalk = false;
break;
case GOSSIP_OPTION_STABLEPET:
if (getClass() != CLASS_HUNTER)
bCanTalk = false;
break;
case GOSSIP_OPTION_GOSSIP:
case GOSSIP_OPTION_SPIRITGUIDE:
case GOSSIP_OPTION_INNKEEPER:
case GOSSIP_OPTION_BANKER:
case GOSSIP_OPTION_PETITIONER:
case GOSSIP_OPTION_TABARDDESIGNER:
case GOSSIP_OPTION_AUCTIONEER:
break; // no checks
default:
sLog.outErrorDb("Creature entry %u have unknown gossip option %u for menu %u", pCreature->GetEntry(), itr->second.option_id, itr->second.menu_id);
bCanTalk = false;
break;
}
}
else if (pSource->GetTypeId() == TYPEID_GAMEOBJECT)
{
GameObject *pGo = (GameObject*)pSource;
switch(itr->second.option_id)
{
case GOSSIP_OPTION_QUESTGIVER:
if (pGo->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER)
PrepareQuestMenu(pSource->GetGUID());
//if (pm->GetQuestMenu()->MenuItemCount() == 0)
cantalking = false;
//pm->GetQuestMenu()->ClearMenu();
break;
case GOSSIP_OPTION_ARMORER:
cantalking = false; // added in special mode
break;
case GOSSIP_OPTION_SPIRITHEALER:
if (!isDead())
cantalking = false;
break;
case GOSSIP_OPTION_VENDOR:
{
VendorItemData const* vItems = pCreature->GetVendorItems();
if (!vItems || vItems->Empty())
{
sLog.outErrorDb("Creature %u (Entry: %u) have UNIT_NPC_FLAG_VENDOR but have empty trading item list.",
pCreature->GetGUIDLow(), pCreature->GetEntry());
cantalking = false;
}
break;
}
case GOSSIP_OPTION_TRAINER:
if (!pCreature->isCanTrainingOf(this, false))
cantalking = false;
break;
case GOSSIP_OPTION_UNLEARNTALENTS:
if (!pCreature->isCanTrainingAndResetTalentsOf(this))
cantalking = false;
break;
case GOSSIP_OPTION_UNLEARNPETSKILLS:
if(!GetPet() || GetPet()->getPetType() != HUNTER_PET || GetPet()->m_spells.size() <= 1 || pCreature->GetCreatureInfo()->trainer_type != TRAINER_TYPE_PETS || pCreature->GetCreatureInfo()->trainer_class != CLASS_HUNTER)
cantalking = false;
break;
case GOSSIP_OPTION_TAXIVENDOR:
if (GetSession()->SendLearnNewTaxiNode(pCreature))
return;
break;
case GOSSIP_OPTION_BATTLEFIELD:
if (!pCreature->isCanInteractWithBattleMaster(this, false))
cantalking = false;
break;
case GOSSIP_OPTION_SPIRITGUIDE:
case GOSSIP_OPTION_INNKEEPER:
case GOSSIP_OPTION_BANKER:
case GOSSIP_OPTION_PETITIONER:
case GOSSIP_OPTION_STABLEPET:
case GOSSIP_OPTION_TABARDDESIGNER:
case GOSSIP_OPTION_AUCTIONEER:
break; // no checks
default:
sLog.outErrorDb("Creature %u (entry: %u) have unknown gossip option %u", pCreature->GetDBTableGUIDLow(), pCreature->GetEntry(), gso->Action);
break;
}
bCanTalk = false;
break;
case GOSSIP_OPTION_GOSSIP:
if (pGo->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER && pGo->GetGoType() != GAMEOBJECT_TYPE_GOOBER)
bCanTalk = false;
break;
default:
bCanTalk = false;
break;
}
}
//note for future dev: should have database fields for BoxMessage & BoxMoney
if (!gso->OptionText.empty() && cantalking)
if (bCanTalk)
{
std::string strOptionText = itr->second.option_text;
std::string strBoxText = itr->second.box_text;
int loc_idx = GetSession()->GetSessionDbLocaleIndex();
if (loc_idx >= 0)
{
std::string OptionText = gso->OptionText;
std::string BoxText = gso->BoxText;
int loc_idx = GetSession()->GetSessionDbLocaleIndex();
uint32 idxEntry = MAKE_PAIR32(menuId, itr->second.id);
if (loc_idx >= 0)
if (GossipMenuItemsLocale const *no = sObjectMgr.GetGossipMenuItemsLocale(idxEntry))
{
if (NpcOptionLocale const *no = sObjectMgr.GetNpcOptionLocale(gso->Id))
{
if (no->OptionText.size() > (size_t)loc_idx && !no->OptionText[loc_idx].empty())
OptionText = no->OptionText[loc_idx];
if (no->OptionText.size() > (size_t)loc_idx && !no->OptionText[loc_idx].empty())
strOptionText = no->OptionText[loc_idx];
if (no->BoxText.size() > (size_t)loc_idx && !no->BoxText[loc_idx].empty())
BoxText = no->BoxText[loc_idx];
}
if (no->BoxText.size() > (size_t)loc_idx && !no->BoxText[loc_idx].empty())
strBoxText = no->BoxText[loc_idx];
}
pMenu->GetGossipMenu().AddMenuItem((uint8)gso->Icon,OptionText, gossipid,gso->Action,BoxText,gso->BoxMoney,gso->Coded);
}
pMenu->GetGossipMenu().AddMenuItem(itr->second.option_icon, strOptionText, 0, itr->second.option_id, strBoxText, itr->second.box_money, itr->second.box_coded);
pMenu->GetGossipMenu().AddGossipMenuItemData(itr->second.action_menu_id, itr->second.action_poi_id, itr->second.action_script_id);
}
}
///some gossips aren't handled in normal way ... so we need to do it this way .. TODO: handle it in normal way ;-)
if (pMenu->Empty())
// some gossips aren't handled in normal way ... so we need to do it this way .. TODO: handle it in normal way ;-)
/*if (pMenu->Empty())
{
if (pCreature->HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_TRAINER))
{
@ -12310,59 +12335,90 @@ void Player::PrepareGossipMenu(WorldObject *pSource, uint32 gossipid)
// output error message if need
pCreature->isCanInteractWithBattleMaster(this, true);
}
}
}*/
}
void Player::SendPreparedGossip(WorldObject *pSource)
{
if (!pSource || pSource->GetTypeId() != TYPEID_UNIT)
if (!pSource)
return;
// in case no gossip flag and quest menu not empty, open quest menu (client expect gossip menu with this flag)
if (!((Creature*)pSource)->HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_GOSSIP) && !PlayerTalkClass->GetQuestMenu().Empty())
if (pSource->GetTypeId() == TYPEID_UNIT)
{
SendPreparedQuest(pSource->GetGUID());
return;
// in case no gossip flag and quest menu not empty, open quest menu (client expect gossip menu with this flag)
if (!((Creature*)pSource)->HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_GOSSIP) && !PlayerTalkClass->GetQuestMenu().Empty())
{
SendPreparedQuest(pSource->GetGUID());
return;
}
}
else if (pSource->GetTypeId() == TYPEID_GAMEOBJECT)
{
// probably need to find a better way here
if (!PlayerTalkClass->GetGossipMenu().GetMenuId() && !PlayerTalkClass->GetQuestMenu().Empty())
{
SendPreparedQuest(pSource->GetGUID());
return;
}
}
// in case non empty gossip menu (that not included quests list size) show it
// (quest entries from quest menu will be included in list)
PlayerTalkClass->SendGossipMenu(GetGossipTextId(pSource), pSource->GetGUID());
uint32 textId = GetGossipTextId(pSource);
if (uint32 menuId = PlayerTalkClass->GetGossipMenu().GetMenuId())
textId = GetGossipTextId(menuId);
PlayerTalkClass->SendGossipMenu(textId, pSource->GetGUID());
}
void Player::OnGossipSelect(WorldObject* pSource, uint32 option)
void Player::OnGossipSelect(WorldObject* pSource, uint32 gossipListId, uint32 menuId)
{
GossipMenu& gossipmenu = PlayerTalkClass->GetGossipMenu();
if (option >= gossipmenu.MenuItemCount())
if (gossipListId >= gossipmenu.MenuItemCount())
return;
uint32 action = gossipmenu.GetItem(option).m_gAction;
uint32 zoneid = GetZoneId();
// if not same, then something funky is going on
if (menuId != gossipmenu.GetMenuId())
return;
uint32 gossipOptionId = gossipmenu.GetItem(gossipListId).m_gOptionId;
uint64 guid = pSource->GetGUID();
GossipOption const *gossip = GetGossipOption(pSource, action);
if (!gossip)
if (pSource->GetTypeId() == TYPEID_GAMEOBJECT)
{
zoneid = 0;
gossip = GetGossipOption(pSource, action);
if (!gossip)
if (gossipOptionId > GOSSIP_OPTION_QUESTGIVER)
{
sLog.outError("Player guid %u request invalid gossip option for GameObject entry %u", GetGUIDLow(), pSource->GetEntry());
return;
}
}
switch(gossip->Action)
GossipMenuItemData pMenuData = gossipmenu.GetItemData(gossipListId);
switch(gossipOptionId)
{
case GOSSIP_OPTION_GOSSIP:
{
uint32 textid = GetGossipTextId(action, zoneid);
if (pMenuData.m_gAction_menu)
{
PrepareGossipMenu(pSource, pMenuData.m_gAction_menu);
SendPreparedGossip(pSource);
}
if (textid == 0)
textid = GetGossipTextId(pSource);
if (pMenuData.m_gAction_poi)
PlayerTalkClass->SendPointOfInterest(pMenuData.m_gAction_poi);
if (pMenuData.m_gAction_script)
{
if (pSource->GetTypeId() == TYPEID_UNIT)
GetMap()->ScriptsStart(sGossipScripts, pMenuData.m_gAction_script, this, pSource);
else if (pSource->GetTypeId() == TYPEID_GAMEOBJECT)
GetMap()->ScriptsStart(sGossipScripts, pMenuData.m_gAction_script, pSource, this);
}
PlayerTalkClass->CloseGossip();
PlayerTalkClass->SendTalking(textid);
break;
}
case GOSSIP_OPTION_SPIRITHEALER:
@ -12413,9 +12469,7 @@ void Player::OnGossipSelect(WorldObject* pSource, uint32 option)
GetSession()->SendAuctionHello(guid, ((Creature*)pSource));
break;
case GOSSIP_OPTION_SPIRITGUIDE:
case GOSSIP_GUARD_SPELLTRAINER:
case GOSSIP_GUARD_SKILLTRAINER:
PrepareGossipMenu(pSource, gossip->Id);
PrepareGossipMenu(pSource);
SendPreparedGossip(pSource);
break;
case GOSSIP_OPTION_BATTLEFIELD:
@ -12431,59 +12485,13 @@ void Player::OnGossipSelect(WorldObject* pSource, uint32 option)
GetSession()->SendBattlegGroundList(guid, bgTypeId);
break;
}
default:
OnPoiSelect(pSource, gossip);
break;
}
}
void Player::OnPoiSelect(WorldObject *pSource, GossipOption const *gossip)
{
if(gossip->GossipId==GOSSIP_GUARD_SPELLTRAINER || gossip->GossipId==GOSSIP_GUARD_SKILLTRAINER)
{
Poi_Icon icon = ICON_POI_BLANK;
//need add more case.
switch(gossip->Action)
{
case GOSSIP_GUARD_BANK:
icon=ICON_POI_SMALL_HOUSE;
break;
case GOSSIP_GUARD_RIDE:
icon=ICON_POI_RWHORSE;
break;
case GOSSIP_GUARD_GUILD:
icon=ICON_POI_BLUETOWER;
break;
default:
icon=ICON_POI_GREYTOWER;
break;
}
uint32 textid = GetGossipTextId(gossip->Action, GetZoneId());
PlayerTalkClass->SendTalking(textid);
// std::string areaname= gossip->OptionText;
// how this could worked player->PlayerTalkClass->SendPointOfInterest( x, y, icon, 2, 15, areaname.c_str() );
}
}
uint32 Player::GetGossipTextId(uint32 action, uint32 zoneid)
{
QueryResult *result= WorldDatabase.PQuery("SELECT textid FROM npc_gossip_textid WHERE action = '%u' AND zoneid ='%u'", action, zoneid );
if (!result)
return 0;
Field *fields = result->Fetch();
uint32 id = fields[0].GetUInt32();
delete result;
return id;
}
uint32 Player::GetGossipTextId(WorldObject *pSource)
{
if (!pSource || pSource->GetTypeId() != TYPEID_UNIT || !((Creature*)pSource)->GetDBTableGUIDLow())
return DEFAULT_GOSSIP_MESSAGE;
return 0;
if (uint32 pos = sObjectMgr.GetNpcGossip(((Creature*)pSource)->GetDBTableGUIDLow()))
return pos;
@ -12491,19 +12499,32 @@ uint32 Player::GetGossipTextId(WorldObject *pSource)
return DEFAULT_GOSSIP_MESSAGE;
}
GossipOption const* Player::GetGossipOption(WorldObject *pSource, uint32 id) const
uint32 Player::GetGossipTextId(uint32 menuId)
{
uint32 textId = DEFAULT_GOSSIP_MESSAGE;
if (!menuId)
return textId;
GossipMenusMapBounds pMenuBounds = sObjectMgr.GetGossipMenusMapBounds(menuId);
for(GossipMenusMap::const_iterator itr = pMenuBounds.first; itr != pMenuBounds.second; ++itr)
{
if (sObjectMgr.IsPlayerMeetToCondition(this, itr->second.cond_1) && sObjectMgr.IsPlayerMeetToCondition(this, itr->second.cond_2))
textId = itr->second.text_id;
}
return textId;
}
uint32 Player::GetDefaultGossipMenuForSource(WorldObject *pSource)
{
if (pSource->GetTypeId() == TYPEID_UNIT)
{
GossipOptionList &iOptlist = ((Creature*)pSource)->GetGossipOptionList();
return ((Creature*)pSource)->GetCreatureInfo()->GossipMenuId;
else if (pSource->GetTypeId() == TYPEID_GAMEOBJECT)
return((GameObject*)pSource)->GetGOInfo()->GetGossipMenuId();
for(GossipOptionList::const_iterator i = iOptlist.begin( ); i != iOptlist.end( ); ++i)
{
if (i->Action == id)
return &*i;
}
}
return NULL;
return 0;
}
/*********************************************************/
@ -19119,7 +19140,7 @@ BGQueueIdBasedOnLevel Player::GetBattleGroundQueueIdFromLevel() const
uint32 queue_id = ( getLevel() / 10) - 1;
if( queue_id >= MAX_BATTLEGROUND_QUEUES )
{
sLog.outError("BattleGround: too high queue_id %u this shouldn't happen", queue_id);
sLog.outError("BattleGround: too high queue_id %u for player %u (acc: %u) with level %u", queue_id, GetGUIDLow(), GetSession()->GetAccountId(), getLevel());
return QUEUE_ID_MAX_LEVEL_80;
}
return BGQueueIdBasedOnLevel(queue_id);
@ -21229,3 +21250,35 @@ void Player::SendDuelCountdown(uint32 counter)
data << uint32(counter); // seconds
GetSession()->SendPacket(&data);
}
bool Player::IsImmunedToSpellEffect(SpellEntry const* spellInfo, uint32 index) const
{
switch(spellInfo->Effect[index])
{
case SPELL_EFFECT_ATTACK_ME:
return true;
default:
break;
}
switch(spellInfo->EffectApplyAuraName[index])
{
case SPELL_AURA_MOD_TAUNT:
return true;
default:
break;
}
return Unit::IsImmunedToSpellEffect(spellInfo, index);
}
void Player::SetHomebindToCurrentPos()
{
m_homebindMapId = GetMapId();
m_homebindZoneId = GetZoneId();
m_homebindX = GetPositionX();
m_homebindY = GetPositionY();
m_homebindZ = GetPositionZ();
// 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'",
m_homebindMapId, m_homebindZoneId, m_homebindX, m_homebindY, m_homebindZ, GetGUIDLow());
}

View file

@ -1067,7 +1067,7 @@ class MANGOS_DLL_SPEC Player : public Unit
void SendInstanceResetWarning(uint32 mapid, Difficulty difficulty, uint32 time);
Creature* GetNPCIfCanInteractWith(uint64 guid, uint32 npcflagmask);
GameObject* GetGameObjectIfCanInteractWith(uint64 guid, GameobjectTypes type) const;
GameObject* GetGameObjectIfCanInteractWith(uint64 guid, uint32 gameobject_type = MAX_GAMEOBJECT_TYPE) const;
void UpdateVisibilityForPlayer();
@ -1300,14 +1300,13 @@ class MANGOS_DLL_SPEC Player : public Unit
/*** GOSSIP SYSTEM ***/
/*********************************************************/
void PrepareGossipMenu(WorldObject *pSource, uint32 gossipid = 0);
void PrepareGossipMenu(WorldObject *pSource, uint32 menuId = 0);
void SendPreparedGossip(WorldObject *pSource);
void OnGossipSelect(WorldObject *pSource, uint32 option);
void OnPoiSelect(WorldObject *pSource, GossipOption const *gossip);
void OnGossipSelect(WorldObject *pSource, uint32 gossipListId, uint32 menuId);
uint32 GetGossipTextId(uint32 action, uint32 zoneid);
uint32 GetGossipTextId(uint32 menuId);
uint32 GetGossipTextId(WorldObject *pSource);
GossipOption const* GetGossipOption(WorldObject *pSource, uint32 id) const;
uint32 GetDefaultGossipMenuForSource(WorldObject *pSource);
/*********************************************************/
/*** QUEST SYSTEM ***/
@ -1556,6 +1555,7 @@ class MANGOS_DLL_SPEC Player : public Unit
TrainerSpellState GetTrainerSpellState(TrainerSpell const* trainer_spell) const;
bool IsSpellFitByClassAndRace( uint32 spell_id ) const;
bool IsNeedCastPassiveSpellAtLearn(SpellEntry const* spellInfo) const;
bool IsImmunedToSpellEffect(SpellEntry const* spellInfo, uint32 index) const;
void SendProficiency(uint8 pr1, uint32 pr2);
void SendInitialSpells();
@ -2172,13 +2172,9 @@ class MANGOS_DLL_SPEC Player : public Unit
float m_recallO;
void SaveRecallPosition();
// Homebind coordinates
uint32 m_homebindMapId;
uint16 m_homebindZoneId;
float m_homebindX;
float m_homebindY;
float m_homebindZ;
void SetHomebindToCurrentPos();
void RelocateToHomebind() { SetLocationMapId(m_homebindMapId); Relocate(m_homebindX,m_homebindY,m_homebindZ); }
bool TeleportToHomebind(uint32 options = 0) { return TeleportTo(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, GetOrientation(),options); }
// currently visible objects at player client
typedef std::set<uint64> ClientGUIDs;
@ -2537,6 +2533,13 @@ class MANGOS_DLL_SPEC Player : public Unit
GridReference<Player> m_gridRef;
MapReference m_mapRef;
// Homebind coordinates
uint32 m_homebindMapId;
uint16 m_homebindZoneId;
float m_homebindX;
float m_homebindY;
float m_homebindZ;
uint32 m_lastFallTime;
float m_lastFallZ;

View file

@ -102,7 +102,7 @@ void WorldSession::HandleQuestgiverHelloOpcode(WorldPacket & recv_data)
if (Script->GossipHello(_player, pCreature))
return;
_player->PrepareGossipMenu(pCreature);
_player->PrepareGossipMenu(pCreature, pCreature->GetCreatureInfo()->GossipMenuId);
_player->SendPreparedGossip(pCreature);
}

View file

@ -2442,38 +2442,6 @@ enum DiminishingGroup
DIMINISHING_LIMITONLY
};
enum SummonType
{
SUMMON_TYPE_CRITTER = 41,
SUMMON_TYPE_GUARDIAN = 61,
SUMMON_TYPE_TOTEM_SLOT1 = 63,
SUMMON_TYPE_WILD = 64,
SUMMON_TYPE_POSESSED = 65,
SUMMON_TYPE_DEMON = 66,
SUMMON_TYPE_SUMMON = 67,
SUMMON_TYPE_TOTEM_SLOT2 = 81,
SUMMON_TYPE_TOTEM_SLOT3 = 82,
SUMMON_TYPE_TOTEM_SLOT4 = 83,
SUMMON_TYPE_TOTEM = 121,
SUMMON_TYPE_UNKNOWN3 = 181,
SUMMON_TYPE_UNKNOWN4 = 187,
SUMMON_TYPE_UNKNOWN1 = 247,
SUMMON_TYPE_CRITTER2 = 407,
SUMMON_TYPE_CRITTER3 = 307,
SUMMON_TYPE_UNKNOWN5 = 409,
SUMMON_TYPE_UNKNOWN2 = 427,
SUMMON_TYPE_POSESSED2 = 428,
SUMMON_TYPE_QUEST_CRITTER = 487,
SUMMON_TYPE_QUEST_WILD = 587,
SUMMON_TYPE_INFERNO = 711,
SUMMON_TYPE_GUARDIAN2 = 713,
SUMMON_TYPE_LIGHTWELL = 1141,
SUMMON_TYPE_GUARDIAN3 = 1161,
SUMMON_TYPE_CREATURE = 1302,
SUMMON_TYPE_ELEMENTAL = 1561,
SUMMON_TYPE_FORCE_OF_NATURE = 1562
};
enum ResponseCodes
{
RESPONSE_SUCCESS = 0x00,

View file

@ -691,8 +691,8 @@ void Spell::prepareDataForTriggerSystem()
if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000800000000060))
m_canTrigger = true;
break;
case SPELLFAMILY_PRIEST: // For Penance heal/damage triggers need do it
if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0001800000000000))
case SPELLFAMILY_PRIEST: // For Penance,Mind Sear,Mind Flay heal/damage triggers need do it
if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0001800000800000) || (m_spellInfo->SpellFamilyFlags2 & 0x00000040))
m_canTrigger = true;
break;
case SPELLFAMILY_ROGUE: // For poisons need do it
@ -1685,8 +1685,16 @@ void Spell::SetTargetMap(uint32 effIndex,uint32 targetMode,UnitList& TagUnitMap)
}
break;
case TARGET_ALL_FRIENDLY_UNITS_IN_AREA:
// Death Pact (in fact selection by player selection)
if (m_spellInfo->Id == 48743)
{
// checked in Spell::CheckCast
if (m_caster->GetTypeId()==TYPEID_PLAYER)
if (Unit* target = m_caster->GetMap()->GetPet(((Player*)m_caster)->GetSelection()))
TagUnitMap.push_back(target);
}
// Wild Growth
if (m_spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && m_spellInfo->SpellIconID == 2864)
else if (m_spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && m_spellInfo->SpellIconID == 2864)
{
Unit* target = m_targets.getUnitTarget();
if(!target)
@ -2217,13 +2225,6 @@ void Spell::SetTargetMap(uint32 effIndex,uint32 targetMode,UnitList& TagUnitMap)
}
break;
case SPELL_EFFECT_SUMMON:
if (m_spellInfo->EffectMiscValueB[effIndex] == SUMMON_TYPE_POSESSED ||
m_spellInfo->EffectMiscValueB[effIndex] == SUMMON_TYPE_POSESSED2)
{
if (m_targets.getUnitTarget())
TagUnitMap.push_back(m_targets.getUnitTarget());
}
else
TagUnitMap.push_back(m_caster);
break;
case SPELL_EFFECT_SUMMON_CHANGE_ITEM:
@ -4299,6 +4300,34 @@ SpellCastResult Spell::CheckCast(bool strict)
// for effects of spells that have only one target
switch(m_spellInfo->Effect[i])
{
case SPELL_EFFECT_INSTAKILL:
// Death Pact
if(m_spellInfo->Id == 48743)
{
if (m_caster->GetTypeId() != TYPEID_PLAYER)
return SPELL_FAILED_ERROR;
if (!((Player*)m_caster)->GetSelection())
return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
Pet* target = m_caster->GetMap()->GetPet(((Player*)m_caster)->GetSelection());
// alive
if (!target || target->isDead())
return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
// undead
if (target->GetCreatureType() != CREATURE_TYPE_UNDEAD)
return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
// owned
if (target->GetOwnerGUID() != m_caster->GetGUID())
return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
float dist = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
if (!target->IsWithinDistInMap(m_caster,dist))
return SPELL_FAILED_OUT_OF_RANGE;
// will set in target selection code
}
break;
case SPELL_EFFECT_DUMMY:
{
if(m_spellInfo->SpellIconID == 1648) // Execute
@ -4567,24 +4596,17 @@ SpellCastResult Spell::CheckCast(bool strict)
// This is generic summon effect
case SPELL_EFFECT_SUMMON:
{
switch(m_spellInfo->EffectMiscValueB[i])
if(SummonPropertiesEntry const *summon_prop = sSummonPropertiesStore.LookupEntry(m_spellInfo->EffectMiscValueB[i]))
{
case SUMMON_TYPE_POSESSED:
case SUMMON_TYPE_POSESSED2:
case SUMMON_TYPE_DEMON:
case SUMMON_TYPE_SUMMON:
case SUMMON_TYPE_ELEMENTAL:
case SUMMON_TYPE_INFERNO:
if(summon_prop->Group == SUMMON_PROP_GROUP_PETS)
{
if(m_caster->GetPetGUID())
return SPELL_FAILED_ALREADY_HAVE_SUMMON;
if(m_caster->GetCharmGUID())
return SPELL_FAILED_ALREADY_HAVE_CHARM;
break;
}
}
break;
}
// Not used for summon?
case SPELL_EFFECT_SUMMON_PHANTASM:
@ -4950,6 +4972,18 @@ SpellCastResult Spell::CheckCasterAuras() const
prevented_reason = SPELL_FAILED_SILENCED;
else if (unitflag & UNIT_FLAG_PACIFIED && m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_PACIFY)
prevented_reason = SPELL_FAILED_PACIFIED;
else if(m_caster->HasAuraType(SPELL_AURA_ALLOW_ONLY_ABILITY))
{
Unit::AuraList const& casingLimit = m_caster->GetAurasByType(SPELL_AURA_ALLOW_ONLY_ABILITY);
for(Unit::AuraList::const_iterator itr = casingLimit.begin(); itr != casingLimit.end(); ++itr)
{
if(!IsAffectedByAura(*itr))
{
prevented_reason = SPELL_FAILED_CASTER_AURASTATE;
break;
}
}
}
// Attr must make flag drop spell totally immune from all effects
if (prevented_reason != SPELL_CAST_OK)
@ -5704,7 +5738,7 @@ void Spell::UpdatePointers()
m_targets.Update(m_caster);
}
bool Spell::IsAffectedByAura(Aura *aura)
bool Spell::IsAffectedByAura(Aura *aura) const
{
return sSpellMgr.IsAffectedByMod(m_spellInfo, aura->getAuraSpellMod());
}

View file

@ -248,8 +248,8 @@ class Spell
void EffectDualWield(uint32 i);
void EffectPickPocket(uint32 i);
void EffectAddFarsight(uint32 i);
void EffectSummonWild(uint32 i);
void EffectSummonGuardian(uint32 i);
void EffectSummonWild(uint32 i, uint32 forceFaction = 0);
void EffectSummonGuardian(uint32 i, uint32 forceFaction = 0);
void EffectHealMechanical(uint32 i);
void EffectJump(uint32 i);
void EffectTeleUnitsFaceCaster(uint32 i);
@ -277,7 +277,7 @@ class Spell
void EffectSummonPlayer(uint32 i);
void EffectActivateObject(uint32 i);
void EffectApplyGlyph(uint32 i);
void EffectSummonTotem(uint32 i);
void EffectSummonTotem(uint32 i, uint8 slot = 0);
void EffectEnchantHeldItem(uint32 i);
void EffectSummonObject(uint32 i);
void EffectResurrect(uint32 i);
@ -299,7 +299,7 @@ class Spell
void EffectMilling(uint32 i);
void EffectRenamePet(uint32 i);
void EffectSendTaxi(uint32 i);
void EffectSummonCritter(uint32 i);
void EffectSummonCritter(uint32 i, uint32 forceFaction = 0);
void EffectKnockBack(uint32 i);
void EffectPlayerPull(uint32 i);
void EffectDispelMechanic(uint32 i);
@ -314,7 +314,6 @@ class Spell
void EffectAddExtraAttacks(uint32 i);
void EffectSpiritHeal(uint32 i);
void EffectSkinPlayerCorpse(uint32 i);
void EffectSummonDemon(uint32 i);
void EffectStealBeneficialBuff(uint32 i);
void EffectUnlearnSpecialization(uint32 i);
void EffectHealPct(uint32 i);
@ -442,7 +441,7 @@ class Spell
void UpdatePointers(); // must be used at call Spell code after time delay (non triggered spell cast/update spell call/etc)
bool IsAffectedByAura(Aura *aura);
bool IsAffectedByAura(Aura *aura) const;
bool CheckTargetCreatureType(Unit* target) const;

View file

@ -314,7 +314,7 @@ enum AuraType
SPELL_AURA_MOD_IGNORE_DAMAGE_REDUCTION_SCHOOL = 269,
SPELL_AURA_MOD_IGNORE_TARGET_RESIST = 270, // Possibly need swap vs 195 aura used only in 1 spell Chaos Bolt Passive
SPELL_AURA_MOD_DAMAGE_FROM_CASTER = 271,
SPELL_AURA_272 = 272,
SPELL_AURA_MAELSTROM_WEAPON = 272,
SPELL_AURA_X_RAY = 273,
SPELL_AURA_274 = 274,
SPELL_AURA_MOD_IGNORE_SHAPESHIFT = 275,
@ -347,7 +347,7 @@ enum AuraType
SPELL_AURA_302 = 302,
SPELL_AURA_303 = 303,
SPELL_AURA_304 = 304,
SPELL_AURA_305 = 305,
SPELL_AURA_MOD_MINIMUM_SPEED = 305,
SPELL_AURA_306 = 306,
TOTAL_AURAS = 307
};

View file

@ -295,7 +295,7 @@ pAuraHandler AuraHandler[TOTAL_AURAS]=
&Aura::HandleUnused, //242 SPELL_AURA_MOD_SPELL_DAMAGE_FROM_HEALING (only 2 test spels in 3.2.2a)
&Aura::HandleNULL, //243 faction reaction override spells
&Aura::HandleComprehendLanguage, //244 SPELL_AURA_COMPREHEND_LANGUAGE
&Aura::HandleNULL, //245 SPELL_AURA_MOD_DURATION_OF_MAGIC_EFFECTS
&Aura::HandleNoImmediateEffect, //245 SPELL_AURA_MOD_DURATION_OF_MAGIC_EFFECTS implemented in Unit::CalculateSpellDuration
&Aura::HandleNoImmediateEffect, //246 SPELL_AURA_MOD_DURATION_OF_EFFECTS_BY_DISPEL implemented in Unit::CalculateSpellDuration
&Aura::HandleNULL, //247 target to become a clone of the caster
&Aura::HandleNoImmediateEffect, //248 SPELL_AURA_MOD_COMBAT_RESULT_CHANCE implemented in Unit::RollMeleeOutcomeAgainst
@ -313,7 +313,7 @@ pAuraHandler AuraHandler[TOTAL_AURAS]=
&Aura::HandleNoImmediateEffect, //260 SPELL_AURA_SCREEN_EFFECT (miscvalue = id in ScreenEffect.dbc) not required any code
&Aura::HandlePhase, //261 SPELL_AURA_PHASE undetectable invisibility? implemented in Unit::isVisibleForOrDetect
&Aura::HandleNULL, //262 ignore combat/aura state?
&Aura::HandleNULL, //263 SPELL_AURA_ALLOW_ONLY_ABILITY player can use only abilities set in SpellClassMask
&Aura::HandleAllowOnlyAbility, //263 SPELL_AURA_ALLOW_ONLY_ABILITY player can use only abilities set in SpellClassMask
&Aura::HandleUnused, //264 unused (3.0.8a-3.2.2a)
&Aura::HandleUnused, //265 unused (3.0.8a-3.2.2a)
&Aura::HandleUnused, //266 unused (3.0.8a-3.2.2a)
@ -322,7 +322,7 @@ pAuraHandler AuraHandler[TOTAL_AURAS]=
&Aura::HandleNoImmediateEffect, //269 SPELL_AURA_MOD_IGNORE_DAMAGE_REDUCTION_SCHOOL implemented in Unit::CalcNotIgnoreDamageRedunction
&Aura::HandleUnused, //270 SPELL_AURA_MOD_IGNORE_TARGET_RESIST (unused in 3.2.2a)
&Aura::HandleNoImmediateEffect, //271 SPELL_AURA_MOD_DAMAGE_FROM_CASTER implemented in Unit::SpellDamageBonus
&Aura::HandleNULL, //272 reduce spell cast time?
&Aura::HandleNoImmediateEffect, //272 SPELL_AURA_MAELSTROM_WEAPON (unclear use for aura, it used in (3.2.2a...3.3.0) in single spell 53817 that spellmode stacked and charged spell expected to be drop as stack
&Aura::HandleNoImmediateEffect, //273 SPELL_AURA_X_RAY (client side implementation)
&Aura::HandleNULL, //274 proc free shot?
&Aura::HandleNoImmediateEffect, //275 SPELL_AURA_MOD_IGNORE_SHAPESHIFT Use SpellClassMask for spell select
@ -355,7 +355,7 @@ pAuraHandler AuraHandler[TOTAL_AURAS]=
&Aura::HandleUnused, //302 unused (3.2.2a)
&Aura::HandleNULL, //303 17 spells
&Aura::HandleNULL, //304 2 spells (alcohol effect?)
&Aura::HandleNULL, //305 2 spells
&Aura::HandleAuraModIncreaseSpeed, //305 SPELL_AURA_MOD_MINIMUM_SPEED
&Aura::HandleNULL //306 1 spell
};
@ -1333,26 +1333,42 @@ bool Aura::isAffectedOnSpell(SpellEntry const *spell) const
void Aura::ReapplyAffectedPassiveAuras( Unit* target )
{
std::set<uint32> affectedPassives;
std::set<uint32> affectedSelf;
std::set<uint32> affectedAuraCaster;
for(Unit::AuraMap::const_iterator itr = target->GetAuras().begin(); itr != target->GetAuras().end(); ++itr)
{
// permanent passive
if (itr->second->IsPassive() && itr->second->IsPermanent() &&
// permanent passive or permanent area aura
if (itr->second->IsPermanent() && (itr->second->IsPassive() || itr->second->IsAreaAura()) &&
// non deleted and not same aura (any with same spell id)
!itr->second->IsDeleted() && itr->second->GetId() != GetId() &&
// only applied by self and affected by aura
itr->second->GetCasterGUID() == target->GetGUID() && isAffectedOnSpell(itr->second->GetSpellProto()))
// and affected by aura
isAffectedOnSpell(itr->second->GetSpellProto()))
{
affectedPassives.insert(itr->second->GetId());
// only applied by self or aura caster
if(itr->second->GetCasterGUID() == target->GetGUID())
affectedSelf.insert(itr->second->GetId());
else if(itr->second->GetCasterGUID() == GetCasterGUID())
affectedAuraCaster.insert(itr->second->GetId());
}
}
for(std::set<uint32>::const_iterator set_itr = affectedPassives.begin(); set_itr != affectedPassives.end(); ++set_itr)
for(std::set<uint32>::const_iterator set_itr = affectedSelf.begin(); set_itr != affectedSelf.end(); ++set_itr)
{
target->RemoveAurasDueToSpell(*set_itr);
target->CastSpell(m_target, *set_itr, true);
}
if (!affectedAuraCaster.empty())
{
Unit* caster = GetCaster();
for(std::set<uint32>::const_iterator set_itr = affectedAuraCaster.begin(); set_itr != affectedAuraCaster.end(); ++set_itr)
{
target->RemoveAurasDueToSpell(*set_itr);
if (caster)
caster->CastSpell(m_target, *set_itr, true);
}
}
}
/*********************************************************/
@ -1402,7 +1418,10 @@ void Aura::HandleAddModifier(bool apply, bool Real)
mod->mask = (uint64)ptr[0] | (uint64)ptr[1]<<32;
mod->mask2= (uint64)ptr[2];
mod->charges = m_procCharges;
// prevent expire spell mods with (charges > 0 && m_stackAmount > 1)
// all this spell expected expire not at use but at spell proc event check
mod->charges = m_spellProto->StackAmount > 1 ? 0 : m_procCharges;
m_spellmod = mod;
}
@ -1412,14 +1431,22 @@ void Aura::HandleAddModifier(bool apply, bool Real)
// reapply talents to own passive persistent auras
ReapplyAffectedPassiveAuras(m_target);
// re-aplly talents and passives applied to pet (it affected by player spellmods)
// re-apply talents/passives/area auras applied to pet (it affected by player spellmods)
if(Pet* pet = m_target->GetPet())
ReapplyAffectedPassiveAuras(pet);
// re-apply talents/passives/area auras applied to totems (it affected by player spellmods)
for(int i = 0; i < MAX_TOTEM; ++i)
if(m_target->m_TotemSlot[i])
if(Creature* totem = m_target->GetMap()->GetCreature(m_target->m_TotemSlot[i]))
ReapplyAffectedPassiveAuras(totem);
// re-apply talents/passives/area auras applied to group members (it affected by player spellmods)
if (Group* group = ((Player*)m_target)->GetGroup())
for(GroupReference *itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
if (Player* member = itr->getSource())
if (member != m_target && member->IsInMap(m_target))
ReapplyAffectedPassiveAuras(member);
}
void Aura::HandleAddTargetTrigger(bool apply, bool /*Real*/)
{
@ -2212,56 +2239,107 @@ void Aura::HandleAuraDummy(bool apply, bool Real)
// AT APPLY
if(apply)
{
switch(GetId())
switch(m_spellProto->SpellFamilyName)
{
case 1515: // Tame beast
// FIX_ME: this is 2.0.12 threat effect replaced in 2.1.x by dummy aura, must be checked for correctness
if (m_target->CanHaveThreatList())
if (Unit* caster = GetCaster())
m_target->AddThreat(caster, 10.0f, false, GetSpellSchoolMask(GetSpellProto()), GetSpellProto());
return;
case 13139: // net-o-matic
// root to self part of (root_target->charge->root_self sequence
if (Unit* caster = GetCaster())
caster->CastSpell(caster, 13138, true, NULL, this);
return;
case 39850: // Rocket Blast
if(roll_chance_i(20)) // backfire stun
m_target->CastSpell(m_target, 51581, true, NULL, this);
return;
case 43873: // Headless Horseman Laugh
m_target->PlayDistanceSound(11965);
return;
case 46354: // Blood Elf Illusion
if (Unit* caster = GetCaster())
case SPELLFAMILY_GENERIC:
switch(GetId())
{
switch(caster->getGender())
{
case GENDER_FEMALE:
caster->CastSpell(m_target, 46356, true, NULL, this);
break;
case GENDER_MALE:
caster->CastSpell(m_target, 46355, true, NULL, this);
break;
default:
break;
}
case 1515: // Tame beast
// FIX_ME: this is 2.0.12 threat effect replaced in 2.1.x by dummy aura, must be checked for correctness
if (m_target->CanHaveThreatList())
if (Unit* caster = GetCaster())
m_target->AddThreat(caster, 10.0f, false, GetSpellSchoolMask(GetSpellProto()), GetSpellProto());
return;
case 13139: // net-o-matic
// root to self part of (root_target->charge->root_self sequence
if (Unit* caster = GetCaster())
caster->CastSpell(caster, 13138, true, NULL, this);
return;
case 39850: // Rocket Blast
if(roll_chance_i(20)) // backfire stun
m_target->CastSpell(m_target, 51581, true, NULL, this);
return;
case 43873: // Headless Horseman Laugh
m_target->PlayDistanceSound(11965);
return;
case 46354: // Blood Elf Illusion
if (Unit* caster = GetCaster())
{
switch(caster->getGender())
{
case GENDER_FEMALE:
caster->CastSpell(m_target, 46356, true, NULL, this);
break;
case GENDER_MALE:
caster->CastSpell(m_target, 46355, true, NULL, this);
break;
default:
break;
}
}
return;
case 46699: // Requires No Ammo
if(m_target->GetTypeId() == TYPEID_PLAYER)
((Player*)m_target)->RemoveAmmo(); // not use ammo and not allow use
return;
}
return;
case 46699: // Requires No Ammo
if(m_target->GetTypeId() == TYPEID_PLAYER)
((Player*)m_target)->RemoveAmmo(); // not use ammo and not allow use
return;
}
break;
case SPELLFAMILY_WARRIOR:
// Overpower
if(m_spellProto->SpellFamilyFlags & UI64LIT(0x0000000000000004))
{
// Must be casting target
if (!m_target->IsNonMeleeSpellCasted(false))
return;
// Earth Shield
if (GetSpellProto()->SpellFamilyName == SPELLFAMILY_SHAMAN && (GetSpellProto()->SpellFamilyFlags & UI64LIT(0x40000000000)))
{
// prevent double apply bonuses
if(m_target->GetTypeId() != TYPEID_PLAYER || !((Player*)m_target)->GetSession()->PlayerLoading())
if (Unit* caster = GetCaster())
m_modifier.m_amount = caster->SpellHealingBonus(m_target, GetSpellProto(), m_modifier.m_amount, SPELL_DIRECT_DAMAGE);
return;
Unit* caster = GetCaster();
if (!caster)
return;
Unit::AuraList const& modifierAuras = caster->GetAurasByType(SPELL_AURA_ADD_FLAT_MODIFIER);
for(Unit::AuraList::const_iterator itr = modifierAuras.begin(); itr != modifierAuras.end(); ++itr)
{
// Unrelenting Assault
if((*itr)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_WARRIOR && (*itr)->GetSpellProto()->SpellIconID == 2775)
{
switch ((*itr)->GetSpellProto()->Id)
{
case 46859: // Unrelenting Assault, rank 1
m_target->CastSpell(m_target,64849,true,NULL,(*itr));
break;
case 46860: // Unrelenting Assault, rank 2
m_target->CastSpell(m_target,64850,true,NULL,(*itr));
break;
default:
break;
}
break;
}
}
return;
}
break;
case SPELLFAMILY_SHAMAN:
// Tidal Force
if (GetId() == 55198)
{
// apply max stack bufs
SpellEntry const* buffEntry = sSpellStore.LookupEntry(55166);
if (!buffEntry)
return;
for(int k = 0; k < buffEntry->StackAmount; ++k)
m_target->CastSpell(m_target, buffEntry, true, NULL, this);
}
// Earth Shield
else if ((GetSpellProto()->SpellFamilyFlags & UI64LIT(0x40000000000)))
{
// prevent double apply bonuses
if(m_target->GetTypeId() != TYPEID_PLAYER || !((Player*)m_target)->GetSession()->PlayerLoading())
if (Unit* caster = GetCaster())
m_modifier.m_amount = caster->SpellHealingBonus(m_target, GetSpellProto(), m_modifier.m_amount, SPELL_DIRECT_DAMAGE);
return;
}
break;
}
}
// AT REMOVE
@ -2668,6 +2746,18 @@ void Aura::HandleAuraDummy(bool apply, bool Real)
case SPELLFAMILY_HUNTER:
break;
case SPELLFAMILY_PALADIN:
switch(GetId())
{
case 20911: // Blessing of Sanctuary
case 25899: // Greater Blessing of Sanctuary
{
if (apply)
m_target->CastSpell(m_target, 67480, true, NULL, this);
else
m_target->RemoveAurasDueToSpell(67480);
return;
}
}
break;
case SPELLFAMILY_SHAMAN:
{
@ -3148,6 +3238,22 @@ void Aura::HandleAuraTransform(bool apply, bool Real)
}
// Murloc costume
case 42365: m_target->SetDisplayId(21723); break;
// Honor the Dead
case 65386:
case 65495:
{
switch(m_target->getGender())
{
case GENDER_MALE:
m_target->SetDisplayId(29203); // Chapman
break;
case GENDER_FEMALE:
case GENDER_NONE:
m_target->SetDisplayId(29204); // Catrina
break;
}
break;
}
default: break;
}
}
@ -5662,6 +5768,8 @@ void Aura::HandleShapeshiftBoosts(bool apply)
((Player*)m_target)->RemoveSpellCooldown(49868);
break;
case FORM_GHOSTWOLF:
spellId1 = 67116;
break;
case FORM_AMBIENT:
case FORM_GHOUL:
case FORM_STEALTH:
@ -5789,6 +5897,7 @@ void Aura::HandleShapeshiftBoosts(bool apply)
void Aura::HandleSpellSpecificBoosts(bool apply)
{
bool cast_at_remove = false; // if spell must be casted at aura remove
uint32 spellId1 = 0;
uint32 spellId2 = 0;
uint32 spellId3 = 0;
@ -5796,6 +5905,33 @@ void Aura::HandleSpellSpecificBoosts(bool apply)
switch(GetSpellProto()->SpellFamilyName)
{
case SPELLFAMILY_MAGE:
{
// Ice Barrier
if (m_spellProto->SpellIconID == 32)
{
if (!apply && (m_removeMode == AURA_REMOVE_BY_DISPEL || (m_removeMode == AURA_REMOVE_BY_DEFAULT && !GetModifier()->m_amount)))
{
Unit::AuraList const& dummyAuras = m_target->GetAurasByType(SPELL_AURA_DUMMY);
for(Unit::AuraList::const_iterator itr = dummyAuras.begin(); itr != dummyAuras.end(); ++itr)
{
// Shattered Barrier
if ((*itr)->GetSpellProto()->SpellIconID == 2945)
{
cast_at_remove = true;
// first rank have 50% chance
if ((*itr)->GetId() != 44745 || roll_chance_i(50))
spellId1 = 55080;
break;
}
}
}
else
return;
}
else
return;
}
case SPELLFAMILY_WARRIOR:
{
if(!apply)
@ -5817,14 +5953,72 @@ void Aura::HandleSpellSpecificBoosts(bool apply)
}
break;
}
case SPELLFAMILY_WARLOCK:
// Fear
if (m_spellProto->SpellFamilyFlags & UI64LIT(0x0000040000000000))
{
if(!apply)
{
Unit* caster = GetCaster();
if(!caster)
return;
Unit::AuraList const& dummyAuras = caster->GetAurasByType(SPELL_AURA_DUMMY);
for(Unit::AuraList::const_iterator itr = dummyAuras.begin(); itr != dummyAuras.end(); ++itr)
{
SpellEntry const* dummyEntry = (*itr)->GetSpellProto();
// Improved Fear
if (dummyEntry->SpellFamilyName == SPELLFAMILY_WARLOCK && dummyEntry->SpellIconID == 98)
{
cast_at_remove = true;
switch((*itr)->GetModifier()->m_amount)
{
// Rank 1
case 0: spellId1 = 60946; break;
// Rank 1
case 1: spellId1 = 60947; break;
}
break;
}
}
}
else
return;
}
else
return;
break;
case SPELLFAMILY_PRIEST:
{
// Shadow Word: Pain (need visual check fro skip improvement talent) or Vampiric Touch
if (m_spellProto->SpellIconID == 234 && m_spellProto->SpellVisual[0] || m_spellProto->SpellIconID == 2213)
{
if (!apply && m_removeMode == AURA_REMOVE_BY_DISPEL)
{
Unit* caster = GetCaster();
if(!caster)
return;
Unit::AuraList const& dummyAuras = caster->GetAurasByType(SPELL_AURA_DUMMY);
for(Unit::AuraList::const_iterator itr = dummyAuras.begin(); itr != dummyAuras.end(); ++itr)
{
// Shadow Affinity
if ((*itr)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_PRIEST
&& (*itr)->GetSpellProto()->SpellIconID == 178)
{
// custom cast code
int32 basepoints0 = (*itr)->GetModifier()->m_amount * caster->GetCreateMana() / 100;
caster->CastCustomSpell(caster, 64103, &basepoints0, NULL, NULL, true, NULL, this);
return;
}
}
}
else
return;
}
switch(GetId())
{
// Dispersion mana reg and immunity
case 47585:
spellId1 = 60069; // Dispersion
spellId2 = 63230; // Dispersion
break;
// Abolish Disease (remove 1 more poison effect with Body and Soul)
case 552:
{
@ -5851,10 +6045,16 @@ void Aura::HandleSpellSpecificBoosts(bool apply)
spellId1 = 64134; // Body and Soul (periodic dispel effect)
break;
}
// Dispersion mana reg and immunity
case 47585:
spellId1 = 60069; // Dispersion
spellId2 = 63230; // Dispersion
break;
default:
return;
}
break;
}
case SPELLFAMILY_ROGUE:
// Sprint (skip non player casted spells by category)
if (GetSpellProto()->SpellFamilyFlags & UI64LIT(0x0000000000000040) && GetSpellProto()->Category == 44)
@ -5963,7 +6163,7 @@ void Aura::HandleSpellSpecificBoosts(bool apply)
// prevent aura deletion, specially in multi-boost case
SetInUse(true);
if (apply)
if (apply || cast_at_remove)
{
if (spellId1)
m_target->CastSpell(m_target, spellId1, true, NULL, this);
@ -6259,47 +6459,10 @@ void Aura::HandleSchoolAbsorb(bool apply, bool Real)
DoneActualBenefit *= caster->CalculateLevelPenalty(GetSpellProto());
m_modifier.m_amount += (int32)DoneActualBenefit;
// now that the correct amount is computed, apply caster aura, if any
switch(m_spellProto->SpellFamilyName)
{
case SPELLFAMILY_PRIEST:
// Power Word: Shield
if (m_spellProto->SpellFamilyFlags & UI64LIT(0x0000000000000001))
{
// Glyph of Power Word: Shield
if(Aura* glyph = caster->GetAura(55672,0))
{
// instant heal glyph m_amount% of the absorbed amount
int32 heal = (glyph->GetModifier()->m_amount * m_modifier.m_amount)/100;
caster->CastCustomSpell(m_target, 56160, &heal, NULL, NULL, true, 0, this);
}
}
break;
default:
break;
}
}
}
else
{
// Ice Barrier (remove effect from Shattered Barrier)
if (m_spellProto->SpellIconID == 32 && m_spellProto->SpellFamilyName == SPELLFAMILY_MAGE)
{
if (!((m_removeMode == AURA_REMOVE_BY_DEFAULT && !m_modifier.m_amount) || m_removeMode == AURA_REMOVE_BY_DISPEL))
return;
if (m_target->HasAura(44745,0)) // Shattered Barrier, rank 1
{
if(roll_chance_i(50))
m_target->CastSpell(m_target, 55080, true, NULL, this);
}
else if (m_target->HasAura(54787,0)) // Shattered Barrier, rank 2
{
m_target->CastSpell(m_target, 55080, true, NULL, this);
}
}
if (caster &&
// Power Word: Shield
m_spellProto->SpellFamilyName == SPELLFAMILY_PRIEST && m_spellProto->Mechanic == MECHANIC_SHIELD &&
@ -7539,3 +7702,26 @@ void Aura::HandleAuraModAllCritChance(bool apply, bool Real)
// included in Player::UpdateSpellCritChance calculation
((Player*)m_target)->UpdateAllSpellCritChances();
}
void Aura::HandleAllowOnlyAbility(bool apply, bool Real)
{
if(!Real)
return;
if(apply)
{
m_target->setAttackTimer(BASE_ATTACK,m_duration);
m_target->setAttackTimer(RANGED_ATTACK,m_duration);
m_target->setAttackTimer(OFF_ATTACK,m_duration);
}
else
{
m_target->resetAttackTimer(BASE_ATTACK);
m_target->resetAttackTimer(RANGED_ATTACK);
m_target->resetAttackTimer(OFF_ATTACK);
}
m_target->UpdateDamagePhysical(BASE_ATTACK);
m_target->UpdateDamagePhysical(RANGED_ATTACK);
m_target->UpdateDamagePhysical(OFF_ATTACK);
}

View file

@ -214,6 +214,7 @@ class MANGOS_DLL_SPEC Aura
void HandlePhase(bool Apply, bool Real);
void HandleModTargetArmorPct(bool Apply, bool Real);
void HandleAuraModAllCritChance(bool Apply, bool Real);
void HandleAllowOnlyAbility(bool Apply, bool Real);
virtual ~Aura();
@ -268,6 +269,13 @@ class MANGOS_DLL_SPEC Aura
{
if (m_procCharges == 0)
return false;
// exist spells that have maxStack > 1 and m_procCharges > 0 (==1 in fact)
// all like stacks have 1 value in one from this fields
// so return true for allow remove one aura from stacks as expired
if (GetStackAmount() > 1)
return true;
m_procCharges--;
SendAuraUpdate(false);
return m_procCharges == 0;

View file

@ -515,12 +515,14 @@ void Spell::EffectSchoolDMG(uint32 effect_idx)
// Ferocious Bite
if (m_caster->GetTypeId()==TYPEID_PLAYER && (m_spellInfo->SpellFamilyFlags & UI64LIT(0x000800000)) && m_spellInfo->SpellVisual[0]==6587)
{
// converts each extra point of energy into ($f1+$AP/410) additional damage
// converts up to 30 points of energy into ($f1+$AP/410) additional damage
float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK);
float multiple = ap / 410 + m_spellInfo->DmgMultiplier[effect_idx];
damage += int32(m_caster->GetPower(POWER_ENERGY) * multiple);
damage += int32(((Player*)m_caster)->GetComboPoints() * ap * 7 / 100);
m_caster->SetPower(POWER_ENERGY,0);
uint32 energy = m_caster->GetPower(POWER_ENERGY);
uint32 used_energy = energy > 30 ? 30 : energy;
damage += int32(used_energy * multiple);
m_caster->SetPower(POWER_ENERGY,energy-used_energy);
}
// Rake
else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000001000) && m_spellInfo->Effect[2]==SPELL_EFFECT_ADD_COMBO_POINTS)
@ -1441,14 +1443,14 @@ void Spell::EffectDummy(uint32 i)
uint32 rage = m_caster->GetPower(POWER_RAGE);
// up to max 30 rage cost
if(rage > 30)
rage = 30;
if(rage > 300)
rage = 300;
// Glyph of Execution bonus
uint32 rage_modified = rage;
if (Aura *aura = m_caster->GetDummyAura(58367))
rage_modified += aura->GetModifier()->m_amount;
rage_modified += aura->GetModifier()->m_amount*10;
int32 basePoints0 = damage+int32(rage_modified * m_spellInfo->DmgMultiplier[i] +
m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.2f);
@ -1465,7 +1467,7 @@ void Spell::EffectDummy(uint32 i)
if ((*itr)->GetSpellProto()->SpellIconID == 1989)
{
// saved rage top stored in next affect
uint32 lastrage = (*itr)->GetSpellProto()->CalculateSimpleValue(1);
uint32 lastrage = (*itr)->GetSpellProto()->CalculateSimpleValue(1)*10;
if(lastrage < rage)
rage -= lastrage;
break;
@ -1491,6 +1493,7 @@ void Spell::EffectDummy(uint32 i)
m_damage+= uint32(damage * m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100);
return;
}
switch(m_spellInfo->Id)
{
// Warrior's Wrath
@ -2337,7 +2340,7 @@ void Spell::EffectTeleportUnits(uint32 i)
if (unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
((Player*)unitTarget)->TeleportTo(((Player*)unitTarget)->m_homebindMapId,((Player*)unitTarget)->m_homebindX,((Player*)unitTarget)->m_homebindY,((Player*)unitTarget)->m_homebindZ,unitTarget->GetOrientation(),unitTarget==m_caster ? TELE_TO_SPELL : 0);
((Player*)unitTarget)->TeleportToHomebind(unitTarget==m_caster ? TELE_TO_SPELL : 0);
return;
}
case TARGET_AREAEFFECT_INSTANT: // in all cases first TARGET_TABLE_X_Y_Z_COORDINATES
@ -2675,6 +2678,9 @@ void Spell::EffectHeal( uint32 /*i*/ )
addhealth += damageAmount;
}
// Death Pact (percent heal)
else if (m_spellInfo->Id==48743)
addhealth = addhealth * unitTarget->GetMaxHealth() / 100;
// Swiftmend - consumes Regrowth or Rejuvenation
else if (m_spellInfo->TargetAuraState == AURA_STATE_SWIFTMEND && unitTarget->HasAuraState(AURA_STATE_SWIFTMEND))
{
@ -2770,21 +2776,21 @@ void Spell::EffectHealthLeech(uint32 i)
sLog.outDebug("HealthLeech :%i", damage);
uint32 curHealth = unitTarget->GetHealth();
damage = m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage );
if (curHealth < damage)
damage = curHealth;
float multiplier = m_spellInfo->EffectMultipleValue[i];
if (Player *modOwner = m_caster->GetSpellModOwner())
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, multiplier);
int32 new_damage = int32(damage*multiplier);
uint32 curHealth = unitTarget->GetHealth();
new_damage = m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, new_damage );
if (curHealth < new_damage)
new_damage = curHealth;
uint32 heal = uint32(damage*multiplier);
if (m_caster->isAlive())
{
new_damage = m_caster->SpellHealingBonus(m_caster, m_spellInfo, new_damage, HEAL);
m_caster->DealHeal(m_caster, uint32(new_damage), m_spellInfo);
heal = m_caster->SpellHealingBonus(m_caster, m_spellInfo, heal, HEAL);
m_caster->DealHeal(m_caster, heal, m_spellInfo);
}
}
@ -3392,54 +3398,99 @@ void Spell::EffectApplyAreaAura(uint32 i)
void Spell::EffectSummonType(uint32 i)
{
switch(m_spellInfo->EffectMiscValueB[i])
uint32 prop_id = m_spellInfo->EffectMiscValueB[i];
SummonPropertiesEntry const *summon_prop = sSummonPropertiesStore.LookupEntry(prop_id);
if(!summon_prop)
{
case SUMMON_TYPE_GUARDIAN:
case SUMMON_TYPE_POSESSED:
case SUMMON_TYPE_POSESSED2:
case SUMMON_TYPE_FORCE_OF_NATURE:
case SUMMON_TYPE_GUARDIAN2:
case SUMMON_TYPE_GUARDIAN3:
// Jewelery statue case (totem like)
if(m_spellInfo->SpellIconID == 2056)
EffectSummonTotem(i);
sLog.outError("EffectSummonType: Unhandled summon type %u", prop_id);
return;
}
switch(summon_prop->Group)
{
// faction handled later on, or loaded from template
case SUMMON_PROP_GROUP_WILD:
case SUMMON_PROP_GROUP_FRIENDLY:
{
switch(summon_prop->Type)
{
case SUMMON_PROP_TYPE_SIEGE_VEH:
case SUMMON_PROP_TYPE_DRAKE_VEH:
{
// TODO
// EffectSummonVehicle(i);
break;
}
case SUMMON_PROP_TYPE_TOTEM:
{
EffectSummonTotem(i, summon_prop->Slot);
break;
}
case SUMMON_PROP_TYPE_SUMMON:
case SUMMON_PROP_TYPE_GUARDIAN:
case SUMMON_PROP_TYPE_ARMY:
case SUMMON_PROP_TYPE_DK:
case SUMMON_PROP_TYPE_CONSTRUCT:
{
// JC golems - 32804, etc -- fits much better totem AI
if(m_spellInfo->SpellIconID == 2056)
EffectSummonTotem(i);
if(prop_id == 832) // scrapbot
EffectSummonWild(i, summon_prop->FactionId);
else
EffectSummonGuardian(i, summon_prop->FactionId);
break;
}
case SUMMON_PROP_TYPE_CRITTER:
{
EffectSummonCritter(i, summon_prop->FactionId);
break;
}
case SUMMON_PROP_TYPE_OTHER:
case SUMMON_PROP_TYPE_PHASING:
case SUMMON_PROP_TYPE_LIGHTWELL:
{
// those are classical totems - effectbasepoints is their hp and not summon ammount!
//SUMMON_TYPE_TOTEM = 121: 23035, battlestands
//SUMMON_TYPE_TOTEM2 = 647: 52893, Anti-Magic Zone (npc used)
if(prop_id == 121 || prop_id == 647)
EffectSummonTotem(i);
else
EffectSummonWild(i, summon_prop->FactionId);
break;
}
default:
sLog.outError("EffectSummonType: Unhandled summon type %u", summon_prop->Type);
break;
}
break;
}
case SUMMON_PROP_GROUP_PETS:
{
// FIXME : multiple summons - not yet supported as pet
//1562 - force of nature - sid 33831
//1161 - feral spirit - sid 51533
if(prop_id == 1562) // 3 uncontrolable instead of one controllable :/
EffectSummonGuardian(i, summon_prop->FactionId);
else
EffectSummonGuardian(i);
EffectSummon(i);
break;
case SUMMON_TYPE_WILD:
case SUMMON_TYPE_QUEST_WILD:
case SUMMON_TYPE_CREATURE:
EffectSummonWild(i);
}
case SUMMON_PROP_GROUP_CONTROLLABLE:
{
// no type here
// maybe wrong - but thats the handler currently used for those
EffectSummonGuardian(i, summon_prop->FactionId);
break;
case SUMMON_TYPE_DEMON:
case SUMMON_TYPE_INFERNO:
EffectSummonDemon(i);
break;
case SUMMON_TYPE_SUMMON:
case SUMMON_TYPE_ELEMENTAL:
EffectSummon(i);
break;
case SUMMON_TYPE_CRITTER:
case SUMMON_TYPE_CRITTER2:
case SUMMON_TYPE_CRITTER3:
case SUMMON_TYPE_QUEST_CRITTER:
EffectSummonCritter(i);
break;
case SUMMON_TYPE_TOTEM_SLOT1:
case SUMMON_TYPE_TOTEM_SLOT2:
case SUMMON_TYPE_TOTEM_SLOT3:
case SUMMON_TYPE_TOTEM_SLOT4:
case SUMMON_TYPE_TOTEM:
EffectSummonTotem(i);
break;
case SUMMON_TYPE_UNKNOWN1:
case SUMMON_TYPE_UNKNOWN2:
case SUMMON_TYPE_UNKNOWN3:
case SUMMON_TYPE_UNKNOWN4:
case SUMMON_TYPE_UNKNOWN5:
}
case SUMMON_PROP_GROUP_VEHICLE:
{
// TODO
// EffectSummonVehicle(i);
break;
}
default:
sLog.outError("EffectSummonType: Unhandled summon type %u", m_spellInfo->EffectMiscValueB[i]);
sLog.outError("EffectSummonType: Unhandled summon group type %u", summon_prop->Group);
break;
}
}
@ -3457,6 +3508,10 @@ void Spell::EffectSummon(uint32 i)
uint32 level = m_caster->getLevel();
Pet* spawnCreature = new Pet(SUMMON_PET);
int32 duration = GetSpellDuration(m_spellInfo);
if(Player* modOwner = m_caster->GetSpellModOwner())
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration);
if (m_caster->GetTypeId()==TYPEID_PLAYER && spawnCreature->LoadPetFromDB((Player*)m_caster,pet_entry))
{
// Summon in dest location
@ -3470,7 +3525,6 @@ void Spell::EffectSummon(uint32 i)
}
// set timer for unsummon
int32 duration = GetSpellDuration(m_spellInfo);
if (duration > 0)
spawnCreature->SetDuration(duration);
@ -3509,7 +3563,6 @@ void Spell::EffectSummon(uint32 i)
}
// set timer for unsummon
int32 duration = GetSpellDuration(m_spellInfo);
if (duration > 0)
spawnCreature->SetDuration(duration);
@ -3780,7 +3833,7 @@ void Spell::EffectAddFarsight(uint32 i)
((Player*)m_caster)->SetFarSightGUID(dynObj->GetGUID());
}
void Spell::EffectSummonWild(uint32 i)
void Spell::EffectSummonWild(uint32 i, uint32 forceFaction)
{
uint32 creature_entry = m_spellInfo->EffectMiscValue[i];
if (!creature_entry)
@ -3796,9 +3849,7 @@ void Spell::EffectSummonWild(uint32 i)
{
uint16 skill202 = ((Player*)m_caster)->GetSkillValue(SKILL_ENGINERING);
if (skill202)
{
level = skill202/5;
}
}
}
@ -3808,6 +3859,8 @@ void Spell::EffectSummonWild(uint32 i)
float center_z = m_targets.m_destZ;
float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
int32 duration = GetSpellDuration(m_spellInfo);
TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_OR_DEAD_DESPAWN;
int32 amount = damage > 0 ? damage : 1;
@ -3832,30 +3885,23 @@ void Spell::EffectSummonWild(uint32 i)
else
m_caster->GetClosePoint(px, py, pz, 3.0f);
int32 duration = GetSpellDuration(m_spellInfo);
if(Creature *summon = m_caster->SummonCreature(creature_entry, px, py, pz, m_caster->GetOrientation(), summonType, duration))
{
summon->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
summon->SetCreatorGUID(m_caster->GetGUID());
TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_OR_DEAD_DESPAWN;
m_caster->SummonCreature(creature_entry, px, py, pz, m_caster->GetOrientation(), summonType, duration);
if(forceFaction)
summon->setFaction(forceFaction);
}
}
}
void Spell::EffectSummonGuardian(uint32 i)
void Spell::EffectSummonGuardian(uint32 i, uint32 forceFaction)
{
uint32 pet_entry = m_spellInfo->EffectMiscValue[i];
if (!pet_entry)
return;
// set timer for unsummon
int32 duration = GetSpellDuration(m_spellInfo);
// Search old Guardian only for players (if casted spell not have duration or cooldown)
// FIXME: some guardians have control spell applied and controlled by player and anyway player can't summon in this time
// so this code hack in fact
if (m_caster->GetTypeId() == TYPEID_PLAYER && (duration <= 0 || GetSpellRecoveryTime(m_spellInfo) == 0))
if(m_caster->FindGuardianWithEntry(pet_entry))
return; // find old guardian, ignore summon
// in another case summon new
uint32 level = m_caster->getLevel();
@ -3879,6 +3925,9 @@ void Spell::EffectSummonGuardian(uint32 i)
float center_z = m_targets.m_destZ;
float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
int32 duration = GetSpellDuration(m_spellInfo);
if(Player* modOwner = m_caster->GetSpellModOwner())
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration);
int32 amount = damage > 0 ? damage : 1;
@ -3930,8 +3979,8 @@ void Spell::EffectSummonGuardian(uint32 i)
spawnCreature->SetOwnerGUID(m_caster->GetGUID());
spawnCreature->setPowerType(POWER_MANA);
spawnCreature->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
spawnCreature->setFaction(m_caster->getFaction());
spawnCreature->SetUInt32Value(UNIT_NPC_FLAGS, spawnCreature->GetCreatureInfo()->npcflag);
spawnCreature->setFaction(forceFaction ? forceFaction : m_caster->getFaction());
spawnCreature->SetUInt32Value(UNIT_FIELD_FLAGS, 0);
spawnCreature->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
spawnCreature->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, 0);
@ -5350,6 +5399,14 @@ void Spell::EffectScriptEffect(uint32 effIndex)
}
return;
}
// Guarded by The Light (Paladin spell with SPELLFAMILY_WARLOCK)
case 63521:
{
// Divine Plea, refresh on target (3 aura slots)
if (Aura* aura = unitTarget->GetAura(54428,0))
aura->RefreshAura();
return;
}
}
break;
}
@ -5745,7 +5802,7 @@ void Spell::EffectStuck(uint32 /*i*/)
return;
// homebind location is loaded always
pTarget->TeleportTo(pTarget->m_homebindMapId,pTarget->m_homebindX,pTarget->m_homebindY,pTarget->m_homebindZ,pTarget->GetOrientation(), (unitTarget==m_caster ? TELE_TO_SPELL : 0));
pTarget->TeleportToHomebind(unitTarget==m_caster ? TELE_TO_SPELL : 0);
// Stuck spell trigger Hearthstone cooldown
SpellEntry const *spellInfo = sSpellStore.LookupEntry(8690);
@ -5833,21 +5890,9 @@ void Spell::EffectApplyGlyph(uint32 i)
}
}
void Spell::EffectSummonTotem(uint32 i)
void Spell::EffectSummonTotem(uint32 i, uint8 slot)
{
uint8 slot = 0;
switch(m_spellInfo->EffectMiscValueB[i])
{
case SUMMON_TYPE_TOTEM_SLOT1: slot = 0; break;
case SUMMON_TYPE_TOTEM_SLOT2: slot = 1; break;
case SUMMON_TYPE_TOTEM_SLOT3: slot = 2; break;
case SUMMON_TYPE_TOTEM_SLOT4: slot = 3; break;
// Battle standard case
case SUMMON_TYPE_TOTEM: slot = 254; break;
// jewelery statue case, like totem without slot
case SUMMON_TYPE_GUARDIAN: slot = 255; break;
default: return;
}
slot = slot ? (slot - 1): 255;
if(slot < MAX_TOTEM)
{
@ -6339,7 +6384,7 @@ void Spell::EffectCharge2(uint32 /*i*/)
m_caster->Attack(unitTarget,true);
}
void Spell::EffectSummonCritter(uint32 i)
void Spell::EffectSummonCritter(uint32 i, uint32 forceFaction)
{
if(m_caster->GetTypeId() != TYPEID_PLAYER)
return;
@ -6399,9 +6444,9 @@ void Spell::EffectSummonCritter(uint32 i)
critter->SetOwnerGUID(m_caster->GetGUID());
critter->SetCreatorGUID(m_caster->GetGUID());
critter->setFaction(m_caster->getFaction());
critter->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
critter->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
critter->setFaction(forceFaction ? forceFaction : m_caster->getFaction());
critter->AIM_Initialize();
critter->InitPetCreateSpells(); // e.g. disgusting oozeling has a create spell as critter...
//critter->InitLevelupSpellsForLevel(); // none?
@ -6764,65 +6809,6 @@ void Spell::EffectSkill(uint32 /*i*/)
sLog.outDebug("WORLD: SkillEFFECT");
}
void Spell::EffectSummonDemon(uint32 i)
{
// select center of summon position
float center_x = m_targets.m_destX;
float center_y = m_targets.m_destY;
float center_z = m_targets.m_destZ;
float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
int32 amount = damage > 0 ? damage : 1;
if (m_spellInfo->EffectMiscValueB[i] == SUMMON_TYPE_INFERNO)
amount = 1;
for(int32 count = 0; count < amount; ++count)
{
float px, py, pz;
// If dest location if present
if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
{
// Summon 1 unit in dest location
if (count == 0)
{
px = m_targets.m_destX;
py = m_targets.m_destY;
pz = m_targets.m_destZ;
}
// Summon in random point all other units if location present
else
m_caster->GetRandomPoint(center_x,center_y,center_z,radius,px,py,pz);
}
// Summon if dest location not present near caster
else
m_caster->GetClosePoint(px,py,pz,3.0f);
int32 duration = GetSpellDuration(m_spellInfo);
Creature* Charmed = m_caster->SummonCreature(m_spellInfo->EffectMiscValue[i], px, py, pz, m_caster->GetOrientation(),TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,duration);
if (!Charmed) // something fatal, not attempt more
return;
// might not always work correctly, maybe the creature that dies from CoD casts the effect on itself and is therefore the caster?
Charmed->SetLevel(m_caster->getLevel());
// TODO: Add damage/mana/hp according to level
// Enslave demon effect, without mana cost and cooldown
if (m_spellInfo->EffectMiscValue[i] == 89) // Inferno summon
{
// Enslave demon effect, without mana cost and cooldown
m_caster->CastSpell(Charmed, 20882, true); // FIXME: enslave does not scale with level, level 62+ minions cannot be enslaved
// Inferno effect for non player calls
if (m_spellInfo->EffectMiscValueB[i]!=SUMMON_TYPE_INFERNO)
Charmed->CastSpell(Charmed, 22703, true, 0);
}
}
}
void Spell::EffectSpiritHeal(uint32 /*i*/)
{
// TODO player can't see the heal-animation - he should respawn some ticks later

View file

@ -1329,10 +1329,8 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons
spellInfo_2->SpellVisual[0] == 99 && spellInfo_1->SpellVisual[0] == 0 ) )
return false;
// Heart of the Wild and (Primal Instinct (Idol of Terror) triggering spell or Agility)
if( spellInfo_1->SpellIconID == 240 && spellInfo_2->SpellIconID == 240 && (
spellInfo_1->SpellVisual[0] == 0 && spellInfo_2->SpellVisual[0] == 78 ||
spellInfo_2->SpellVisual[0] == 0 && spellInfo_1->SpellVisual[0] == 78 ) )
// Heart of the Wild, Agility and various Idol Triggers
if(spellInfo_1->SpellIconID == 240 && spellInfo_2->SpellIconID == 240)
return false;
// Personalized Weather (thunder effect should overwrite rainy aura)
@ -1414,6 +1412,10 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons
if( spellId_1 == 35081 && spellInfo_2->SpellIconID==561 && spellInfo_2->SpellVisual[0]==7992)
return false;
// Blessing of Sanctuary (multi-family check, some from 16 spell icon spells)
if (spellInfo_1->Id == 67480 && spellInfo_2->Id == 20911)
return false;
break;
}
}
@ -1642,8 +1644,13 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons
// Concentration Aura and Improved Concentration Aura and Aura Mastery
if ((spellInfo_1->SpellIconID == 1487) && (spellInfo_2->SpellIconID == 1487))
return false;
}
// Blessing of Sanctuary (multi-family check, some from 16 spell icon spells)
if (spellInfo_2->Id == 67480 && spellInfo_1->Id == 20911)
return false;
// Combustion and Fire Protection Aura (multi-family check)
if( spellInfo_2->Id == 11129 && spellInfo_1->SpellIconID == 33 && spellInfo_1->SpellVisual[0] == 321 )
return false;
@ -1663,6 +1670,10 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons
if( spellInfo_1->SpellIconID==220 && spellInfo_2->SpellIconID==220 &&
spellInfo_1->SpellFamilyFlags != spellInfo_2->SpellFamilyFlags )
return false;
// Ghost Wolf
if (spellInfo_1->SpellIconID == 67 && spellInfo_2->SpellIconID == 67)
return false;
}
// Bloodlust and Bloodthirst (multi-family check)
if( spellInfo_1->Id == 2825 && spellInfo_2->SpellIconID == 38 && spellInfo_2->SpellVisual[0] == 0 )
@ -1711,13 +1722,22 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons
if (spellInfo_1->SpellFamilyName != spellInfo_2->SpellFamilyName)
return false;
bool dummy_only = true;
for (int i = 0; i < 3; ++i)
{
if (spellInfo_1->Effect[i] != spellInfo_2->Effect[i] ||
spellInfo_1->EffectItemType[i] != spellInfo_2->EffectItemType[i] ||
spellInfo_1->EffectMiscValue[i] != spellInfo_2->EffectMiscValue[i] ||
spellInfo_1->EffectApplyAuraName[i] != spellInfo_2->EffectApplyAuraName[i])
return false;
// ignore dummy only spells
if(spellInfo_1->Effect[i] && spellInfo_1->Effect[i] != SPELL_EFFECT_DUMMY && spellInfo_1->EffectApplyAuraName[i] != SPELL_AURA_DUMMY)
dummy_only = false;
}
if (dummy_only)
return false;
return true;
}

View file

@ -160,12 +160,20 @@ void Totem::SetTypeBySummonSpell(SpellEntry const * spellProto)
bool Totem::IsImmunedToSpellEffect(SpellEntry const* spellInfo, uint32 index) const
{
// TODO: possibly all negative auras immune?
switch(spellInfo->Effect[index])
{
case SPELL_EFFECT_ATTACK_ME:
return true;
default:
break;
}
switch(spellInfo->EffectApplyAuraName[index])
{
case SPELL_AURA_PERIODIC_DAMAGE:
case SPELL_AURA_PERIODIC_LEECH:
case SPELL_AURA_MOD_FEAR:
case SPELL_AURA_TRANSFORM:
case SPELL_AURA_MOD_TAUNT:
return true;
default:
break;

View file

@ -1693,8 +1693,47 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
// Death Prevention Aura
SpellEntry const* preventDeathSpell = NULL;
int32 preventDeathAmount = 0;
// full absorb cases (by chance)
AuraList const& vAbsorb = pVictim->GetAurasByType(SPELL_AURA_SCHOOL_ABSORB);
for(AuraList::const_iterator i = vAbsorb.begin(); i != vAbsorb.end() && RemainingDamage > 0; ++i)
{
// only work with proper school mask damage
Modifier* i_mod = (*i)->GetModifier();
if (!(i_mod->m_miscvalue & schoolMask))
continue;
SpellEntry const* i_spellProto = (*i)->GetSpellProto();
// Fire Ward or Frost Ward
if(i_spellProto->SpellFamilyName == SPELLFAMILY_MAGE && i_spellProto->SpellFamilyFlags & UI64LIT(0x0000000000000108))
{
int chance = 0;
Unit::AuraList const& auras = pVictim->GetAurasByType(SPELL_AURA_ADD_PCT_MODIFIER);
for (Unit::AuraList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
{
SpellEntry const* itr_spellProto = (*itr)->GetSpellProto();
// Frost Warding (chance full absorb)
if (itr_spellProto->SpellFamilyName == SPELLFAMILY_MAGE && itr_spellProto->SpellIconID == 501)
{
// chance stored in next dummy effect
chance = itr_spellProto->CalculateSimpleValue(1);
break;
}
}
if(roll_chance_i(chance))
{
int32 amount = RemainingDamage;
RemainingDamage = 0;
// Frost Warding (mana regen)
pVictim->CastCustomSpell(pVictim, 57776, &amount, NULL, NULL, true, NULL, *i);
break;
}
}
}
// Need remove expired auras after
bool existExpired = false;
// absorb without mana cost
AuraList const& vSchoolAbsorb = pVictim->GetAurasByType(SPELL_AURA_SCHOOL_ABSORB);
for(AuraList::const_iterator i = vSchoolAbsorb.begin(); i != vSchoolAbsorb.end() && RemainingDamage > 0; ++i)
@ -1802,7 +1841,17 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
preventDeathAmount = (*i)->GetModifier()->m_amount;
continue;
}
// Power Word: Shield
if (spellProto->SpellFamilyFlags & UI64LIT(00000001) && spellProto->Mechanic == MECHANIC_SHIELD)
{
// Glyph of Power Word: Shield
if (Aura *glyph = pVictim->GetAura(55672,0))
{
int32 heal = int32(glyph->GetModifier()->m_amount *
(RemainingDamage >= currentAbsorb ? currentAbsorb : RemainingDamage) / 100);
pVictim->CastCustomSpell(pVictim, 56160, &heal, NULL, NULL, true, 0, *i);
}
}
// Reflective Shield
if (spellProto->SpellFamilyFlags == 0x1 && canReflect)
{
@ -1945,13 +1994,18 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
else
currentAbsorb = RemainingDamage;
float manaMultiplier = (*i)->GetSpellProto()->EffectMultipleValue[(*i)->GetEffIndex()];
if(Player *modOwner = pVictim->GetSpellModOwner())
modOwner->ApplySpellMod((*i)->GetId(), SPELLMOD_MULTIPLE_VALUE, manaMultiplier);
if (float manaMultiplier = (*i)->GetSpellProto()->EffectMultipleValue[(*i)->GetEffIndex()])
{
if(Player *modOwner = pVictim->GetSpellModOwner())
modOwner->ApplySpellMod((*i)->GetId(), SPELLMOD_MULTIPLE_VALUE, manaMultiplier);
int32 maxAbsorb = int32(pVictim->GetPower(POWER_MANA) / manaMultiplier);
if (currentAbsorb > maxAbsorb)
currentAbsorb = maxAbsorb;
int32 maxAbsorb = int32(pVictim->GetPower(POWER_MANA) / manaMultiplier);
if (currentAbsorb > maxAbsorb)
currentAbsorb = maxAbsorb;
int32 manaReduction = int32(currentAbsorb * manaMultiplier);
pVictim->ApplyPowerMod(POWER_MANA, manaReduction, false);
}
(*i)->GetModifier()->m_amount -= currentAbsorb;
if((*i)->GetModifier()->m_amount <= 0)
@ -1960,12 +2014,40 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
next = vManaShield.begin();
}
int32 manaReduction = int32(currentAbsorb * manaMultiplier);
pVictim->ApplyPowerMod(POWER_MANA, manaReduction, false);
RemainingDamage -= currentAbsorb;
}
// effects dependent from full absorb amount
if (int32 full_absorb = damage - RemainingDamage - *resist)
{
Unit::AuraList const& auras = pVictim->GetAurasByType(SPELL_AURA_DUMMY);
for (Unit::AuraList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
{
SpellEntry const* itr_spellProto = (*itr)->GetSpellProto();
// Incanter's Absorption
if (itr_spellProto->SpellFamilyName == SPELLFAMILY_GENERIC &&
itr_spellProto->SpellIconID == 2941)
{
int32 amount = int32(full_absorb * (*itr)->GetModifier()->m_amount / 100);
// apply normalized part of already accumulated amount in aura
if (Aura* spdAura = pVictim->GetAura(44413,0))
amount += spdAura->GetModifier()->m_amount * spdAura->GetAuraDuration() / spdAura->GetAuraMaxDuration();
// limit 5 health percents
int32 health_5percent = pVictim->GetMaxHealth()*5/100;
if(amount > health_5percent)
amount = health_5percent;
// Incanter's Absorption (triggered absorb based spell power, will replace existed if any)
pVictim->CastCustomSpell(pVictim, 44413, &amount, NULL, NULL, true);
break;
}
}
}
// only split damage if not damaging yourself
if(pVictim != this)
{
@ -3867,7 +3949,7 @@ void Unit::RemoveSingleAuraDueToSpellByDispel(uint32 spellId, uint64 casterGUID,
{
SpellEntry const* spellEntry = sSpellStore.LookupEntry(spellId);
// Custom dispel case
// Custom dispel cases
// Unstable Affliction
if(spellEntry->SpellFamilyName == SPELLFAMILY_WARLOCK && (spellEntry->SpellFamilyFlags & UI64LIT(0x010000000000)))
{
@ -3881,9 +3963,43 @@ void Unit::RemoveSingleAuraDueToSpellByDispel(uint32 spellId, uint64 casterGUID,
// backfire damage and silence
dispeler->CastCustomSpell(dispeler, 31117, &damage, NULL, NULL, true, NULL, NULL,casterGUID);
}
return;
}
else
// Flame Shock
if (spellEntry->SpellFamilyName == SPELLFAMILY_SHAMAN && (spellEntry->SpellFamilyFlags & UI64LIT(0x10000000)))
{
Unit* caster = NULL;
uint32 triggeredSpell = 0;
if (Aura* dotAura = GetAura(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_SHAMAN, UI64LIT(0x10000000), 0x00000000, casterGUID))
caster = dotAura->GetCaster();
if (caster && !caster->isDead())
{
Unit::AuraList const& auras = caster->GetAurasByType(SPELL_AURA_DUMMY);
for (Unit::AuraList::const_iterator i = auras.begin(); i != auras.end(); ++i)
{
switch((*i)->GetId())
{
case 51480: triggeredSpell=64694; break;// Lava Flows, Rank 1
case 51481: triggeredSpell=65263; break;// Lava Flows, Rank 2
case 51482: triggeredSpell=65264; break;// Lava Flows, Rank 3
default: continue;
}
break;
}
}
// Remove spell auras from stack
RemoveSingleSpellAurasByCasterSpell(spellId, casterGUID, AURA_REMOVE_BY_DISPEL);
// Haste
if (triggeredSpell)
caster->CastSpell(caster, triggeredSpell, true);
return;
}
RemoveSingleSpellAurasByCasterSpell(spellId, casterGUID, AURA_REMOVE_BY_DISPEL);
}
void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, uint64 casterGUID, Unit *stealer)
@ -5600,6 +5716,34 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu
{
switch(dummySpell->Id)
{
// Leader of the Pack
case 24932:
{
// dummy m_amount store health percent (!=0 if Improved Leader of the Pack applied)
int32 heal_percent = triggeredByAura->GetModifier()->m_amount;
if (!heal_percent)
return false;
// check explicitly only to prevent mana cast when halth cast cooldown
if (cooldown && ((Player*)this)->HasSpellCooldown(34299))
return false;
// health
triggered_spell_id = 34299;
basepoints0 = GetMaxHealth() * heal_percent / 100;
target = this;
// mana to caster
if (triggeredByAura->GetCasterGUID() == GetGUID())
{
if (SpellEntry const* manaCastEntry = sSpellStore.LookupEntry(60889))
{
int32 mana_percent = manaCastEntry->CalculateSimpleValue(0) * heal_percent;
CastCustomSpell(this, manaCastEntry, &mana_percent, NULL, NULL, true, castItem, triggeredByAura);
}
}
break;
}
// Healing Touch (Dreamwalker Raiment set)
case 28719:
{
@ -5825,13 +5969,6 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu
basepoints0 = GetAttackTime(BASE_ATTACK) * int32(ap*0.022f + 0.044f * holy) / 1000;
break;
}
// Sacred Shield
if (dummySpell->SpellFamilyFlags & UI64LIT(0x0008000000000000))
{
triggered_spell_id = 58597;
target = this;
break;
}
// Righteous Vengeance
if (dummySpell->SpellIconID == 3025)
{
@ -5853,14 +5990,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu
// Judgement of Light
case 20185:
{
// Get judgement caster
Unit *caster = triggeredByAura->GetCaster();
if (!caster)
return false;
float ap = caster->GetTotalAttackPowerValue(BASE_ATTACK);
int32 holy = caster->SpellBaseDamageBonus(SPELL_SCHOOL_MASK_HOLY) +
caster->SpellBaseDamageBonusForVictim(SPELL_SCHOOL_MASK_HOLY, this);
basepoints0 = int32(ap*0.10f + 0.10f*holy);
basepoints0 = int32( pVictim->GetMaxHealth() * triggeredByAura->GetModifier()->m_amount / 100 );
pVictim->CastCustomSpell(pVictim, 20267, &basepoints0, NULL, NULL, true, NULL, triggeredByAura);
return true;
}
@ -5932,6 +6062,20 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu
}
break;
}
// Spiritual Attunement
case 31785:
case 33776:
{
// if healed by another unit (pVictim)
if(this == pVictim)
return false;
// heal amount
basepoints0 = triggerAmount*damage/100;
target = this;
triggered_spell_id = 31786;
break;
}
// Seal of Vengeance (damage calc on apply aura)
case 31801:
{
@ -5969,20 +6113,6 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu
// Replenishment
CastSpell(this, 57669, true, NULL, triggeredByAura);
break;
// Spiritual Attunement
case 31785:
case 33776:
{
// if healed by another unit (pVictim)
if(this == pVictim)
return false;
// heal amount
basepoints0 = triggerAmount*damage/100;
target = this;
triggered_spell_id = 31786;
break;
}
// Paladin Tier 6 Trinket (Ashtongue Talisman of Zeal)
case 40470:
{
@ -6011,29 +6141,6 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu
break;
}
// Seal of Corruption (damage calc on apply aura)
case 53736:
{
if(effIndex != 0) // effect 1,2 used by seal unleashing code
return false;
triggered_spell_id = 53742;
// Add 5-stack effect
int8 stacks = 0;
AuraList const& auras = target->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
for(AuraList::const_iterator itr = auras.begin(); itr!=auras.end(); ++itr)
{
if( ((*itr)->GetId() == 53742) && (*itr)->GetCasterGUID()==GetGUID())
{
stacks = (*itr)->GetStackAmount();
break;
}
}
if(stacks >= 5)
CastSpell(target,53739,true,NULL,triggeredByAura);
break;
}
// Light's Beacon (heal target area aura)
case 53651:
{
@ -6069,6 +6176,43 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu
beacon->CastCustomSpell(beacon,triggered_spell_id,&basepoints0,NULL,NULL,true,castItem,triggeredByAura,pVictim->GetGUID());
return true;
}
// Seal of Corruption (damage calc on apply aura)
case 53736:
{
if(effIndex != 0) // effect 1,2 used by seal unleashing code
return false;
triggered_spell_id = 53742;
// Add 5-stack effect
int8 stacks = 0;
AuraList const& auras = target->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
for(AuraList::const_iterator itr = auras.begin(); itr!=auras.end(); ++itr)
{
if( ((*itr)->GetId() == 53742) && (*itr)->GetCasterGUID()==GetGUID())
{
stacks = (*itr)->GetStackAmount();
break;
}
}
if(stacks >= 5)
CastSpell(target,53739,true,NULL,triggeredByAura);
break;
}
// Glyph of Flash of Light
case 54936:
{
triggered_spell_id = 54957;
basepoints0 = triggerAmount*damage/100;
break;
}
// Glyph of Holy Light
case 54937:
{
triggered_spell_id = 54968;
basepoints0 = triggerAmount*damage/100;
break;
}
// Glyph of Divinity
case 54939:
{
@ -6082,18 +6226,23 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu
}
return true;
}
// Glyph of Flash of Light
case 54936:
// Sacred Shield (buff)
case 58597:
{
triggered_spell_id = 54957;
basepoints0 = triggerAmount*damage/100;
triggered_spell_id = 66922;
SpellEntry const* triggeredEntry = sSpellStore.LookupEntry(triggered_spell_id);
if (!triggeredEntry)
return false;
basepoints0 = int32(damage / (GetSpellDuration(triggeredEntry) / triggeredEntry->EffectAmplitude[0]));
target = this;
break;
}
// Glyph of Holy Light
case 54937:
// Sacred Shield (talent rank)
case 53601:
{
triggered_spell_id = 54968;
basepoints0 = triggerAmount*damage/100;
triggered_spell_id = 58597;
target = this;
break;
}
}
@ -6324,7 +6473,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu
uint32 spell = (*itr)->GetSpellProto()->EffectTriggerSpell[(*itr)->GetEffIndex()];
CastSpell(this, spell, true, castItem, triggeredByAura);
if ((*itr)->DropAuraCharge())
RemoveAurasDueToSpell((*itr)->GetId());
RemoveSingleSpellAurasFromStack((*itr)->GetId());
return true;
}
}
@ -6426,7 +6575,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu
}
CastSpell(target, spell, true, castItem, triggeredByAura);
if ((*itr)->DropAuraCharge())
RemoveAurasDueToSpell((*itr)->GetId());
RemoveSingleSpellAurasFromStack((*itr)->GetId());
return true;
}
}
@ -6919,16 +7068,8 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggeredB
}
case SPELLFAMILY_DRUID:
{
// Leader of the Pack
if (auraSpellInfo->Id == 24932)
{
if (triggerAmount == 0)
return false;
basepoints[0] = triggerAmount * GetMaxHealth() / 100;
trigger_spell_id = 34299;
}
// Druid Forms Trinket
else if (auraSpellInfo->Id==37336)
if (auraSpellInfo->Id==37336)
{
switch(m_form)
{
@ -7152,7 +7293,7 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggeredB
case SPELL_SCHOOL_NATURE: trigger_spell_id = 50488; break;
case SPELL_SCHOOL_FROST: trigger_spell_id = 50485; break;
case SPELL_SCHOOL_SHADOW: trigger_spell_id = 50489; break;
case SPELL_SCHOOL_ARCANE: trigger_spell_id = 54373; break;
case SPELL_SCHOOL_ARCANE: trigger_spell_id = 50486; break;
default:
return false;
}
@ -7525,6 +7666,9 @@ bool Unit::HandleOverrideClassScriptAuraProc(Unit *pVictim, uint32 damage, Aura
case 5497: // Improved Mana Gems (Serpent-Coil Braid)
triggered_spell_id = 37445; // Mana Surge
break;
case 6953: // Warbringer
RemoveAurasAtMechanicImmunity(IMMUNE_TO_ROOT_AND_SNARE_MASK,0,true);
return true;
case 8152: // Serendipity
{
// if heal your target over maximum health
@ -8904,10 +9048,9 @@ bool Unit::isSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolM
case SPELL_DAMAGE_CLASS_RANGED:
{
if (pVictim)
{
crit_chance = GetUnitCriticalChance(attackType, pVictim);
crit_chance+= GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, schoolMask);
}
crit_chance+= GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, schoolMask);
break;
}
default:
@ -10252,7 +10395,12 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
// Apply strongest slow aura mod to speed
int32 slow = GetMaxNegativeAuraModifier(SPELL_AURA_MOD_DECREASE_SPEED);
if (slow)
{
speed *=(100.0f + slow)/100.0f;
float min_speed = (float)GetMaxPositiveAuraModifier(SPELL_AURA_MOD_MINIMUM_SPEED) / 100.0f;
if (speed < min_speed)
speed = min_speed;
}
SetSpeed(mtype, speed, forced);
}
@ -10710,6 +10858,9 @@ int32 Unit::CalculateSpellDuration(SpellEntry const* spellProto, uint8 effect_in
durationMod_always+=target->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DURATION_OF_EFFECTS_BY_DISPEL, spellProto->Dispel);
// Find max mod (negative bonus)
int32 durationMod_not_stack = target->GetMaxNegativeAuraModifierByMiscValue(SPELL_AURA_MECHANIC_DURATION_MOD_NOT_STACK, mechanic);
if (!IsPositiveSpell(spellProto->Id))
durationMod_always += target->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DURATION_OF_MAGIC_EFFECTS, spellProto->DmgClass);
int32 durationMod = 0;
// Select strongest negative mod
@ -11578,6 +11729,7 @@ bool InitTriggerAuraData()
isTriggerAura[SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE] = true;
isTriggerAura[SPELL_AURA_MOD_DAMAGE_FROM_CASTER] = true;
isTriggerAura[SPELL_AURA_MOD_SPELL_CRIT_CHANCE] = true;
isTriggerAura[SPELL_AURA_MAELSTROM_WEAPON] = true;
isNonTriggerAura[SPELL_AURA_MOD_POWER_REGEN]=true;
isNonTriggerAura[SPELL_AURA_REDUCE_PUSHBACK]=true;
@ -11868,6 +12020,13 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag
continue;
}
break;
case SPELL_AURA_MAELSTROM_WEAPON:
sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s maelstrom aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());
// remove all stack;
RemoveSpellsCausingAura(SPELL_AURA_MAELSTROM_WEAPON);
triggeredByAura->SetInUse(false); // this safe, aura locked
continue; // avoid re-remove attempts
default:
// nothing do, just charges counter
break;
@ -11890,7 +12049,7 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag
removedSpells.unique();
// Remove auras from removedAuras
for(RemoveSpellList::const_iterator i = removedSpells.begin(); i != removedSpells.end();++i)
RemoveAurasDueToSpell(*i);
RemoveSingleSpellAurasFromStack(*i);
}
}

View file

@ -1176,7 +1176,7 @@ void World::SetInitialWorldSettings()
sObjectMgr.LoadQuestLocales();
sObjectMgr.LoadNpcTextLocales();
sObjectMgr.LoadPageTextLocales();
sObjectMgr.LoadNpcOptionLocales();
sObjectMgr.LoadGossipMenuItemsLocales();
sObjectMgr.LoadPointOfInterestLocales();
sObjectMgr.SetDBCLocaleIndex(GetDefaultDbcLocale()); // Get once for all the locale index of DBC language (console/broadcasts)
sLog.outString( ">>> Localization strings loaded" );
@ -1405,8 +1405,14 @@ void World::SetInitialWorldSettings()
sLog.outString( "Loading Npc Text Id..." );
sObjectMgr.LoadNpcTextId(); // must be after load Creature and NpcText
sLog.outString( "Loading Npc Options..." );
sObjectMgr.LoadNpcOptions();
sLog.outString( "Loading Gossip scripts..." );
sObjectMgr.LoadGossipScripts(); // must be before gossip menu options
sLog.outString( "Loading Gossip menus..." );
sObjectMgr.LoadGossipMenu();
sLog.outString( "Loading Gossip menu options..." );
sObjectMgr.LoadGossipMenuItems();
sLog.outString( "Loading Vendors..." );
sObjectMgr.LoadVendors(); // must be after load CreatureTemplate and ItemTemplate

View file

@ -355,7 +355,7 @@ void WorldSession::LogoutPlayer(bool Save)
///- Teleport to home if the player is in an invalid instance
if(!_player->m_InstanceValid && !_player->isGameMaster())
{
_player->TeleportTo(_player->m_homebindMapId, _player->m_homebindX, _player->m_homebindY, _player->m_homebindZ, _player->GetOrientation());
_player->TeleportToHomebind();
//this is a bad place to call for far teleport because we need player to be in world for successful logout
//maybe we should implement delayed far teleport logout?
}

View file

@ -152,6 +152,14 @@ bool ChatHandler::HandleCharacterDeleteCommand(const char* args)
return true;
}
/// Close RA connection
bool ChatHandler::HandleQuitCommand(const char* /*args*/)
{
// processed in RASocket
SendSysMessage(LANG_QUIT_WRONG_USE_ERROR);
return true;
}
/// Exit the realm
bool ChatHandler::HandleServerExitCommand(const char* /*args*/)
{

View file

@ -225,7 +225,10 @@ void RASocket::OnRead()
if (strlen(buff))
{
sLog.outRALog("Got '%s' cmd.\n",buff);
sWorld.QueueCliCommand(&RASocket::zprint , buff);
if (strncmp(buff,"quit",4)==0)
SetCloseAndDelete();
else
sWorld.QueueCliCommand(&RASocket::zprint, buff);
}
else
Sendf("mangos>");

View file

@ -25,8 +25,8 @@ extern DatabasePostgre WorldDatabase;
extern DatabaseMysql WorldDatabase;
#endif
const char CreatureInfosrcfmt[]="iiiiiiiiiisssiiiiiiiiiiffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiisiiffliiiiiiiliiis";
const char CreatureInfodstfmt[]="iiiiiiiiiisssiiiiiiiiiiffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiisiiffliiiiiiiliiii";
const char CreatureInfosrcfmt[]="iiiiiiiiiisssiiiiiiiiiiiffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiisiiffliiiiiiiliiis";
const char CreatureInfodstfmt[]="iiiiiiiiiisssiiiiiiiiiiiffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiisiiffliiiiiiiliiii";
const char CreatureDataAddonInfofmt[]="iiiiiis";
const char CreatureModelfmt[]="iffbi";
const char CreatureInfoAddonInfofmt[]="iiiiiis";

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__
#define __REVISION_NR_H__
#define REVISION_NR "8913"
#define REVISION_NR "8971"
#endif // __REVISION_NR_H__

View file

@ -1,6 +1,6 @@
#ifndef __REVISION_SQL_H__
#define __REVISION_SQL_H__
#define REVISION_DB_CHARACTERS "required_8874_01_characters_character_skills"
#define REVISION_DB_MANGOS "required_8912_01_mangos_spell_proc_event"
#define REVISION_DB_MANGOS "required_8965_02_mangos_command"
#define REVISION_DB_REALMD "required_8728_01_realmd_account"
#endif // __REVISION_SQL_H__