Merge branch 'master' into Playerbot

This commit is contained in:
Yunfan Li
2023-07-04 10:05:31 +08:00
59 changed files with 1562 additions and 845 deletions

View File

@@ -113,7 +113,6 @@ bool LoadRealmInfo(Acore::Asio::IoContext& ioContext);
AsyncAcceptor* StartRaSocketAcceptor(Acore::Asio::IoContext& ioContext);
void ShutdownCLIThread(std::thread* cliThread);
void AuctionListingRunnable();
void ShutdownAuctionListingThread(std::thread* thread);
void WorldUpdateLoop();
variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, [[maybe_unused]] std::string& cfg_service);
@@ -399,9 +398,9 @@ int main(int argc, char** argv)
cliThread.reset(new std::thread(CliThread), &ShutdownCLIThread);
}
// Launch CliRunnable thread
std::shared_ptr<std::thread> auctionLisingThread;
auctionLisingThread.reset(new std::thread(AuctionListingRunnable),
// Launch auction listing thread
std::shared_ptr<std::thread> auctionListingThread;
auctionListingThread.reset(new std::thread(AuctionListingRunnable),
[](std::thread* thr)
{
thr->join();
@@ -729,42 +728,41 @@ void AuctionListingRunnable()
while (!World::IsStopped())
{
if (AsyncAuctionListingMgr::IsAuctionListingAllowed())
Milliseconds diff = AsyncAuctionListingMgr::GetDiff();
AsyncAuctionListingMgr::ResetDiff();
if (!AsyncAuctionListingMgr::GetTempList().empty() || !AsyncAuctionListingMgr::GetList().empty())
{
uint32 diff = AsyncAuctionListingMgr::GetDiff();
AsyncAuctionListingMgr::ResetDiff();
if (AsyncAuctionListingMgr::GetTempList().size() || AsyncAuctionListingMgr::GetList().size())
{
std::lock_guard<std::mutex> guard(AsyncAuctionListingMgr::GetLock());
std::lock_guard<std::mutex> guard(AsyncAuctionListingMgr::GetTempLock());
for (auto const& delayEvent: AsyncAuctionListingMgr::GetTempList())
AsyncAuctionListingMgr::GetList().emplace_back(delayEvent);
AsyncAuctionListingMgr::GetTempList().clear();
}
for (auto& itr: AsyncAuctionListingMgr::GetList())
{
if (itr._pickupTimer <= diff)
{
std::lock_guard<std::mutex> guard(AsyncAuctionListingMgr::GetTempLock());
for (auto const& delayEvent : AsyncAuctionListingMgr::GetTempList())
AsyncAuctionListingMgr::GetList().emplace_back(delayEvent);
AsyncAuctionListingMgr::GetTempList().clear();
itr._pickupTimer = Milliseconds::zero();
}
for (auto& itr : AsyncAuctionListingMgr::GetList())
else
{
if (itr._msTimer <= diff)
itr._msTimer = 0;
else
itr._msTimer -= diff;
itr._pickupTimer -= diff;
}
}
for (std::list<AuctionListItemsDelayEvent>::iterator itr = AsyncAuctionListingMgr::GetList().begin(); itr != AsyncAuctionListingMgr::GetList().end(); ++itr)
{
if ((*itr)._msTimer != 0)
continue;
for (auto itr = AsyncAuctionListingMgr::GetList().begin(); itr != AsyncAuctionListingMgr::GetList().end(); ++itr)
{
if ((*itr)._pickupTimer != Milliseconds::zero())
continue;
if ((*itr).Execute())
AsyncAuctionListingMgr::GetList().erase(itr);
if ((*itr).Execute())
AsyncAuctionListingMgr::GetList().erase(itr);
break;
}
break;
}
}
std::this_thread::sleep_for(1ms);
@@ -773,15 +771,6 @@ void AuctionListingRunnable()
LOG_INFO("server", "Auction House Listing thread exiting without problems.");
}
void ShutdownAuctionListingThread(std::thread* thread)
{
if (thread)
{
thread->join();
delete thread;
}
}
variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, [[maybe_unused]] std::string& configService)
{
options_description all("Allowed options");
@@ -791,7 +780,7 @@ variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, [
("dry-run,d", "Dry run")
("config,c", value<fs::path>(&configFile)->default_value(fs::path(sConfigMgr->GetConfigPath() + std::string(_ACORE_CORE_CONFIG))), "use <arg> as configuration file");
#if AC_PLATFORM == WARHEAD_PLATFORM_WINDOWS
#if AC_PLATFORM == AC_PLATFORM_WINDOWS
options_description win("Windows platform specific options");
win.add_options()
("service,s", value<std::string>(&configService)->default_value(""), "Windows service options: [install | uninstall]");

View File

@@ -3843,6 +3843,13 @@ ChangeFaction.MaxMoney = 0
Pet.RankMod.Health = 1
#
# AuctionHouse.SearchTimeout
# Description: Time (in milliseconds) after which an auction house search is discarded.
# Default: 1000 - (1 second)
AuctionHouse.SearchTimeout = 1000
#
###################################################################################################

View File

@@ -988,7 +988,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (!IsSmart())
break;
if (targets.empty())
if (e.target.type == SMART_TARGET_NONE || e.target.type == SMART_TARGET_SELF)
{
CAST_AI(SmartAI, me->AI())->StopFollow(false);
break;
@@ -1644,6 +1644,15 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
break;
}
if (e.action.orientation.turnAngle)
{
float turnOri = me->GetOrientation() + (static_cast<float>(e.action.orientation.turnAngle) * M_PI / 180.0f);
me->SetFacingTo(turnOri);
if (e.action.orientation.quickChange)
me->SetOrientation(turnOri);
break;
}
if (e.GetTargetType() == SMART_TARGET_SELF)
{
me->SetFacingTo((me->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && me->GetTransGUID() ? me->GetTransportHomePosition() : me->GetHomePosition()).GetOrientation());
@@ -2824,6 +2833,48 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
}
break;
}
case SMART_ACTION_SET_SCALE:
{
float scale = static_cast<float>(e.action.setScale.scale) / 100.0f;
for (WorldObject* target : targets)
{
if (IsUnit(target))
{
target->ToUnit()->SetObjectScale(scale);
}
}
break;
}
case SMART_ACTION_SUMMON_RADIAL:
{
if (!me)
break;
TempSummonType spawnType = (e.action.radialSummon.summonDuration > 0) ? TEMPSUMMON_TIMED_DESPAWN : TEMPSUMMON_CORPSE_DESPAWN;
float startAngle = me->GetOrientation() + (static_cast<float>(e.action.radialSummon.startAngle) * M_PI / 180.0f);
float stepAngle = static_cast<float>(e.action.radialSummon.stepAngle) * M_PI / 180.0f;
if (e.action.radialSummon.dist)
{
for (uint32 itr = 0; itr < e.action.radialSummon.repetitions; itr++)
{
Position summonPos = me->GetPosition();
summonPos.RelocatePolarOffset(itr * stepAngle, static_cast<float>(e.action.radialSummon.dist));
me->SummonCreature(e.action.radialSummon.summonEntry, summonPos, spawnType, e.action.radialSummon.summonDuration);
}
break;
}
for (uint32 itr = 0; itr < e.action.radialSummon.repetitions; itr++)
{
float currentAngle = startAngle + (itr * stepAngle);
me->SummonCreature(e.action.radialSummon.summonEntry, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), currentAngle, spawnType, e.action.radialSummon.summonDuration);
}
break;
}
default:
LOG_ERROR("sql.sql", "SmartScript::ProcessAction: Entry {} SourceType {}, Event {}, Unhandled Action type {}", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
break;
@@ -4157,34 +4208,105 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
break;
case SMART_EVENT_NEAR_PLAYERS:
{
ObjectVector units;
GetWorldObjectsInDist(units, static_cast<float>(e.event.nearPlayer.radius));
uint32 playerCount = 0;
ObjectVector targets;
GetWorldObjectsInDist(targets, static_cast<float>(e.event.nearPlayer.radius));
if (!units.empty())
if (!targets.empty())
{
if (!unit || unit->GetTypeId() != TYPEID_PLAYER)
return;
for (WorldObject* target : targets)
{
if (IsPlayer(target))
playerCount++;
if (units.size() >= e.event.nearPlayer.minCount)
ProcessAction(e, unit);
if (playerCount >= e.event.nearPlayer.minCount)
ProcessAction(e, target->ToUnit());
}
}
RecalcTimer(e, e.event.nearPlayer.checkTimer, e.event.nearPlayer.checkTimer);
RecalcTimer(e, e.event.nearPlayer.repeatMin, e.event.nearPlayer.repeatMax);
break;
}
case SMART_EVENT_NEAR_PLAYERS_NEGATION:
{
ObjectVector units;
GetWorldObjectsInDist(units, static_cast<float>(e.event.nearPlayerNegation.radius));
uint32 playerCount = 0;
ObjectVector targets;
GetWorldObjectsInDist(targets, static_cast<float>(e.event.nearPlayerNegation.radius));
if (!units.empty())
if (!targets.empty())
{
if (!unit || unit->GetTypeId() != TYPEID_PLAYER)
return;
for (WorldObject* target : targets)
{
if (IsPlayer(target))
playerCount++;
}
if (units.size() < e.event.nearPlayerNegation.minCount)
if (playerCount <= e.event.nearPlayerNegation.maxCount)
ProcessAction(e, unit);
}
RecalcTimer(e, e.event.nearPlayerNegation.checkTimer, e.event.nearPlayerNegation.checkTimer);
RecalcTimer(e, e.event.nearPlayerNegation.repeatMin, e.event.nearPlayerNegation.repeatMax);
break;
}
case SMART_EVENT_NEAR_UNIT:
{
uint32 unitCount = 0;
ObjectVector targets;
GetWorldObjectsInDist(targets, static_cast<float>(e.event.nearUnit.range));
if (!targets.empty())
{
if (e.event.nearUnit.type)
{
for (WorldObject* target : targets)
{
if (IsGameObject(target) && target->GetEntry() == e.event.nearUnit.entry)
unitCount++;
}
}
else
{
for (WorldObject* target : targets)
{
if (IsCreature(target) && target->GetEntry() == e.event.nearUnit.entry)
unitCount++;
}
}
if (unitCount >= e.event.nearUnit.count)
ProcessAction(e, unit);
}
RecalcTimer(e, e.event.nearUnit.timer, e.event.nearUnit.timer);
break;
}
case SMART_EVENT_AREA_CASTING:
{
if (!me || !me->IsEngaged())
return;
float range = static_cast<float>(e.event.areaCasting.range);
ThreatContainer::StorageType threatList = me->GetThreatMgr().GetThreatList();
for (ThreatContainer::StorageType::const_iterator i = threatList.begin(); i != threatList.end(); ++i)
{
if (Unit* target = ObjectAccessor::GetUnit(*me, (*i)->getUnitGuid()))
{
if (e.event.areaCasting.range && !me->IsWithinDistInMap(target, range))
continue;
if (!target || !target->IsNonMeleeSpellCast(false, false, true))
continue;
if (e.event.areaCasting.spellId > 0)
if (Spell* currSpell = target->GetCurrentSpell(CURRENT_GENERIC_SPELL))
if (currSpell->m_spellInfo->Id != e.event.areaCasting.spellId)
continue;
ProcessAction(e, target);
RecalcTimer(e, e.event.areaCasting.repeatMin, e.event.areaCasting.repeatMin);
return;
}
}
// If no targets are found and it's off cooldown, check again
RecalcTimer(e, e.event.areaCasting.checkTimer, e.event.areaCasting.checkTimer);
break;
}
default:
@@ -4223,6 +4345,12 @@ void SmartScript::InitTimer(SmartScriptHolder& e)
case SMART_EVENT_DISTANCE_GAMEOBJECT:
RecalcTimer(e, e.event.distance.repeat, e.event.distance.repeat);
break;
case SMART_EVENT_NEAR_UNIT:
RecalcTimer(e, e.event.nearUnit.timer, e.event.nearUnit.timer);
break;
case SMART_EVENT_AREA_CASTING:
RecalcTimer(e, e.event.areaCasting.repeatMin, e.event.areaCasting.repeatMax);
break;
default:
e.active = true;
break;
@@ -4276,6 +4404,7 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff)
{
case SMART_EVENT_NEAR_PLAYERS:
case SMART_EVENT_NEAR_PLAYERS_NEGATION:
case SMART_EVENT_NEAR_UNIT:
case SMART_EVENT_UPDATE:
case SMART_EVENT_UPDATE_OOC:
case SMART_EVENT_UPDATE_IC:
@@ -4285,6 +4414,7 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff)
case SMART_EVENT_TARGET_MANA_PCT:
case SMART_EVENT_RANGE:
case SMART_EVENT_VICTIM_CASTING:
case SMART_EVENT_AREA_CASTING:
case SMART_EVENT_FRIENDLY_HEALTH:
case SMART_EVENT_FRIENDLY_IS_CC:
case SMART_EVENT_FRIENDLY_MISSING_BUFF:

View File

@@ -280,6 +280,10 @@ void SmartAIMgr::LoadSmartAIFromDB()
if (temp.event.minMaxRepeat.min == 0 && temp.event.minMaxRepeat.max == 0)
temp.event.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE;
break;
case SMART_EVENT_AREA_CASTING:
if (temp.event.areaCasting.repeatMin == 0 && temp.event.areaCasting.repeatMax == 0)
temp.event.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE;
break;
case SMART_EVENT_FRIENDLY_IS_CC:
if (temp.event.friendlyCC.repeatMin == 0 && temp.event.friendlyCC.repeatMax == 0)
temp.event.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE;
@@ -346,10 +350,12 @@ void SmartAIMgr::LoadSmartAIFromDB()
case SMART_EVENT_TARGET_MANA_PCT:
case SMART_EVENT_RANGE:
case SMART_EVENT_VICTIM_CASTING:
case SMART_EVENT_AREA_CASTING:
case SMART_EVENT_TARGET_BUFFED:
case SMART_EVENT_IS_BEHIND_TARGET:
case SMART_EVENT_INSTANCE_PLAYER_ENTER:
case SMART_EVENT_TRANSPORT_ADDCREATURE:
case SMART_EVENT_NEAR_PLAYERS:
return true;
default:
return false;
@@ -572,6 +578,8 @@ bool SmartAIMgr::CheckUnusedEventParams(SmartScriptHolder const& e)
case SMART_EVENT_SUMMONED_UNIT_DIES: return sizeof(SmartEvent::summoned);
case SMART_EVENT_NEAR_PLAYERS: return sizeof(SmartEvent::nearPlayer);
case SMART_EVENT_NEAR_PLAYERS_NEGATION: return sizeof(SmartEvent::nearPlayerNegation);
case SMART_EVENT_NEAR_UNIT: return sizeof(SmartEvent::nearUnit);
case SMART_EVENT_AREA_CASTING: return sizeof(SmartEvent::areaCasting);
default:
LOG_WARN("sql.sql", "SmartAIMgr: entryorguid {} source_type {} id {} action_type {} is using an event {} with no unused params specified in SmartAIMgr::CheckUnusedEventParams(), please report this.",
e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.GetEventType());
@@ -671,7 +679,7 @@ bool SmartAIMgr::CheckUnusedActionParams(SmartScriptHolder const& e)
case SMART_ACTION_SET_COUNTER: return sizeof(SmartAction::setCounter);
case SMART_ACTION_STORE_TARGET_LIST: return sizeof(SmartAction::storeTargets);
case SMART_ACTION_WP_RESUME: return NO_PARAMS;
case SMART_ACTION_SET_ORIENTATION: return NO_PARAMS;
case SMART_ACTION_SET_ORIENTATION: return sizeof(SmartAction::orientation);
case SMART_ACTION_CREATE_TIMED_EVENT: return sizeof(SmartAction::timeEvent);
case SMART_ACTION_PLAYMOVIE: return sizeof(SmartAction::movie);
case SMART_ACTION_MOVE_TO_POS: return sizeof(SmartAction::moveToPos);
@@ -763,6 +771,8 @@ bool SmartAIMgr::CheckUnusedActionParams(SmartScriptHolder const& e)
case SMART_ACTION_MUSIC: return sizeof(SmartAction::music);
case SMART_ACTION_SET_GUID: return sizeof(SmartAction::setGuid);
case SMART_ACTION_DISABLE: return sizeof(SmartAction::disable);
case SMART_ACTION_SET_SCALE: return sizeof(SmartAction::setScale);
case SMART_ACTION_SUMMON_RADIAL: return sizeof(SmartAction::radialSummon);
default:
LOG_WARN("sql.sql", "SmartAIMgr: entryorguid {} source_type {} id {} action_type {} is using an action with no unused params specified in SmartAIMgr::CheckUnusedActionParams(), please report this.",
e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
@@ -1041,6 +1051,16 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
if (!IsMinMaxValid(e, e.event.targetCasting.repeatMin, e.event.targetCasting.repeatMax))
return false;
break;
case SMART_EVENT_AREA_CASTING:
if (e.event.areaCasting.spellId > 0 && !sSpellMgr->GetSpellInfo(e.event.areaCasting.spellId))
{
LOG_ERROR("scripts.ai.sai", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} uses non-existent Spell entry {}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.spellHit.spell);
return false;
}
if (!IsMinMaxValid(e, e.event.areaCasting.repeatMin, e.event.areaCasting.repeatMax))
return false;
break;
case SMART_EVENT_PASSENGER_BOARDED:
case SMART_EVENT_PASSENGER_REMOVED:
if (!IsMinMaxValid(e, e.event.minMax.repeatMin, e.event.minMax.repeatMax))
@@ -1270,6 +1290,10 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_EVENT_GO_EVENT_INFORM:
case SMART_EVENT_NEAR_PLAYERS:
case SMART_EVENT_NEAR_PLAYERS_NEGATION:
if (!IsMinMaxValid(e, e.event.nearPlayer.repeatMin, e.event.nearPlayer.repeatMax))
return false;
break;
case SMART_EVENT_NEAR_UNIT:
case SMART_EVENT_TIMED_EVENT_TRIGGERED:
case SMART_EVENT_INSTANCE_PLAYER_ENTER:
case SMART_EVENT_TRANSPORT_RELOCATE:
@@ -1920,6 +1944,8 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_ACTION_PLAY_CINEMATIC:
case SMART_ACTION_SET_GUID:
case SMART_ACTION_DISABLE:
case SMART_ACTION_SET_SCALE:
case SMART_ACTION_SUMMON_RADIAL:
break;
default:
LOG_ERROR("sql.sql", "SmartAIMgr: Not handled action_type({}), event_type({}), Entry {} SourceType {} Event {}, skipped.", e.GetActionType(), e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id);

View File

@@ -206,10 +206,12 @@ enum SMART_EVENT
/* AC Custom Events */
SMART_EVENT_AC_START = 100,
SMART_EVENT_NEAR_PLAYERS = 101, // min, radius, first timer, check timer
SMART_EVENT_NEAR_PLAYERS_NEGATION = 102, // min, radius, first timer, check timer
SMART_EVENT_NEAR_PLAYERS = 101, // min, radius, first timer, repeatMin, repeatMax
SMART_EVENT_NEAR_PLAYERS_NEGATION = 102, // max, radius, first timer, repeatMin, repeatMax
SMART_EVENT_NEAR_UNIT = 103, // type (0: creature 1: gob), entry, count, range, timer
SMART_EVENT_AREA_CASTING = 104, // spellId (0: any), range (0: any), repeatMin, repeatMax, checkTimer
SMART_EVENT_AC_END = 103
SMART_EVENT_AC_END = 105
};
struct SmartEvent
@@ -482,17 +484,37 @@ struct SmartEvent
uint32 minCount;
uint32 radius;
uint32 firstTimer;
uint32 checkTimer;
uint32 repeatMin;
uint32 repeatMax;
} nearPlayer;
struct
{
uint32 minCount;
uint32 maxCount;
uint32 radius;
uint32 firstTimer;
uint32 checkTimer;
uint32 repeatMin;
uint32 repeatMax;
} nearPlayerNegation;
struct
{
uint32 type;
uint32 entry;
uint32 count;
uint32 range;
uint32 timer;
} nearUnit;
struct
{
uint32 spellId;
uint32 range;
uint32 repeatMin;
uint32 repeatMax;
uint32 checkTimer;
} areaCasting;
struct
{
uint32 param1;
@@ -588,7 +610,7 @@ enum SMART_ACTION
SMART_ACTION_SET_COUNTER = 63, // id, value, reset (0/1)
SMART_ACTION_STORE_TARGET_LIST = 64, // varID,
SMART_ACTION_WP_RESUME = 65, // none
SMART_ACTION_SET_ORIENTATION = 66, // quick change, random orientation? (0/1)
SMART_ACTION_SET_ORIENTATION = 66, // quick change, random orientation? (0/1), turnAngle
SMART_ACTION_CREATE_TIMED_EVENT = 67, // id, InitialMin, InitialMax, RepeatMin(only if it repeats), RepeatMax(only if it repeats), chance
SMART_ACTION_PLAYMOVIE = 68, // entry
SMART_ACTION_MOVE_TO_POS = 69, // PointId (optional x,y,z offset), transport, controlled, ContactDistance
@@ -693,9 +715,11 @@ enum SMART_ACTION
SMART_ACTION_DO_ACTION = 223, // ActionId
SMART_ACTION_ATTACK_STOP = 224, //
SMART_ACTION_SET_GUID = 225, // Sends the invoker's or the base object's own ObjectGuid to target
SMART_ACTION_DISABLE = 226, // Disable the targeted creatures, setting them Invisible and Immune to All
SMART_ACTION_DISABLE = 226, // state
SMART_ACTION_SET_SCALE = 227, // scale
SMART_ACTION_SUMMON_RADIAL = 228, // summonEntry, summonDuration, repetitions, startAngle, stepAngle, dist
SMART_ACTION_AC_END = 227, // placeholder
SMART_ACTION_AC_END = 229, // placeholder
};
enum class SmartActionSummonCreatureFlags
@@ -1289,6 +1313,7 @@ struct SmartAction
{
uint32 quickChange;
uint32 random;
uint32 turnAngle;
} orientation;
struct
@@ -1376,6 +1401,21 @@ struct SmartAction
{
SAIBool state;
} disable;
struct
{
uint32 scale;
} setScale;
struct
{
uint32 summonEntry;
uint32 summonDuration;
uint32 repetitions;
uint32 startAngle;
uint32 stepAngle;
uint32 dist;
} radialSummon;
//! Note for any new future actions
//! All parameters must have type uint32
@@ -1777,8 +1817,10 @@ const uint32 SmartAIEventMask[SMART_EVENT_AC_END][2] =
{ 0, 0 }, // 98
{ 0, 0 }, // 99
{ 0, 0 }, // 100
{SMART_EVENT_NEAR_PLAYERS, SMART_SCRIPT_TYPE_MASK_CREATURE },
{SMART_EVENT_NEAR_PLAYERS_NEGATION, SMART_SCRIPT_TYPE_MASK_CREATURE }
{SMART_EVENT_NEAR_PLAYERS, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
{SMART_EVENT_NEAR_PLAYERS_NEGATION, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
{SMART_EVENT_NEAR_UNIT, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
{SMART_EVENT_AREA_CASTING, SMART_SCRIPT_TYPE_MASK_CREATURE }
};
enum SmartEventFlags

View File

@@ -2394,6 +2394,11 @@ bool AchievementMgr::CanUpdateCriteria(AchievementCriteriaEntry const* criteria,
return true;
}
CompletedAchievementMap const& AchievementMgr::GetCompletedAchievements()
{
return _completedAchievements;
}
AchievementGlobalMgr* AchievementGlobalMgr::instance()
{
static AchievementGlobalMgr instance;

View File

@@ -300,6 +300,7 @@ public:
void RemoveCriteriaProgress(AchievementCriteriaEntry const* entry);
CriteriaProgress* GetCriteriaProgress(AchievementCriteriaEntry const* entry);
CompletedAchievementMap const& GetCompletedAchievements();
private:
enum ProgressType { PROGRESS_SET, PROGRESS_ACCUMULATE, PROGRESS_HIGHEST, PROGRESS_RESET };

View File

@@ -727,7 +727,7 @@ void AuctionHouseObject::BuildListOwnerItems(WorldPacket& data, Player* player,
bool AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player,
std::wstring const& wsearchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax, uint8 usable,
uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality,
uint32& count, uint32& totalcount, uint8 /*getAll*/, AuctionSortOrderVector const& sortOrder)
uint32& count, uint32& totalcount, uint8 /*getAll*/, AuctionSortOrderVector const& sortOrder, Milliseconds searchTimeout)
{
uint32 itrcounter = 0;
@@ -754,14 +754,11 @@ bool AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player
for (AuctionEntryMap::const_iterator itr = _auctionsMap.begin(); itr != _auctionsMap.end(); ++itr)
{
if (!AsyncAuctionListingMgr::IsAuctionListingAllowed()) // pussywizard: World::Update is waiting for us...
if ((itrcounter++) % 100 == 0) // check condition every 100 iterations
{
if ((itrcounter++) % 100 == 0) // check condition every 100 iterations
if (GetMSTimeDiff(GameTime::GetGameTimeMS(), GetTimeMS()) >= searchTimeout) // pussywizard: stop immediately if diff is high or waiting too long
{
if (sWorldUpdateTime.GetAverageUpdateTime() >= 30 || GetMSTimeDiff(GameTime::GetGameTimeMS(), GetTimeMS()) >= 10ms) // pussywizard: stop immediately if diff is high or waiting too long
{
return false;
}
return false;
}
}

View File

@@ -162,7 +162,7 @@ public:
bool BuildListAuctionItems(WorldPacket& data, Player* player,
std::wstring const& searchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax, uint8 usable,
uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality,
uint32& count, uint32& totalcount, uint8 getAll, AuctionSortOrderVector const& sortOrder);
uint32& count, uint32& totalcount, uint8 getAll, AuctionSortOrderVector const& sortOrder, Milliseconds searchTimeout);
private:
AuctionEntryMap _auctionsMap;
@@ -184,7 +184,6 @@ public:
AuctionHouseObject* GetAuctionsMap(uint32 factionTemplateId);
AuctionHouseObject* GetAuctionsMapByHouseId(uint8 auctionHouseId);
AuctionHouseObject* GetBidsMap(uint32 factionTemplateId);
Item* GetAItem(ObjectGuid itemGuid)
{

View File

@@ -174,11 +174,11 @@ void KillRewarder::_RewardXP(Player* player, float rate)
}
}
void KillRewarder::_RewardReputation(Player* player, float rate)
void KillRewarder::_RewardReputation(Player* player)
{
// 4.3. Give reputation (player must not be on BG).
// Even dead players and corpses are rewarded.
player->RewardReputation(_victim, rate);
player->RewardReputation(_victim);
}
void KillRewarder::_RewardKillCredit(Player* player)
@@ -208,7 +208,6 @@ void KillRewarder::_RewardPlayer(Player* player, bool isDungeon)
if (!_isPvP || _isBattleGround)
{
float xpRate = _group ? _groupRate * float(player->GetLevel()) / _aliveSumLevel : /*Personal rate is 100%.*/ 1.0f; // Group rate depends on the sum of levels.
float reputationRate = _group ? _groupRate * float(player->GetLevel()) / _sumLevel : /*Personal rate is 100%.*/ 1.0f; // Group rate depends on the sum of levels.
sScriptMgr->OnRewardKillRewarder(player, isDungeon, xpRate); // Personal rate is 100%.
if (_xp)
@@ -219,7 +218,7 @@ void KillRewarder::_RewardPlayer(Player* player, bool isDungeon)
if (!_isBattleGround)
{
// If killer is in dungeon then all members receive full reputation at kill.
_RewardReputation(player, isDungeon ? 1.0f : reputationRate);
_RewardReputation(player);
_RewardKillCredit(player);
}
}

View File

@@ -37,7 +37,7 @@ private:
void _RewardHonor(Player* player);
void _RewardXP(Player* player, float rate);
void _RewardReputation(Player* player, float rate);
void _RewardReputation(Player* player);
void _RewardKillCredit(Player* player);
void _RewardPlayer(Player* player, bool isDungeon);
void _RewardGroup();

View File

@@ -5858,7 +5858,7 @@ float Player::CalculateReputationGain(ReputationSource source, uint32 creatureOr
}
// Calculates how many reputation points player gains in victim's enemy factions
void Player::RewardReputation(Unit* victim, float rate)
void Player::RewardReputation(Unit* victim)
{
if (!victim || victim->GetTypeId() == TYPEID_PLAYER)
return;
@@ -5887,7 +5887,6 @@ void Player::RewardReputation(Unit* victim, float rate)
if (Rep->RepFaction1 && (!Rep->TeamDependent || teamId == TEAM_ALLIANCE))
{
float donerep1 = CalculateReputationGain(REPUTATION_SOURCE_KILL, victim->GetLevel(), static_cast<float>(Rep->RepValue1), ChampioningFaction ? ChampioningFaction : Rep->RepFaction1);
donerep1 *= rate;
FactionEntry const* factionEntry1 = sFactionStore.LookupEntry(ChampioningFaction ? ChampioningFaction : Rep->RepFaction1);
if (factionEntry1)
@@ -5899,7 +5898,6 @@ void Player::RewardReputation(Unit* victim, float rate)
if (Rep->RepFaction2 && (!Rep->TeamDependent || teamId == TEAM_HORDE))
{
float donerep2 = CalculateReputationGain(REPUTATION_SOURCE_KILL, victim->GetLevel(), static_cast<float>(Rep->RepValue2), ChampioningFaction ? ChampioningFaction : Rep->RepFaction2);
donerep2 *= rate;
FactionEntry const* factionEntry2 = sFactionStore.LookupEntry(ChampioningFaction ? ChampioningFaction : Rep->RepFaction2);
if (factionEntry2)
@@ -13444,7 +13442,10 @@ LootItem* Player::StoreLootItem(uint8 lootSlot, Loot* loot, InventoryResult& msg
LootItem* item = loot->LootItemInSlot(lootSlot, this, &qitem, &ffaitem, &conditem);
if (!item || item->is_looted)
{
SendEquipError(EQUIP_ERR_ALREADY_LOOTED, nullptr, nullptr);
if (!sScriptMgr->CanSendErrorAlreadyLooted(this))
{
SendEquipError(EQUIP_ERR_ALREADY_LOOTED, nullptr, nullptr);
}
return nullptr;
}

View File

@@ -2086,7 +2086,7 @@ public:
ReputationMgr& GetReputationMgr() { return *m_reputationMgr; }
[[nodiscard]] ReputationMgr const& GetReputationMgr() const { return *m_reputationMgr; }
[[nodiscard]] ReputationRank GetReputationRank(uint32 faction_id) const;
void RewardReputation(Unit* victim, float rate);
void RewardReputation(Unit* victim);
void RewardReputation(Quest const* quest);
float CalculateReputationGain(ReputationSource source, uint32 creatureOrQuestLevel, float rep, int32 faction, bool noQuestBonus = false);

View File

@@ -668,23 +668,24 @@ void WorldSession::HandleAuctionListOwnerItems(WorldPacket& recvData)
recvData >> listfrom; // not used in fact (this list does not have page control in client)
// pussywizard:
const uint32 delay = 4500;
const uint32 now = GameTime::GetGameTimeMS().count();
const Milliseconds now = GameTime::GetGameTimeMS();
if (_lastAuctionListOwnerItemsMSTime > now) // list is pending
return;
uint32 diff = getMSTimeDiff(_lastAuctionListOwnerItemsMSTime, now);
const Milliseconds delay = Milliseconds(4500);
Milliseconds diff = GetMSTimeDiff(_lastAuctionListOwnerItemsMSTime, now);
if (diff > delay)
diff = delay;
_lastAuctionListOwnerItemsMSTime = now + delay; // set longest possible here, actual exectuing will change this to getMSTime of that moment
_player->m_Events.AddEvent(new AuctionListOwnerItemsDelayEvent(guid, _player->GetGUID(), true), _player->m_Events.CalculateTime(delay - diff));
_lastAuctionListOwnerItemsMSTime = now + delay; // set longest possible here, actual executing will change this to getMSTime of that moment
_player->m_Events.AddEvent(new AuctionListOwnerItemsDelayEvent(guid, _player->GetGUID()), _player->m_Events.CalculateTime(delay.count() - diff.count()));
}
void WorldSession::HandleAuctionListOwnerItemsEvent(ObjectGuid creatureGuid)
{
LOG_DEBUG("network", "WORLD: Received CMSG_AUCTION_LIST_OWNER_ITEMS");
_lastAuctionListOwnerItemsMSTime = GameTime::GetGameTimeMS().count(); // pussywizard
_lastAuctionListOwnerItemsMSTime = GameTime::GetGameTimeMS(); // pussywizard
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(creatureGuid, UNIT_NPC_FLAG_AUCTIONEER);
if (!creature)
@@ -757,17 +758,17 @@ void WorldSession::HandleAuctionListItems(WorldPacket& recvData)
}
// pussywizard:
const uint32 delay = 2000;
const uint32 now = GameTime::GetGameTimeMS().count();
uint32 diff = getMSTimeDiff(_lastAuctionListItemsMSTime, now);
const Milliseconds delay = 2s;
const Milliseconds now = GameTime::GetGameTimeMS();
Milliseconds diff = GetMSTimeDiff(_lastAuctionListItemsMSTime, now);
if (diff > delay)
{
diff = delay;
}
_lastAuctionListItemsMSTime = now + delay - diff;
std::lock_guard<std::mutex> guard(AsyncAuctionListingMgr::GetTempLock());
AsyncAuctionListingMgr::GetTempList().push_back(AuctionListItemsDelayEvent(delay - diff, _player->GetGUID(), guid, searchedname, listfrom, levelmin, levelmax, usable, auctionSlotID,
auctionMainCategory, auctionSubCategory, quality, getAll, sortOrder));
AsyncAuctionListingMgr::GetTempList().emplace_back(delay - diff, _player->GetGUID(), guid, searchedname, listfrom, levelmin, levelmax, usable, auctionSlotID,
auctionMainCategory, auctionSubCategory, quality, getAll, sortOrder);
}
void WorldSession::HandleAuctionListPendingSales(WorldPacket& recvData)

View File

@@ -93,6 +93,8 @@ void WorldSession::HandleAutostoreLootItemOpcode(WorldPacket& recvData)
loot = &creature->loot;
}
sScriptMgr->OnAfterCreatureLoot(player);
InventoryResult msg;
LootItem* lootItem = player->StoreLootItem(lootSlot, loot, msg);
if (msg != EQUIP_ERR_OK && lguid.IsItem() && loot->loot_type != LOOT_CORPSE)
@@ -209,6 +211,7 @@ void WorldSession::HandleLootMoneyOpcode(WorldPacket& /*recvData*/)
}
else
{
sScriptMgr->OnAfterCreatureLootMoney(player);
player->ModifyMoney(loot->gold);
player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, loot->gold);

View File

@@ -22,11 +22,9 @@
#include "Player.h"
#include "SpellAuraEffects.h"
uint32 AsyncAuctionListingMgr::auctionListingDiff = 0;
bool AsyncAuctionListingMgr::auctionListingAllowed = false;
Milliseconds AsyncAuctionListingMgr::auctionListingDiff = Milliseconds::zero();
std::list<AuctionListItemsDelayEvent> AsyncAuctionListingMgr::auctionListingList;
std::list<AuctionListItemsDelayEvent> AsyncAuctionListingMgr::auctionListingListTemp;
std::mutex AsyncAuctionListingMgr::auctionListingLock;
std::mutex AsyncAuctionListingMgr::auctionListingTempLock;
bool AuctionListOwnerItemsDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
@@ -60,18 +58,19 @@ bool AuctionListItemsDelayEvent::Execute()
wstrToLower(wsearchedname);
uint32 searchTimeout = sWorld->getIntConfig(CONFIG_AUCTION_HOUSE_SEARCH_TIMEOUT);
bool result = auctionHouse->BuildListAuctionItems(data, plr,
wsearchedname, _listfrom, _levelmin, _levelmax, _usable,
_auctionSlotID, _auctionMainCategory, _auctionSubCategory, _quality,
count, totalcount, _getAll, _sortOrder);
count, totalcount, _getAll, _sortOrder, Milliseconds(searchTimeout));
if (!result)
return false;
data.put<uint32>(0, count);
data << (uint32) totalcount;
data << (uint32) 300; // clientside search cooldown [ms] (gray search button)
plr->GetSession()->SendPacket(&data);
if (result)
{
data.put<uint32>(0, count);
data << (uint32) totalcount;
data << (uint32) 300; // clientside search cooldown [ms] (gray search button)
plr->GetSession()->SendPacket(&data);
}
return true;
}

