Merge remote-tracking branch 'acore/master' into test-staging

This commit is contained in:
Keleborn
2026-04-03 13:46:54 -07:00
72 changed files with 920 additions and 916 deletions
@@ -612,6 +612,15 @@ AllowLoggingIPAddressesInDatabase = 1
Allow.IP.Based.Action.Logging = 0
#
# LogSpamReports
# Description: Allow logging spam reports from players in the chat, mail or calendar into the database.
# Default: 1 - (Enabled)
# 0 - (Disabled)
#
LogSpamReports = 1
#
# Appender config values: Given an appender "name"
# Appender.name
@@ -353,6 +353,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_UPD_REM_AT_LOGIN_FLAG, "UPDATE characters set at_login = at_login & ~ ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_ALL_AT_LOGIN_FLAGS, "UPDATE characters SET at_login = at_login | ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_BUG_REPORT, "INSERT INTO bugreport (type, content) VALUES(?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_SPAM_REPORT, "INSERT INTO spam_reports (SpamType, SpammerGuid, Unk1, MailIdOrMessageType, ChannelId, SecondsSinceMessage, Description, Time) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_PETITION_NAME, "UPDATE petition SET name = ? WHERE petition_id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_PETITION_SIGNATURE, "INSERT INTO petition_sign (ownerguid, petition_id, playerguid, player_account) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_ACCOUNT_ONLINE, "UPDATE characters SET online = 0 WHERE account = ?", CONNECTION_ASYNC);
@@ -277,6 +277,7 @@ enum CharacterDatabaseStatements : uint32
CHAR_UPD_REM_AT_LOGIN_FLAG,
CHAR_UPD_ALL_AT_LOGIN_FLAGS,
CHAR_INS_BUG_REPORT,
CHAR_INS_SPAM_REPORT,
CHAR_UPD_PETITION_NAME,
CHAR_INS_PETITION_SIGNATURE,
CHAR_UPD_ACCOUNT_ONLINE,
+3 -2
View File
@@ -23,8 +23,9 @@ PossessedAI::PossessedAI(Creature* c) : CreatureAI(c) { me->SetReactState(REACT_
NullCreatureAI::NullCreatureAI(Creature* c) : CreatureAI(c)
{
me->SetReactState(REACT_PASSIVE);
// TODO: Remove once WorldObject casting is ported (triggers won't create combat refs from spell casts)
me->SetIsCombatDisallowed(true);
// TODO: Remove once WorldObject spell casting is ported (triggers won't create combat refs from spell casts)
if (me->IsTrigger())
me->SetIsCombatDisallowed(true);
}
int32 NullCreatureAI::Permissible(Creature const* creature)
@@ -44,6 +44,7 @@ SmartAI::SmartAI(Creature* c) : CreatureAI(c)
mCanRepeatPath = false;
mEvadeDisabled = false;
mSuppressEvade = false;
mCanAutoAttack = true;
@@ -698,6 +699,9 @@ void SmartAI::MovementInform(uint32 MovementType, uint32 Data)
void SmartAI::EnterEvadeMode(EvadeReason /*why*/)
{
if (mSuppressEvade)
return;
if (mEvadeDisabled)
{
GetScript()->ProcessEventsFor(SMART_EVENT_EVADE);
@@ -181,6 +181,7 @@ public:
void SetSwim(bool swim = true);
void SetEvadeDisabled(bool disable = true);
void SetSuppressEvade(bool suppress) { mSuppressEvade = suppress; }
void SetInvincibilityHpLevel(uint32 level) { mInvincibilityHpLevel = level; }
@@ -249,6 +250,7 @@ private:
uint32 GetWPCount() { return mWayPoints ? mWayPoints->Nodes.size() : 0; }
bool mCanRepeatPath;
bool mEvadeDisabled;
bool mSuppressEvade;
bool mCanAutoAttack;
bool mForcedPaused;
uint32 mInvincibilityHpLevel;
@@ -1039,7 +1039,16 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (!me)
break;
// Suppress evade during script-initiated combat stop so
// JustExitedCombat does not trigger EnterEvadeMode.
if (SmartAI* sai = CAST_AI(SmartAI, me->AI()))
sai->SetSuppressEvade(true);
me->CombatStop(true);
if (SmartAI* sai = CAST_AI(SmartAI, me->AI()))
sai->SetSuppressEvade(false);
break;
}
case SMART_ACTION_CALL_GROUPEVENTHAPPENS:
@@ -745,6 +745,13 @@ void BattlegroundIC::HandleContestedNodes(ICNodePoint* nodePoint)
(*itr)->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
}
}
else if (nodePoint->nodeType == NODE_TYPE_REFINERY || nodePoint->nodeType == NODE_TYPE_QUARRY)
{
// nodePoint->faction is the assaulting team (set before this call);
// remove the siege damage buff from the team that previously controlled the node.
uint32 auraSpellId = (nodePoint->nodeType == NODE_TYPE_REFINERY) ? SPELL_OIL_REFINERY : SPELL_QUARRY;
RemoveAuraOnTeam(auraSpellId, GetOtherTeamId(nodePoint->faction));
}
else if (nodePoint->nodeType == NODE_TYPE_WORKSHOP)
{
DelObject(BG_IC_GO_SEAFORIUM_BOMBS_1);
@@ -663,6 +663,8 @@ bool Creature::UpdateEntry(uint32 Entry, const CreatureData* data, bool changele
ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true);
}
SetIsCombatDisallowed(cInfo->HasFlagsExtra(CREATURE_FLAG_EXTRA_CANNOT_ENTER_COMBAT));
SetDetectionDistance(cInfo->detection_range);
// Update movement
@@ -56,7 +56,7 @@ enum CreatureFlagsExtra : uint32
CREATURE_FLAG_EXTRA_GHOST_VISIBILITY = 0x00000400, // creature will only be visible to dead players
CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK = 0x00000800, // creature will use offhand attacks
CREATURE_FLAG_EXTRA_NO_SELL_VENDOR = 0x00001000, // players can't sell items to this vendor
CREATURE_FLAG_EXTRA_IGNORE_COMBAT = 0x00002000,
CREATURE_FLAG_EXTRA_CANNOT_ENTER_COMBAT = 0x00002000, // creature is not allowed to enter combat
CREATURE_FLAG_EXTRA_WORLDEVENT = 0x00004000, // custom flag for world event creatures (left room for merging)
CREATURE_FLAG_EXTRA_GUARD = 0x00008000, // Creature is guard
CREATURE_FLAG_EXTRA_IGNORE_FEIGN_DEATH = 0x00010000, // creature ignores feign death
@@ -44,7 +44,7 @@ AC_API_EXPORT EnumText EnumUtils<CreatureFlagsExtra>::ToString(CreatureFlagsExtr
case CREATURE_FLAG_EXTRA_GHOST_VISIBILITY: return { "CREATURE_FLAG_EXTRA_GHOST_VISIBILITY", "CREATURE_FLAG_EXTRA_GHOST_VISIBILITY", "creature will only be visible to dead players" };
case CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK: return { "CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK", "CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK", "/ @todo: Implement CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK (creature will use offhand attacks)" };
case CREATURE_FLAG_EXTRA_NO_SELL_VENDOR: return { "CREATURE_FLAG_EXTRA_NO_SELL_VENDOR", "CREATURE_FLAG_EXTRA_NO_SELL_VENDOR", "players can't sell items to this vendor" };
case CREATURE_FLAG_EXTRA_IGNORE_COMBAT: return { "CREATURE_FLAG_EXTRA_IGNORE_COMBAT", "CREATURE_FLAG_EXTRA_IGNORE_COMBAT", "" };
case CREATURE_FLAG_EXTRA_CANNOT_ENTER_COMBAT: return { "CREATURE_FLAG_EXTRA_CANNOT_ENTER_COMBAT", "CREATURE_FLAG_EXTRA_CANNOT_ENTER_COMBAT", "creature is not allowed to enter combat" };
case CREATURE_FLAG_EXTRA_WORLDEVENT: return { "CREATURE_FLAG_EXTRA_WORLDEVENT", "CREATURE_FLAG_EXTRA_WORLDEVENT", "custom flag for world event creatures (left room for merging)" };
case CREATURE_FLAG_EXTRA_GUARD: return { "CREATURE_FLAG_EXTRA_GUARD", "CREATURE_FLAG_EXTRA_GUARD", "Creature is guard" };
case CREATURE_FLAG_EXTRA_IGNORE_FEIGN_DEATH: return { "CREATURE_FLAG_EXTRA_IGNORE_FEIGN_DEATH", "CREATURE_FLAG_EXTRA_IGNORE_FEIGN_DEATH", "creature ignores feign death" };
@@ -88,7 +88,7 @@ AC_API_EXPORT CreatureFlagsExtra EnumUtils<CreatureFlagsExtra>::FromIndex(std::s
case 10: return CREATURE_FLAG_EXTRA_GHOST_VISIBILITY;
case 11: return CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK;
case 12: return CREATURE_FLAG_EXTRA_NO_SELL_VENDOR;
case 13: return CREATURE_FLAG_EXTRA_IGNORE_COMBAT;
case 13: return CREATURE_FLAG_EXTRA_CANNOT_ENTER_COMBAT;
case 14: return CREATURE_FLAG_EXTRA_WORLDEVENT;
case 15: return CREATURE_FLAG_EXTRA_GUARD;
case 16: return CREATURE_FLAG_EXTRA_IGNORE_FEIGN_DEATH;
@@ -129,7 +129,7 @@ AC_API_EXPORT std::size_t EnumUtils<CreatureFlagsExtra>::ToIndex(CreatureFlagsEx
case CREATURE_FLAG_EXTRA_GHOST_VISIBILITY: return 10;
case CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK: return 11;
case CREATURE_FLAG_EXTRA_NO_SELL_VENDOR: return 12;
case CREATURE_FLAG_EXTRA_IGNORE_COMBAT: return 13;
case CREATURE_FLAG_EXTRA_CANNOT_ENTER_COMBAT: return 13;
case CREATURE_FLAG_EXTRA_WORLDEVENT: return 14;
case CREATURE_FLAG_EXTRA_GUARD: return 15;
case CREATURE_FLAG_EXTRA_IGNORE_FEIGN_DEATH: return 16;
+1 -1
View File
@@ -13049,7 +13049,7 @@ PartyResult Player::CanUninviteFromGroup(ObjectGuid targetPlayerGUID) const
if (state == lfg::LFG_STATE_FINISHED_DUNGEON)
return ERR_PARTY_LFG_BOOT_DUNGEON_COMPLETE;
if (grp->isRollLootActive())
if (grp->isRollLootActive() && ObjectAccessor::FindConnectedPlayer(targetPlayerGUID))
return ERR_PARTY_LFG_BOOT_LOOT_ROLLS;
/// @todo: Should also be sent when anyone has recently left combat, with an aprox ~5 seconds timer.
+4 -1
View File
@@ -5697,6 +5697,9 @@ void Unit::RemoveAllAurasExceptType(AuraType type)
// Xinef: We should not remove passive auras on evade, if npc has player owner (scripted one cast auras)
void Unit::RemoveEvadeAuras()
{
if (IsCharmedOwnedByPlayerOrPlayer())
return;
for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
{
Aura const* aura = iter->second->GetBase();
@@ -6768,7 +6771,7 @@ void Unit::ProcSkillsAndAuras(Unit* actor, Unit* victim, uint32 procAttacker, ui
}
else if (healInfo && healInfo->GetHeal())
spellTypeMask = PROC_SPELL_TYPE_HEAL;
else if (damageInfo && damageInfo->GetDamage())
else if (damageInfo && (damageInfo->GetDamage() || damageInfo->GetAbsorb()))
spellTypeMask = PROC_SPELL_TYPE_DAMAGE;
else if (procSpellInfo)
spellTypeMask = PROC_SPELL_TYPE_NO_DMG_HEAL;
+32 -29
View File
@@ -607,35 +607,7 @@ bool Group::RemoveMember(ObjectGuid guid, const RemoveMethod& method /*= GROUP_R
}
// Remove player from loot rolls
for (Rolls::iterator it = RollId.begin(); it != RollId.end();)
{
Roll* roll = *it;
Roll::PlayerVote::iterator itr2 = roll->playerVote.find(guid);
if (itr2 == roll->playerVote.end())
{
++it;
continue;
}
if (itr2->second == GREED || itr2->second == DISENCHANT)
--roll->totalGreed;
else if (itr2->second == NEED)
--roll->totalNeed;
else if (itr2->second == PASS)
--roll->totalPass;
if (itr2->second != NOT_VALID)
--roll->totalPlayersRolling;
roll->playerVote.erase(itr2);
// Xinef: itr can be erased inside
// Xinef: player is removed from all vote lists so it will not pass above playerVote == playerVote.end statement during second iteration
if (CountRollVote(guid, roll->itemGUID, MAX_ROLL_TYPE))
it = RollId.begin();
else
++it;
}
RemovePlayerFromRolls(guid);
// Update subgroups
member_witerator slot = _getMemberWSlot(guid);
@@ -1429,6 +1401,37 @@ void Group::EndRoll(Loot* pLoot, Map* allowedMap)
}
}
void Group::RemovePlayerFromRolls(ObjectGuid guid)
{
for (Rolls::iterator it = RollId.begin(); it != RollId.end();)
{
Roll* roll = *it;
Roll::PlayerVote::iterator itr2 = roll->playerVote.find(guid);
if (itr2 == roll->playerVote.end())
{
++it;
continue;
}
if (itr2->second == GREED || itr2->second == DISENCHANT)
--roll->totalGreed;
else if (itr2->second == NEED)
--roll->totalNeed;
else if (itr2->second == PASS)
--roll->totalPass;
if (itr2->second != NOT_VALID)
--roll->totalPlayersRolling;
roll->playerVote.erase(itr2);
if (CountRollVote(guid, roll->itemGUID, MAX_ROLL_TYPE))
it = RollId.begin();
else
++it;
}
}
void Group::CountTheRoll(Rolls::iterator rollI, Map* allowedMap)
{
Roll* roll = *rollI;
+1
View File
@@ -302,6 +302,7 @@ public:
void CountTheRoll(Rolls::iterator roll, Map* allowedMap);
bool CountRollVote(ObjectGuid playerGUID, ObjectGuid Guid, uint8 Choise);
void EndRoll(Loot* loot, Map* allowedMap);
void RemovePlayerFromRolls(ObjectGuid guid);
Rolls GetRolls() const { return RollId; }
+13 -3
View File
@@ -754,11 +754,21 @@ void WorldSession::HandleCalendarEventModeratorStatus(WorldPacket& recvData)
void WorldSession::HandleCalendarComplain(WorldPackets::Calendar::CalendarComplain& packet)
{
ObjectGuid guid = _player->GetGUID();
if (sWorld->getBoolConfig(CONFIG_LOGSPAMREPORTS))
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_SPAM_REPORT);
LOG_DEBUG("network", "CMSG_CALENDAR_COMPLAIN [{}] EventId [{}] guid [{}]", guid.ToString(), packet.EventId, packet.ComplainGuid.ToString());
stmt->SetData(0, 2); // SpamType 2 = Calendar
stmt->SetData(1, packet.ComplainGuid.GetCounter());
stmt->SetData(2, 0);
stmt->SetData(3, 0);
stmt->SetData(4, 0);
stmt->SetData(5, 0);
stmt->SetData(6, "EventId: " + std::to_string(packet.EventId));
stmt->SetData(7, GameTime::GetGameTime().count());
// what to do with complains?
CharacterDatabase.Execute(stmt);
}
}
void WorldSession::HandleCalendarGetNumPending(WorldPacket& /*recvData*/)
+15 -2
View File
@@ -1157,8 +1157,21 @@ void WorldSession::HandleComplainOpcode(WorldPackets::Misc::Complain& packet)
// Complaint Received message
SendPacket(WorldPackets::Misc::ComplainResult().Write());
LOG_DEBUG("network", "REPORT SPAM: type {}, {}, unk1 {}, unk2 {}, unk3 {}, unk4 {}, message {}",
packet.SpamType, packet.SpammerGuid.ToString(), packet.Unk1, packet.Unk2, packet.Unk3, packet.Unk4, packet.Description);
if (sWorld->getBoolConfig(CONFIG_LOGSPAMREPORTS))
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_SPAM_REPORT);
stmt->SetData(0, packet.SpamType);
stmt->SetData(1, packet.SpammerGuid.GetCounter());
stmt->SetData(2, packet.Unk1);
stmt->SetData(3, packet.MailIdOrMessageType);
stmt->SetData(4, packet.ChannelId);
stmt->SetData(5, packet.SecondsSinceMessage);
stmt->SetData(6, packet.Description);
stmt->SetData(7, GameTime::GetGameTime().count());
CharacterDatabase.Execute(stmt);
}
}
void WorldSession::HandleRealmSplitOpcode(WorldPacket& recv_data)
+4
View File
@@ -142,6 +142,8 @@ bool WorldSession::SendLearnNewTaxiNode(Creature* unit)
update << uint8(1);
SendPacket(&update);
sScriptMgr->OnPlayerLearnTaxiNode(GetPlayer(), curloc);
return true;
}
else
@@ -154,6 +156,8 @@ void WorldSession::SendDiscoverNewTaxiNode(uint32 nodeid)
{
WorldPacket msg(SMSG_NEW_TAXI_PATH, 0);
SendPacket(&msg);
sScriptMgr->OnPlayerLearnTaxiNode(GetPlayer(), nodeid);
}
}
+4 -4
View File
@@ -405,10 +405,10 @@ LootItem::LootItem(LootStoreItem const& li)
randomSuffix = GenerateEnchSuffixFactor(itemid);
randomPropertyId = Item::GenerateItemRandomPropertyId(itemid);
count = 0;
is_looted = 0;
is_blocked = 0;
is_underthreshold = 0;
is_counted = 0;
is_looted = false;
is_blocked = false;
is_underthreshold = false;
is_counted = false;
rollWinnerGUID = ObjectGuid::Empty;
groupid = li.groupid;
}
+5 -1
View File
@@ -1445,6 +1445,10 @@ enum AcoreStrings
LANG_DEBUG_FACTIONCHANGE_GOLD_FAIL = 30122,
LANG_DEBUG_FACTIONCHANGE_GOLD_OK = 30123,
LANG_DEBUG_FACTIONCHANGE_GOLD_NOLIMIT = 30124,
LANG_DEBUG_FACTIONCHANGE_NA = 30125
LANG_DEBUG_FACTIONCHANGE_NA = 30125,
// Achievement commands
LANG_ACHIEVEMENT_ADD_ONLINE = 30126,
LANG_ACHIEVEMENT_ADD_OFFLINE = 30127
};
#endif
@@ -367,6 +367,7 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* creature, uint32 di
creature->StopMoving();
_lastSplineId = 0;
_smoothSplineLaunched = false;
_hasBeenStalled = true;
}
// Set home position to current position.
@@ -931,6 +931,11 @@ void ScriptMgr::OnPlayerGetReputationPriceDiscount(Player const* player, Faction
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_GET_REPUTATION_PRICE_DISCOUNT, script->OnPlayerGetReputationPriceDiscount(player, factionTemplate, discount));
}
void ScriptMgr::OnPlayerLearnTaxiNode(Player const* player, uint32 nodeId)
{
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_LEARN_TAXI_NODE, script->OnPlayerLearnTaxiNode(player, nodeId));
}
PlayerScript::PlayerScript(const char* name, std::vector<uint16> enabledHooks)
: ScriptObject(name, PLAYERHOOK_END)
{
@@ -216,6 +216,7 @@ enum PlayerHook
PLAYERHOOK_ON_SEND_LIST_INVENTORY,
PLAYERHOOK_ON_GIVE_REPUTATION,
PLAYERHOOK_ON_GET_REPUTATION_PRICE_DISCOUNT,
PLAYERHOOK_ON_LEARN_TAXI_NODE,
PLAYERHOOK_END
};
@@ -827,6 +828,14 @@ public:
* @param discount Float value of the discount, as a multiplier of the base price
*/
virtual void OnPlayerGetReputationPriceDiscount(Player const* /*player*/, FactionTemplateEntry const* /*factionTemplate*/, float& /*discount*/) {}
/**
* @brief This hook is called when a player learns a new flight path node.
*
* @param player Contains information about the Player
* @param nodeId The id of the learned taxi node
*/
virtual void OnPlayerLearnTaxiNode(Player const* /*player*/, uint32 /*nodeId*/) {}
};
#endif
+1
View File
@@ -487,6 +487,7 @@ public: /* PlayerScript */
void OnPlayerSendListInventory(Player* player, ObjectGuid vendorGuid, uint32& vendorEntry);
void OnPlayerGetReputationPriceDiscount(Player const* player, Creature const* creature, float& discount);
void OnPlayerGetReputationPriceDiscount(Player const* player, FactionTemplateEntry const* factionTemplate, float& discount);
void OnPlayerLearnTaxiNode(Player const* player, uint32 nodeId);
// Anti cheat
void AnticheatSetCanFlybyServer(Player* player, bool apply);
@@ -16,6 +16,7 @@
*/
#include "CalendarPackets.h"
#include "Packet.h"
void WorldPackets::Calendar::GetEvent::Read()
{
@@ -149,14 +149,14 @@ void WorldPackets::Misc::Complain::Read()
{
case 0:
_worldPacket >> Unk1; // const 0
_worldPacket >> Unk2; // probably mail id
_worldPacket >> Unk3; // const 0
_worldPacket >> MailIdOrMessageType; // probably mail id
_worldPacket >> ChannelId; // const 0
break;
case 1:
_worldPacket >> Unk1; // probably language
_worldPacket >> Unk2; // message type?
_worldPacket >> Unk3; // probably channel id
_worldPacket >> Unk4; // unk random value
_worldPacket >> MailIdOrMessageType;
_worldPacket >> ChannelId;
_worldPacket >> SecondsSinceMessage;
_worldPacket >> Description; // spam description string (messagetype, channel name, player name, message)
break;
}
+6 -6
View File
@@ -218,13 +218,13 @@ namespace WorldPackets
void Read() override;
uint8 SpamType = 0; // 0 - mail, 1 - chat
uint8 SpamType = 0; // 0 - mail, 1 - chat
ObjectGuid SpammerGuid;
uint32 Unk1 = 0;
uint32 Unk2 = 0;
uint32 Unk3 = 0;
uint32 Unk4 = 0;
std::string Description = "";
uint32 Unk1 = 0;
uint32 MailIdOrMessageType = 0;
uint32 ChannelId = 0;
uint32 SecondsSinceMessage = 0;
std::string Description = "";
};
class ComplainResult final : public ServerPacket
+4
View File
@@ -731,6 +731,10 @@ void WorldSession::LogoutPlayer(bool save)
// a) in group; b) not in raid group; c) logging out normally (not being kicked or disconnected) d) LeaveGroupOnLogout is enabled
if (_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && !_player->GetGroup()->isLFGGroup() && m_Socket && sWorld->getBoolConfig(CONFIG_LEAVE_GROUP_ON_LOGOUT))
_player->RemoveFromGroup();
// Remove player from active loot rolls in LFG groups (player stays in group but should not block rolls)
else if (Group* group = _player->GetGroup())
if (group->isLFGGroup())
group->RemovePlayerFromRolls(_player->GetGUID());
// pussywizard: checked second time after being removed from a group
if (!_player->IsBeingTeleportedFar() && !_player->m_InstanceValid && !_player->IsGameMaster())
+6 -1
View File
@@ -2471,6 +2471,11 @@ void Spell::EffectSummonType(SpellEffIndex effIndex)
TempSummonType summonType = (duration <= 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN;
Unit* summoner = m_originalCaster;
if (summoner->IsPet())
if (Unit* owner = summoner->GetOwner())
summoner = owner;
for (uint32 count = 0; count < numSummons; ++count)
{
Position pos;
@@ -2480,7 +2485,7 @@ void Spell::EffectSummonType(SpellEffIndex effIndex)
// randomize position for multiple summons
pos = m_caster->GetRandomPoint(*destTarget, radius);
summon = m_originalCaster->SummonCreature(entry, pos, summonType, duration, 0, nullptr, personalSpawn);
summon = summoner->SummonCreature(entry, pos, summonType, duration, 0, nullptr, personalSpawn);
if (!summon)
continue;
+2
View File
@@ -603,6 +603,8 @@ void WorldConfig::BuildConfigCache()
SetConfigValue<bool>(CONFIG_IP_BASED_ACTION_LOGGING, "Allow.IP.Based.Action.Logging", false);
SetConfigValue<bool>(CONFIG_LOGSPAMREPORTS, "LogSpamReports", true);
// Whether to use LoS from game objects
SetConfigValue<bool>(CONFIG_CHECK_GOBJECT_LOS, "CheckGameObjectLoS", true);
+1
View File
@@ -112,6 +112,7 @@ enum ServerConfigs
CONFIG_ENABLE_CONTINENT_TRANSPORT_PRELOADING,
CONFIG_MINIGOB_MANABONK,
CONFIG_IP_BASED_ACTION_LOGGING,
CONFIG_LOGSPAMREPORTS,
CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA,
CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA,
CONFIG_CHECK_GOBJECT_LOS,
+20 -6
View File
@@ -15,8 +15,10 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AchievementMgr.h"
#include "Chat.h"
#include "CommandScript.h"
#include "Language.h"
#include "Player.h"
using namespace Acore::ChatCommands;
@@ -30,7 +32,7 @@ public:
{
static ChatCommandTable achievementCommandTable =
{
{ "add", HandleAchievementAddCommand, SEC_GAMEMASTER, Console::No },
{ "add", HandleAchievementAddCommand, SEC_GAMEMASTER, Console::Yes },
{ "checkall", HandleAchievementCheckAllCommand, SEC_ADMINISTRATOR, Console::Yes }
};
static ChatCommandTable commandTable =
@@ -40,15 +42,27 @@ public:
return commandTable;
}
static bool HandleAchievementAddCommand(ChatHandler* handler, AchievementEntry const* achievementEntry)
static bool HandleAchievementAddCommand(ChatHandler* handler, AchievementEntry const* achievementEntry, Optional<PlayerIdentifier> player)
{
Player* target = handler->getSelectedPlayer();
if (!target)
if (!player)
player = PlayerIdentifier::FromTargetOrSelf(handler);
if (!player)
{
handler->SendErrorMessage(LANG_NO_CHAR_SELECTED);
handler->SendErrorMessage(LANG_PLAYER_NOT_FOUND);
return false;
}
target->CompletedAchievement(achievementEntry);
if (player->IsConnected())
{
player->GetConnectedPlayer()->CompletedAchievement(achievementEntry);
handler->PSendSysMessage(LANG_ACHIEVEMENT_ADD_ONLINE, achievementEntry->ID, achievementEntry->name[0], player->GetName());
}
else
{
sAchievementMgr->CompletedAchievementForOfflinePlayer(player->GetGUID().GetCounter(), achievementEntry);
handler->PSendSysMessage(LANG_ACHIEVEMENT_ADD_OFFLINE, achievementEntry->ID, achievementEntry->name[0], player->GetName());
}
return true;
}
@@ -452,59 +452,6 @@ private:
uint32 _mightyBlowTimer;
};
// npc_lokhtos_darkbargainer
enum LokhtosItems
{
ITEM_THRORIUM_BROTHERHOOD_CONTRACT = 18628,
ITEM_SULFURON_INGOT = 17203
};
enum LokhtosQuests
{
QUEST_A_BINDING_CONTRACT = 7604
};
enum LokhtosSpells
{
SPELL_CREATE_THORIUM_BROTHERHOOD_CONTRACT_DND = 23059
};
struct npc_lokhtos_darkbargainer : public ScriptedAI
{
npc_lokhtos_darkbargainer(Creature* creature) : ScriptedAI(creature) { }
void sGossipSelect(Player* player, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
if (action == GOSSIP_ACTION_INFO_DEF + 1)
{
CloseGossipMenuFor(player);
player->CastSpell(player, SPELL_CREATE_THORIUM_BROTHERHOOD_CONTRACT_DND, false);
}
if (action == GOSSIP_ACTION_TRADE)
player->GetSession()->SendListInventory(me->GetGUID());
}
void sGossipHello(Player* player) override
{
if (me->IsQuestGiver())
player->PrepareQuestMenu(me->GetGUID());
if (me->IsVendor() && player->GetReputationRank(59) >= REP_FRIENDLY)
AddGossipItemFor(player, 4781, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
if (player->GetQuestRewardStatus(QUEST_A_BINDING_CONTRACT) != 1 &&
!player->HasItemCount(ITEM_THRORIUM_BROTHERHOOD_CONTRACT, 1, true) &&
player->HasItemCount(ITEM_SULFURON_INGOT))
AddGossipItemFor(player, 4781, 1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
if (player->GetReputationRank(59) < REP_FRIENDLY)
SendGossipMenuFor(player, 3673, me->GetGUID());
else
SendGossipMenuFor(player, 3677, me->GetGUID());
}
};
// npc_rocknot
enum RocknotSays
{
@@ -641,7 +588,6 @@ void AddSC_blackrock_depths()
new at_ring_of_law();
RegisterBlackrockDepthsCreatureAI(npc_grimstone);
RegisterBlackrockDepthsCreatureAI(npc_phalanx);
RegisterBlackrockDepthsCreatureAI(npc_lokhtos_darkbargainer);
RegisterBlackrockDepthsCreatureAI(npc_rocknot);
RegisterBlackrockDepthsCreatureAI(brd_ironhand_guardian);
}
@@ -95,13 +95,7 @@ enum Events
enum Misc
{
TEXT_ID_SUMMON_1 = 4995,
TEXT_ID_SUMMON_2 = 5011,
TEXT_ID_SUMMON_3 = 5012,
GOSSIP_ITEM_SUMMON_1 = 4093,
GOSSIP_ITEM_SUMMON_2 = 4109,
GOSSIP_ITEM_SUMMON_3 = 4108,
MENU_ID_RAGNAROS_SUMMON = 4108,
FACTION_MAJORDOMO_FRIENDLY = 1080,
SUMMON_GROUP_ADDS = 1,
@@ -507,46 +501,14 @@ struct boss_majordomo : public BossAI
}
}
void sGossipHello(Player* player) override
void sGossipSelect(Player* player, uint32 menuId, uint32 /*gossipListId*/) override
{
AddGossipItemFor(player, GOSSIP_ITEM_SUMMON_1, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
SendGossipMenuFor(player, TEXT_ID_SUMMON_1, me->GetGUID());
}
void sGossipSelect(Player* player, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
switch (action)
if (menuId == MENU_ID_RAGNAROS_SUMMON)
{
case GOSSIP_ACTION_INFO_DEF:
{
AddGossipItemFor(player, GOSSIP_ITEM_SUMMON_2, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
SendGossipMenuFor(player, TEXT_ID_SUMMON_2, me->GetGUID());
break;
}
case GOSSIP_ACTION_INFO_DEF+1:
{
AddGossipItemFor(player, GOSSIP_ITEM_SUMMON_2, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
SendGossipMenuFor(player, TEXT_ID_SUMMON_2, me->GetGUID());
break;
}
case GOSSIP_ACTION_INFO_DEF+2:
{
AddGossipItemFor(player, GOSSIP_ITEM_SUMMON_3, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
SendGossipMenuFor(player, TEXT_ID_SUMMON_3, me->GetGUID());
break;
}
case GOSSIP_ACTION_INFO_DEF+3:
{
CloseGossipMenuFor(player);
me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP);
Talk(SAY_RAG_SUM_1, player);
DoAction(ACTION_START_RAGNAROS_INTRO);
break;
}
default:
CloseGossipMenuFor(player);
break;
CloseGossipMenuFor(player);
me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP);
Talk(SAY_RAG_SUM_1, player);
DoAction(ACTION_START_RAGNAROS_INTRO);
}
}
@@ -149,7 +149,7 @@ struct boss_moroes : public BossAI
_vanished = true;
Talk(SAY_SPECIAL);
DoCastSelf(SPELL_VANISH);
me->SetImmuneToAll(true);
me->SetImmuneToAll(true, true);
scheduler.Schedule(5s, 7s, [this](TaskContext)
{
me->SetImmuneToAll(false);
@@ -187,7 +187,6 @@ struct boss_kalecgos : public BossAI
me->SetRegeneratingHealth(false);
me->RemoveAllAuras();
me->SetReactState(REACT_PASSIVE);
me->CombatStop();
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetFaction(FACTION_FRIENDLY);
}, 1s);
@@ -296,39 +296,8 @@ public:
};
};
/*######
## npc_augustus_the_touched
######*/
class npc_augustus_the_touched : public CreatureScript
{
public:
npc_augustus_the_touched() : CreatureScript("npc_augustus_the_touched") { }
bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
if (action == GOSSIP_ACTION_TRADE)
player->GetSession()->SendListInventory(creature->GetGUID());
return true;
}
bool OnGossipHello(Player* player, Creature* creature) override
{
if (creature->IsQuestGiver())
player->PrepareQuestMenu(creature->GetGUID());
if (creature->IsVendor() && player->GetQuestRewardStatus(6164))
AddGossipItemFor(player, GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID());
return true;
}
};
void AddSC_eastern_plaguelands()
{
new npc_eris_hevenfire();
new npc_balance_of_light_and_shadow();
new npc_augustus_the_touched();
}
@@ -293,63 +293,6 @@ public:
};
};
/*######
## npc_parqual_fintallas
######*/
enum ParqualFintallas
{
SPELL_MARK_OF_SHAME = 6767,
QUEST_ID_TEST_OF_LORE = 6628,
GOSSIP_MENU_ID_TEST_OF_LORE = 4764,
GOSSIP_TEXTID_PARQUAL_FINTALLAS = 5821,
GOSSIP_TEXTID_TEST_OF_LORE = 5822,
};
class npc_parqual_fintallas : public CreatureScript
{
public:
npc_parqual_fintallas() : CreatureScript("npc_parqual_fintallas") { }
bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
if (action == GOSSIP_ACTION_INFO_DEF + 1)
{
CloseGossipMenuFor(player);
creature->CastSpell(player, SPELL_MARK_OF_SHAME, false);
}
if (action == GOSSIP_ACTION_INFO_DEF + 2)
{
CloseGossipMenuFor(player);
player->AreaExploredOrEventHappens(6628);
}
return true;
}
bool OnGossipHello(Player* player, Creature* creature) override
{
if (creature->IsQuestGiver())
{
player->PrepareQuestMenu(creature->GetGUID());
}
if (player->GetQuestStatus(QUEST_ID_TEST_OF_LORE) == QUEST_STATUS_INCOMPLETE && !player->HasAura(SPELL_MARK_OF_SHAME))
{
AddGossipItemFor(player, GOSSIP_MENU_ID_TEST_OF_LORE, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
AddGossipItemFor(player, GOSSIP_MENU_ID_TEST_OF_LORE, 1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
AddGossipItemFor(player, GOSSIP_MENU_ID_TEST_OF_LORE, 3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
SendGossipMenuFor(player, GOSSIP_TEXTID_TEST_OF_LORE, creature->GetGUID());
}
else
{
SendGossipMenuFor(player, GOSSIP_TEXTID_PARQUAL_FINTALLAS, creature->GetGUID());
}
return true;
}
};
/*######
## ALLIANCE
#######*/
@@ -4037,7 +3980,6 @@ void AddSC_undercity()
{
new npc_lady_sylvanas_windrunner();
new npc_highborne_lamenter();
new npc_parqual_fintallas();
new npc_varian_wrynn();
new npc_thrall_bfu();
@@ -78,6 +78,7 @@ struct boss_falric : public BossAI
me->m_Events.AddEventAtOffset([this]()
{
_startingFight = false;
me->SetImmuneToPC(false);
me->SetInCombatWithZone();
}, 8s);
}
@@ -78,6 +78,7 @@ struct boss_marwyn : public BossAI
me->m_Events.AddEventAtOffset([this]()
{
_startingFight = false;
me->SetImmuneToPC(false);
me->SetInCombatWithZone();
}, 8s);
}
@@ -1499,7 +1499,6 @@ enum eFightEvents
EVENT_LK_SUMMON_LA,
EVENT_LK_SUMMON_NEXT_ICE_WALL,
EVENT_SAY_OPENING,
EVENT_DECREASE_REQ_COUNT_BY_100,
};
struct npc_hor_lich_king : public NullCreatureAI
@@ -1515,13 +1514,14 @@ struct npc_hor_lich_king : public NullCreatureAI
EventMap events;
SummonList summons;
uint8 currentWall;
uint8 reqKillCount;
uint8 summonsCount;
uint8 div2;
void Reset() override
{
currentWall = 0;
reqKillCount = 0;
summonsCount = 0;
div2 = 0;
events.Reset();
events.RescheduleEvent(EVENT_LK_CHECK_COMBAT, 1s);
}
@@ -1529,24 +1529,21 @@ struct npc_hor_lich_king : public NullCreatureAI
{
if (action == ACTION_START_LK_FIGHT_REAL)
events.ScheduleEvent(EVENT_LK_START_FOLLOWING, 1500ms);
else if ((action == ACTION_INFORM_TRASH_DIED && reqKillCount) || action == ACTION_CHECK_TRASH_DIED)
}
void WallCompleted()
{
++currentWall;
instance->SetData(ACTION_DELETE_ICE_WALL, 1);
if (currentWall <= 3)
{
if ((action == ACTION_CHECK_TRASH_DIED && reqKillCount == 0) || (action == ACTION_INFORM_TRASH_DIED && (--reqKillCount) == 0))
{
events.CancelEvent(EVENT_DECREASE_REQ_COUNT_BY_100); // just in case, magic happens sometimes
++currentWall;
instance->SetData(ACTION_DELETE_ICE_WALL, 1);
if (currentWall <= 3)
{
events.ScheduleEvent(EVENT_LK_SUMMON_NEXT_ICE_WALL, 1s);
events.ScheduleEvent(EVENT_LK_SUMMON, currentWall == 3 ? 11s : 7500ms);
}
else
me->RemoveAura(SPELL_REMORSELESS_WINTER);
if (Creature* c = instance->GetCreature(NPC_SYLVANAS_PART2))
c->AI()->DoAction(ACTION_INFORM_WALL_DESTROYED);
}
events.ScheduleEvent(EVENT_LK_SUMMON_NEXT_ICE_WALL, 1s);
events.ScheduleEvent(EVENT_LK_SUMMON, currentWall == 3 ? 11s : 7500ms);
}
else
me->RemoveAura(SPELL_REMORSELESS_WINTER);
if (Creature* c = instance->GetCreature(NPC_SYLVANAS_PART2))
c->AI()->DoAction(ACTION_INFORM_WALL_DESTROYED);
}
void MovementInform(uint32 type, uint32 /*id*/) override
@@ -1569,17 +1566,14 @@ struct npc_hor_lich_king : public NullCreatureAI
else if (currentWall == 4)
{
Talk(SAY_LK_NOWHERE_TO_RUN);
instance->SetData(DATA_LICH_KING, DONE);
instance->SetBossState(DATA_LICH_KING, DONE);
}
}
}
void JustSummoned(Creature* s) override
{
++reqKillCount;
if (events.HasTimeUntilEvent(EVENT_DECREASE_REQ_COUNT_BY_100))
events.RescheduleEvent(EVENT_DECREASE_REQ_COUNT_BY_100, 10s);
++summonsCount;
summons.Summon(s);
s->SetHomePosition(PathWaypoints[WP_STOP[currentWall + 1]]);
s->GetMotionMaster()->MovePoint(0, PathWaypoints[WP_STOP[currentWall + 1]]);
@@ -1592,6 +1586,14 @@ struct npc_hor_lich_king : public NullCreatureAI
s->SetHomePosition(PathWaypoints[WP_STOP[currentWall + 1]]);
}
void SummonedCreatureDies(Creature* /*summon*/, Unit* /*killer*/) override
{
if (!summonsCount)
return;
if (--summonsCount == 0)
WallCompleted();
}
void SummonedCreatureDespawn(Creature* s) override
{
summons.Despawn(s);
@@ -1623,7 +1625,7 @@ struct npc_hor_lich_king : public NullCreatureAI
{
me->GetMotionMaster()->MovementExpired();
me->StopMoving();
reqKillCount = 255;
summonsCount = 255;
leader->InterruptNonMeleeSpells(true);
me->CastSpell(leader, SPELL_HARVEST_SOUL);
events.ScheduleEvent(EVENT_LK_KILL_LEADER, 3s);
@@ -1720,15 +1722,6 @@ struct npc_hor_lich_king : public NullCreatureAI
events.ScheduleEvent(EVENT_LK_SUMMON_LA, 17s + 700ms);
break;
}
if (currentWall <= 3)
{
reqKillCount = 100;
events.RescheduleEvent(EVENT_DECREASE_REQ_COUNT_BY_100, 10s);
}
break;
case EVENT_DECREASE_REQ_COUNT_BY_100:
reqKillCount = (reqKillCount <= 100 ? 0 : reqKillCount - 100);
DoAction(ACTION_CHECK_TRASH_DIED);
break;
case EVENT_LK_SUMMON_GHOULS:
DoCastAOE(SPELL_SUMMON_RAGING_GHOULS);
@@ -2003,9 +1996,6 @@ struct npc_hor_raging_ghoul : public ScriptedAI
void JustDied(Unit* /*killer*/) override
{
me->SetCorpseDelay(10);
if (InstanceScript* instance = me->GetInstanceScript())
if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_BOSS))
lichKing->AI()->DoAction(ACTION_INFORM_TRASH_DIED);
}
};
@@ -2059,9 +2049,6 @@ struct npc_hor_risen_witch_doctor : public ScriptedAI
void JustDied(Unit* /*killer*/) override
{
me->SetCorpseDelay(10);
if (InstanceScript* instance = me->GetInstanceScript())
if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_BOSS))
lichKing->AI()->DoAction(ACTION_INFORM_TRASH_DIED);
}
};
@@ -2110,9 +2097,6 @@ struct npc_hor_lumbering_abomination : public ScriptedAI
void JustDied(Unit* /*killer*/) override
{
me->SetCorpseDelay(10);
if (InstanceScript* instance = me->GetInstanceScript())
if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_BOSS))
lichKing->AI()->DoAction(ACTION_INFORM_TRASH_DIED);
}
};
@@ -27,14 +27,22 @@
enum Data
{
DATA_INTRO,
// Boss encounters (indices for SetBossState)
DATA_FALRIC,
DATA_MARWYN,
DATA_LICH_KING,
MAX_ENCOUNTER,
// Non-boss data (SetData/GetData only)
DATA_INTRO,
DATA_FROSTSWORN_GENERAL,
DATA_LK_INTRO,
DATA_LICH_KING,
DATA_BATTERED_HILT,
MAX_ENCOUNTER,
DATA_WAVE_NUMBER,
DATA_LK_BATTLE,
DATA_SHIP_CAPTAIN,
// Actions
ACTION_SHOW_TRASH,
ACTION_SPIRITUAL_REFLECTIONS_COPY,
ACTION_SPIRITUAL_REFLECTIONS_ACTIVATE,
@@ -42,9 +50,6 @@ enum Data
ACTION_START_LK_FIGHT,
ACTION_STOP_LK_FIGHT,
ACTION_DELETE_ICE_WALL,
DATA_WAVE_NUMBER,
DATA_LK_BATTLE,// in progress
DATA_SHIP_CAPTAIN,
};
enum Creatures
@@ -249,8 +254,6 @@ enum hMisc
ACTION_START_INTRO,
ACTION_SKIP_INTRO,
ACTION_START_LK_FIGHT_REAL,
ACTION_INFORM_TRASH_DIED,
ACTION_CHECK_TRASH_DIED,
ACTION_INFORM_WALL_DESTROYED,
QUEST_DELIVRANCE_FROM_THE_PIT_A2 = 24710,
@@ -468,6 +468,7 @@ public:
leader->AI()->DoAction(ACTION_START_INTRO);
break;
case ACTION_START_LK_FIGHT:
SetBossState(DATA_LICH_KING, IN_PROGRESS);
_isLichKingFightActive = true;
DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_RETREATING_TIMED_EVENT);
DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_RETREATING_TIMED_EVENT);
@@ -516,15 +517,13 @@ public:
lichKing->SetSpeed(MOVE_RUN, lichKing->GetCreatureTemplate()->speed_run);
}
_isLichKingFightActive = false;
SetBossState(DATA_LICH_KING, FAIL);
_outroTimer = 0;
_outroStep = 0;
[[fallthrough]];
case ACTION_DELETE_ICE_WALL:
HandleGameObject(GO_ICE_WALL, true);
break;
case DATA_LICH_KING:
SetBossState(DATA_LICH_KING, static_cast<EncounterState>(data));
break;
case DATA_BATTERED_HILT:
{
if (GetPersistentData(PERSISTENT_DATA_BATTERED_HILT))
@@ -688,14 +688,14 @@ public:
if (_instance->GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) != IN_PROGRESS)
return;
// Blizzlike: ship wipes when all players are in cannons (vehicles) with nobody on deck
// See: azerothcore/azerothcore-wotlk#17856
bool playerOnDeck = false;
me->GetMap()->DoForAllPlayers([&](Player* player)
{
if (!player->GetVehicle() && player->IsAlive())
playerOnDeck = true;
});
// Wipe if no player is on the deck
if (!playerOnDeck)
{
// Script runs on enemy ship. We want to kill our ship.
@@ -653,6 +653,8 @@ public:
_lastTalkTimeBuff = 0;
_bFrostmournePhase = false;
_bFordringMustFallYell = false;
me->SetRegeneratingHealth(true);
me->SetIsCombatDisallowed(false);
_Reset();
DoAction(ACTION_RESTORE_LIGHT);
@@ -792,12 +794,15 @@ public:
if (_phase == PHASE_THREE && HealthBelowPct(10) && !me->HasUnitState(UNIT_STATE_CASTING))
{
_phase = PHASE_OUTRO;
me->SetRegeneratingHealth(false);
me->SetIsCombatDisallowed(true);
EntryCheckPredicate pred(NPC_STRANGULATE_VEHICLE);
summons.DoAction(ACTION_TELEPORT_BACK, pred);
events.Reset();
summons.DespawnAll();
me->SetReactState(REACT_PASSIVE);
me->AttackStop();
me->CombatStop(true);
me->GetMap()->SetZoneMusic(AREA_THE_FROZEN_THRONE, MUSIC_FURY_OF_FROSTMOURNE);
me->InterruptNonMeleeSpells(true);
me->CastSpell((Unit*)nullptr, SPELL_FURY_OF_FROSTMOURNE, false);
@@ -1253,6 +1258,9 @@ public:
void EnterEvadeMode(EvadeReason why) override
{
if (_phase == PHASE_OUTRO || _phase == PHASE_FROSTMOURNE)
return;
EntryCheckPredicate pred(NPC_STRANGULATE_VEHICLE);
summons.DoAction(ACTION_TELEPORT_BACK, pred);
instance->SetBossState(DATA_THE_LICH_KING, FAIL);
@@ -341,18 +341,12 @@ public:
_done = true;
Talk(SAY_VALITHRIA_SUCCESS);
_instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
_instance->DoRemoveAurasDueToSpellOnPlayers(70766);
me->RemoveAurasDueToSpell(SPELL_CORRUPTION_VALITHRIA);
me->CastSpell(me, SPELL_ACHIEVEMENT_CHECK, true);
me->CastSpell((Unit*)nullptr, SPELL_DREAMWALKERS_RAGE, false);
_events.Reset();
_events.ScheduleEvent(EVENT_DREAM_SLIP, 3500ms);
_instance->SetBossState(DATA_VALITHRIA_DREAMWALKER, DONE);
if (Creature* trigger = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_VALITHRIA_TRIGGER)))
trigger->AI()->EnterEvadeMode();
if (Creature* lichKing = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_VALITHRIA_LICH_KING)))
lichKing->AI()->Reset();
lichKing->AI()->EnterEvadeMode();
}
else if (!_over75PercentTalkDone && me->HealthAbovePctHealed(75, heal))
{
@@ -361,11 +355,8 @@ public:
}
else if (_instance->GetBossState(DATA_VALITHRIA_DREAMWALKER) == NOT_STARTED)
{
if (Creature* trigger = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_VALITHRIA_TRIGGER)))
{
trigger->AI()->DoAction(ACTION_ENTER_COMBAT);
_instance->SetBossState(DATA_VALITHRIA_DREAMWALKER, IN_PROGRESS);
}
if (Creature* archmage = me->FindNearestCreature(NPC_RISEN_ARCHMAGE, 30.0f))
DoZoneInCombat(archmage); // archmage chain will put trigger in combat
}
}
@@ -388,7 +379,7 @@ public:
Talk(SAY_VALITHRIA_DEATH);
_instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
if (Creature* trigger = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_VALITHRIA_TRIGGER)))
trigger->AI()->EnterEvadeMode();
trigger->AI()->DoAction(ACTION_DEATH);
}
}
}
@@ -404,6 +395,8 @@ public:
me->SetDisplayId(11686);
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->DespawnOrUnsummon(4s);
if (Creature* trigger = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_VALITHRIA_TRIGGER)))
Unit::Kill(me, trigger);
if (Creature* lichKing = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_VALITHRIA_LICH_KING)))
lichKing->CastSpell(lichKing, SPELL_SPAWN_CHEST, false);
_instance->SetData(DATA_WEEKLY_QUEST_ID, 0); // show hidden npc if necessary
@@ -434,13 +427,8 @@ public:
void UpdateAI(uint32 diff) override
{
// does not enter combat
if (_instance->GetBossState(DATA_VALITHRIA_DREAMWALKER) == NOT_STARTED)
{
uint32 startingHealth = me->GetMaxHealth() * 0.5f;
if (me->GetHealth() != startingHealth) // healing when boss cannot be engaged (lower spire not finished, cheating) doesn't start the fight, prevent winning this way
me->SetHealth(startingHealth);
if (_instance->GetBossState(DATA_VALITHRIA_DREAMWALKER) != IN_PROGRESS)
return;
}
_events.Update(diff);
@@ -508,31 +496,26 @@ public:
void Reset() override
{
events.Reset();
summons.DespawnAll();
if (instance->GetBossState(DATA_VALITHRIA_DREAMWALKER) != DONE)
instance->SetBossState(DATA_VALITHRIA_DREAMWALKER, NOT_STARTED);
_Reset();
me->SetReactState(REACT_PASSIVE);
checkTimer = 5000;
}
void JustEngagedWith(Unit* target) override
void JustEnteredCombat(Unit* target) override
{
if (IsEngaged())
return;
if (!instance->CheckRequiredBosses(DATA_VALITHRIA_DREAMWALKER, target->ToPlayer()))
{
me->setActive(true);
EnterEvadeMode();
EnterEvadeMode(EVADE_REASON_SEQUENCE_BREAK);
instance->DoCastSpellOnPlayers(LIGHT_S_HAMMER_TELEPORT);
return;
}
if (instance->GetBossState(DATA_VALITHRIA_DREAMWALKER) == DONE)
{
me->CombatStop();
return;
}
EngagementStart(target);
me->setActive(true);
me->SetInCombatWithZone();
DoZoneInCombat();
instance->SetBossState(DATA_VALITHRIA_DREAMWALKER, IN_PROGRESS);
if (Creature* valithria = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_VALITHRIA_DREAMWALKER)))
valithria->AI()->DoAction(ACTION_ENTER_COMBAT);
@@ -547,66 +530,31 @@ public:
(*itr)->AI()->DoAction(ACTION_ENTER_COMBAT);
}
void AttackStart(Unit* target) override
{
if (target->IsPlayer())
BossAI::AttackStart(target);
}
void EnterEvadeMode(EvadeReason why = EVADE_REASON_OTHER) override
{
CreatureAI::EnterEvadeMode(why);
}
void MoveInLineOfSight(Unit* /*who*/) override {}
bool CanAIAttack(Unit const* target) const override
void JustExitedCombat() override
{
return target->IsPlayer();
}
EngagementOver();
void JustReachedHome() override
{
if (instance->GetBossState(DATA_VALITHRIA_DREAMWALKER) != DONE)
DoAction(ACTION_DEATH); // setActive(false) in ValithriaDespawner
else
_JustReachedHome();
me->setActive(false);
if (!me->IsAlive())
return;
if (instance->GetBossState(DATA_VALITHRIA_DREAMWALKER) == DONE)
return;
DoAction(ACTION_DEATH);
}
void DoAction(int32 action) override
{
if (action == ACTION_DEATH)
{
instance->SetBossState(DATA_VALITHRIA_DREAMWALKER, NOT_STARTED);
me->m_Events.AddEventAtOffset(new ValithriaDespawner(me), 5s);
else if (action == ACTION_ENTER_COMBAT)
{
if (!me->IsInCombat())
me->SetInCombatWithZone();
if (Creature* lichKing = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_VALITHRIA_LICH_KING)))
lichKing->AI()->EnterEvadeMode();
}
}
void UpdateAI(uint32 diff) override
{
if (!me->IsInCombat())
return;
if (checkTimer <= diff)
{
checkTimer = 3000;
me->SetInCombatWithZone();
for (ThreatReference const* ref : me->GetThreatMgr().GetUnsortedThreatList())
{
if (Unit* target = ref->GetVictim())
if (target->IsAlive() && target->IsPlayer() && me->GetExactDist(target) < 200.0f && !target->IsImmunedToDamageOrSchool(SPELL_SCHOOL_MASK_ALL))
return;
}
EnterEvadeMode();
}
else
checkTimer -= diff;
}
private:
uint16 checkTimer;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -712,7 +660,7 @@ public:
struct npc_risen_archmageAI : public ScriptedAI
{
npc_risen_archmageAI(Creature* creature) : ScriptedAI(creature),
_instance(creature->GetInstanceScript())
_instance(creature->GetInstanceScript()), _isInitialArchmage(false)
{
}
@@ -725,23 +673,34 @@ public:
{
_events.Reset();
_events.ScheduleEvent(EVENT_FROSTBOLT_VOLLEY, 5s, 15s);
_events.ScheduleEvent(EVENT_MANA_VOID, 15s, 25s);
_events.ScheduleEvent(EVENT_MANA_VOID, 20s, 25s);
_events.ScheduleEvent(EVENT_COLUMN_OF_FROST, 10s, 20s);
_isInitialArchmage = me->GetSpawnId() != 0;
}
void JustEngagedWith(Unit* /*target*/) override
void JustEnteredCombat(Unit* who) override
{
me->FinishSpell(CURRENT_CHANNELED_SPELL, false);
me->SetInCombatWithZone();
if (me->GetSpawnId())
if (IsEngaged())
return;
me->InterruptNonMeleeSpells(false);
EngagementStart(who);
if (_isInitialArchmage)
{
if (Creature* lichKing = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_VALITHRIA_LICH_KING)))
DoZoneInCombat(lichKing);
if (Creature* trigger = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_VALITHRIA_TRIGGER)))
trigger->AI()->DoAction(ACTION_ENTER_COMBAT);
DoZoneInCombat(trigger);
}
}
void DoAction(int32 action) override
{
if (action == ACTION_ENTER_COMBAT && !me->IsInCombat())
me->SetInCombatWithZone();
if (action == ACTION_ENTER_COMBAT)
DoZoneInCombat();
}
void JustSummoned(Creature* summon) override
@@ -754,10 +713,8 @@ public:
void UpdateAI(uint32 diff) override
{
if (!me->IsInCombat())
if (me->GetSpawnId())
if (!me->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
me->CastSpell(me, SPELL_CORRUPTION, true);
if (!me->IsInCombat() && me->IsAlive() && _isInitialArchmage && !me->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
DoCastSelf(SPELL_CORRUPTION);
if (!UpdateVictim())
return;
@@ -796,6 +753,7 @@ public:
private:
EventMap _events;
InstanceScript* _instance;
bool _isInitialArchmage;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -345,11 +345,17 @@ public:
return;
}
uint32 corpseDelay = creature->GetCorpseDelay();
uint32 respawnDelay = creature->GetRespawnDelay();
creature->SetCorpseDelay(1);
creature->SetRespawnDelay(2);
if (CreatureData const* data = creature->GetCreatureData())
creature->SetPosition(data->posX, data->posY, data->posZ, data->orientation);
creature->DespawnOrUnsummon();
creature->SetRespawnTime(5);
creature->SetCorpseDelay(corpseDelay);
creature->SetRespawnDelay(respawnDelay);
}
};
@@ -730,7 +736,7 @@ public:
{
me->SetReactState(REACT_DEFENSIVE);
_didUnderTenPercentText = false;
_wipeCheckTimer = 3000;
_wipeCheckTimer = 1000;
_handledWP4 = false;
_events.Reset();
@@ -868,6 +874,27 @@ public:
void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (!_wipeCheckTimer)
{
_wipeCheckTimer = 1000;
Player* player = nullptr;
Acore::AnyPlayerInObjectRangeCheck check(me, 60.0f);
Acore::PlayerSearcher<Acore::AnyPlayerInObjectRangeCheck> searcher(me, player, check);
Cell::VisitObjects(me, searcher, 60.0f);
// wipe
if (!player)
{
damage *= 100;
if (damage >= me->GetHealth())
{
FrostwingGauntletRespawner respawner;
Acore::CreatureWorker<FrostwingGauntletRespawner> worker(me, respawner);
Cell::VisitObjects(me, worker, 333.0f);
return;
}
}
}
if (HealthBelowPct(10))
{
if (!_didUnderTenPercentText)
@@ -885,37 +912,21 @@ public:
}
}
void UpdateEscortAI(uint32 /*diff*/) override {}
void UpdateEscortAI(uint32 diff) override
{
if (_wipeCheckTimer <= diff)
_wipeCheckTimer = 0;
else
_wipeCheckTimer -= diff;
}
void UpdateAI(uint32 diff) override
{
npc_escortAI::UpdateAI(diff);
//Position pos = me->GetHomePosition();
if (!me->isActiveObject()/* && me->GetExactDist(&pos) < 5.0f*/) // during event
if (!me->isActiveObject())
return;
if (_wipeCheckTimer <= diff)
{
_wipeCheckTimer = 3000;
Player* player = nullptr;
Acore::AnyPlayerInObjectRangeCheck check(me, 140.0f);
Acore::PlayerSearcher<Acore::AnyPlayerInObjectRangeCheck> searcher(me, player, check);
Cell::VisitObjects(me, searcher, 140.0f);
// wipe
if (!player || me->GetExactDist(4357.0f, 2606.0f, 350.0f) > 125.0f)
{
//Talk(SAY_CROK_DEATH);
FrostwingGauntletRespawner respawner;
Acore::CreatureWorker<FrostwingGauntletRespawner> worker(me, respawner);
Cell::VisitObjects(me, worker, 333.0f);
return;
}
}
else
_wipeCheckTimer -= diff;
UpdateVictim();
_events.Update(diff);
@@ -968,7 +979,7 @@ public:
bool CanAIAttack(Unit const* target) const override
{
// do not see targets inside Frostwing Halls when we are not there
return !target->IsPlayer() && (me->GetPositionY() > 2660.0f) == (target->GetPositionY() > 2660.0f) && target->GetEntry() != NPC_SINDRAGOSA;
return (me->GetPositionY() > 2660.0f) == (target->GetPositionY() > 2660.0f);
}
private:
@@ -995,18 +1006,30 @@ public:
struct boss_sister_svalnaAI : public BossAI
{
boss_sister_svalnaAI(Creature* creature) : BossAI(creature, DATA_SISTER_SVALNA)
boss_sister_svalnaAI(Creature* creature) : BossAI(creature, DATA_SISTER_SVALNA),
_isEventInProgress(false)
{
}
void InitializeAI() override
{
if (!me->isDead())
Reset();
me->SetReactState(REACT_PASSIVE);
}
void Reset() override
{
_Reset();
me->SetImmuneToAll(true);
me->SetReactState(REACT_DEFENSIVE);
_isEventInProgress = false;
}
void JustReachedHome() override
{
_JustReachedHome();
me->SetReactState(REACT_PASSIVE);
me->SetCanFly(true);
me->SetDisableGravity(true);
me->SendMovementFlagUpdate();
me->SetDisableGravity(false);
}
void JustDied(Unit* /*killer*/) override
@@ -1080,6 +1103,8 @@ public:
break;
case ACTION_START_GAUNTLET:
me->setActive(true);
me->SetImmuneToAll(true);
_isEventInProgress = true;
events.ScheduleEvent(EVENT_SVALNA_START, 25s);
break;
case ACTION_RESURRECT_CAPTAINS:
@@ -1106,16 +1131,21 @@ public:
}
}
void JustExitedCombat() override
{
if (_isEventInProgress)
return;
CreatureAI::JustExitedCombat();
}
void MovementInform(uint32 type, uint32 id) override
{
if (type != EFFECT_MOTION_TYPE || id != POINT_LAND)
return;
_isEventInProgress = false;
me->setActive(false);
me->SetImmuneToAll(false);
me->SetCanFly(false);
me->SetDisableGravity(false);
me->SetReactState(REACT_AGGRESSIVE);
DoZoneInCombat(nullptr, 150.0f);
}
void SpellHitTarget(Unit* target, SpellInfo const* spell) override
@@ -1140,43 +1170,51 @@ public:
void UpdateAI(uint32 diff) override
{
if (!me->isActiveObject())
if (!UpdateVictim() && !_isEventInProgress)
return;
UpdateVictim();
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
while (uint32 eventId = events.ExecuteEvent())
{
case EVENT_SVALNA_START:
Talk(SAY_SVALNA_EVENT_START);
break;
case EVENT_SVALNA_RESURRECT:
Talk(SAY_SVALNA_RESURRECT_CAPTAINS);
me->CastSpell(me, SPELL_REVIVE_CHAMPION, false);
break;
case EVENT_SVALNA_COMBAT:
Talk(SAY_SVALNA_AGGRO);
break;
case EVENT_IMPALING_SPEAR:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, false, -SPELL_IMPALING_SPEAR))
{
DoCast(me, SPELL_AETHER_SHIELD);
me->AddAura(70203, me);
DoCast(target, SPELL_IMPALING_SPEAR);
}
events.ScheduleEvent(EVENT_IMPALING_SPEAR, 20s, 25s);
break;
default:
break;
switch (eventId)
{
case EVENT_SVALNA_START:
Talk(SAY_SVALNA_EVENT_START);
break;
case EVENT_SVALNA_RESURRECT:
Talk(SAY_SVALNA_RESURRECT_CAPTAINS);
me->CastSpell(me, SPELL_REVIVE_CHAMPION, false);
break;
case EVENT_SVALNA_COMBAT:
me->SetReactState(REACT_DEFENSIVE);
Talk(SAY_SVALNA_AGGRO);
break;
case EVENT_IMPALING_SPEAR:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, false, -SPELL_IMPALING_SPEAR))
{
DoCast(me, SPELL_AETHER_SHIELD);
me->AddAura(70203, me);
DoCast(target, SPELL_IMPALING_SPEAR);
}
events.ScheduleEvent(EVENT_IMPALING_SPEAR, 20s, 25s);
break;
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
DoMeleeAttackIfReady();
}
private:
bool _isEventInProgress;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -113,8 +113,6 @@ Position const FirstWaveLocations[FIRST_WAVE_SIZE] =
{ 481.388f, -507.109f, 104.724f, 3.26377f },
};
Position const SecondaryWavesInitialPoint = { 478.743f, -505.576f, 104.724f };
enum GraufPoints
{
POINT_BREACH = 0,
@@ -122,11 +120,19 @@ enum GraufPoints
POINT_RIGHT = 2,
};
uint32 const PATH_INITIAL = 2689300;
uint32 const PATH_RIGHT = 2689301;
uint32 const PATH_LEFT = 2689302;
enum GraufPaths
{
PATH_INITIAL = 2689300,
PATH_RIGHT = 2689301,
PATH_LEFT = 2689302,
PATH_BREACH_RIGHT = 2689303,
PATH_BREACH_LEFT = 2689304,
PATH_GAUNTLET_ADDS = 2669000,
};
float const BreachFacing = 2.670354f;
float const BreathFacingRight = 3.124139f;
float const BreathFacingLeft = 3.228859f;
enum Events
{
@@ -139,6 +145,7 @@ enum Events
// Grauf
EVENT_GRAUF_START = 10,
EVENT_GRAUF_LEAVE_BREACH = 11,
EVENT_GRAUF_BREATH_START = 12,
EVENT_GRAUF_REMOVE_AURA = 13,
};
@@ -193,7 +200,10 @@ public:
case NPC_YMIRJAR_WITCH_DOCTOR:
case NPC_YMIRJAR_HARPOONER:
if (_firstWaveSummoned)
summon->GetMotionMaster()->MovePoint(1, SecondaryWavesInitialPoint);
{
summon->LoadPath(PATH_GAUNTLET_ADDS);
summon->GetMotionMaster()->MoveWaypoint(PATH_GAUNTLET_ADDS, false);
}
break;
default:
break;
@@ -404,6 +414,7 @@ public:
_summons.DespawnAll();
me->SetReactState(REACT_PASSIVE);
me->SetSpeedRate(MOVE_RUN, 2.5f);
_flyingToSide = false;
}
void DoAction(int32 param) override
@@ -430,9 +441,19 @@ public:
{
if (type == ESCORT_MOTION_TYPE && me->movespline->Finalized())
{
me->SetFacingTo(BreachFacing);
Talk(EMOTE_ON_RANGE);
_events.ScheduleEvent(EVENT_GRAUF_LEAVE_BREACH, 10s);
if (_flyingToSide)
{
_flyingToSide = false;
me->SetFacingTo(_lastSide == POINT_LEFT ? BreathFacingLeft : BreathFacingRight);
Talk(EMOTE_DEEP_BREATH);
_events.ScheduleEvent(EVENT_GRAUF_BREATH_START, 2s);
}
else
{
me->SetFacingTo(BreachFacing);
Talk(EMOTE_ON_RANGE);
_events.ScheduleEvent(EVENT_GRAUF_LEAVE_BREACH, 10s);
}
}
}
@@ -443,16 +464,20 @@ public:
{
case EVENT_GRAUF_LEAVE_BREACH:
_lastSide = RAND(POINT_LEFT, POINT_RIGHT);
me->GetMotionMaster()->MovePath(_lastSide == POINT_LEFT ? PATH_LEFT : PATH_RIGHT, FORCED_MOVEMENT_RUN);
if (_lastSide == POINT_LEFT)
DoCast(me, SPELL_FREEZING_CLOUD_LEFT_PERIODIC);
else
DoCast(me, SPELL_FREEZING_CLOUD_RIGHT_PERIODIC);
_flyingToSide = true;
me->GetMotionMaster()->MovePath(
_lastSide == POINT_LEFT ? PATH_BREACH_LEFT : PATH_BREACH_RIGHT,
FORCED_MOVEMENT_RUN);
break;
case EVENT_GRAUF_BREATH_START:
me->GetMotionMaster()->MovePath(
_lastSide == POINT_LEFT ? PATH_LEFT : PATH_RIGHT,
FORCED_MOVEMENT_RUN);
DoCast(me, _lastSide == POINT_LEFT
? SPELL_FREEZING_CLOUD_LEFT_PERIODIC
: SPELL_FREEZING_CLOUD_RIGHT_PERIODIC);
if (Creature* skadi = _instance->GetCreature(DATA_SKADI_THE_RUTHLESS))
skadi->AI()->DoAction(ACTION_DRAKE_BREATH);
_events.ScheduleEvent(EVENT_GRAUF_REMOVE_AURA, 10s);
break;
case EVENT_GRAUF_REMOVE_AURA:
@@ -480,6 +505,7 @@ public:
EventMap _events;
SummonList _summons;
uint8 _lastSide = POINT_LEFT;
bool _flyingToSide = false;
};
};
@@ -507,7 +533,7 @@ class spell_skadi_launch_harpoon : public SpellScript
}
};
// 50255 - Poisoned Spear
// 50255, 59331 - Poisoned Spear
class spell_skadi_poisoned_spear : public SpellScript
{
PrepareSpellScript(spell_skadi_poisoned_spear);
@@ -157,8 +157,7 @@ public:
{
if (pSummoned)
{
pSummoned->GetMotionMaster()->MoveFollow(me, 0.0f, 0.0f);
pSummoned->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
pSummoned->GetMotionMaster()->MoveFollow(me, 0.0f, 0.0f, MOTION_SLOT_ACTIVE, true, false);
spheres.Summon(pSummoned);
if (pInstance)
pInstance->SetGuidData(DATA_ADD_TRASH_MOB, pSummoned->GetGUID());
@@ -239,50 +239,6 @@ public:
}
};
/*######
## npc_iruk
######*/
enum Iruk
{
GOSSIP_MENU_ID_NPC_IRUK = 9280,
GOSSIP_OPTION_SEARCH_CORPSE = 0,
NPC_TEXT_THIS_YOUNG_TUSKARR = 12585,
QUEST_SPIRITS_WATCH_OVER_US = 11961,
SPELL_CREATE_TOTEM_OF_ISSLIRUK = 46816
};
class npc_iruk : public CreatureScript
{
public:
npc_iruk() : CreatureScript("npc_iruk") { }
bool OnGossipHello(Player* player, Creature* creature) override
{
if (player->GetQuestStatus(QUEST_SPIRITS_WATCH_OVER_US) == QUEST_STATUS_INCOMPLETE)
AddGossipItemFor(player, GOSSIP_MENU_ID_NPC_IRUK, GOSSIP_OPTION_SEARCH_CORPSE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
SendGossipMenuFor(player, NPC_TEXT_THIS_YOUNG_TUSKARR, creature->GetGUID());
return true;
}
bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
if (action == GOSSIP_ACTION_INFO_DEF + 1)
{
player->CastSpell(player, SPELL_CREATE_TOTEM_OF_ISSLIRUK, true);
CloseGossipMenuFor(player);
}
return true;
}
};
/*######
## npc_nerubar_victim
######*/
@@ -1379,7 +1335,6 @@ void AddSC_borean_tundra()
RegisterSpellScript(spell_q11919_q11940_drake_hunt_aura);
new npc_sinkhole_kill_credit();
new npc_khunok_the_behemoth();
new npc_iruk();
new npc_nerubar_victim();
new npc_lurgglbr();
RegisterSpellScript(spell_arcane_chains_character_force_cast);
@@ -20,7 +20,6 @@
#include "Player.h"
#include "ScriptedCreature.h"
#include "ScriptedEscortAI.h"
#include "ScriptedGossip.h"
#include "SpellInfo.h"
#include "SpellScript.h"
@@ -227,73 +226,6 @@ public:
}
};
/*######
## npc_razael_and_lyana
######*/
enum Razael
{
QUEST_REPORTS_FROM_THE_FIELD = 11221,
NPC_RAZAEL = 23998,
NPC_LYANA = 23778,
GOSSIP_TEXTID_RAZAEL1 = 11562,
GOSSIP_TEXTID_RAZAEL2 = 11564,
GOSSIP_TEXTID_LYANA1 = 11586,
GOSSIP_TEXTID_LYANA2 = 11588
};
class npc_razael_and_lyana : public CreatureScript
{
public:
npc_razael_and_lyana() : CreatureScript("npc_razael_and_lyana") { }
bool OnGossipHello(Player* player, Creature* creature) override
{
if (creature->IsQuestGiver())
player->PrepareQuestMenu(creature->GetGUID());
if (player->GetQuestStatus(QUEST_REPORTS_FROM_THE_FIELD) == QUEST_STATUS_INCOMPLETE)
switch (creature->GetEntry())
{
case NPC_RAZAEL:
if (!player->GetReqKillOrCastCurrentCount(QUEST_REPORTS_FROM_THE_FIELD, NPC_RAZAEL))
{
AddGossipItemFor(player, 8870, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
SendGossipMenuFor(player, GOSSIP_TEXTID_RAZAEL1, creature->GetGUID());
return true;
}
break;
case NPC_LYANA:
if (!player->GetReqKillOrCastCurrentCount(QUEST_REPORTS_FROM_THE_FIELD, NPC_LYANA))
{
AddGossipItemFor(player, 8879, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
SendGossipMenuFor(player, GOSSIP_TEXTID_LYANA1, creature->GetGUID());
return true;
}
break;
}
SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID());
return true;
}
bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
switch (action)
{
case GOSSIP_ACTION_INFO_DEF + 1:
SendGossipMenuFor(player, GOSSIP_TEXTID_RAZAEL2, creature->GetGUID());
player->TalkedToCreature(NPC_RAZAEL, creature->GetGUID());
break;
case GOSSIP_ACTION_INFO_DEF + 2:
SendGossipMenuFor(player, GOSSIP_TEXTID_LYANA2, creature->GetGUID());
player->TalkedToCreature(NPC_LYANA, creature->GetGUID());
break;
}
return true;
}
};
enum RodinLightningSpells
{
SPELL_RODIN_LIGHTNING_START = 44787,
@@ -490,7 +422,6 @@ void AddSC_howling_fjord()
new npc_attracted_reef_bull();
new npc_apothecary_hanes();
new npc_plaguehound_tracker();
new npc_razael_and_lyana();
RegisterCreatureAI(npc_rodin_lightning_enabler);
RegisterSpellScript(spell_hawk_hunting);
RegisterSpellScript(spell_the_cleansing_shrine_cast);
@@ -387,64 +387,6 @@ public:
}
};
/*######
## avatar_of_freya
######*/
enum Freya
{
QUEST_FREYA_PACT = 12621,
SPELL_FREYA_CONVERSATION = 52045,
GOSSIP_AVATAR_MENU_1 = 9720,
GOSSIP_AVATAR_MENU_2 = 9721,
GOSSIP_AVATAR_MENU_3 = 9722,
GOSSIP_TEXTID_AVATAR_1 = 13303,
GOSSIP_TEXTID_AVATAR_2 = 13304,
GOSSIP_TEXTID_AVATAR_3 = 13305,
};
class npc_avatar_of_freya : public CreatureScript
{
public:
npc_avatar_of_freya() : CreatureScript("npc_avatar_of_freya") { }
bool OnGossipHello(Player* player, Creature* creature) override
{
if (creature->IsQuestGiver())
player->PrepareQuestMenu(creature->GetGUID());
if (player->GetQuestStatus(QUEST_FREYA_PACT) == QUEST_STATUS_INCOMPLETE)
AddGossipItemFor(player, GOSSIP_AVATAR_MENU_1, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
SendGossipMenuFor(player, GOSSIP_TEXTID_AVATAR_1, creature);
return true;
}
bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
switch (action)
{
case GOSSIP_ACTION_INFO_DEF+1:
AddGossipItemFor(player, GOSSIP_AVATAR_MENU_2, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
SendGossipMenuFor(player, GOSSIP_TEXTID_AVATAR_2, creature);
break;
case GOSSIP_ACTION_INFO_DEF+2:
AddGossipItemFor(player, GOSSIP_AVATAR_MENU_3, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
SendGossipMenuFor(player, GOSSIP_TEXTID_AVATAR_3, creature);
break;
case GOSSIP_ACTION_INFO_DEF+3:
player->CastSpell(player, SPELL_FREYA_CONVERSATION, true);
CloseGossipMenuFor(player);
break;
}
return true;
}
};
/*######
## npc_bushwhacker
######*/
@@ -1187,7 +1129,6 @@ void AddSC_sholazar_basin()
new go_pressure_valve();
new go_brazier();
new npc_vekjik();
new npc_avatar_of_freya();
new npc_bushwhacker();
new npc_engineer_helice();
new npc_adventurous_dwarf();
@@ -22,7 +22,6 @@
#include "ScriptedCreature.h"
#include "SpellMgr.h"
#include "ScriptedEscortAI.h"
#include "ScriptedGossip.h"
#include "SpellAuraEffects.h"
#include "SpellScript.h"
#include "SpellScriptLoader.h"
@@ -339,56 +338,6 @@ class spell_q13007_iron_colossus : public SpellScript
}
};
/*######
## npc_roxi_ramrocket
######*/
enum RoxiRamrocket
{
SPELL_MECHANO_HOG = 60866,
SPELL_MEKGINEERS_CHOPPER = 60867
};
class npc_roxi_ramrocket : public CreatureScript
{
public:
npc_roxi_ramrocket() : CreatureScript("npc_roxi_ramrocket") { }
bool OnGossipHello(Player* player, Creature* creature) override
{
//Quest Menu
if (creature->IsQuestGiver())
player->PrepareQuestMenu(creature->GetGUID());
//Trainer Menu
if (creature->IsTrainer())
AddGossipItemFor(player, GOSSIP_ICON_TRAINER, GOSSIP_TEXT_TRAIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRAIN);
//Vendor Menu
if (creature->IsVendor())
if (player->HasSpell(SPELL_MECHANO_HOG) || player->HasSpell(SPELL_MEKGINEERS_CHOPPER))
AddGossipItemFor(player, GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID());
return true;
}
bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
switch (action)
{
case GOSSIP_ACTION_TRAIN:
player->GetSession()->SendTrainerList(creature);
break;
case GOSSIP_ACTION_TRADE:
player->GetSession()->SendListInventory(creature->GetGUID());
break;
}
return true;
}
};
/*######
## npc_brunnhildar_prisoner
######*/
@@ -1386,7 +1335,6 @@ void AddSC_storm_peaks()
new npc_iron_watcher();
new npc_time_lost_proto_drake();
RegisterSpellScript(spell_q13007_iron_colossus);
new npc_roxi_ramrocket();
new npc_brunnhildar_prisoner();
new npc_freed_protodrake();
new npc_icefang();
@@ -83,6 +83,9 @@ public:
void Reset() override
{
if (_defeated)
return;
faction_Timer = 8000;
envelopingWinds_Timer = 9000;
shock_Timer = 5000;
@@ -93,6 +96,15 @@ public:
Talk(SAY_SUMMON);
}
void JustReachedHome() override
{
if (_defeated)
{
Talk(SAY_FREE);
me->SetNpcFlag(UNIT_NPC_FLAG_QUESTGIVER);
}
}
void UpdateAI(uint32 diff) override
{
if (faction_Timer)
@@ -110,12 +122,11 @@ public:
if (HealthBelowPct(30))
{
me->SetFaction(FACTION_FRIENDLY);
me->SetNpcFlag(UNIT_NPC_FLAG_QUESTGIVER);
_defeated = true;
me->RemoveAllAuras();
me->GetThreatMgr().ClearAllThreat();
me->SetFaction(FACTION_FRIENDLY);
me->CombatStop(true);
Talk(SAY_FREE);
me->GetThreatMgr().ClearAllThreat();
return;
}
@@ -140,6 +151,7 @@ public:
uint32 faction_Timer;
uint32 envelopingWinds_Timer;
uint32 shock_Timer;
bool _defeated = false;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -20,7 +20,6 @@
#include "Group.h"
#include "Player.h"
#include "ScriptedCreature.h"
#include "ScriptedGossip.h"
#include "SpellScript.h"
#include "SpellScriptLoader.h"
@@ -657,98 +656,6 @@ public:
};
};
/*######
## npc_drake_dealer_hurlunk
######*/
class npc_drake_dealer_hurlunk : public CreatureScript
{
public:
npc_drake_dealer_hurlunk() : CreatureScript("npc_drake_dealer_hurlunk") { }
bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
if (action == GOSSIP_ACTION_TRADE)
player->GetSession()->SendListInventory(creature->GetGUID());
return true;
}
bool OnGossipHello(Player* player, Creature* creature) override
{
if (creature->IsVendor() && player->GetReputationRank(1015) == REP_EXALTED)
AddGossipItemFor(player, GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID());
return true;
}
};
/*######
## npc_flanis_swiftwing_and_kagrosh
######*/
enum Flanis : uint32
{
QUEST_THE_FATE_OF_FLANIS = 10583,
ITEM_FLAUNISS_PACK = 30658,
GOSSIP_MENU_FLANIS = 8356,
};
enum Kagrosh : uint32
{
QUEST_THE_FATE_OF_KAGROSH = 10601,
ITEM_KAGROSHS_PACK = 30659,
GOSSIP_MENU_KAGROSH = 8371,
};
class npcs_flanis_swiftwing_and_kagrosh : public CreatureScript
{
public:
npcs_flanis_swiftwing_and_kagrosh() : CreatureScript("npcs_flanis_swiftwing_and_kagrosh") { }
bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
if (action == GOSSIP_ACTION_INFO_DEF + 1)
{
ItemPosCountVec dest;
uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, ITEM_FLAUNISS_PACK, 1, nullptr);
if (msg == EQUIP_ERR_OK)
{
player->StoreNewItem(dest, ITEM_FLAUNISS_PACK, true);
}
}
if (action == GOSSIP_ACTION_INFO_DEF + 2)
{
ItemPosCountVec dest;
uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, ITEM_KAGROSHS_PACK, 1, nullptr);
if (msg == EQUIP_ERR_OK)
{
player->StoreNewItem(dest, ITEM_KAGROSHS_PACK, true);
}
}
CloseGossipMenuFor(player);
return true;
}
bool OnGossipHello(Player* player, Creature* creature) override
{
if (player->GetQuestStatus(QUEST_THE_FATE_OF_FLANIS) == QUEST_STATUS_INCOMPLETE && !player->HasItemCount(ITEM_FLAUNISS_PACK, 1, true))
AddGossipItemFor(player, GOSSIP_MENU_FLANIS, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
if (player->GetQuestStatus(QUEST_THE_FATE_OF_KAGROSH) == QUEST_STATUS_INCOMPLETE && !player->HasItemCount(ITEM_KAGROSHS_PACK, 1, true))
AddGossipItemFor(player, GOSSIP_MENU_KAGROSH, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID());
return true;
}
};
/*####
# npc_karynaku
####*/
@@ -2219,8 +2126,6 @@ void AddSC_shadowmoon_valley()
new npc_mature_netherwing_drake();
RegisterCreatureAI(npc_enslaved_netherwing_drake);
new npc_dragonmaw_peon();
new npc_drake_dealer_hurlunk();
new npcs_flanis_swiftwing_and_kagrosh();
new npc_karynaku();
new npc_lord_illidan_stormrage();
new go_crystal_prison();
@@ -20,101 +20,6 @@
#include "Player.h"
#include "ScriptedCreature.h"
#include "ScriptedEscortAI.h"
#include "ScriptedGossip.h"
/*
##################################################
Shattrath City Flask Vendors provides flasks to people exalted with 3 fActions:
Haldor the Compulsive
Arcanist Xorith
Both sell special flasks for use in Outlands 25man raids only,
purchasable for one Mark of Illidari each
Purchase requires exalted reputation with Scryers/Aldor, Cenarion Expedition and The Sha'tar
##################################################
*/
class npc_shattrathflaskvendors : public CreatureScript
{
public:
npc_shattrathflaskvendors() : CreatureScript("npc_shattrathflaskvendors") { }
bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
if (action == GOSSIP_ACTION_TRADE)
player->GetSession()->SendListInventory(creature->GetGUID());
return true;
}
bool OnGossipHello(Player* player, Creature* creature) override
{
if (creature->GetEntry() == 23484)
{
// Aldor vendor
if (creature->IsVendor() && (player->GetReputationRank(932) == REP_EXALTED) && (player->GetReputationRank(935) == REP_EXALTED) && (player->GetReputationRank(942) == REP_EXALTED))
{
AddGossipItemFor(player, GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
SendGossipMenuFor(player, 11085, creature->GetGUID());
}
else
{
SendGossipMenuFor(player, 11083, creature->GetGUID());
}
}
if (creature->GetEntry() == 23483)
{
// Scryers vendor
if (creature->IsVendor() && (player->GetReputationRank(934) == REP_EXALTED) && (player->GetReputationRank(935) == REP_EXALTED) && (player->GetReputationRank(942) == REP_EXALTED))
{
AddGossipItemFor(player, GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
SendGossipMenuFor(player, 11085, creature->GetGUID());
}
else
{
SendGossipMenuFor(player, 11084, creature->GetGUID());
}
}
return true;
}
};
/*######
# npc_zephyr
######*/
enum Zephyr : int32
{
GOSSIP_MENU_ZEPHYR = 9205,
SPELL_TELEPORT_CAVERNS_OF_TIME = 37778,
};
class npc_zephyr : public CreatureScript
{
public:
npc_zephyr() : CreatureScript("npc_zephyr") { }
bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
if (action == GOSSIP_ACTION_INFO_DEF + 1)
player->CastSpell(player, SPELL_TELEPORT_CAVERNS_OF_TIME, false);
return true;
}
bool OnGossipHello(Player* player, Creature* creature) override
{
if (player->GetReputationRank(989) >= REP_REVERED)
AddGossipItemFor(player, GOSSIP_MENU_ZEPHYR, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID());
return true;
}
};
/*######
# npc_kservant
@@ -415,8 +320,6 @@ struct npc_shattrath_daily_quest : public NullCreatureAI
void AddSC_shattrath_city()
{
new npc_shattrathflaskvendors();
new npc_zephyr();
new npc_kservant();
RegisterCreatureAI(npc_shattrath_daily_quest);
}
+1 -7
View File
@@ -314,11 +314,7 @@ public:
struct npc_training_dummy : NullCreatureAI
{
npc_training_dummy(Creature* creature) : NullCreatureAI(creature)
{
// TODO: Remove once WorldObject casting is ported
me->SetIsCombatDisallowed(false);
}
npc_training_dummy(Creature* creature) : NullCreatureAI(creature) { }
void JustEnteredCombat(Unit* who) override
{
@@ -363,8 +359,6 @@ struct npc_target_dummy : NullCreatureAI
{
npc_target_dummy(Creature* creature) : NullCreatureAI(creature)
{
// TODO: Remove once WorldObject casting is ported
me->SetIsCombatDisallowed(false);
_deathTimer = 15s;
}
-2
View File
@@ -1360,8 +1360,6 @@ inline constexpr uint64 IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK =
(UI64LIT(1) << MECHANIC_SNARE) |
(UI64LIT(1) << MECHANIC_STUN) |
(UI64LIT(1) << MECHANIC_FREEZE) |
(UI64LIT(1) << MECHANIC_SILENCE) |
(UI64LIT(1) << MECHANIC_DISARM) |
(UI64LIT(1) << MECHANIC_KNOCKOUT) |
(UI64LIT(1) << MECHANIC_POLYMORPH) |
(UI64LIT(1) << MECHANIC_BANISH) |