Merge branch 'master' into Playerbot

This commit is contained in:
Yunfan Li
2025-06-01 14:55:09 +08:00
66 changed files with 1638 additions and 550 deletions
+111
View File
@@ -26,8 +26,10 @@
#include "MapReference.h"
#include "Player.h"
#include "ScriptMgr.h"
#include "TemporarySummon.h"
#include "Vehicle.h"
#include "ZoneScript.h"
#include <functional>
//Disable CreatureAI when charmed
void CreatureAI::OnCharmed(bool /*apply*/)
@@ -383,6 +385,115 @@ void CreatureAI::MoveBackwardsChecks()
me->GetMotionMaster()->MoveBackwards(victim, moveDist);
}
int32 CreatureAI::VisualizeBoundary(uint32 duration, Unit* owner, bool fill, bool checkZ) const
{
static constexpr float BOUNDARY_STEP = 5.0f;
static constexpr uint32 BOUNDARY_VISUALIZE_CREATURE = 21659; // Floaty Flavor Eye
static constexpr float BOUNDARY_VISUALIZE_CREATURE_SCALE = 0.25f;
static constexpr uint32 BOUNDARY_MAX_SPAWNS = 8000;
static constexpr float BOUNDARY_MAX_DISTANCE = MAX_SEARCHER_DISTANCE;
float boundaryStep = fill && checkZ ? BOUNDARY_STEP * 2 : BOUNDARY_STEP;
Position const boundaryDirections[6] = {
{boundaryStep, 0, 0 },
{-boundaryStep, 0, 0 },
{0, boundaryStep, 0 },
{0, -boundaryStep, 0 },
{0, 0, boundaryStep },
{0, 0, -boundaryStep}
};
if (!owner)
return -1;
if (!_boundary || _boundary->empty())
return LANG_CREATURE_MOVEMENT_NOT_BOUNDED;
Position startPosition = owner->GetPosition();
if (!IsInBoundary(&startPosition)) // fall back to creature position
{
startPosition = me->GetPosition();
if (!IsInBoundary(&startPosition)) // fall back to creature home position
{
startPosition = me->GetHomePosition();
if (!IsInBoundary(&startPosition))
return LANG_CREATURE_NO_INTERIOR_POINT_FOUND;
}
}
// Helper to spawn visualization creature
auto spawnVisualizationCreature = [owner, duration, checkZ](Position const& pos)
{
if (TempSummon* summon =
owner->SummonCreature(BOUNDARY_VISUALIZE_CREATURE, pos, TEMPSUMMON_TIMED_DESPAWN, duration))
{
summon->SetObjectScale(BOUNDARY_VISUALIZE_CREATURE_SCALE);
summon->SetUnitFlag(UNIT_FLAG_STUNNED);
summon->SetImmuneToAll(true);
summon->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE_2);
if (!checkZ)
summon->SetDisableGravity(false);
}
};
struct PositionHash
{
std::size_t operator()(Position const& pos) const
{
// Convert to fixed precision coordinates.
// We lose precision here, but we don't care about the exact position
int32 x = int32(pos.m_positionX);
int32 y = int32(pos.m_positionY);
int32 z = int32(pos.m_positionZ);
return std::hash<int32_t>()(x) ^ std::hash<int32_t>()(y) ^ std::hash<int32_t>()(z);
}
};
std::unordered_set<Position, PositionHash> visited;
std::queue<Position> queue;
queue.push(startPosition);
visited.insert(startPosition);
uint8 maxDirections = checkZ ? 6 : 4;
uint32 spawns = 0;
while (!queue.empty())
{
Position currentPosition = queue.front();
queue.pop();
for (uint8 i = 0; i < maxDirections; ++i)
{
Position const& direction = boundaryDirections[i];
Position nextPosition = currentPosition;
nextPosition.RelocateOffset(direction);
if (startPosition.GetExactDist(&nextPosition) > BOUNDARY_MAX_DISTANCE)
break;
if (visited.find(nextPosition) != visited.end())
continue; // already visited
visited.insert(nextPosition);
bool isInBoundary = IsInBoundary(&nextPosition);
if ((isInBoundary && fill) || !isInBoundary)
{
spawnVisualizationCreature(currentPosition);
++spawns;
if (spawns > BOUNDARY_MAX_SPAWNS)
return LANG_CREATURE_MOVEMENT_MAYBE_UNBOUNDED;
}
if (isInBoundary)
queue.push(nextPosition); // continue visiting
}
}
return 0;
}
bool CreatureAI::IsInBoundary(Position const* who) const
{
if (!_boundary)
+3
View File
@@ -209,6 +209,9 @@ public:
virtual void PetStopAttack() { }
// intended for encounter design/debugging. do not use for other purposes. expensive.
int32 VisualizeBoundary(uint32 duration, Unit* owner = nullptr, bool fill = false, bool checkZ = false) const;
// boundary system methods
virtual bool CheckInRoom();
CreatureBoundary const* GetBoundary() const { return _boundary; }
@@ -96,7 +96,7 @@ void BattlegroundRV::PostUpdateImpl(uint32 diff)
if (player->GetPositionZ() < 27.0f)
TeleportUnitToNewZ(player, 28.28f, true);
for (uint8 i = SUMMON_SLOT_TOTEM; i < MAX_TOTEM_SLOT; ++i)
for (uint8 i = SUMMON_SLOT_TOTEM_FIRE; i < MAX_TOTEM_SLOT; ++i)
if (player->m_SummonSlot[i])
if (Creature* totem = GetBgMap()->GetCreature(player->m_SummonSlot[i]))
if (totem->GetPositionZ() < 28.0f)
+23 -12
View File
@@ -2672,29 +2672,30 @@ bool Creature::CanCreatureAttack(Unit const* victim, bool skipDistCheck) const
// pussywizard: don't check distance to home position if recently damaged (allow kiting away from spawnpoint!)
// xinef: this should include taunt auras
if (!isWorldBoss() && (GetLastLeashExtensionTime() + 12 > GameTime::GetGameTime().count() || HasTauntAura()))
if (!isWorldBoss() && (GetLastLeashExtensionTime() + GetLeashTimer() > GameTime::GetGameTime().count() || HasTauntAura()))
return true;
}
if (skipDistCheck)
return true;
// xinef: added size factor for huge npcs
float dist = std::min<float>(GetDetectionRange() + GetObjectSize() * 2, 150.0f);
float dist = sWorld->getFloatConfig(CONFIG_CREATURE_LEASH_RADIUS);
if (Unit* unit = GetCharmerOrOwner())
if (GetCharmerOrOwner())
{
dist = std::min<float>(GetMap()->GetVisibilityRange() + GetObjectSize() * 2, 150.0f);
return victim->IsWithinDist(unit, dist);
return IsWithinDist(victim, dist);
}
if (!dist)
return true;
float x, y, z;
x = y = z = 0.0f;
if (GetMotionMaster()->GetMotionSlot(MOTION_SLOT_IDLE)->GetResetPosition(x, y, z))
return IsInDist2d(x, y, dist);
else
{
// to prevent creatures in air ignore attacks because distance is already too high...
if (GetMovementTemplate().IsFlightAllowed())
return victim->IsInDist2d(&m_homePosition, dist);
else
return victim->IsInDist(&m_homePosition, dist);
}
return IsInDist2d(&m_homePosition, dist);
}
CreatureAddon const* Creature::GetCreatureAddon() const
@@ -3730,6 +3731,16 @@ void Creature::UpdateLeashExtensionTime()
(*GetLastLeashExtensionTimePtr()) = GameTime::GetGameTime().count();
}
uint8 Creature::GetLeashTimer() const
{ // Based on testing on Classic, seems to range from ~11s for low level mobs (1-5) to ~16s for high level mobs (70+)
uint8 timerOffset = 11;
uint8 timerModifier = uint8(GetLevel() / 10) - 2;
// Formula is likely not quite correct, but better than flat timer
return std::max<uint8>(timerOffset, timerOffset + timerModifier);
}
bool Creature::CanPeriodicallyCallForAssistance() const
{
if (!IsInCombat())
@@ -391,6 +391,7 @@ public:
void ClearLastLeashExtensionTimePtr();
time_t GetLastLeashExtensionTime() const;
void UpdateLeashExtensionTime();
uint8 GetLeashTimer() const;
bool IsFreeToMove();
static constexpr uint32 MOVE_CIRCLE_CHECK_INTERVAL = 3000;
@@ -8840,13 +8840,7 @@ void Player::SendInitWorldStates(uint32 zoneId, uint32 areaId)
battlefield->FillInitialWorldStates(packet);
break;
}
[[fallthrough]];
default:
packet.Worldstates.reserve(4);
packet.Worldstates.emplace_back(WORLD_STATE_GENERIC_UNK_2, 0);
packet.Worldstates.emplace_back(WORLD_STATE_GENERIC_UNK_1, 0);
packet.Worldstates.emplace_back(WORLD_STATE_GENERIC_UNK_0, 0);
packet.Worldstates.emplace_back(WORLD_STATE_GENERIC_UNK_3, 0);
break;
}
}
+4 -4
View File
@@ -51,17 +51,17 @@ void Totem::InitStats(uint32 duration)
if (Unit* owner = ObjectAccessor::GetUnit(*this, m_owner))
{
uint32 slot = m_Properties->Slot;
if (owner->IsPlayer() && slot >= SUMMON_SLOT_TOTEM && slot < MAX_TOTEM_SLOT)
if (owner->IsPlayer() && slot >= SUMMON_SLOT_TOTEM_FIRE && slot < MAX_TOTEM_SLOT)
{
WorldPackets::Totem::TotemCreated data;
data.Totem = GetGUID();
data.Slot = slot - SUMMON_SLOT_TOTEM;
data.Slot = slot - SUMMON_SLOT_TOTEM_FIRE;
data.Duration = duration;
data.SpellID = GetUInt32Value(UNIT_CREATED_BY_SPELL);
owner->ToPlayer()->SendDirectMessage(data.Write());
// set display id depending on caster's race
SetDisplayId(owner->GetModelForTotem(PlayerTotemType(m_Properties->Id)));
SetDisplayId(sObjectMgr->GetModelForTotem(SummonSlot(slot), Races(owner->getRace())));
}
SetLevel(owner->GetLevel());
@@ -133,7 +133,7 @@ void Totem::UnSummon(uint32 msTime)
if (Unit* owner = GetOwner())
{
// clear owner's totem slot
for (uint8 i = SUMMON_SLOT_TOTEM; i < MAX_TOTEM_SLOT; ++i)
for (uint8 i = SUMMON_SLOT_TOTEM_FIRE; i < MAX_TOTEM_SLOT; ++i)
{
if (owner->m_SummonSlot[i] == GetGUID())
{
+3 -270
View File
@@ -19261,7 +19261,7 @@ uint32 Unit::GetCombatRatingDamageReduction(CombatRating cr, float rate, float c
return CalculatePct(damage, percent);
}
uint32 Unit::GetModelForForm(ShapeshiftForm form, uint32 spellId) const
uint32 Unit::GetModelForForm(ShapeshiftForm form, uint32 spellId)
{
// Hardcoded cases
switch (spellId)
@@ -19276,177 +19276,8 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form, uint32 spellId) const
if (IsPlayer())
{
switch (form)
{
case FORM_CAT:
// Based on Hair color
if (getRace() == RACE_NIGHTELF)
{
uint8 hairColor = GetByteValue(PLAYER_BYTES, 3);
switch (hairColor)
{
case 7: // Violet
case 8:
return 29405;
case 3: // Light Blue
return 29406;
case 0: // Green
case 1: // Light Green
case 2: // Dark Green
return 29407;
case 4: // White
return 29408;
default: // original - Dark Blue
return 892;
}
}
// Based on Skin color
else if (getRace() == RACE_TAUREN)
{
uint8 skinColor = GetByteValue(PLAYER_BYTES, 0);
// Male
if (getGender() == GENDER_MALE)
{
switch (skinColor)
{
case 12: // White
case 13:
case 14:
case 18: // Completly White
return 29409;
case 9: // Light Brown
case 10:
case 11:
return 29410;
case 6: // Brown
case 7:
case 8:
return 29411;
case 0: // Dark
case 1:
case 2:
case 3: // Dark Grey
case 4:
case 5:
return 29412;
default: // original - Grey
return 8571;
}
}
// Female
else switch (skinColor)
{
case 10: // White
return 29409;
case 6: // Light Brown
case 7:
return 29410;
case 4: // Brown
case 5:
return 29411;
case 0: // Dark
case 1:
case 2:
case 3:
return 29412;
default: // original - Grey
return 8571;
}
}
else if (Player::TeamIdForRace(getRace()) == TEAM_ALLIANCE)
return 892;
else
return 8571;
case FORM_DIREBEAR:
case FORM_BEAR:
// Based on Hair color
if (getRace() == RACE_NIGHTELF)
{
uint8 hairColor = GetByteValue(PLAYER_BYTES, 3);
switch (hairColor)
{
case 0: // Green
case 1: // Light Green
case 2: // Dark Green
return 29413; // 29415?
case 6: // Dark Blue
return 29414;
case 4: // White
return 29416;
case 3: // Light Blue
return 29417;
default: // original - Violet
return 2281;
}
}
// Based on Skin color
else if (getRace() == RACE_TAUREN)
{
uint8 skinColor = GetByteValue(PLAYER_BYTES, 0);
// Male
if (getGender() == GENDER_MALE)
{
switch (skinColor)
{
case 0: // Dark (Black)
case 1:
case 2:
return 29418;
case 3: // White
case 4:
case 5:
case 12:
case 13:
case 14:
return 29419;
case 9: // Light Brown/Grey
case 10:
case 11:
case 15:
case 16:
case 17:
return 29420;
case 18: // Completly White
return 29421;
default: // original - Brown
return 2289;
}
}
// Female
else switch (skinColor)
{
case 0: // Dark (Black)
case 1:
return 29418;
case 2: // White
case 3:
return 29419;
case 6: // Light Brown/Grey
case 7:
case 8:
case 9:
return 29420;
case 10: // Completly White
return 29421;
default: // original - Brown
return 2289;
}
}
else if (Player::TeamIdForRace(getRace()) == TEAM_ALLIANCE)
return 2281;
else
return 2289;
case FORM_FLIGHT:
if (Player::TeamIdForRace(getRace()) == TEAM_ALLIANCE)
return 20857;
return 20872;
case FORM_FLIGHT_EPIC:
if (Player::TeamIdForRace(getRace()) == TEAM_ALLIANCE)
return 21243;
return 21244;
default:
break;
}
if (uint32 ModelId = sObjectMgr->GetModelForShapeshift(form, ToPlayer()))
return ModelId;
}
uint32 modelid = 0;
@@ -19472,104 +19303,6 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form, uint32 spellId) const
return modelid;
}
uint32 Unit::GetModelForTotem(PlayerTotemType totemType)
{
switch (getRace())
{
case RACE_ORC:
{
switch (totemType)
{
case SUMMON_TYPE_TOTEM_FIRE: // fire
return 30758;
case SUMMON_TYPE_TOTEM_EARTH: // earth
return 30757;
case SUMMON_TYPE_TOTEM_WATER: // water
return 30759;
case SUMMON_TYPE_TOTEM_AIR: // air
return 30756;
}
break;
}
case RACE_DWARF:
{
switch (totemType)
{
case SUMMON_TYPE_TOTEM_FIRE: // fire
return 30754;
case SUMMON_TYPE_TOTEM_EARTH: // earth
return 30753;
case SUMMON_TYPE_TOTEM_WATER: // water
return 30755;
case SUMMON_TYPE_TOTEM_AIR: // air
return 30736;
}
break;
}
case RACE_TROLL:
{
switch (totemType)
{
case SUMMON_TYPE_TOTEM_FIRE: // fire
return 30762;
case SUMMON_TYPE_TOTEM_EARTH: // earth
return 30761;
case SUMMON_TYPE_TOTEM_WATER: // water
return 30763;
case SUMMON_TYPE_TOTEM_AIR: // air
return 30760;
}
break;
}
case RACE_TAUREN:
{
switch (totemType)
{
case SUMMON_TYPE_TOTEM_FIRE: // fire
return 4589;
case SUMMON_TYPE_TOTEM_EARTH: // earth
return 4588;
case SUMMON_TYPE_TOTEM_WATER: // water
return 4587;
case SUMMON_TYPE_TOTEM_AIR: // air
return 4590;
}
break;
}
case RACE_DRAENEI:
{
switch (totemType)
{
case SUMMON_TYPE_TOTEM_FIRE: // fire
return 19074;
case SUMMON_TYPE_TOTEM_EARTH: // earth
return 19073;
case SUMMON_TYPE_TOTEM_WATER: // water
return 19075;
case SUMMON_TYPE_TOTEM_AIR: // air
return 19071;
}
break;
}
default: // One standard for other races.
{
switch (totemType)
{
case SUMMON_TYPE_TOTEM_FIRE: // fire
return 4589;
case SUMMON_TYPE_TOTEM_EARTH: // earth
return 4588;
case SUMMON_TYPE_TOTEM_WATER: // water
return 4587;
case SUMMON_TYPE_TOTEM_AIR: // air
return 4590;
}
break;
}
}
return 0;
}
Unit* Unit::GetRedirectThreatTarget() const
{
return _redirectThreatInfo.GetTargetGUID() ? ObjectAccessor::GetUnit(*this, _redirectThreatInfo.GetTargetGUID()) : nullptr;
+2 -19
View File
@@ -26,6 +26,7 @@
#include "ItemTemplate.h"
#include "MotionMaster.h"
#include "Object.h"
#include "SharedDefines.h"
#include "SpellAuraDefines.h"
#include "SpellDefines.h"
#include "ThreatMgr.h"
@@ -599,23 +600,6 @@ enum ReactiveType
MAX_REACTIVE
};
#define SUMMON_SLOT_PET 0
#define SUMMON_SLOT_TOTEM 1
#define MAX_TOTEM_SLOT 5
#define SUMMON_SLOT_MINIPET 5
#define SUMMON_SLOT_QUEST 6
#define MAX_SUMMON_SLOT 7
#define MAX_GAMEOBJECT_SLOT 4
enum PlayerTotemType
{
SUMMON_TYPE_TOTEM_FIRE = 63,
SUMMON_TYPE_TOTEM_EARTH = 81,
SUMMON_TYPE_TOTEM_WATER = 82,
SUMMON_TYPE_TOTEM_AIR = 83,
};
/// Spell cooldown flags sent in SMSG_SPELL_COOLDOWN
enum SpellCooldownFlags
{
@@ -1898,8 +1882,7 @@ public:
void RestoreDisplayId();
void SetNativeDisplayId(uint32 displayId) { SetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID, displayId); }
[[nodiscard]] uint32 GetModelForForm(ShapeshiftForm form, uint32 spellId) const;
uint32 GetModelForTotem(PlayerTotemType totemType);
[[nodiscard]] uint32 GetModelForForm(ShapeshiftForm form, uint32 spellId);
// Unit positons
[[nodiscard]] virtual bool IsInWater() const;
+133
View File
@@ -1785,6 +1785,139 @@ void ObjectMgr::LoadCreatureModelInfo()
LOG_INFO("server.loading", " ");
}
void ObjectMgr::LoadPlayerTotemModels()
{
uint32 oldMSTime = getMSTime();
QueryResult result = WorldDatabase.Query("SELECT TotemID, RaceID, ModelID from player_totem_model");
if (!result)
{
LOG_INFO("server.loading", ">> Loaded 0 player totem model records. DB table `player_totem_model` is empty.");
return;
}
uint32 count = 0;
do
{
Field* fields = result->Fetch();
SummonSlot totemSlot = SummonSlot(fields[0].Get<uint8>());
uint8 race = fields[1].Get<uint8>();
uint32 displayId = fields[2].Get<uint32>();
if (totemSlot < SUMMON_SLOT_TOTEM_FIRE || totemSlot >= MAX_TOTEM_SLOT)
{
LOG_ERROR("sql.sql", "Wrong TotemSlot {} in `player_totem_model` table, skipped.", totemSlot);
continue;
}
ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(race);
if (!raceEntry)
{
LOG_ERROR("sql.sql", "Race {} defined in `player_totem_model` does not exists, skipped.", uint32(race));
continue;
}
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(displayId);
if (!displayEntry)
{
LOG_ERROR("sql.sql", "TotemSlot: {} defined in `player_totem_model` has non-existing model ({}), skipped.", totemSlot, displayId);
continue;
}
_playerTotemModel[std::make_pair(totemSlot, Races(race))] = displayId;
++count;
} while (result->NextRow());
LOG_INFO("server.loading", ">> Loaded {} player totem model records in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
}
uint32 ObjectMgr::GetModelForTotem(SummonSlot totemSlot, Races race) const
{
auto itr = _playerTotemModel.find(std::make_pair(totemSlot, race));
if (itr != _playerTotemModel.end())
return itr->second;
LOG_ERROR("misc", "TotemSlot {} with RaceID ({}) have no totem model data defined, set to default model.", totemSlot, race);
return 0;
}
void ObjectMgr::LoadPlayerShapeshiftModels()
{
uint32 oldMSTime = getMSTime();
QueryResult result = WorldDatabase.Query("SELECT ShapeshiftID, RaceID, CustomizationID, GenderID, ModelID from player_shapeshift_model");
if (!result)
{
LOG_INFO("server.loading", ">> Loaded 0 player shapeshift model records. DB table `player_shapeshift_model` is empty.");
return;
}
uint32 count = 0;
do
{
Field* fields = result->Fetch();
ShapeshiftForm shapeshiftForm = ShapeshiftForm(fields[0].Get<uint8>());
uint8 race = fields[1].Get<uint8>();
uint8 customizationID = fields[2].Get<uint8>();
uint8 genderID = Gender(fields[3].Get<uint8>());
uint32 modelId = fields[4].Get<uint32>();
ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(race);
if (!raceEntry)
{
LOG_ERROR("sql.sql", "Race {} defined in `player_shapeshift_model` does not exists, skipped.", uint32(race));
continue;
}
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(modelId);
if (!displayEntry)
{
LOG_ERROR("sql.sql", "ShapeshiftForm: {}, Race: {} defined in `player_shapeshift_model` has non-existing model ({}), skipped.", shapeshiftForm, race, modelId);
continue;
}
_playerShapeshiftModel[std::make_tuple(shapeshiftForm, race, customizationID, genderID)] = modelId;
++count;
} while (result->NextRow());
LOG_INFO("server.loading", ">> Loaded {} player totem model records in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
}
uint32 ObjectMgr::GetModelForShapeshift(ShapeshiftForm form, Player* player) const
{
uint8 customizationID;
if (player->GetTeamId() == TEAM_ALLIANCE)
customizationID = player->GetByteValue(PLAYER_BYTES, 3); // Use Hair Color
else
customizationID = player->GetByteValue(PLAYER_BYTES, 0); // Use Skin Color
auto itr = _playerShapeshiftModel.find(std::make_tuple(form, player->getRace(), customizationID, player->getGender()));
if (itr != _playerShapeshiftModel.end())
return itr->second; // Explicit combination
itr = _playerShapeshiftModel.find(std::make_tuple(form, player->getRace(), customizationID, GENDER_NONE));
if (itr != _playerShapeshiftModel.end())
return itr->second; // Combination applied to both genders
itr = _playerShapeshiftModel.find(std::make_tuple(form, player->getRace(), 255, player->getGender()));
if (itr != _playerShapeshiftModel.end())
return itr->second; // Default gender-dependent model
itr = _playerShapeshiftModel.find(std::make_tuple(form, player->getRace(), 255, GENDER_NONE));
if (itr != _playerShapeshiftModel.end())
return itr->second; // Last resort
LOG_DEBUG("entities.player", "ShapeshiftForm {} with RaceID ({}) have no shapeshift model data defined, using fallback data.", form, player->getRace());
return 0;
}
void ObjectMgr::LoadLinkedRespawn()
{
uint32 oldMSTime = getMSTime();
+14
View File
@@ -711,6 +711,10 @@ struct DungeonEncounter
typedef std::list<DungeonEncounter const*> DungeonEncounterList;
typedef std::unordered_map<uint32, DungeonEncounterList> DungeonEncounterContainer;
typedef std::map<std::pair<SummonSlot /*TotemSlot*/, Races /*RaceId*/>, uint32 /*DisplayId*/> PlayerTotemModelMap;
typedef std::map<std::tuple<ShapeshiftForm /*ShapeshiftID*/, uint8 /*RaceID*/, uint8 /*CustomizationID*/, uint8 /*GenderID*/>, uint32 /*ModelID*/> PlayerShapeshiftModelMap;
static constexpr uint32 MAX_QUEST_MONEY_REWARDS = 10;
typedef std::array<uint32, MAX_QUEST_MONEY_REWARDS> QuestMoneyRewardArray;
typedef std::unordered_map<uint32, QuestMoneyRewardArray> QuestMoneyRewardStore;
@@ -777,6 +781,10 @@ public:
[[nodiscard]] ItemTemplateContainer const* GetItemTemplateStore() const { return &_itemTemplateStore; }
[[nodiscard]] std::vector<ItemTemplate*> const* GetItemTemplateStoreFast() const { return &_itemTemplateStoreFast; }
uint32 GetModelForTotem(SummonSlot totemSlot, Races race) const;
uint32 GetModelForShapeshift(ShapeshiftForm form, Player* player) const;
ItemSetNameEntry const* GetItemSetNameEntry(uint32 itemId)
{
ItemSetNameContainer::iterator itr = _itemSetNameStore.find(itemId);
@@ -1036,6 +1044,8 @@ public:
void LoadCreatureAddons();
void LoadGameObjectAddons();
void LoadCreatureModelInfo();
void LoadPlayerTotemModels();
void LoadPlayerShapeshiftModels();
void LoadEquipmentTemplates();
void LoadCreatureMovementOverrides();
void LoadGameObjectLocales();
@@ -1623,6 +1633,10 @@ private:
std::set<uint32> _transportMaps; // Helper container storing map ids that are for transports only, loaded from gameobject_template
PlayerTotemModelMap _playerTotemModel;
PlayerShapeshiftModelMap _playerShapeshiftModel;
QuestMoneyRewardStore _questMoneyRewards;
struct GameobjectInstanceSavedState
+1 -1
View File
@@ -686,7 +686,7 @@ void WorldSession::HandleTotemDestroyed(WorldPackets::Totem::TotemDestroyed& tot
return;
uint8 slotId = totemDestroyed.Slot;
slotId += SUMMON_SLOT_TOTEM;
slotId += SUMMON_SLOT_TOTEM_FIRE;
if (slotId >= MAX_TOTEM_SLOT)
return;
@@ -3679,13 +3679,6 @@ void AuraEffect::HandleAuraControlVehicle(AuraApplication const* aurApp, uint8 m
}
else
{
if (GetId() == 53111) // Devour Humanoid
{
Unit::Kill(target, caster);
if (caster->IsCreature())
caster->ToCreature()->RemoveCorpse();
}
caster->_ExitVehicle();
// some SPELL_AURA_CONTROL_VEHICLE auras have a dummy effect on the player - remove them
caster->RemoveAurasDueToSpell(GetId());
+2 -2
View File
@@ -3952,7 +3952,7 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex)
{
int32 basepoints0 = damage;
// Cast Absorb on totems
for (uint8 slot = SUMMON_SLOT_TOTEM; slot < MAX_TOTEM_SLOT; ++slot)
for (uint8 slot = SUMMON_SLOT_TOTEM_FIRE; slot < MAX_TOTEM_SLOT; ++slot)
{
if (!unitTarget->m_SummonSlot[slot])
continue;
@@ -5260,7 +5260,7 @@ void Spell::EffectDestroyAllTotems(SpellEffIndex /*effIndex*/)
return;
int32 mana = 0;
for (uint8 slot = SUMMON_SLOT_TOTEM; slot < MAX_TOTEM_SLOT; ++slot)
for (uint8 slot = SUMMON_SLOT_TOTEM_FIRE; slot < MAX_TOTEM_SLOT; ++slot)
{
if (!m_caster->m_SummonSlot[slot])
continue;
@@ -5105,6 +5105,13 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->AttributesEx6 |= SPELL_ATTR6_ALLOW_WHILE_RIDING_VEHICLE;
});
// Eye of Acherus Flight (Boost)
ApplySpellFix({ 51923 }, [](SpellInfo* spellInfo)
{
spellInfo->Effects[EFFECT_0].ApplyAuraName = SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED;
spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(TARGET_UNIT_CASTER);
});
// Shattering Throw
ApplySpellFix({ 64382 }, [](SpellInfo* spellInfo)
{
@@ -5112,6 +5119,27 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->Attributes |= SPELL_ATTR0_ALLOW_ITEM_SPELL_IN_PVP;
});
ApplySpellFix({
43444, // Explosive Trap (Hex Lord Malacrass)
43447, // Freezing Trap (Hex Lord Malacrass)
43449, // Snake Trap (Hex Lord Malacrass)
}, [](SpellInfo* spellInfo)
{
spellInfo->Effects[EFFECT_0].Effect = SPELL_EFFECT_SUMMON_OBJECT_SLOT1;
});
// Arcane Missiles
ApplySpellFix({ 58529, 61592 }, [](SpellInfo* spellInfo)
{
spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(TARGET_UNIT_TARGET_ENEMY);
});
// Siphon Bloodgem
ApplySpellFix({ 34367 }, [](SpellInfo* spellInfo)
{
spellInfo->ChannelInterruptFlags &= ~AURA_INTERRUPT_FLAG_TURNING;
});
for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i)
{
SpellInfo* spellInfo = mSpellInfoMap[i];
+1
View File
@@ -193,6 +193,7 @@ enum WorldFloatConfigs
CONFIG_LISTEN_RANGE_SAY,
CONFIG_LISTEN_RANGE_TEXTEMOTE,
CONFIG_LISTEN_RANGE_YELL,
CONFIG_CREATURE_LEASH_RADIUS,
CONFIG_CREATURE_FAMILY_FLEE_ASSISTANCE_RADIUS,
CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS,
CONFIG_CHANCE_OF_GM_SURVEY,
+7
View File
@@ -794,6 +794,7 @@ void World::LoadConfigSettings(bool reload)
_int_configs[CONFIG_EVENT_ANNOUNCE] = sConfigMgr->GetOption<int32>("Event.Announce", 0);
_float_configs[CONFIG_CREATURE_LEASH_RADIUS] = sConfigMgr->GetOption<float>("CreatureLeashRadius", 30.0f);
_float_configs[CONFIG_CREATURE_FAMILY_FLEE_ASSISTANCE_RADIUS] = sConfigMgr->GetOption<float>("CreatureFamilyFleeAssistanceRadius", 30.0f);
_float_configs[CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS] = sConfigMgr->GetOption<float>("CreatureFamilyAssistanceRadius", 10.0f);
_int_configs[CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY] = sConfigMgr->GetOption<int32>("CreatureFamilyAssistanceDelay", 2000);
@@ -1402,6 +1403,12 @@ void World::SetInitialWorldSettings()
LOG_INFO("server.loading", "Loading SpellInfo Custom Attributes...");
sSpellMgr->LoadSpellInfoCustomAttributes();
LOG_INFO("server.loading", "Loading Player Totem models...");
sObjectMgr->LoadPlayerTotemModels();
LOG_INFO("server.loading", "Loading Player Shapeshift models...");
sObjectMgr->LoadPlayerShapeshiftModels();
LOG_INFO("server.loading", "Loading GameObject Models...");
LoadGameObjectModelList(_dataPath);
@@ -596,12 +596,6 @@ enum WorldStateIDs
WORLD_STATE_TRIAL_OF_THE_CRUSADER_UI_COUNT = 4389,
WORLD_STATE_TRIAL_OF_THE_CRUSADER_UI_SHOW = 4390,
// Unknown
WORLD_STATE_GENERIC_UNK_0 = 2322,
WORLD_STATE_GENERIC_UNK_1 = 2323,
WORLD_STATE_GENERIC_UNK_2 = 2324,
WORLD_STATE_GENERIC_UNK_3 = 2325,
// Stranglethorn Fishing Extravaganza (Are these custom IDs?)
WORLD_STATE_STRANGLETHORN_VALE_FISHING_PREV_WIN_TIME = 197,
WORLD_STATE_STRANGLETHORN_VALE_FISHING_HAS_WINNER = 198,