View File

@@ -25,30 +25,28 @@
class AuctionListOwnerItemsDelayEvent : public BasicEvent
{
public:
AuctionListOwnerItemsDelayEvent(ObjectGuid _creatureGuid, ObjectGuid guid, bool o) : creatureGuid(_creatureGuid), playerguid(guid), owner(o) {}
AuctionListOwnerItemsDelayEvent(ObjectGuid _creatureGuid, ObjectGuid guid) : creatureGuid(_creatureGuid), playerguid(guid) {}
~AuctionListOwnerItemsDelayEvent() override {}
bool Execute(uint64 e_time, uint32 p_time) override;
void Abort(uint64 /*e_time*/) override {}
bool getOwner() { return owner; }
private:
ObjectGuid creatureGuid;
ObjectGuid playerguid;
bool owner;
};
class AuctionListItemsDelayEvent
{
public:
AuctionListItemsDelayEvent(uint32 msTimer, ObjectGuid playerguid, ObjectGuid creatureguid, std::string searchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax,
AuctionListItemsDelayEvent(Milliseconds pickupTimer, ObjectGuid playerguid, ObjectGuid creatureguid, std::string searchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax,
uint8 usable, uint32 auctionSlotID, uint32 auctionMainCategory, uint32 auctionSubCategory, uint32 quality, uint8 getAll, AuctionSortOrderVector sortOrder) :
_msTimer(msTimer), _playerguid(playerguid), _creatureguid(creatureguid), _searchedname(searchedname), _listfrom(listfrom), _levelmin(levelmin), _levelmax(levelmax),_usable(usable),
_pickupTimer(pickupTimer), _playerguid(playerguid), _creatureguid(creatureguid), _searchedname(searchedname), _listfrom(listfrom), _levelmin(levelmin), _levelmax(levelmax),_usable(usable),
_auctionSlotID(auctionSlotID), _auctionMainCategory(auctionMainCategory), _auctionSubCategory(auctionSubCategory), _quality(quality), _getAll(getAll), _sortOrder(sortOrder) { }
bool Execute();
uint32 _msTimer;
Milliseconds _pickupTimer;
ObjectGuid _playerguid;
ObjectGuid _creatureguid;
std::string _searchedname;
@@ -67,23 +65,17 @@ public:
class AsyncAuctionListingMgr
{
public:
static void Update(uint32 diff) { auctionListingDiff += diff; }
static uint32 GetDiff() { return auctionListingDiff; }
static void ResetDiff() { auctionListingDiff = 0; }
static bool IsAuctionListingAllowed() { return auctionListingAllowed; }
static void SetAuctionListingAllowed(bool a) { auctionListingAllowed = a; }
static void Update(Milliseconds diff) { auctionListingDiff += diff; }
static Milliseconds GetDiff() { return auctionListingDiff; }
static void ResetDiff() { auctionListingDiff = Milliseconds::zero(); }
static std::list<AuctionListItemsDelayEvent>& GetList() { return auctionListingList; }
static std::list<AuctionListItemsDelayEvent>& GetTempList() { return auctionListingListTemp; }
static std::mutex& GetLock() { return auctionListingLock; }
static std::mutex& GetTempLock() { return auctionListingTempLock; }
private:
static uint32 auctionListingDiff;
static bool auctionListingAllowed;
static Milliseconds auctionListingDiff;
static std::list<AuctionListItemsDelayEvent> auctionListingList;
static std::list<AuctionListItemsDelayEvent> auctionListingListTemp;
static std::mutex auctionListingLock;
static std::mutex auctionListingTempLock;
};

View File

@@ -913,6 +913,37 @@ bool ScriptMgr::CanSendMail(Player* player, ObjectGuid receiverGuid, ObjectGuid
return true;
}
bool ScriptMgr::CanSendErrorAlreadyLooted(Player* player)
{
auto ret = IsValidBoolScript<PlayerScript>([&](PlayerScript* script)
{
return !script->CanSendErrorAlreadyLooted(player);
});
if (ret && *ret)
{
return false;
}
return true;
}
void ScriptMgr::OnAfterCreatureLoot(Player* player)
{
ExecuteScript<PlayerScript>([&](PlayerScript* script)
{
script->OnAfterCreatureLoot(player);
});
}
void ScriptMgr::OnAfterCreatureLootMoney(Player* player)
{
ExecuteScript<PlayerScript>([&](PlayerScript* script)
{
script->OnAfterCreatureLootMoney(player);
});
}
void ScriptMgr::PetitionBuy(Player* player, Creature* creature, uint32& charterid, uint32& cost, uint32& type)
{
ExecuteScript<PlayerScript>([&](PlayerScript* script)
@@ -986,7 +1017,7 @@ void ScriptMgr::OnGetMaxSkillValue(Player* player, uint32 skill, int32& result,
void ScriptMgr::OnUpdateGatheringSkill(Player *player, uint32 skillId, uint32 currentLevel, uint32 gray, uint32 green, uint32 yellow, uint32 &gain) {
ExecuteScript<PlayerScript>([&](PlayerScript* script)
{
script->OnUpdateGatheringSkill(player, skillId, gray, green, yellow, currentLevel, gain);
script->OnUpdateGatheringSkill(player, skillId, currentLevel, gray, green, yellow, gain);
});
}

View File

@@ -1483,6 +1483,30 @@ public:
virtual void AnticheatUpdateMovementInfo(Player* /*player*/, MovementInfo const& /*movementInfo*/) { }
[[nodiscard]] virtual bool AnticheatHandleDoubleJump(Player* /*player*/, Unit* /*mover*/) { return true; }
[[nodiscard]] virtual bool AnticheatCheckMovementInfo(Player* /*player*/, MovementInfo const& /*movementInfo*/, Unit* /*mover*/, bool /*jump*/) { return true; }
/**
* @brief This hook is called, to avoid displaying the error message that the body has already been stripped
*
* @param player Contains information about the Player
*
* @return true Avoiding displaying the error message that the loot has already been taken.
*/
virtual bool CanSendErrorAlreadyLooted(Player* /*player*/) { return true; }
/**
* @brief It is used when an item is taken from a creature.
*
* @param player Contains information about the Player
*
*/
virtual void OnAfterCreatureLoot(Player* /*player*/) { }
/**
* @brief After a creature's money is taken
*
* @param player Contains information about the Player
*/
virtual void OnAfterCreatureLootMoney(Player* /*player*/) { }
};
class AccountScript : public ScriptObject
@@ -2447,6 +2471,9 @@ public: /* PlayerScript */
void OnPlayerEnterCombat(Player* player, Unit* enemy);
void OnPlayerLeaveCombat(Player* player);
void OnQuestAbandon(Player* player, uint32 questId);
bool CanSendErrorAlreadyLooted(Player* player);
void OnAfterCreatureLoot(Player* player);
void OnAfterCreatureLootMoney(Player* player);
// Anti cheat
void AnticheatSetSkipOnePacketForASH(Player* player, bool apply);

View File

@@ -1079,8 +1079,8 @@ public: // opcodes handlers
void HandleEnterPlayerVehicle(WorldPacket& data);
void HandleUpdateProjectilePosition(WorldPacket& recvPacket);
uint32 _lastAuctionListItemsMSTime;
uint32 _lastAuctionListOwnerItemsMSTime;
Milliseconds _lastAuctionListItemsMSTime;
Milliseconds _lastAuctionListOwnerItemsMSTime;
void HandleTeleportTimeout(bool updateInSessions);
bool HandleSocketClosed();

View File

@@ -1918,6 +1918,16 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b
}
}
break;
case 34026: // Kill Command
// Dungeon Set 3
if (caster->HasAura(37483))
{
if (apply)
{
caster->CastSpell(caster, 37482, true);
}
}
break;
}
break;
case SPELLFAMILY_PALADIN:

View File

@@ -3436,6 +3436,11 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex)
spell_bonus += int32(0.08f * m_caster->GetTotalAttackPowerValue(BASE_ATTACK));
spell_bonus += int32(0.13f * m_caster->SpellBaseDamageBonusDone(m_spellInfo->GetSchoolMask()));
break;
case 42463: // Seals of the Pure for Seal of Vengeance/Corruption
case 53739:
if (AuraEffect const* sealsOfPure = m_caster->GetAuraEffect(SPELL_AURA_ADD_PCT_MODIFIER, SPELLFAMILY_PALADIN, 25, 0))
AddPct(totalDamagePercentMod, sealsOfPure->GetAmount());
break;
case 53385: // Divine Storm deals normalized damage
normalized = true;
break;

View File

@@ -413,6 +413,7 @@ enum WorldIntConfigs
CONFIG_LFG_KICK_PREVENTION_TIMER,
CONFIG_CHANGE_FACTION_MAX_MONEY,
CONFIG_WATER_BREATH_TIMER,
CONFIG_AUCTION_HOUSE_SEARCH_TIMEOUT,
INT_CONFIG_VALUE_COUNT
};

View File

@@ -1286,6 +1286,8 @@ void World::LoadConfigSettings(bool reload)
_bool_configs[CONFIG_ALLOWS_RANK_MOD_FOR_PET_HEALTH] = sConfigMgr->GetOption<bool>("Pet.RankMod.Health", true);
_int_configs[CONFIG_AUCTION_HOUSE_SEARCH_TIMEOUT] = sConfigMgr->GetOption<uint32>("AuctionHouse.SearchTimeout", 1000);
///- Read the "Data" directory from the config file
std::string dataPath = sConfigMgr->GetOption<std::string>("DataDir", "./");
if (dataPath.empty() || (dataPath.at(dataPath.length() - 1) != '/' && dataPath.at(dataPath.length() - 1) != '\\'))
@@ -2352,41 +2354,27 @@ void World::Update(uint32 diff)
sScriptMgr->OnPlayerbotUpdate(diff);
// pussywizard:
// acquire mutex now, this is kind of waiting for listing thread to finish it's work (since it can't process next packet)
// so we don't have to do it in every packet that modifies auctions
AsyncAuctionListingMgr::SetAuctionListingAllowed(false);
// pussywizard: handle auctions when the timer has passed
if (_timers[WUPDATE_AUCTIONS].Passed())
{
std::lock_guard<std::mutex> guard(AsyncAuctionListingMgr::GetLock());
METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update expired auctions"));
// pussywizard: handle auctions when the timer has passed
if (_timers[WUPDATE_AUCTIONS].Passed())
{
METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update expired auctions"));
_timers[WUPDATE_AUCTIONS].Reset();
_timers[WUPDATE_AUCTIONS].Reset();
// pussywizard: handle expired auctions, auctions expired when realm was offline are also handled here (not during loading when many required things aren't loaded yet)
sAuctionMgr->Update();
}
AsyncAuctionListingMgr::Update(diff);
if (currentGameTime > _mail_expire_check_timer)
{
sObjectMgr->ReturnOrDeleteOldMails(true);
_mail_expire_check_timer = currentGameTime + 6h;
}
{
/// <li> Handle session updates when the timer has passed
METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update sessions"));
UpdateSessions(diff);
}
// pussywizard: handle expired auctions, auctions expired when realm was offline are also handled here (not during loading when many required things aren't loaded yet)
sAuctionMgr->Update();
}
// end of section with mutex
AsyncAuctionListingMgr::SetAuctionListingAllowed(true);
AsyncAuctionListingMgr::Update(Milliseconds(diff));
if (currentGameTime > _mail_expire_check_timer)
{
sObjectMgr->ReturnOrDeleteOldMails(true);
_mail_expire_check_timer = currentGameTime + 6h;
}
METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update sessions"));
UpdateSessions(diff);
/// <li> Handle weather updates when the timer has passed
if (_timers[WUPDATE_WEATHERS].Passed())

View File

@@ -157,7 +157,6 @@ void AddSC_isle_of_queldanas();
void AddSC_redridge_mountains();
void AddSC_silverpine_forest();
void AddSC_stormwind_city();
void AddSC_stranglethorn_vale();
void AddSC_tirisfal_glades();
void AddSC_undercity();
void AddSC_western_plaguelands();
@@ -310,7 +309,6 @@ void AddEasternKingdomsScripts()
AddSC_redridge_mountains();
AddSC_silverpine_forest();
AddSC_stormwind_city();
AddSC_stranglethorn_vale();
AddSC_tirisfal_glades();
AddSC_undercity();
AddSC_western_plaguelands();

View File

@@ -1,129 +0,0 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* ScriptData
SDName: Stranglethorn_Vale
SD%Complete: 100
SDComment: Quest support: 592
SDCategory: Stranglethorn Vale
EndScriptData */
/* ContentData
npc_yenniku
EndContentData */
#include "Player.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellInfo.h"
/*######
## npc_yenniku
######*/
class npc_yenniku : public CreatureScript
{
public:
npc_yenniku() : CreatureScript("npc_yenniku") { }
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_yennikuAI(creature);
}
struct npc_yennikuAI : public ScriptedAI
{
npc_yennikuAI(Creature* creature) : ScriptedAI(creature)
{
bReset = false;
}
uint32 Reset_Timer;
bool bReset;
void Reset() override
{
Reset_Timer = 0;
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE);
}
void SpellHit(Unit* caster, SpellInfo const* spell) override
{
if (bReset || spell->Id != 3607)
return;
if (Player* player = caster->ToPlayer())
{
if (player->GetQuestStatus(592) == QUEST_STATUS_INCOMPLETE) //Yenniku's Release
{
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_STUN);
me->CombatStop(); //stop combat
me->GetThreatMgr().ClearAllThreat(); //unsure of this
me->SetFaction(FACTION_HORDE_GENERIC);
bReset = true;
Reset_Timer = 60000;
}
}
}
void JustEngagedWith(Unit* /*who*/) override { }
void UpdateAI(uint32 diff) override
{
if (bReset)
{
if (Reset_Timer <= diff)
{
EnterEvadeMode();
bReset = false;
me->SetFaction(FACTION_TROLL_BLOODSCALP);
return;
}
Reset_Timer -= diff;
if (me->IsInCombat() && me->GetVictim())
{
if (Player* player = me->GetVictim()->ToPlayer())
{
if (player->GetTeamId() == TEAM_HORDE)
{
me->CombatStop();
me->GetThreatMgr().ClearAllThreat();
}
}
}
}
//Return since we have no target
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
}
};
};
/*######
##
######*/
void AddSC_stranglethorn_vale()
{
new npc_yenniku();
}

View File

@@ -46,43 +46,6 @@ public:
}
};
struct npc_midsummer_bonfire : public ScriptedAI
{
npc_midsummer_bonfire(Creature* creature) : ScriptedAI(creature)
{
me->IsAIEnabled = true;
goGUID.Clear();
if (GameObject* go = me->SummonGameObject(GO_MIDSUMMER_BONFIRE, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0))
{
goGUID = go->GetGUID();
me->RemoveGameObject(go, false);
}
}
ObjectGuid goGUID;
void SpellHit(Unit*, SpellInfo const* spellInfo) override
{
if (!goGUID)
return;
// Extinguish fire
if (spellInfo->Id == SPELL_STAMP_OUT_BONFIRE)
{
if (GameObject* go = ObjectAccessor::GetGameObject(*me, goGUID))
go->SetPhaseMask(2, true);
}
else if (spellInfo->Id == SPELL_LIGHT_BONFIRE)
{
if (GameObject* go = ObjectAccessor::GetGameObject(*me, goGUID))
{
go->SetPhaseMask(1, true);
go->SendCustomAnim(1);
}
}
}
};
struct npc_midsummer_torch_target : public ScriptedAI
{
npc_midsummer_torch_target(Creature* creature) : ScriptedAI(creature)
@@ -542,7 +505,6 @@ void AddSC_event_midsummer_scripts()
{
// NPCs
new go_midsummer_bonfire();
RegisterCreatureAI(npc_midsummer_bonfire);
RegisterCreatureAI(npc_midsummer_torch_target);
// Spells

View File

@@ -66,231 +66,7 @@ public:
}
};
enum WanderingShay
{
QUEST_WANDERING_SHAY = 2845,
SPELL_SHAY_BELL = 11402,
NPC_ROCKBITER = 7765,
TALK_0 = 0,
TALK_1 = 1,
TALK_2 = 2,
TALK_3 = 3,
TALK_4 = 4,
EVENT_WANDERING_START = 1,
EVENT_WANDERING_TALK = 2,
EVENT_WANDERING_RANDOM = 3,
EVENT_FINAL_TALK = 4,
EVENT_CHECK_FOLLOWER = 5
};
class npc_shay_leafrunner : public CreatureScript
{
public:
npc_shay_leafrunner() : CreatureScript("npc_shay_leafrunner") {}
struct npc_shay_leafrunnerAI : public ScriptedAI
{
npc_shay_leafrunnerAI(Creature* creature) : ScriptedAI(creature) {}
void InitializeAI() override
{
me->SetNpcFlag(UNIT_NPC_FLAG_QUESTGIVER);
me->SetImmuneToAll(true);
me->RestoreFaction();
_events.Reset();
_playerGUID.Clear();
_rockbiterGUID.Clear();
}
void JustRespawned() override
{
InitializeAI();
me->SetHomePosition(me->GetPosition());
}
void MoveInLineOfSight(Unit* target) override
{
if (!_playerGUID || target->GetEntry() != NPC_ROCKBITER || !me->IsInRange(target, 0.f, 10.f))
{
if (!me->IsInCombat() && !me->GetVictim())
{
if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID))
{
if (Unit* victim = player->GetVictim())
{
if (me->CanStartAttack(victim))
{
AttackStart(victim);
}
}
}
}
return;
}
_rockbiterGUID = target->GetGUID();
Talk(TALK_4, target);
me->SetControlled(true, UNIT_STATE_ROOT);
if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID))
{
player->GroupEventHappens(QUEST_WANDERING_SHAY, me);
}
_events.CancelEvent(EVENT_WANDERING_START);
_events.ScheduleEvent(EVENT_FINAL_TALK, 5s);
}
void EnterEvadeMode(EvadeReason why) override
{
_EnterEvadeMode(why);
if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID))
{
me->GetMotionMaster()->MoveFollow(player, 3.f, M_PI);
}
}
void FailQuest(Player* player, bool despawn)
{
if (player)
{
player->FailQuest(QUEST_WANDERING_SHAY);
if (Group* group = player->GetGroup())
{
for (GroupReference* groupRef = group->GetFirstMember(); groupRef != nullptr; groupRef = groupRef->next())
{
if (Player* member = groupRef->GetSource())
{
if (member->GetGUID() != player->GetGUID())
{
member->FailQuest(QUEST_WANDERING_SHAY);
}
}
}
}
}
if (despawn)
{
me->DespawnOrUnsummon(1);
}
}
void JustDied(Unit* /*killer*/) override
{
if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID))
{
FailQuest(player, false);
}
}
void sQuestAccept(Player* player, Quest const* quest) override
{
if (quest->GetQuestId() == QUEST_WANDERING_SHAY)
{
_playerGUID = player->GetGUID();
Talk(TALK_0, player);
me->RemoveNpcFlag(UNIT_NPC_FLAG_QUESTGIVER);
me->SetImmuneToAll(false);
me->SetFaction(FACTION_ESCORT_N_NEUTRAL_ACTIVE);
me->GetMotionMaster()->MoveFollow(player, 3.f, M_PI);
_events.ScheduleEvent(EVENT_WANDERING_START, 40s, 70s);
_events.ScheduleEvent(EVENT_CHECK_FOLLOWER, 30s);
}
}
void SpellHit(Unit* caster, SpellInfo const* spellInfo) override
{
if (spellInfo->Id == SPELL_SHAY_BELL)
{
_playerGUID = caster->GetGUID();
Talk(TALK_1, caster);
me->GetMotionMaster()->MoveIdle();
me->GetMotionMaster()->MoveFollow(caster, 3.f, M_PI);
}
}
void UpdateAI(uint32 diff) override
{
if (UpdateVictim())
{
DoMeleeAttackIfReady();
return;
}
_events.Update(diff);
while (uint32 eventId = _events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_WANDERING_START:
{
Position pos = me->GetFirstCollisionPosition(15.f, rand_norm() * static_cast<float>(2 * M_PI));
me->GetMotionMaster()->MovePoint(0, pos);
Talk(TALK_2);
_events.ScheduleEvent(EVENT_WANDERING_START, 60s, 70s);
_events.ScheduleEvent(EVENT_WANDERING_TALK, 3s);
_events.ScheduleEvent(EVENT_WANDERING_RANDOM, 8s);
break;
}
case EVENT_WANDERING_TALK:
Talk(TALK_3);
break;
case EVENT_WANDERING_RANDOM:
me->SetHomePosition(me->GetPosition());
me->GetMotionMaster()->MoveRandom(15.f);
break;
case EVENT_FINAL_TALK:
if (Creature* robckbiter = ObjectAccessor::GetCreature(*me, _rockbiterGUID))
{
robckbiter->AI()->Talk(TALK_0, me);
}
me->DespawnOrUnsummon(10 * IN_MILLISECONDS);
break;
case EVENT_CHECK_FOLLOWER:
{
Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID);
if (!player || !player->IsAlive() || !me->IsInRange(player, 0.f, 50.f))
{
FailQuest(player, true);
}
break;
}
default:
break;
}
}
}
private:
ObjectGuid _playerGUID;
ObjectGuid _rockbiterGUID;
EventMap _events;
};
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_shay_leafrunnerAI(creature);
}
};
void AddSC_feralas()
{
new spell_gordunni_trap();
new npc_shay_leafrunner();
}

View File

@@ -97,9 +97,6 @@ public:
if (m_pInstance)
m_pInstance->SetData(TYPE_IONAR, NOT_STARTED);
// Ionar is immune to nature damage
me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_NATURE, true);
}
void ScheduleEvents(bool spark)

View File

@@ -74,7 +74,10 @@ struct boss_exarch_maladaar : public BossAI
_Reset();
ScheduleHealthCheckEvent(25, [&] {
Talk(SAY_SUMMON);
DoCastSelf(SPELL_SUMMON_AVATAR);
scheduler.Schedule(100ms, [this](TaskContext)
{
DoCastSelf(SPELL_SUMMON_AVATAR);
});
});
}
@@ -133,6 +136,11 @@ struct boss_exarch_maladaar : public BossAI
_JustDied();
}
void JustSummoned(Creature* /*creature*/) override
{
// Override JustSummoned() so we don't despawn the Avatar.
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())

View File

@@ -208,25 +208,27 @@ class spell_murmur_thundering_storm : public SpellScript
}
};
// 33711/38794 - Murmur's Touch
class spell_murmur_touch : public AuraScript
// 33686 - Shockwave (Murmur's Touch final explosion)
class spell_shockwave_knockback : public SpellScript
{
PrepareAuraScript(spell_murmur_touch);
PrepareSpellScript(spell_shockwave_knockback);
void HandleAfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
bool Validate(SpellInfo const* /*spellInfo*/) override
{
if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE)
return ValidateSpellInfo({ SPELL_SHOCKWAVE_SERVERSIDE });
}
void HandleOnHit()
{
if (Unit* target = GetHitUnit())
{
if (GetTarget())
{
GetTarget()->CastSpell(GetTarget(), SPELL_SHOCKWAVE_SERVERSIDE, true);
}
target->CastSpell(target, SPELL_SHOCKWAVE_SERVERSIDE, true);
}
}
void Register() override
{
AfterEffectRemove += AuraEffectRemoveFn(spell_murmur_touch::HandleAfterRemove, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL);
OnHit += SpellHitFn(spell_shockwave_knockback::HandleOnHit);
}
};
@@ -252,6 +254,6 @@ void AddSC_boss_murmur()
{
RegisterShadowLabyrinthCreatureAI(boss_murmur);
RegisterSpellScript(spell_murmur_thundering_storm);
RegisterSpellScript(spell_murmur_touch);
RegisterSpellScript(spell_shockwave_knockback);
RegisterSpellScript(spell_murmur_sonic_boom_effect);
}

View File

@@ -55,6 +55,8 @@ enum EventSpells
SPELL_SUMMON_COLDWAVE = 45952,
SPELL_SUMMON_FROSTWIND = 45953,
SPELL_CHILLING_AURA = 46542,
/*
SPELL_SUMMON_ICE_SPEAR_BUNNY= 46359, // any dest
SPELL_ICE_SPEAR_KNOCKBACK = 46360, // src caster
@@ -63,6 +65,11 @@ enum EventSpells
*/
};
enum CreatureIds
{
NPC_AHUNITE_HAILSTONE = 25755
};
enum eEvents
{
EVENT_EMERGE = 1,
@@ -81,245 +88,241 @@ enum eEvents
EVENT_SPELL_SUMMON_COLDWAVE,
};
class boss_ahune : public CreatureScript
enum Misc
{
public:
boss_ahune() : CreatureScript("boss_ahune") { }
SET_GUID_INVOKER = 1
};
CreatureAI* GetAI(Creature* pCreature) const override
struct boss_ahune : public ScriptedAI
{
boss_ahune(Creature* c) : ScriptedAI(c), summons(me)
{
return GetTheSlavePensAI<boss_ahuneAI>(pCreature);
SetCombatMovement(false);
SetEquipmentSlots(false, 54806, EQUIP_UNEQUIP, EQUIP_UNEQUIP);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
InvokerGUID.Clear();
events.Reset();
events.RescheduleEvent(EVENT_EMERGE, 12000);
events.RescheduleEvent(EVENT_INVOKER_SAY_1, 1000);
events.RescheduleEvent(EVENT_SUMMON_TOTEMS, 4000);
}
struct boss_ahuneAI : public ScriptedAI
EventMap events;
SummonList summons;
ObjectGuid InvokerGUID;
void StartPhase1()
{
boss_ahuneAI(Creature* c) : ScriptedAI(c), summons(me)
me->CastSpell(me, SPELL_AHUNES_SHIELD, true);
events.RescheduleEvent(EVENT_TOTEMS_ATTACK, 80000);
events.RescheduleEvent(EVENT_SPELL_COLD_SLAP, 1200);
events.RescheduleEvent(EVENT_SPELL_SUMMON_HAILSTONE, 2000);
events.RescheduleEvent(EVENT_SPELL_SUMMON_COLDWAVE, 5000);
}
void JustEngagedWith(Unit* /*who*/) override
{
DoZoneInCombat();
events.Reset();
StartPhase1();
}
void SetGUID(ObjectGuid guid, int32 id) override
{
if (id == SET_GUID_INVOKER)
{
SetCombatMovement(false);
SetEquipmentSlots(false, 54806, EQUIP_UNEQUIP, EQUIP_UNEQUIP);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
InvokerGUID.Clear();
events.Reset();
events.RescheduleEvent(EVENT_EMERGE, 12000);
events.RescheduleEvent(EVENT_INVOKER_SAY_1, 1000);
events.RescheduleEvent(EVENT_SUMMON_TOTEMS, 4000);
InvokerGUID = guid;
}
}
EventMap events;
SummonList summons;
ObjectGuid InvokerGUID;
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim() && !me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE))
return;
bool CanBeSeen(Player const* player) override
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
if (player->IsGameMaster())
case 0:
break;
case EVENT_EMERGE:
me->SetVisible(true);
me->CastSpell(me, SPELL_EMERGE_0, false);
events.RescheduleEvent(EVENT_ATTACK, 2000);
break;
case EVENT_SUMMON_TOTEMS:
for (uint8 i = 0; i < 3; ++i)
DoSummon(NPC_TOTEM, TotemPos[i], 10 * 60 * 1000, TEMPSUMMON_TIMED_DESPAWN);
break;
case EVENT_INVOKER_SAY_1:
if (Player* plr = ObjectAccessor::GetPlayer(*me, InvokerGUID))
{
return true;
plr->Say("The Ice Stone has melted!", LANG_UNIVERSAL);
plr->CastSpell(plr, SPELL_MAKE_BONFIRE, true);
}
Group const* group = player->GetGroup();
return group && sLFGMgr->GetDungeon(group->GetGUID()) == lfg::LFG_DUNGEON_FROST_LORD_AHUNE;
}
void StartPhase1()
{
me->CastSpell(me, SPELL_AHUNES_SHIELD, true);
events.RescheduleEvent(EVENT_TOTEMS_ATTACK, 80000);
events.RescheduleEvent(EVENT_SPELL_COLD_SLAP, 1200);
events.RescheduleEvent(EVENT_SPELL_SUMMON_HAILSTONE, 2000);
events.RescheduleEvent(EVENT_SPELL_SUMMON_COLDWAVE, 5000);
}
void JustEngagedWith(Unit* /*who*/) override
{
DoZoneInCombat();
events.RescheduleEvent(EVENT_INVOKER_SAY_2, 2000);
break;
case EVENT_INVOKER_SAY_2:
if (Player* plr = ObjectAccessor::GetPlayer(*me, InvokerGUID))
plr->Say("Ahune, your strength grows no more!", LANG_UNIVERSAL);
events.RescheduleEvent(EVENT_INVOKER_SAY_3, 2000);
break;
case EVENT_INVOKER_SAY_3:
if (Player* plr = ObjectAccessor::GetPlayer(*me, InvokerGUID))
plr->Say("Your frozen reign will not come to pass!", LANG_UNIVERSAL);
break;
case EVENT_ATTACK:
events.Reset();
if (Player* plr = ObjectAccessor::GetPlayer(*me, InvokerGUID))
AttackStart(plr);
me->SetInCombatWithZone();
if (!me->IsInCombat())
{
EnterEvadeMode(EVADE_REASON_OTHER);
return;
}
else
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
break;
case EVENT_TOTEMS_ATTACK:
for (uint8 i = 0; i < 3; ++i)
if (Creature* bunny = me->FindNearestCreature(NPC_TOTEM_BUNNY_1 + i, 150.0f, true))
bunny->CastSpell(me, SPELL_TOTEM_BEAM, false);
events.RescheduleEvent(EVENT_SUBMERGE, 10000);
break;
case EVENT_SUBMERGE:
Talk(EMOTE_RETREAT);
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->CastSpell(me, SPELL_SUBMERGE_0, true);
me->CastSpell(me, SPELL_SELF_STUN, true);
if (Creature* c = DoSummon(NPC_FROZEN_CORE, *me, 24000, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN))
{
c->SetHealth(me->GetHealth());
}
events.Reset();
events.RescheduleEvent(EVENT_COMBAT_EMERGE, 25000);
events.RescheduleEvent(EVENT_EMERGE_WARNING, 20000);
break;
case EVENT_EMERGE_WARNING:
Talk(EMOTE_RESURFACE);
break;
case EVENT_COMBAT_EMERGE:
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->RemoveAura(SPELL_SELF_STUN);
me->CastSpell(me, SPELL_EMERGE_0, false);
// me->CastSpell(me, SPELL_AHUNE_RESURFACES, true); // done in SummonedCreatureDespawn
me->RemoveAura(SPELL_SUBMERGE_0);
summons.DespawnEntry(NPC_FROZEN_CORE);
StartPhase1();
}
break;
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim() && !me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE))
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch(events.ExecuteEvent())
{
case 0:
break;
case EVENT_EMERGE:
me->SetVisible(true);
me->CastSpell(me, SPELL_EMERGE_0, false);
events.RescheduleEvent(EVENT_ATTACK, 2000);
break;
case EVENT_SUMMON_TOTEMS:
for (uint8 i = 0; i < 3; ++i)
DoSummon(NPC_TOTEM, TotemPos[i], 10 * 60 * 1000, TEMPSUMMON_TIMED_DESPAWN);
break;
case EVENT_INVOKER_SAY_1:
if (Player* plr = ObjectAccessor::GetPlayer(*me, InvokerGUID))
{
plr->Say("The Ice Stone has melted!", LANG_UNIVERSAL);
plr->CastSpell(plr, SPELL_MAKE_BONFIRE, true);
}
events.RescheduleEvent(EVENT_INVOKER_SAY_2, 2000);
break;
case EVENT_INVOKER_SAY_2:
if (Player* plr = ObjectAccessor::GetPlayer(*me, InvokerGUID))
plr->Say("Ahune, your strength grows no more!", LANG_UNIVERSAL);
events.RescheduleEvent(EVENT_INVOKER_SAY_3, 2000);
break;
case EVENT_INVOKER_SAY_3:
if (Player* plr = ObjectAccessor::GetPlayer(*me, InvokerGUID))
plr->Say("Your frozen reign will not come to pass!", LANG_UNIVERSAL);
break;
case EVENT_ATTACK:
events.Reset();
if (Player* plr = ObjectAccessor::GetPlayer(*me, InvokerGUID))
AttackStart(plr);
me->SetInCombatWithZone();
if (!me->IsInCombat())
{
EnterEvadeMode(EVADE_REASON_OTHER);
return;
}
else
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
break;
case EVENT_TOTEMS_ATTACK:
for (uint8 i = 0; i < 3; ++i)
if (Creature* bunny = me->FindNearestCreature(NPC_TOTEM_BUNNY_1 + i, 150.0f, true))
bunny->CastSpell(me, SPELL_TOTEM_BEAM, false);
events.RescheduleEvent(EVENT_SUBMERGE, 10000);
break;
case EVENT_SUBMERGE:
Talk(EMOTE_RETREAT);
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->CastSpell(me, SPELL_SUBMERGE_0, true);
me->CastSpell(me, SPELL_SELF_STUN, true);
if (Creature* c = DoSummon(NPC_FROZEN_CORE, *me, 24000, TEMPSUMMON_TIMED_DESPAWN))
{
c->SetHealth(me->GetHealth());
}
events.Reset();
events.RescheduleEvent(EVENT_COMBAT_EMERGE, 25000);
events.RescheduleEvent(EVENT_EMERGE_WARNING, 20000);
break;
case EVENT_EMERGE_WARNING:
Talk(EMOTE_RESURFACE);
break;
case EVENT_COMBAT_EMERGE:
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->RemoveAura(SPELL_SELF_STUN);
me->CastSpell(me, SPELL_EMERGE_0, false);
// me->CastSpell(me, SPELL_AHUNE_RESURFACES, true); // done in SummonedCreatureDespawn
me->RemoveAura(SPELL_SUBMERGE_0);
StartPhase1();
break;
case EVENT_SPELL_COLD_SLAP:
if (Unit* target = SelectTarget(SelectTargetMethod::MaxDistance, 0, 5.0f, true))
if (target->GetPositionZ() < me->GetPositionZ() + 6.0f)
{
int32 dmg = urand(5500, 6000);
me->CastCustomSpell(target, SPELL_COLD_SLAP, &dmg, nullptr, nullptr, false);
float x, y, z;
target->GetNearPoint(target, x, y, z, target->GetObjectSize(), 30.0f, target->GetAngle(me->GetPositionX(), me->GetPositionY()) + M_PI);
target->GetMotionMaster()->MoveJump(x, y, z + 20.0f, 10.0f, 20.0f);
}
events.RepeatEvent(1500);
break;
case EVENT_SPELL_SUMMON_HAILSTONE:
{
float dist = (float)urand(3, 10);
float angle = rand_norm() * 2 * M_PI;
me->CastSpell(MinionSummonPos.GetPositionX() + cos(angle)*dist, MinionSummonPos.GetPositionY() + std::sin(angle)*dist, MinionSummonPos.GetPositionZ(), SPELL_SUMMON_HAILSTONE, false);
events.RepeatEvent(30000);
}
break;
case EVENT_SPELL_SUMMON_COLDWAVE:
for (uint8 i = 0; i < 2; ++i)
{
float dist = (float)urand(3, 10);
float angle = rand_norm() * 2 * M_PI;
me->CastSpell(MinionSummonPos.GetPositionX() + cos(angle)*dist, MinionSummonPos.GetPositionY() + std::sin(angle)*dist, MinionSummonPos.GetPositionZ(), SPELL_SUMMON_COLDWAVE, false);
}
{
float dist = (float)urand(3, 10);
float angle = rand_norm() * 2 * M_PI;
me->CastSpell(MinionSummonPos.GetPositionX() + cos(angle)*dist, MinionSummonPos.GetPositionY() + std::sin(angle)*dist, MinionSummonPos.GetPositionZ(), SPELL_SUMMON_FROSTWIND, false);
}
events.RepeatEvent(6000);
break;
default:
break;
}
DoMeleeAttackIfReady();
}
void MoveInLineOfSight(Unit* /*who*/) override {}
void EnterEvadeMode(EvadeReason why) override
{
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
events.Reset();
summons.DespawnAll();
me->DespawnOrUnsummon(1);
ScriptedAI::EnterEvadeMode(why);
}
void JustSummoned(Creature* summon) override
{
if (summon)
{
summons.Summon(summon);
summon->SetInCombatWithZone();
}
}
void SummonedCreatureDespawn(Creature* summon) override
{
if (summon && summon->GetEntry() == NPC_FROZEN_CORE)
{
if (summon->GetHealth() > 0)
case EVENT_SPELL_COLD_SLAP:
if (Unit* target = SelectTarget(SelectTargetMethod::MaxDistance, 0, 5.0f, true))
if (target->GetPositionZ() < me->GetPositionZ() + 6.0f)
{
me->SetHealth(summon->GetHealth());
summon->CastSpell(summon, SPELL_AHUNE_RESURFACES, true);
int32 dmg = urand(5500, 6000);
me->CastCustomSpell(target, SPELL_COLD_SLAP, &dmg, nullptr, nullptr, false);
float x, y, z;
target->GetNearPoint(target, x, y, z, target->GetObjectSize(), 30.0f, target->GetAngle(me->GetPositionX(), me->GetPositionY()) + M_PI);
target->GetMotionMaster()->MoveJump(x, y, z + 20.0f, 10.0f, 20.0f);
}
else
Unit::Kill(me, me, false);
events.RepeatEvent(1500);
break;
case EVENT_SPELL_SUMMON_HAILSTONE:
{
float dist = (float)urand(3, 10);
float angle = rand_norm() * 2 * M_PI;
me->CastSpell(MinionSummonPos.GetPositionX() + cos(angle) * dist, MinionSummonPos.GetPositionY() + std::sin(angle) * dist, MinionSummonPos.GetPositionZ(), SPELL_SUMMON_HAILSTONE, false);
}
break;
case EVENT_SPELL_SUMMON_COLDWAVE:
for (uint8 i = 0; i < 2; ++i)
{
float dist = (float)urand(3, 10);
float angle = rand_norm() * 2 * M_PI;
me->CastSpell(MinionSummonPos.GetPositionX() + cos(angle) * dist, MinionSummonPos.GetPositionY() + std::sin(angle) * dist, MinionSummonPos.GetPositionZ(), SPELL_SUMMON_COLDWAVE, false);
}
{
float dist = (float)urand(3, 10);
float angle = rand_norm() * 2 * M_PI;
me->CastSpell(MinionSummonPos.GetPositionX() + cos(angle) * dist, MinionSummonPos.GetPositionY() + std::sin(angle) * dist, MinionSummonPos.GetPositionZ(), SPELL_SUMMON_FROSTWIND, false);
}
events.RepeatEvent(12000);
break;
default:
break;
}
DoMeleeAttackIfReady();
}
void MoveInLineOfSight(Unit* /*who*/) override {}
void EnterEvadeMode(EvadeReason why) override
{
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
events.Reset();
summons.DespawnAll();
me->DespawnOrUnsummon(1);
ScriptedAI::EnterEvadeMode(why);
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
summon->SetInCombatWithZone();
if (summon->GetEntry() == NPC_AHUNITE_HAILSTONE)
{
// Doesn't work when cast normally or when added to
// creature template addon. Needs further investigation.
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_CHILLING_AURA))
{
Aura::TryRefreshStackOrCreate(spellInfo, MAX_EFFECT_MASK, summon, summon);
}
}
}
void JustDied(Unit* /*killer*/) override
void SummonedCreatureDespawn(Creature* summon) override
{
if (summon && summon->GetEntry() == NPC_FROZEN_CORE)
{
summons.DespawnAll();
me->DespawnOrUnsummon(15000);
if (GameObject* chest = me->SummonGameObject(187892, MinionSummonPos.GetPositionX(), MinionSummonPos.GetPositionY(), MinionSummonPos.GetPositionZ(), M_PI / 2, 0.0f, 0.0f, 0.0f, 0.0f, 900000000)) // loot
me->RemoveGameObject(chest, false);
bool finished = false;
Map::PlayerList const& players = me->GetMap()->GetPlayers();
if (!players.IsEmpty())
for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i)
if (Player* player = i->GetSource())
{
player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, 25740, 1, me);
if (player->GetGroup() && !finished)
{
finished = true;
sLFGMgr->FinishDungeon(player->GetGroup()->GetGUID(), lfg::LFG_DUNGEON_FROST_LORD_AHUNE, me->FindMap());
}
}
if (summon->GetHealth() > 0)
{
me->SetHealth(summon->GetHealth());
summon->CastSpell(summon, SPELL_AHUNE_RESURFACES, true);
}
else
Unit::Kill(me, me, false);
}
};
}
void JustDied(Unit* /*killer*/) override
{
summons.DespawnAll();
me->DespawnOrUnsummon(15000);
if (GameObject* chest = me->SummonGameObject(187892, MinionSummonPos.GetPositionX(), MinionSummonPos.GetPositionY(), MinionSummonPos.GetPositionZ(), M_PI / 2, 0.0f, 0.0f, 0.0f, 0.0f, 900000000)) // loot
me->RemoveGameObject(chest, false);
bool finished = false;
me->GetMap()->DoForAllPlayers([&](Player* player)
{
player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, 25740, 1, me);
if (player->GetGroup() && !finished)
{
finished = true;
sLFGMgr->FinishDungeon(player->GetGroup()->GetGUID(), lfg::LFG_DUNGEON_FROST_LORD_AHUNE, me->FindMap());
}
});
}
};
class go_ahune_ice_stone : public GameObjectScript
@@ -331,8 +334,18 @@ public:
{
if (!player || !go)
return true;
if (!player->HasItemCount(ITEM_MAGMA_TOTEM))
return true;
if (!player->IsGameMaster())
{
if (Group const* group = player->GetGroup())
{
if (sLFGMgr->GetDungeon(group->GetGUID()) != lfg::LFG_DUNGEON_FROST_LORD_AHUNE)
{
return true;
}
}
}
if (go->FindNearestCreature(NPC_AHUNE, 200.0f, true))
return true;
@@ -345,10 +358,20 @@ public:
{
if (!player || !go)
return true;
if (!player->IsGameMaster())
{
if (Group const* group = player->GetGroup())
{
if (sLFGMgr->GetDungeon(group->GetGUID()) != lfg::LFG_DUNGEON_FROST_LORD_AHUNE)
{
return true;
}
}
}
if (action != GOSSIP_ACTION_INFO_DEF + 1337)
return true;
if (!player->HasItemCount(ITEM_MAGMA_TOTEM))
return true;
if (go->FindNearestCreature(NPC_AHUNE, 200.0f, true))
return true;
@@ -360,7 +383,11 @@ public:
c->SetVisible(false);
c->SetDisplayId(AHUNE_DEFAULT_MODEL);
c->SetFloatValue(UNIT_FIELD_COMBATREACH, 18.0f);
CAST_AI(boss_ahune::boss_ahuneAI, c->AI())->InvokerGUID = player->GetGUID();
if (c->AI())
{
c->AI()->SetGUID(player->GetGUID(), SET_GUID_INVOKER);
}
if (Creature* bunny = go->SummonCreature(NPC_AHUNE_SUMMON_LOC_BUNNY, AhuneSummonPos, TEMPSUMMON_TIMED_DESPAWN, 12000))
if (Creature* crystal_trigger = go->SummonCreature(WORLD_TRIGGER, go->GetPositionX(), go->GetPositionY(), 5.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 12000))
crystal_trigger->CastSpell(bunny, SPELL_STARTING_BEAM, false);
@@ -371,30 +398,8 @@ public:
}
};
class npc_ahune_frozen_core : public CreatureScript
{
public:
npc_ahune_frozen_core() : CreatureScript("npc_ahune_frozen_core") { }
CreatureAI* GetAI(Creature* pCreature) const override
{
return GetTheSlavePensAI<npc_ahune_frozen_coreAI>(pCreature);
}
struct npc_ahune_frozen_coreAI : public NullCreatureAI
{
npc_ahune_frozen_coreAI(Creature* c) : NullCreatureAI(c) {}
void JustDied(Unit* /*killer*/) override
{
me->DespawnOrUnsummon();
}
};
};
void AddSC_boss_ahune()
{
new go_ahune_ice_stone();
new boss_ahune();
new npc_ahune_frozen_core();
RegisterTheSlavePensCreatureAI(boss_ahune);
}

View File

@@ -30,8 +30,6 @@ enum MekgineerSteamrigger
SPELL_SAW_BLADE = 31486,
SPELL_ELECTRIFIED_NET = 35107,
SPELL_ENRAGE = 26662,
SPELL_REPAIR_N = 31532,
SPELL_REPAIR_H = 37936,
SPELL_SUMMON_MECHANICS_1 = 31528,
SPELL_SUMMON_MECHANICS_2 = 31529,

View File

@@ -85,7 +85,15 @@ struct boss_swamplord_muselek : public BossAI
bool CanShootVictim()
{
return me->GetVictim() && !me->IsWithinRange(me->GetVictim(), 10.0f) && me->IsWithinLOSInMap(me->GetVictim());
Unit* victim = me->GetVictim();
if (!victim || !me->IsWithinLOSInMap(victim) || !me->IsWithinRange(victim, 30.f) || me->IsWithinRange(victim, 10.f))
{
_canChase = true;
return false;
}
return true;
}
void JustEngagedWith(Unit* /*who*/) override
@@ -100,6 +108,8 @@ struct boss_swamplord_muselek : public BossAI
me->LoadEquipment(1, true);
DoCastVictim(SPELL_SHOOT);
me->GetMotionMaster()->Clear();
me->StopMoving();
_canChase = false;
}
else if (_canChase)
{
@@ -121,7 +131,7 @@ struct boss_swamplord_muselek : public BossAI
context.Repeat(20s, 30s);
}).Schedule(30s, 40s, [this](TaskContext context)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, false, true))
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 40.0f, false, true))
{
_markTarget = target->GetGUID();
_canChase = false;
@@ -135,6 +145,7 @@ struct boss_swamplord_muselek : public BossAI
{
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveForwards(me->GetVictim(), 10.0f);
_canChase = false;
}
me->m_Events.AddEventAtOffset([this]()

View File

@@ -17,6 +17,7 @@
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "TaskScheduler.h"
#include "hellfire_ramparts.h"
enum Says
@@ -83,11 +84,6 @@ struct boss_vazruden_the_herald : public BossAI
}
}
void JustDied(Unit*) override
{
instance->SetBossState(DATA_VAZRUDEN, DONE);
}
void MovementInform(uint32 type, uint32 id) override
{
if (type == POINT_MOTION_TYPE && id == POINT_MIDDLE)
@@ -105,7 +101,7 @@ struct boss_vazruden_the_herald : public BossAI
{
Talk(SAY_INTRO);
me->GetMotionMaster()->MovePoint(POINT_MIDDLE, -1406.5f, 1746.5f, 85.0f, false);
me->setActive(true);
_JustEngagedWith();
}
else if (summons.size() == 0)
{
@@ -139,11 +135,11 @@ struct boss_vazruden_the_herald : public BossAI
}
};
struct boss_nazan : public BossAI
struct boss_nazan : public ScriptedAI
{
boss_nazan(Creature* creature) : BossAI(creature, DATA_VAZRUDEN)
boss_nazan(Creature* creature) : ScriptedAI(creature)
{
scheduler.SetValidator([this]
_scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
@@ -162,11 +158,11 @@ struct boss_nazan : public BossAI
void JustEngagedWith(Unit*) override
{
scheduler.CancelGroup(GROUP_PHASE_2);
scheduler.Schedule(5ms, GROUP_PHASE_1, [this](TaskContext context)
_scheduler.CancelAll();
_scheduler.Schedule(5ms, GROUP_PHASE_1, [this](TaskContext context)
{
me->GetMotionMaster()->MovePoint(POINT_FLIGHT, NazanPos[urand(0, 2)], false);
scheduler.DelayAll(7s);
_scheduler.DelayAll(7s);
context.Repeat(30s);
}).Schedule(5s, GROUP_PHASE_1, [this](TaskContext context)
{
@@ -191,6 +187,7 @@ struct boss_nazan : public BossAI
{
if (param == ACTION_FLY_DOWN)
{
_scheduler.CancelGroup(GROUP_PHASE_1);
Talk(EMOTE_NAZAN);
me->SetReactState(REACT_PASSIVE);
me->InterruptNonMeleeSpells(true);
@@ -205,9 +202,8 @@ struct boss_nazan : public BossAI
me->SetCanFly(false);
me->SetDisableGravity(false);
me->SetReactState(REACT_AGGRESSIVE);
scheduler.CancelGroup(GROUP_PHASE_1);
me->GetMotionMaster()->MoveChase(me->GetVictim());
scheduler.Schedule(5s, GROUP_PHASE_2, [this](TaskContext context)
_scheduler.Schedule(5s, GROUP_PHASE_2, [this](TaskContext context)
{
DoCastVictim(SPELL_CONE_OF_FIRE);
context.Repeat(12s);
@@ -216,9 +212,10 @@ struct boss_nazan : public BossAI
DoCastRandomTarget(SPELL_FIREBALL);
context.Repeat(4s, 6s);
});
if (IsHeroic())
{
scheduler.Schedule(10s, GROUP_PHASE_2, [this](TaskContext context)
_scheduler.Schedule(10s, GROUP_PHASE_2, [this](TaskContext context)
{
DoCastSelf(SPELL_BELLOWING_ROAR);
context.Repeat(30s);
@@ -227,24 +224,28 @@ struct boss_nazan : public BossAI
}
}
void UpdateAI(uint32 /*diff*/) override
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
_scheduler.Update(diff, [this]
{
if (!me->IsLevitating())
DoMeleeAttackIfReady();
});
if (!me->IsLevitating())
DoMeleeAttackIfReady();
}
private:
TaskScheduler _scheduler;
};
struct boss_vazruden : public BossAI
struct boss_vazruden : public ScriptedAI
{
boss_vazruden(Creature* creature) : BossAI(creature, DATA_VAZRUDEN)
boss_vazruden(Creature* creature) : ScriptedAI(creature)
{
scheduler.SetValidator([this]
_scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
@@ -263,7 +264,7 @@ struct boss_vazruden : public BossAI
void JustEngagedWith(Unit*) override
{
scheduler.Schedule(5s, [this](TaskContext /*context*/)
_scheduler.Schedule(5s, [this](TaskContext /*context*/)
{
Talk(SAY_AGGRO);
}).Schedule(4s, [this](TaskContext context)
@@ -280,7 +281,7 @@ struct boss_vazruden : public BossAI
_hasSpoken = true;
Talk(SAY_KILL);
}
scheduler.Schedule(6s, [this](TaskContext /*context*/)
_scheduler.Schedule(6s, [this](TaskContext /*context*/)
{
_hasSpoken = false;
});
@@ -305,13 +306,16 @@ struct boss_vazruden : public BossAI
if (!UpdateVictim())
return;
scheduler.Update(diff);
DoMeleeAttackIfReady();
_scheduler.Update(diff, [this]
{
DoMeleeAttackIfReady();
});
}
private:
bool _hasSpoken;
bool _nazanCalled;
TaskScheduler _scheduler;
};
class spell_vazruden_fireball : public SpellScript

View File

@@ -144,17 +144,21 @@ class spell_capacitus_polarity_charge : public SpellScript
void HandleTargets(std::list<WorldObject*>& targetList)
{
uint8 count = 0;
for (std::list<WorldObject*>::iterator ihit = targetList.begin(); ihit != targetList.end(); ++ihit)
if ((*ihit)->GetGUID() != GetCaster()->GetGUID())
if (Player* target = (*ihit)->ToPlayer())
for (auto& ihit : targetList)
if (ihit->GetGUID() != GetCaster()->GetGUID())
if (Player* target = ihit->ToPlayer())
if (target->HasAura(GetTriggeringSpell()->Id))
++count;
uint32 spellId = GetSpellInfo()->Id == SPELL_POSITIVE_CHARGE ? SPELL_POSITIVE_CHARGE_STACK : SPELL_NEGATIVE_CHARGE_STACK;
if (count)
{
uint32 spellId = GetSpellInfo()->Id == SPELL_POSITIVE_CHARGE ? SPELL_POSITIVE_CHARGE_STACK : SPELL_NEGATIVE_CHARGE_STACK;
GetCaster()->SetAuraStack(spellId, GetCaster(), count);
}
else
{
GetCaster()->RemoveAurasDueToSpell(spellId);
}
}
void HandleDamage(SpellEffIndex /*effIndex*/)
@@ -174,6 +178,29 @@ class spell_capacitus_polarity_charge : public SpellScript
}
};
class spell_capacitus_polarity_charge_aura : public AuraScript
{
PrepareAuraScript(spell_capacitus_polarity_charge_aura);
void HandleAfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE)
{
Unit* target = GetTarget();
if (!target)
return;
target->RemoveAurasDueToSpell(SPELL_POSITIVE_CHARGE_STACK);
target->RemoveAurasDueToSpell(SPELL_NEGATIVE_CHARGE_STACK);
}
}
void Register() override
{
AfterEffectRemove += AuraEffectRemoveFn(spell_capacitus_polarity_charge_aura::HandleAfterRemove, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL);
}
};
class spell_capacitus_polarity_shift : public SpellScript
{
PrepareSpellScript(spell_capacitus_polarity_shift);
@@ -194,5 +221,6 @@ void AddSC_boss_mechano_lord_capacitus()
{
RegisterMechanarCreatureAI(boss_mechano_lord_capacitus);
RegisterSpellScript(spell_capacitus_polarity_charge);
RegisterSpellScript(spell_capacitus_polarity_charge_aura);
RegisterSpellScript(spell_capacitus_polarity_shift);
}