Merge branch 'azerothcore:master' into Playerbot

This commit is contained in:
ZhengPeiRu21
2022-05-10 08:46:47 -06:00
committed by GitHub
54 changed files with 1435 additions and 1325 deletions
+6 -6
View File
@@ -13,7 +13,7 @@
#define SIZE_OF_GRIDS 533.3333f
#define MMAP_MAGIC 0x4d4d4150 // 'MMAP'
#define MMAP_VERSION 13
#define MMAP_VERSION 14
struct MmapTileHeader
{
@@ -41,11 +41,11 @@ enum NavArea
NAV_AREA_EMPTY = 0,
// areas 1-60 will be used for destructible areas (currently skipped in vmaps, WMO with flag 1)
// ground is the highest value to make recast choose ground over water when merging surfaces very close to each other (shallow water would be walkable)
NAV_AREA_GROUND_STEEP = 11,
NAV_AREA_GROUND = 10,
NAV_AREA_GROUND = 11,
NAV_AREA_GROUND_STEEP = 10,
NAV_AREA_WATER = 9,
NAV_AREA_MAGMA_SLIME = 8, // don't need to differentiate between them
NAV_AREA_MAX_VALUE = NAV_AREA_GROUND_STEEP,
NAV_AREA_MAX_VALUE = NAV_AREA_GROUND,
NAV_AREA_MIN_VALUE = NAV_AREA_MAGMA_SLIME,
NAV_AREA_ALL_MASK = 0x3F // max allowed value
};
@@ -53,10 +53,10 @@ enum NavArea
enum NavTerrainFlag
{
NAV_EMPTY = 0x00,
NAV_GROUND_STEEP = 1 << (NAV_AREA_MAX_VALUE - NAV_AREA_GROUND_STEEP),
NAV_GROUND = 1 << (NAV_AREA_MAX_VALUE - NAV_AREA_GROUND),
NAV_GROUND_STEEP = 1 << (NAV_AREA_MAX_VALUE - NAV_AREA_GROUND_STEEP),
NAV_WATER = 1 << (NAV_AREA_MAX_VALUE - NAV_AREA_WATER),
NAV_MAGMA_SLIME = 1 << (NAV_AREA_MAX_VALUE - NAV_AREA_MAGMA_SLIME)
};
#endif
#endif /* _MAPDEFINES_H */
+20 -10
View File
@@ -327,7 +327,7 @@ namespace VMAP
float pos_x;
float pos_y;
float pos_z;
short type;
short material;
};
#pragma pack(pop)
//=================================================================
@@ -532,19 +532,29 @@ namespace VMAP
delete[] vectorarray;
}
// ----- liquid
liquid = 0;
if (liquidflags & 1)
liquid = nullptr;
if (liquidflags & 3)
{
WMOLiquidHeader hlq;
READ_OR_RETURN(&blockId, 4);
CMP_OR_RETURN(blockId, "LIQU");
READ_OR_RETURN(&blocksize, sizeof(int));
READ_OR_RETURN(&hlq, sizeof(WMOLiquidHeader));
liquid = new WmoLiquid(hlq.xtiles, hlq.ytiles, Vector3(hlq.pos_x, hlq.pos_y, hlq.pos_z), hlq.type);
uint32 size = hlq.xverts * hlq.yverts;
READ_OR_RETURN(liquid->GetHeightStorage(), size * sizeof(float));
size = hlq.xtiles * hlq.ytiles;
READ_OR_RETURN(liquid->GetFlagsStorage(), size);
uint32 liquidType;
READ_OR_RETURN(&liquidType, sizeof(uint32));
if (liquidflags & 1)
{
WMOLiquidHeader hlq;
READ_OR_RETURN(&hlq, sizeof(WMOLiquidHeader));
liquid = new WmoLiquid(hlq.xtiles, hlq.ytiles, Vector3(hlq.pos_x, hlq.pos_y, hlq.pos_z), liquidType);
uint32 size = hlq.xverts * hlq.yverts;
READ_OR_RETURN(liquid->GetHeightStorage(), size * sizeof(float));
size = hlq.xtiles * hlq.ytiles;
READ_OR_RETURN(liquid->GetFlagsStorage(), size);
}
else
{
liquid = new WmoLiquid(0, 0, Vector3::zero(), liquidType);
liquid->GetHeightStorage()[0] = bounds.high().z;
}
}
return true;
+42 -14
View File
@@ -107,8 +107,16 @@ namespace VMAP
WmoLiquid::WmoLiquid(uint32 width, uint32 height, const Vector3& corner, uint32 type):
iTilesX(width), iTilesY(height), iCorner(corner), iType(type)
{
iHeight = new float[(width + 1) * (height + 1)];
iFlags = new uint8[width * height];
if (width && height)
{
iHeight = new float[(width + 1) * (height + 1)];
iFlags = new uint8[width * height];
}
else
{
iHeight = new float[1];
iFlags = nullptr;
}
}
WmoLiquid::WmoLiquid(const WmoLiquid& other): iHeight(0), iFlags(0)
@@ -157,6 +165,13 @@ namespace VMAP
bool WmoLiquid::GetLiquidHeight(const Vector3& pos, float& liqHeight) const
{
// simple case
if (!iFlags)
{
liqHeight = iHeight[0];
return true;
}
float tx_f = (pos.x - iCorner.x) / LIQUID_TILE_SIZE;
uint32 tx = uint32(tx_f);
if (tx_f < 0.0f || tx >= iTilesX)
@@ -219,8 +234,8 @@ namespace VMAP
{
return 2 * sizeof(uint32) +
sizeof(Vector3) +
(iTilesX + 1) * (iTilesY + 1) * sizeof(float) +
iTilesX * iTilesY;
sizeof(uint32) +
(iFlags ? ((iTilesX + 1) * (iTilesY + 1) * sizeof(float) + iTilesX * iTilesY) : sizeof(float));
}
bool WmoLiquid::writeToFile(FILE* wf)
@@ -231,12 +246,17 @@ namespace VMAP
fwrite(&iCorner, sizeof(Vector3), 1, wf) == 1 &&
fwrite(&iType, sizeof(uint32), 1, wf) == 1)
{
uint32 size = (iTilesX + 1) * (iTilesY + 1);
if (fwrite(iHeight, sizeof(float), size, wf) == size)
if (iTilesX && iTilesY)
{
size = iTilesX * iTilesY;
result = fwrite(iFlags, sizeof(uint8), size, wf) == size;
uint32 size = (iTilesX + 1) * (iTilesY + 1);
if (fwrite(iHeight, sizeof(float), size, wf) == size)
{
size = iTilesX * iTilesY;
result = fwrite(iFlags, sizeof(uint8), size, wf) == size;
}
}
else
result = fwrite(iHeight, sizeof(float), 1, wf) == 1;
}
return result;
@@ -252,13 +272,21 @@ namespace VMAP
fread(&liquid->iCorner, sizeof(Vector3), 1, rf) == 1 &&
fread(&liquid->iType, sizeof(uint32), 1, rf) == 1)
{
uint32 size = (liquid->iTilesX + 1) * (liquid->iTilesY + 1);
liquid->iHeight = new float[size];
if (fread(liquid->iHeight, sizeof(float), size, rf) == size)
if (liquid->iTilesX && liquid->iTilesY)
{
size = liquid->iTilesX * liquid->iTilesY;
liquid->iFlags = new uint8[size];
result = fread(liquid->iFlags, sizeof(uint8), size, rf) == size;
uint32 size = (liquid->iTilesX + 1) * (liquid->iTilesY + 1);
liquid->iHeight = new float[size];
if (fread(liquid->iHeight, sizeof(float), size, rf) == size)
{
size = liquid->iTilesX * liquid->iTilesY;
liquid->iFlags = new uint8[size];
result = fread(liquid->iFlags, sizeof(uint8), size, rf) == size;
}
}
else
{
liquid->iHeight = new float[1];
result = fread(liquid->iHeight, sizeof(float), 1, rf) == 1;
}
}
+2 -2
View File
@@ -23,8 +23,8 @@
namespace VMAP
{
const char VMAP_MAGIC[] = "VMAP_4.6";
const char RAW_VMAP_MAGIC[] = "VMAP046"; // used in extracted vmap files with raw data
const char VMAP_MAGIC[] = "VMAP_4.7";
const char RAW_VMAP_MAGIC[] = "VMAP047"; // used in extracted vmap files with raw data
const char GAMEOBJECT_MODELS[] = "GameObjectModels.dtree";
// defined in TileAssembler.cpp currently...
@@ -392,11 +392,13 @@ void Guardian::InitStats(uint32 duration)
{
Minion::InitStats(duration);
Unit* m_owner = GetOwner();
InitStatsForLevel(m_owner->getLevel());
if (Unit* m_owner = GetOwner())
{
InitStatsForLevel(m_owner->getLevel());
if (m_owner->GetTypeId() == TYPEID_PLAYER && HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN))
m_charmInfo->InitCharmCreateSpells();
if (m_owner->GetTypeId() == TYPEID_PLAYER && HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN))
m_charmInfo->InitCharmCreateSpells();
}
SetReactState(REACT_AGGRESSIVE);
}
+6 -6
View File
@@ -349,14 +349,14 @@ public:
m_flags = 0;
}
[[nodiscard]] T_FLAGS GetFlags() const { return m_flags; }
[[nodiscard]] bool HasFlag(FLAG_TYPE flag) const { return m_flags & (1 << flag); }
void AddFlag(FLAG_TYPE flag) { m_flags |= (1 << flag); }
void DelFlag(FLAG_TYPE flag) { m_flags &= ~(1 << flag); }
[[nodiscard]] T_FLAGS GetFlags() const { return m_flags; }
[[nodiscard]] bool HasFlag(FLAG_TYPE flag) const { return m_flags & (1 << flag); }
void AddFlag(FLAG_TYPE flag) { m_flags |= (1 << flag); }
void DelFlag(FLAG_TYPE flag) { m_flags &= ~(1 << flag); }
[[nodiscard]] T_VALUES GetValue(FLAG_TYPE flag) const { return m_values[flag]; }
void SetValue(FLAG_TYPE flag, T_VALUES value) { m_values[flag] = value; }
void AddValue(FLAG_TYPE flag, T_VALUES value) { m_values[flag] += value; }
void SetValue(FLAG_TYPE flag, T_VALUES value) { m_values[flag] = value; }
void AddValue(FLAG_TYPE flag, T_VALUES value) { m_values[flag] += value; }
private:
T_VALUES m_values[ARRAY_SIZE];
+6 -1
View File
@@ -2947,7 +2947,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo
// Check for attack from behind
// xinef: if from behind or spell requires cast from behind
if (!victim->HasInArc(M_PI, this) || spellInfo->HasAttribute(SPELL_ATTR0_CU_REQ_CASTER_BEHIND_TARGET))
if (!victim->HasInArc(M_PI, this))
{
if (!victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION))
{
@@ -2958,6 +2958,11 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo
canParry = false;
canBlock = false;
}
else // Only deterrence as of 3.3.5
{
if (spellInfo->HasAttribute(SPELL_ATTR0_CU_REQ_CASTER_BEHIND_TARGET))
canParry = false;
}
}
// Check creatures flags_extra for disable parry
+8 -1
View File
@@ -807,6 +807,14 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData)
{
if (pProto->SellPrice > 0)
{
uint32 money = pProto->SellPrice * count;
if (_player->GetMoney() >= MAX_MONEY_AMOUNT - money) // prevent exceeding gold limit
{
_player->SendEquipError(EQUIP_ERR_TOO_MUCH_GOLD, nullptr, nullptr);
_player->SendSellError(SELL_ERR_UNK, creature, itemguid, 0);
return;
}
if (sWorld->getBoolConfig(CONFIG_ITEMDELETE_VENDOR))
recoveryItem(pItem);
@@ -839,7 +847,6 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData)
_player->UpdateTitansGrip();
}
uint32 money = pProto->SellPrice * count;
_player->ModifyMoney(money);
_player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS, money);
}
+25 -27
View File
@@ -1336,7 +1336,8 @@ GridMap::GridMap()
_maxHeight = nullptr;
_minHeight = nullptr;
// Liquid data
_liquidType = 0;
_liquidGlobalEntry = 0;
_liquidGlobalFlags = 0;
_liquidOffX = 0;
_liquidOffY = 0;
_liquidWidth = 0;
@@ -1513,7 +1514,8 @@ bool GridMap::loadLiquidData(FILE* in, uint32 offset, uint32 /*size*/)
if (fread(&header, sizeof(header), 1, in) != 1 || header.fourcc != MapLiquidMagic.asUInt)
return false;
_liquidType = header.liquidType;
_liquidGlobalEntry = header.liquidType;
_liquidGlobalFlags = header.liquidFlags;
_liquidOffX = header.offsetX;
_liquidOffY = header.offsetY;
_liquidWidth = header.width;
@@ -1893,7 +1895,7 @@ inline LiquidData const GridMap::GetLiquidData(float x, float y, float z, float
LiquidData liquidData;
// Check water type (if no water return)
if (_liquidType || _liquidFlags)
if (_liquidGlobalFlags || _liquidFlags)
{
// Get cell
float cx = MAP_RESOLUTION * (32 - x / SIZE_OF_GRIDS);
@@ -1903,38 +1905,34 @@ inline LiquidData const GridMap::GetLiquidData(float x, float y, float z, float
int y_int = (int) cy & (MAP_RESOLUTION - 1);
// Check water type in cell
int idx = (x_int >> 3) * 16 + (y_int >> 3);
uint8 type = _liquidFlags ? _liquidFlags[idx] : _liquidType;
uint32 entry = 0;
if (_liquidEntry)
int idx=(x_int>>3)*16 + (y_int>>3);
uint8 type = _liquidFlags ? _liquidFlags[idx] : _liquidGlobalFlags;
uint32 entry = _liquidEntry ? _liquidEntry[idx] : _liquidGlobalEntry;
if (LiquidTypeEntry const* liquidEntry = sLiquidTypeStore.LookupEntry(entry))
{
if (LiquidTypeEntry const* liquidEntry = sLiquidTypeStore.LookupEntry(_liquidEntry[idx]))
type &= MAP_LIQUID_TYPE_DARK_WATER;
uint32 liqTypeIdx = liquidEntry->Type;
if (entry < 21)
{
entry = liquidEntry->Id;
type &= MAP_LIQUID_TYPE_DARK_WATER;
uint32 liqTypeIdx = liquidEntry->Type;
if (entry < 21)
if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(getArea(x, y)))
{
if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(getArea(x, y)))
uint32 overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type];
if (!overrideLiquid && area->zone)
{
uint32 overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type];
if (!overrideLiquid && area->zone)
{
area = sAreaTableStore.LookupEntry(area->zone);
if (area)
overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type];
}
area = sAreaTableStore.LookupEntry(area->zone);
if (area)
overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type];
}
if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(overrideLiquid))
{
entry = overrideLiquid;
liqTypeIdx = liq->Type;
}
if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(overrideLiquid))
{
entry = overrideLiquid;
liqTypeIdx = liq->Type;
}
}
type |= 1 << liqTypeIdx;
}
type |= 1 << liqTypeIdx;
}
// Check req liquid type mask
+4 -3
View File
@@ -128,7 +128,8 @@ struct map_heightHeader
struct map_liquidHeader
{
uint32 fourcc;
uint16 flags;
uint8 flags;
uint8 liquidFlags;
uint16 liquidType;
uint8 offsetX;
uint8 offsetY;
@@ -158,7 +159,6 @@ enum LiquidStatus
#define MAP_ALL_LIQUIDS (MAP_LIQUID_TYPE_WATER | MAP_LIQUID_TYPE_OCEAN | MAP_LIQUID_TYPE_MAGMA | MAP_LIQUID_TYPE_SLIME)
#define MAP_LIQUID_TYPE_DARK_WATER 0x10
#define MAP_LIQUID_TYPE_WMO_WATER 0x20
#define MAX_HEIGHT 100000.0f // can be use for find ground height at surface
#define INVALID_HEIGHT -100000.0f // for check, must be equal to VMAP_INVALID_HEIGHT, real value for unknown height is VMAP_INVALID_HEIGHT_VALUE
@@ -227,7 +227,8 @@ class GridMap
uint8* _liquidFlags;
float* _liquidMap;
uint16 _gridArea;
uint16 _liquidType;
uint16 _liquidGlobalEntry;
uint8 _liquidGlobalFlags;
uint8 _liquidOffX;
uint8 _liquidOffY;
uint8 _liquidWidth;
+2 -2
View File
@@ -608,11 +608,11 @@ void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id,
}
}
void MotionMaster::MoveCharge(PathGenerator const& path, float speed /*= SPEED_CHARGE*/)
void MotionMaster::MoveCharge(PathGenerator const& path, float speed /*= SPEED_CHARGE*/, ObjectGuid targetGUID /*= ObjectGuid::Empty*/)
{
G3D::Vector3 dest = path.GetActualEndPosition();
MoveCharge(dest.x, dest.y, dest.z, speed, EVENT_CHARGE_PREPATH);
MoveCharge(dest.x, dest.y, dest.z, speed, EVENT_CHARGE_PREPATH, nullptr, false, 0.0f, targetGUID);
// Charge movement is not started when using EVENT_CHARGE_PREPATH
Movement::MoveSplineInit init(_owner);
+1 -1
View File
@@ -219,7 +219,7 @@ public:
void MoveTakeoff(uint32 id, float x, float y, float z, float speed = 0.0f); // pussywizard: added for easy calling by passing 3 floats x, y, z
void MoveCharge(float x, float y, float z, float speed = SPEED_CHARGE, uint32 id = EVENT_CHARGE, const Movement::PointsArray* path = nullptr, bool generatePath = false, float orientation = 0.0f, ObjectGuid targetGUID = ObjectGuid::Empty);
void MoveCharge(PathGenerator const& path, float speed = SPEED_CHARGE);
void MoveCharge(PathGenerator const& path, float speed = SPEED_CHARGE, ObjectGuid targetGUID = ObjectGuid::Empty);
void MoveKnockbackFrom(float srcX, float srcY, float speedXY, float speedZ);
void MoveJumpTo(float angle, float speedXY, float speedZ);
void MoveJump(Position const& pos, float speedXY, float speedZ, uint32 id = 0)
@@ -27,304 +27,31 @@
#define MIN_QUIET_DISTANCE 28.0f
#define MAX_QUIET_DISTANCE 43.0f
template<class T>
void FleeingMovementGenerator<T>::_setTargetLocation(T* owner)
{
if (!owner)
return;
if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting())
{
return;
}
if (!_setMoveData(owner))
return;
float x, y, z;
if (!_getPoint(owner, x, y, z)) {
i_nextCheckTime.Reset(100);
return;
}
owner->AddUnitState(UNIT_STATE_FLEEING_MOVE);
Movement::MoveSplineInit init(owner);
init.MoveTo(x, y, z, true);
init.SetWalk(false);
init.Launch();
}
template<class T>
bool FleeingMovementGenerator<T>::_getPoint(T* owner, float& x, float& y, float& z)
{
if (!owner)
return false;
const Map* _map = owner->GetMap();
x = owner->GetPositionX();
y = owner->GetPositionY();
z = owner->GetPositionZ();
float temp_x, temp_y, angle;
// primitive path-finding
for (uint8 i = 0; i < 18; ++i)
{
if (i_only_forward && i > 2)
break;
float distance = 5.0f;
switch (i)
{
case 0:
angle = i_cur_angle;
break;
case 1:
angle = i_cur_angle;
distance /= 2;
break;
case 2:
angle = i_cur_angle;
distance /= 4;
break;
case 3:
angle = i_cur_angle + static_cast<float>(M_PI / 4);
break;
case 4:
angle = i_cur_angle - static_cast<float>(M_PI / 4);
break;
case 5:
angle = i_cur_angle + static_cast<float>(M_PI / 4);
distance /= 2;
break;
case 6:
angle = i_cur_angle - static_cast<float>(M_PI / 4);
distance /= 2;
break;
case 7:
angle = i_cur_angle + static_cast<float>(M_PI / 2);
break;
case 8:
angle = i_cur_angle - static_cast<float>(M_PI / 2);
break;
case 9:
angle = i_cur_angle + static_cast<float>(M_PI / 2);
distance /= 2;
break;
case 10:
angle = i_cur_angle - static_cast<float>(M_PI / 2);
distance /= 2;
break;
case 11:
angle = i_cur_angle + static_cast<float>(M_PI / 4);
distance /= 4;
break;
case 12:
angle = i_cur_angle - static_cast<float>(M_PI / 4);
distance /= 4;
break;
case 13:
angle = i_cur_angle + static_cast<float>(M_PI / 2);
distance /= 4;
break;
case 14:
angle = i_cur_angle - static_cast<float>(M_PI / 2);
distance /= 4;
break;
case 15:
angle = i_cur_angle + static_cast<float>(3 * M_PI / 4);
distance /= 2;
break;
case 16:
angle = i_cur_angle - static_cast<float>(3 * M_PI / 4);
distance /= 2;
break;
case 17:
angle = i_cur_angle + static_cast<float>(M_PI);
distance /= 2;
break;
default:
angle = 0.0f;
distance = 0.0f;
break;
}
temp_x = x + distance * cos(angle);
temp_y = y + distance * std::sin(angle);
float temp_z = z;
if (!_map->CanReachPositionAndGetValidCoords(owner, temp_x, temp_y, temp_z, true, true))
{
break;
}
if (!(temp_z - z) || distance / std::fabs(temp_z - z) > 1.0f)
{
float temp_z_left = _map->GetHeight(owner->GetPhaseMask(), temp_x + 1.0f * cos(angle + static_cast<float>(M_PI / 2)), temp_y + 1.0f * std::sin(angle + static_cast<float>(M_PI / 2)), z, true);
float temp_z_right = _map->GetHeight(owner->GetPhaseMask(), temp_x + 1.0f * cos(angle - static_cast<float>(M_PI / 2)), temp_y + 1.0f * std::sin(angle - static_cast<float>(M_PI / 2)), z, true);
if (std::fabs(temp_z_left - temp_z) < 1.2f && std::fabs(temp_z_right - temp_z) < 1.2f)
{
// use new values
x = temp_x;
y = temp_y;
z = temp_z;
return true;
}
}
}
i_to_distance_from_caster = 0.0f;
i_nextCheckTime.Reset(urand(500, 1000));
return false;
}
template<class T>
bool FleeingMovementGenerator<T>::_setMoveData(T* owner)
{
float cur_dist_xyz = owner->GetDistance(i_caster_x, i_caster_y, i_caster_z);
if (i_to_distance_from_caster > 0.0f)
{
if ((i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz < i_to_distance_from_caster) ||
// if we reach lower distance
(i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz > i_last_distance_from_caster) ||
// if we can't be close
(i_last_distance_from_caster < i_to_distance_from_caster && cur_dist_xyz > i_to_distance_from_caster) ||
// if we reach bigger distance
(cur_dist_xyz > MAX_QUIET_DISTANCE) || // if we are too far
(i_last_distance_from_caster > MIN_QUIET_DISTANCE && cur_dist_xyz < MIN_QUIET_DISTANCE))
// if we leave 'quiet zone'
{
// we are very far or too close, stopping
i_to_distance_from_caster = 0.0f;
i_nextCheckTime.Reset(urand(500, 1000));
return false;
}
else
{
// now we are running, continue
i_last_distance_from_caster = cur_dist_xyz;
return true;
}
}
float cur_dist;
float angle_to_caster;
if (Unit* fright = ObjectAccessor::GetUnit(*owner, i_frightGUID))
{
cur_dist = fright->GetDistance(owner);
if (cur_dist < cur_dist_xyz)
{
i_caster_x = fright->GetPositionX();
i_caster_y = fright->GetPositionY();
i_caster_z = fright->GetPositionZ();
angle_to_caster = fright->GetAngle(owner);
}
else
{
cur_dist = cur_dist_xyz;
angle_to_caster = owner->GetAngle(i_caster_x, i_caster_y) + static_cast<float>(M_PI);
}
}
else
{
cur_dist = cur_dist_xyz;
angle_to_caster = owner->GetAngle(i_caster_x, i_caster_y) + static_cast<float>(M_PI);
}
// if we too close may use 'path-finding' else just stop
i_only_forward = cur_dist >= MIN_QUIET_DISTANCE / 3;
//get angle and 'distance from caster' to run
float angle;
if (i_cur_angle == 0.0f && i_last_distance_from_caster == 0.0f) //just started, first time
{
angle = (float)rand_norm() * (1.0f - cur_dist / MIN_QUIET_DISTANCE) * static_cast<float>(M_PI / 3) + (float)rand_norm() * static_cast<float>(M_PI * 2 / 3);
i_to_distance_from_caster = MIN_QUIET_DISTANCE;
i_only_forward = true;
}
else if (cur_dist < MIN_QUIET_DISTANCE)
{
angle = static_cast<float>(M_PI / 6) + (float)rand_norm() * static_cast<float>(M_PI * 2 / 3);
i_to_distance_from_caster = cur_dist * 2 / 3 + (float)rand_norm() * (MIN_QUIET_DISTANCE - cur_dist * 2 / 3);
}
else if (cur_dist > MAX_QUIET_DISTANCE)
{
angle = (float)rand_norm() * static_cast<float>(M_PI / 3) + static_cast<float>(M_PI * 2 / 3);
i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + (float)rand_norm() * (MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f);
}
else
{
angle = (float)rand_norm() * static_cast<float>(M_PI);
i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + (float)rand_norm() * (MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f);
}
int8 sign = (float)rand_norm() > 0.5f ? 1 : -1;
i_cur_angle = sign * angle + angle_to_caster;
// current distance
i_last_distance_from_caster = cur_dist;
return true;
}
template<class T>
void FleeingMovementGenerator<T>::DoInitialize(T* owner)
{
if (!owner)
{
return;
}
_path = nullptr;
owner->SetUnitFlag(UNIT_FLAG_FLEEING);
owner->AddUnitState(UNIT_STATE_FLEEING | UNIT_STATE_FLEEING_MOVE);
_Init(owner);
if (Unit* fright = ObjectAccessor::GetUnit(*owner, i_frightGUID))
{
i_caster_x = fright->GetPositionX();
i_caster_y = fright->GetPositionY();
i_caster_z = fright->GetPositionZ();
}
else
{
i_caster_x = owner->GetPositionX();
i_caster_y = owner->GetPositionY();
i_caster_z = owner->GetPositionZ();
}
i_only_forward = true;
i_cur_angle = 0.0f;
i_last_distance_from_caster = 0.0f;
i_to_distance_from_caster = 0.0f;
_setTargetLocation(owner);
owner->AddUnitState(UNIT_STATE_FLEEING);
SetTargetLocation(owner);
}
template<>
void FleeingMovementGenerator<Creature>::_Init(Creature* owner)
template<class T>
void FleeingMovementGenerator<T>::DoFinalize(T*)
{
if (!owner)
return;
//owner->SetTargetGuid(ObjectGuid());
is_water_ok = owner->CanEnterWater();
is_land_ok = owner->CanWalk();
}
template<>
void FleeingMovementGenerator<Player>::_Init(Player* )
{
is_water_ok = true;
is_land_ok = true;
}
template<>
void FleeingMovementGenerator<Player>::DoFinalize(Player* owner)
{
owner->RemoveUnitFlag(UNIT_FLAG_FLEEING);
owner->ClearUnitState(UNIT_STATE_FLEEING | UNIT_STATE_FLEEING_MOVE);
owner->ClearUnitState(UNIT_STATE_FLEEING);
owner->StopMoving();
}
template<>
@@ -332,8 +59,11 @@ void FleeingMovementGenerator<Creature>::DoFinalize(Creature* owner)
{
owner->RemoveUnitFlag(UNIT_FLAG_FLEEING);
owner->ClearUnitState(UNIT_STATE_FLEEING | UNIT_STATE_FLEEING_MOVE);
if (owner->GetVictim())
owner->SetTarget(owner->GetVictim()->GetGUID());
if (Unit* victim = owner->GetVictim())
{
owner->SetTarget(victim->GetGUID());
}
}
template<class T>
@@ -343,36 +73,140 @@ void FleeingMovementGenerator<T>::DoReset(T* owner)
}
template<class T>
bool FleeingMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
bool FleeingMovementGenerator<T>::DoUpdate(T* owner, uint32 diff)
{
if (!owner || !owner->IsAlive())
{
return false;
}
if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting())
{
_path = nullptr;
_interrupt = true;
owner->StopMoving();
return true;
}
else
_interrupt = false;
i_nextCheckTime.Update(time_diff);
if (i_nextCheckTime.Passed() && owner->movespline->Finalized())
_setTargetLocation(owner);
_timer.Update(diff);
if (!_interrupt && _timer.Passed() && owner->movespline->Finalized())
{
SetTargetLocation(owner);
}
return true;
}
template<class T>
void FleeingMovementGenerator<T>::SetTargetLocation(T* owner)
{
if (!owner)
{
return;
}
if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting())
{
_path = nullptr;
_interrupt = true;
owner->StopMoving();
return;
}
owner->AddUnitState(UNIT_STATE_FLEEING_MOVE);
Position destination = owner->GetPosition();
GetPoint(owner, destination);
// Add LOS check for target point
if (!owner->IsWithinLOS(destination.GetPositionX(), destination.GetPositionY(), destination.GetPositionZ()))
{
_timer.Reset(200);
return;
}
if (!_path)
{
_path = std::make_unique<PathGenerator>(owner);
}
else
{
_path->Clear();
}
_path->SetPathLengthLimit(30.0f);
bool result = _path->CalculatePath(destination.GetPositionX(), destination.GetPositionY(), destination.GetPositionZ());
if (!result || (_path->GetPathType() & PathType(PATHFIND_NOPATH | PATHFIND_SHORTCUT | PATHFIND_FARFROMPOLY)))
{
_timer.Reset(100);
return;
}
Movement::MoveSplineInit init(owner);
init.MovebyPath(_path->GetPath());
init.SetWalk(false);
int32 traveltime = init.Launch();
_timer.Reset(traveltime + urand(800, 1500));
}
template<class T>
void FleeingMovementGenerator<T>::GetPoint(T* owner, Position& position)
{
float casterDistance, casterAngle;
if (Unit* fleeTarget = ObjectAccessor::GetUnit(*owner, _fleeTargetGUID))
{
casterDistance = fleeTarget->GetDistance(owner);
if (casterDistance > 0.2f)
{
casterAngle = fleeTarget->GetAngle(owner);
}
else
{
casterAngle = frand(0.0f, 2.0f * float(M_PI));
}
}
else
{
casterDistance = 0.0f;
casterAngle = frand(0.0f, 2.0f * float(M_PI));
}
float distance, angle;
if (casterDistance < MIN_QUIET_DISTANCE)
{
distance = frand(0.4f, 1.3f) * (MIN_QUIET_DISTANCE - casterDistance);
angle = casterAngle + frand(-float(M_PI) / 8.0f, float(M_PI) / 8.0f);
}
else if (casterDistance > MAX_QUIET_DISTANCE)
{
distance = frand(0.4f, 1.0f) * (MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE);
angle = -casterAngle + frand(-float(M_PI) / 4.0f, float(M_PI) / 4.0f);
}
else // we are inside quiet range
{
distance = frand(0.6f, 1.2f) * (MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE);
angle = frand(0.0f, 2.0f * float(M_PI));
}
// In MovePositionToFirstCollision we have added owner's orientation
// so now let's subtract it
angle -= owner->GetOrientation();
owner->MovePositionToFirstCollision(position, distance, angle);
}
template void FleeingMovementGenerator<Player>::DoInitialize(Player*);
template void FleeingMovementGenerator<Creature>::DoInitialize(Creature*);
template bool FleeingMovementGenerator<Player>::_setMoveData(Player*);
template bool FleeingMovementGenerator<Creature>::_setMoveData(Creature*);
template bool FleeingMovementGenerator<Player>::_getPoint(Player*, float&, float&, float&);
template bool FleeingMovementGenerator<Creature>::_getPoint(Creature*, float&, float&, float&);
template void FleeingMovementGenerator<Player>::_setTargetLocation(Player*);
template void FleeingMovementGenerator<Creature>::_setTargetLocation(Creature*);
template void FleeingMovementGenerator<Player>::DoReset(Player*);
template void FleeingMovementGenerator<Creature>::DoReset(Creature*);
template bool FleeingMovementGenerator<Player>::DoUpdate(Player*, uint32);
template bool FleeingMovementGenerator<Creature>::DoUpdate(Creature*, uint32);
template void FleeingMovementGenerator<Player>::SetTargetLocation(Player*);
template void FleeingMovementGenerator<Creature>::SetTargetLocation(Creature*);
template void FleeingMovementGenerator<Player>::GetPoint(Player*, Position&);
template void FleeingMovementGenerator<Creature>::GetPoint(Creature*, Position&);
void TimedFleeingMovementGenerator::Finalize(Unit* owner)
{
@@ -23,34 +23,24 @@
template<class T>
class FleeingMovementGenerator : public MovementGeneratorMedium< T, FleeingMovementGenerator<T> >
{
public:
FleeingMovementGenerator(ObjectGuid fright) : i_frightGUID(fright), i_nextCheckTime(0) {}
public:
explicit FleeingMovementGenerator(ObjectGuid fleeTargetGUID) : _path(nullptr), _fleeTargetGUID(fleeTargetGUID), _timer(0), _interrupt(false) {}
void DoInitialize(T*);
void DoFinalize(T*);
void DoReset(T*);
bool DoUpdate(T*, uint32);
MovementGeneratorType GetMovementGeneratorType() override { return FLEEING_MOTION_TYPE; }
MovementGeneratorType GetMovementGeneratorType() { return FLEEING_MOTION_TYPE; }
void DoInitialize(T*);
void DoFinalize(T*);
void DoReset(T*);
bool DoUpdate(T*, uint32);
private:
void _setTargetLocation(T*);
bool _getPoint(T*, float& x, float& y, float& z);
bool _setMoveData(T* owner);
void _Init(T* );
private:
void SetTargetLocation(T*);
void GetPoint(T*, Position& position);
bool is_water_ok : 1;
bool is_land_ok : 1;
bool i_only_forward: 1;
float i_caster_x;
float i_caster_y;
float i_caster_z;
float i_last_distance_from_caster;
float i_to_distance_from_caster;
float i_cur_angle;
ObjectGuid i_frightGUID;
TimeTracker i_nextCheckTime;
std::unique_ptr<PathGenerator> _path;
ObjectGuid _fleeTargetGUID;
TimeTracker _timer;
bool _interrupt;
};
class TimedFleeingMovementGenerator : public FleeingMovementGenerator<Creature>
@@ -1537,7 +1537,7 @@ void AuraEffect::HandleModInvisibility(AuraApplication const* aurApp, uint8 mode
if (apply)
{
// apply glow vision
if (target->GetTypeId() == TYPEID_PLAYER && type == INVISIBILITY_GENERAL)
if (target->GetTypeId() == TYPEID_PLAYER && (type == INVISIBILITY_GENERAL || type == INVISIBILITY_UNK10))
target->SetByteFlag(PLAYER_FIELD_BYTES2, PLAYER_FIELD_BYTES_2_OFFSET_AURA_VISION, PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW);
target->m_invisibility.AddFlag(type);
@@ -1568,14 +1568,14 @@ void AuraEffect::HandleModInvisibility(AuraApplication const* aurApp, uint8 mode
}
if (!found)
{
target->m_invisibility.DelFlag(type);
// if not have invisibility auras of type INVISIBILITY_GENERAL
// remove glow vision
if (target->GetTypeId() == TYPEID_PLAYER && type == INVISIBILITY_GENERAL)
if (target->GetTypeId() == TYPEID_PLAYER && !target->m_invisibility.HasFlag(INVISIBILITY_GENERAL) && !target->m_invisibility.HasFlag(INVISIBILITY_UNK10))
{
target->RemoveByteFlag(PLAYER_FIELD_BYTES2, PLAYER_FIELD_BYTES_2_OFFSET_AURA_VISION, PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW);
}
target->m_invisibility.DelFlag(type);
}
}
+2 -2
View File
@@ -4875,7 +4875,7 @@ void Spell::EffectCharge(SpellEffIndex /*effIndex*/)
if (!m_preGeneratedPath)
{
Position pos = unitTarget->GetFirstCollisionPosition(unitTarget->GetCombatReach(), unitTarget->GetRelativeAngle(m_caster));
m_caster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ, speed);
m_caster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ, speed, EVENT_CHARGE, nullptr, false, 0.0f, targetGUID);
if (m_caster->GetTypeId() == TYPEID_PLAYER)
{
@@ -4884,7 +4884,7 @@ void Spell::EffectCharge(SpellEffIndex /*effIndex*/)
}
else
{
m_caster->GetMotionMaster()->MoveCharge(*m_preGeneratedPath, speed);
m_caster->GetMotionMaster()->MoveCharge(*m_preGeneratedPath, speed, targetGUID);
if (m_caster->GetTypeId() == TYPEID_PLAYER)
{
-8
View File
@@ -1360,14 +1360,6 @@ bool SpellInfo::IsSingleTarget() const
if (AttributesEx5 & SPELL_ATTR5_LIMIT_N)
return true;
switch (GetSpellSpecific())
{
case SPELL_SPECIFIC_JUDGEMENT:
return true;
default:
break;
}
return false;
}
@@ -3672,8 +3672,8 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->Effects[EFFECT_0].BasePoints = 0;
});
// Krolmir, Hammer of Storms (13010)
ApplySpellFix({ 56606, 56541 }, [](SpellInfo* spellInfo)
// Riding Jokkum
ApplySpellFix({ 56606 }, [](SpellInfo* spellInfo)
{
spellInfo->Effects[EFFECT_0].BasePoints = 1;
});
@@ -15,330 +15,446 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* ScriptData
SDName: boss_kri, boss_yauj, boss_vem : The Bug Trio
SD%Complete: 100
SDComment:
SDCategory: Temple of Ahn'Qiraj
EndScriptData */
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "temple_of_ahnqiraj.h"
#include "TaskScheduler.h"
enum Spells
{
SPELL_CLEAVE = 26350,
SPELL_TOXIC_VOLLEY = 25812,
SPELL_POISON_CLOUD = 38718, //Only Spell with right dmg.
SPELL_ENRAGE = 34624, //Changed cause 25790 is cast on gamers too. Same prob with old explosion of twin emperors.
SPELL_BLOODY_DEATH = 25770,
SPELL_FULL_HEAL = 17683,
SPELL_CHARGE = 26561,
SPELL_KNOCKBACK = 26027,
// Kri
SPELL_CLEAVE = 26350,
SPELL_THRASH = 3391,
SPELL_TOXIC_VOLLEY = 25812,
SPELL_POISON_CLOUD = 26590,
SPELL_HEAL = 25807,
SPELL_FEAR = 19408
// Vem
SPELL_CHARGE = 26561,
SPELL_KNOCKBACK = 18670,
SPELL_KNOCKDOWN = 19128,
SPELL_VENGEANCE = 25790,
// Yauj
SPELL_HEAL = 25807,
SPELL_FEAR = 26580,
SPELL_RAVAGE = 3242,
SPELL_DISPEL = 25808,
NPC_YAUJ_BROOD = 15621
};
class boss_kri : public CreatureScript
enum Misc
{
public:
boss_kri() : CreatureScript("boss_kri") { }
ACTION_CONSUME = 0,
CreatureAI* GetAI(Creature* creature) const override
{
return GetTempleOfAhnQirajAI<boss_kriAI>(creature);
}
EMOTE_DEVOURED = 0,
struct boss_kriAI : public ScriptedAI
{
boss_kriAI(Creature* creature) : ScriptedAI(creature)
{
instance = creature->GetInstanceScript();
}
POINT_CONSUME = 0,
InstanceScript* instance;
uint32 Cleave_Timer;
uint32 ToxicVolley_Timer;
uint32 Check_Timer;
bool VemDead;
bool Death;
void Reset() override
{
Cleave_Timer = urand(4000, 8000);
ToxicVolley_Timer = urand(6000, 12000);
Check_Timer = 2000;
VemDead = false;
Death = false;
}
void EnterCombat(Unit* /*who*/) override
{
}
void JustDied(Unit* /*killer*/) override
{
if (instance->GetData(DATA_BUG_TRIO_DEATH) < 2)// Unlootable if death
me->RemoveDynamicFlag(UNIT_DYNFLAG_LOOTABLE);
instance->SetData(DATA_BUG_TRIO_DEATH, 1);
}
void UpdateAI(uint32 diff) override
{
//Return since we have no target
if (!UpdateVictim())
return;
//Cleave_Timer
if (Cleave_Timer <= diff)
{
DoCastVictim(SPELL_CLEAVE);
Cleave_Timer = urand(5000, 12000);
}
else Cleave_Timer -= diff;
//ToxicVolley_Timer
if (ToxicVolley_Timer <= diff)
{
DoCastVictim(SPELL_TOXIC_VOLLEY);
ToxicVolley_Timer = urand(10000, 15000);
}
else ToxicVolley_Timer -= diff;
if (!HealthAbovePct(5) && !Death)
{
DoCastVictim(SPELL_POISON_CLOUD);
Death = true;
}
if (!VemDead)
{
//Checking if Vem is dead. If yes we will enrage.
if (Check_Timer <= diff)
{
if (instance->GetData(DATA_VEMISDEAD))
{
DoCast(me, SPELL_ENRAGE);
VemDead = true;
}
Check_Timer = 2000;
}
else Check_Timer -= diff;
}
DoMeleeAttackIfReady();
}
};
VEM_WAYPOINT_PATH = 876030
};
class boss_vem : public CreatureScript
const Position resetPoint = { -8582.0f, 2047.0f, -1.62f }; // Taken from CMangos
struct boss_bug_trio : public BossAI
{
public:
boss_vem() : CreatureScript("boss_vem") { }
boss_bug_trio(Creature* creature) : BossAI(creature, DATA_BUG_TRIO) { Reset(); }
CreatureAI* GetAI(Creature* creature) const override
bool CheckInRoom() override
{
return GetTempleOfAhnQirajAI<boss_vemAI>(creature);
if (me->GetExactDist2d(resetPoint) <= 10.f)
{
EnterEvadeMode(EVADE_REASON_BOUNDARY);
return false;
}
return true;
}
struct boss_vemAI : public ScriptedAI
void EnterCombatWithTrio(Unit* who)
{
boss_vemAI(Creature* creature) : ScriptedAI(creature)
if (Creature* vem = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_VEM)))
if (vem->GetGUID() != me->GetGUID())
vem->GetAI()->AttackStart(who);
if (Creature* kri = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_KRI)))
if (kri->GetGUID() != me->GetGUID())
kri->GetAI()->AttackStart(who);
if (Creature* yauj = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_YAUJ)))
if (yauj->GetGUID() != me->GetGUID())
yauj->GetAI()->AttackStart(who);
}
void EvadeAllBosses(EvadeReason why)
{
if (Creature* vem = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_VEM)))
{
instance = creature->GetInstanceScript();
if (vem->GetGUID() != me->GetGUID())
{
if (vem->IsAlive() && !vem->IsInEvadeMode())
vem->AI()->EnterEvadeMode(why);
else
vem->Respawn();
}
}
InstanceScript* instance;
uint32 Charge_Timer;
uint32 KnockBack_Timer;
uint32 Enrage_Timer;
bool Enraged;
void Reset() override
if (Creature* kri = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_KRI)))
{
Charge_Timer = urand(15000, 27000);
KnockBack_Timer = urand(8000, 20000);
Enrage_Timer = 120000;
Enraged = false;
if (kri->GetGUID() != me->GetGUID())
{
if (kri->IsAlive() && !kri->IsInEvadeMode())
kri->AI()->EnterEvadeMode(why);
else
kri->Respawn();
}
}
void JustDied(Unit* /*killer*/) override
if (Creature* yauj = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_YAUJ)))
{
instance->SetData(DATA_VEM_DEATH, 0);
if (instance->GetData(DATA_BUG_TRIO_DEATH) < 2)// Unlootable if death
me->RemoveDynamicFlag(UNIT_DYNFLAG_LOOTABLE);
instance->SetData(DATA_BUG_TRIO_DEATH, 1);
if (yauj->GetGUID() != me->GetGUID())
{
if (yauj->IsAlive() && !yauj->IsInEvadeMode())
yauj->AI()->EnterEvadeMode(why);
else
yauj->Respawn();
}
}
}
void DoAction(int32 action) override
{
if (action != ACTION_CONSUME || dying)
{
return;
}
void EnterCombat(Unit* /*who*/) override
{
}
isEating = true;
me->SetSpeed(MOVE_RUN, 45.f/7.f); // From sniffs
me->SetReactState(REACT_PASSIVE);
}
void UpdateAI(uint32 diff) override
void MovementInform(uint32 type, uint32 /*id*/) override
{
if (type != POINT_MOTION_TYPE)
return;
me->GetMotionMaster()->MoveIdle();
me->SetSpeed(MOVE_RUN, 15.f/7.f); // From sniffs
DoCastSelf(SPELL_FULL_HEAL, true);
DoResetThreat();
isEating = false;
_scheduler.Schedule(4s, [this](TaskContext /*context*/)
{
//Return since we have no target
if (!UpdateVictim())
me->SetReactState(REACT_AGGRESSIVE);
if (Unit* target = me->GetVictim())
{
me->GetMotionMaster()->MoveChase(target);
AttackStart(target);
}
});
}
void EnterEvadeMode(EvadeReason why) override
{
BossAI::EnterEvadeMode(why);
EvadeAllBosses(why);
}
void Reset() override
{
BossAI::Reset();
_scheduler.CancelAll();
dying = false;
isEating = false;
instance->SetData(DATA_BUG_TRIO_DEATH, 0);
me->SetSpeed(MOVE_RUN, 15.f / 7.f); // From sniffs
me->SetStandState(UNIT_STAND_STATE_STAND);
me->SetControlled(false, UNIT_STATE_ROOT);
if (me->GetEntry() == NPC_VEM)
{
me->GetMotionMaster()->MovePath(VEM_WAYPOINT_PATH, true);
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim() || isEating || !CheckInRoom())
return;
_scheduler.Update(diff, [this]
{
DoMeleeAttackIfReady();
});
}
void DamageTaken(Unit* who, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (me->HealthBelowPctDamaged(1, damage) && instance->GetData(DATA_BUG_TRIO_DEATH) < 2 && who->GetGUID() != me->GetGUID())
{
damage = 0;
if (isEating)
return;
//Charge_Timer
if (Charge_Timer <= diff)
_scheduler.CancelAll();
me->SetStandState(UNIT_STAND_STATE_DEAD);
me->SetReactState(REACT_PASSIVE);
me->SetControlled(true, UNIT_STATE_ROOT);
DoFinalSpell();
// Move the other bugs to this bug position
if (Creature* vem = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_VEM)))
{
Unit* target = SelectTarget(SelectTargetMethod::Random, 0);
if (target)
if (vem->GetGUID() != me->GetGUID())
{
DoCast(target, SPELL_CHARGE);
//me->SendMonsterMove(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, true, 1);
AttackStart(target);
vem->AI()->DoAction(ACTION_CONSUME);
vem->GetMotionMaster()->MovePoint(POINT_CONSUME, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ());
}
Charge_Timer = urand(8000, 16000);
}
else Charge_Timer -= diff;
//KnockBack_Timer
if (KnockBack_Timer <= diff)
if (Creature* kri = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_KRI)))
{
DoCastVictim(SPELL_KNOCKBACK);
if (DoGetThreat(me->GetVictim()))
DoModifyThreatPercent(me->GetVictim(), -80);
KnockBack_Timer = urand(15000, 25000);
if (kri->GetGUID() != me->GetGUID())
{
kri->AI()->DoAction(ACTION_CONSUME);
kri->GetMotionMaster()->MovePoint(POINT_CONSUME, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ());
}
}
else KnockBack_Timer -= diff;
//Enrage_Timer
if (!Enraged && Enrage_Timer <= diff)
if (Creature* yauj = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_YAUJ)))
{
DoCast(me, SPELL_ENRAGE);
Enraged = true;
if (yauj->GetGUID() != me->GetGUID())
{
yauj->AI()->DoAction(ACTION_CONSUME);
yauj->GetMotionMaster()->MovePoint(POINT_CONSUME, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ());
}
}
else Charge_Timer -= diff;
DoMeleeAttackIfReady();
_scheduler.Schedule(4s, [this](TaskContext /*context*/)
{
if (!me->IsInEvadeMode())
{
DoCastSelf(SPELL_BLOODY_DEATH, true);
Talk(EMOTE_DEVOURED);
me->DespawnOrUnsummon(1000);
}
});
}
};
}
void DoFinalSpell()
{
switch (me->GetEntry())
{
case NPC_KRI:
DoCastSelf(SPELL_POISON_CLOUD, true);
break;
case NPC_VEM:
DoCastSelf(SPELL_VENGEANCE, true);
break;
case NPC_YAUJ:
for (uint8 i = 0; i < 10; ++i)
{
Position randomPos;
me->GetRandomContactPoint(me, randomPos.m_positionX, randomPos.m_positionY, randomPos.m_positionZ);
if (Creature* summon = me->SummonCreature(NPC_YAUJ_BROOD, randomPos))
DoZoneInCombat(summon);
}
break;
default:
break;
}
}
void JustDied(Unit* killer) override
{
if (killer->GetGUID() == me->GetGUID())
{
instance->SetData(DATA_BUG_TRIO_DEATH, 1);
me->RemoveDynamicFlag(UNIT_DYNFLAG_LOOTABLE);
return;
}
BossAI::JustDied(killer);
}
TaskScheduler _scheduler;
bool dying;
bool isEating;
};
class boss_yauj : public CreatureScript
struct boss_kri : public boss_bug_trio
{
public:
boss_yauj() : CreatureScript("boss_yauj") { }
CreatureAI* GetAI(Creature* creature) const override
boss_kri(Creature* creature) : boss_bug_trio(creature)
{
return GetTempleOfAhnQirajAI<boss_yaujAI>(creature);
}
struct boss_yaujAI : public ScriptedAI
void EnterCombat(Unit* who) override
{
boss_yaujAI(Creature* creature) : ScriptedAI(creature)
EnterCombatWithTrio(who);
_scheduler.Schedule(4s, 8s, [this](TaskContext context)
{
instance = creature->GetInstanceScript();
}
InstanceScript* instance;
uint32 Heal_Timer;
uint32 Fear_Timer;
uint32 Check_Timer;
bool VemDead;
void Reset() override
DoCastVictim(SPELL_CLEAVE);
context.Repeat(5s, 12s);
})
.Schedule(6s, 30s, [this](TaskContext context)
{
Heal_Timer = urand(25000, 40000);
Fear_Timer = urand(12000, 24000);
Check_Timer = 2000;
VemDead = false;
}
void JustDied(Unit* /*killer*/) override
DoCastVictim(SPELL_TOXIC_VOLLEY);
context.Repeat(10s, 25s);
})
.Schedule(6s, [this](TaskContext context)
{
if (instance->GetData(DATA_BUG_TRIO_DEATH) < 2)// Unlootable if death
me->RemoveDynamicFlag(UNIT_DYNFLAG_LOOTABLE);
instance->SetData(DATA_BUG_TRIO_DEATH, 1);
DoCastVictim(SPELL_THRASH);
context.Repeat(2s, 6s);
});
}
};
for (uint8 i = 0; i < 10; ++i)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
struct boss_vem : public boss_bug_trio
{
boss_vem(Creature* creature) : boss_bug_trio(creature)
{
}
void EnterCombat(Unit* who) override
{
EnterCombatWithTrio(who);
_scheduler.Schedule(15s, 27s, [this](TaskContext context)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, [this](Unit* target) -> bool
{
if (Creature* Summoned = me->SummonCreature(15621, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 90000))
Summoned->AI()->AttackStart(target);
}
}
}
if (target->GetTypeId() != TYPEID_PLAYER)
return false;
if (me->IsWithinMeleeRange(target) || target == me->GetVictim())
return false;
if (!me->IsWithinLOS(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ()))
return false;
void EnterCombat(Unit* /*who*/) override
return true;
}))
{
DoCast(target, SPELL_CHARGE);
}
context.Repeat(8s, 16s);
})
.Schedule(10s, 20s, [this](TaskContext context)
{
}
void UpdateAI(uint32 diff) override
DoCastVictim(SPELL_KNOCKBACK);
context.Repeat(10s, 20s);
})
.Schedule(5s, 8s, [this](TaskContext context)
{
//Return since we have no target
if (!UpdateVictim())
return;
//Fear_Timer
if (Fear_Timer <= diff)
DoCastVictim(SPELL_KNOCKDOWN);
context.Repeat(15s, 20s);
})
.Schedule(1s, [this](TaskContext context)
{
if (instance->GetData(DATA_BUG_TRIO_DEATH) == 2 && !me->HasAura(SPELL_VENGEANCE)) // Vem is the only one left.
{
DoCastVictim(SPELL_FEAR);
DoResetThreat();
Fear_Timer = 20000;
DoCastSelf(SPELL_VENGEANCE, true);
}
else Fear_Timer -= diff;
context.Repeat(1s);
});
}
};
//Casting Heal to other twins or herself.
if (Heal_Timer <= diff)
struct boss_yauj : public boss_bug_trio
{
boss_yauj(Creature* creature) : boss_bug_trio(creature)
{
}
void EnterCombat(Unit* who) override
{
EnterCombatWithTrio(who);
_scheduler.Schedule(20s, 30s, [this](TaskContext context)
{
if (me->GetHealthPct() <= 93.f)
{
switch (urand(0, 2))
{
case 0:
if (Creature* kri = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_KRI)))
DoCast(kri, SPELL_HEAL);
break;
case 1:
if (Creature* vem = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_VEM)))
DoCast(vem, SPELL_HEAL);
break;
case 2:
DoCast(me, SPELL_HEAL);
break;
}
Heal_Timer = 15000 + rand() % 15000;
DoCastSelf(SPELL_HEAL);
}
else Heal_Timer -= diff;
//Checking if Vem is dead. If yes we will enrage.
if (Check_Timer <= diff)
else if (Unit* friendly = DoSelectLowestHpFriendly(100.f))
{
if (!VemDead)
{
if (instance->GetData(DATA_VEMISDEAD))
{
DoCast(me, SPELL_ENRAGE);
VemDead = true;
}
}
Check_Timer = 2000;
DoCast(friendly, SPELL_HEAL);
}
else Check_Timer -= diff;
context.Repeat(10s, 30s);
})
.Schedule(12s, 24s, [this](TaskContext context)
{
DoCastAOE(SPELL_FEAR);
DoResetThreat();
context.Repeat(20s);
})
.Schedule(12s, [this](TaskContext context)
{
DoCastVictim(SPELL_RAVAGE);
context.Repeat(10s, 15s);
})
.Schedule(10s, 30s, [this](TaskContext context)
{
std::list<Creature*> targets = DoFindFriendlyCC(50.f);
if (!targets.empty())
{
if (Creature* target = *(targets.begin()))
me->CastSpell(target, SPELL_DISPEL);
}
context.Repeat(10s, 15s);
});
}
};
DoMeleeAttackIfReady();
class spell_vem_knockback : public SpellScript
{
PrepareSpellScript(spell_vem_knockback);
void HandleScriptEffect(SpellEffIndex effIndex)
{
PreventHitDefaultEffect(effIndex);
if (Unit* target = GetHitUnit())
{
if (Creature* cCaster = GetCaster()->ToCreature())
{
cCaster->getThreatMgr().modifyThreatPercent(target, -80);
}
}
};
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_vem_knockback::HandleScriptEffect, EFFECT_2, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
class spell_vem_vengeance : public SpellScript
{
PrepareSpellScript(spell_vem_vengeance);
void FilterTargets(std::list<WorldObject*>& targets)
{
targets.remove_if([&](WorldObject const* target) -> bool
{
return target->GetEntry() != NPC_YAUJ && target->GetEntry() != NPC_VEM && target->GetEntry() != NPC_KRI;
});
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_vem_vengeance::FilterTargets, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ENTRY);
}
};
void AddSC_bug_trio()
{
new boss_kri();
new boss_vem();
new boss_yauj();
RegisterCreatureAI(boss_kri);
RegisterCreatureAI(boss_vem);
RegisterCreatureAI(boss_yauj);
RegisterSpellScript(spell_vem_knockback);
RegisterSpellScript(spell_vem_vengeance);
}
@@ -56,6 +56,7 @@ public:
ObjectGuid SkeramGUID;
ObjectGuid VemGUID;
ObjectGuid KriGUID;
ObjectGuid YaujGUID;
ObjectGuid VeklorGUID;
ObjectGuid VeknilashGUID;
ObjectGuid ViscidusGUID;
@@ -88,6 +89,9 @@ public:
case NPC_KRI:
KriGUID = creature->GetGUID();
break;
case NPC_YAUJ:
YaujGUID = creature->GetGUID();
break;
case NPC_VEKLOR:
VeklorGUID = creature->GetGUID();
break;
@@ -106,11 +110,6 @@ public:
{
switch (type)
{
case DATA_VEMISDEAD:
if (IsBossDied[0])
return 1;
break;
case DATA_VEKLORISDEAD:
if (IsBossDied[1])
return 1;
@@ -140,6 +139,8 @@ public:
return VemGUID;
case DATA_KRI:
return KriGUID;
case DATA_YAUJ:
return YaujGUID;
case DATA_VEKLOR:
return VeklorGUID;
case DATA_VEKNILASH:
@@ -155,12 +156,11 @@ public:
{
switch (type)
{
case DATA_VEM_DEATH:
IsBossDied[0] = true;
break;
case DATA_BUG_TRIO_DEATH:
++BugTrioDeathCount;
if (data != 0)
++BugTrioDeathCount;
else
BugTrioDeathCount = 0;
break;
case DATA_VEKLOR_DEATH:
@@ -27,8 +27,8 @@ enum DataTypes
DATA_SKERAM = 1,
DATA_KRI = 2,
DATA_VEM = 3,
DATA_VEMISDEAD = 4,
DATA_VEM_DEATH = 5,
DATA_YAUJ = 4,
DATA_BUG_TRIO = 5,
DATA_VEKLOR = 6,
DATA_VEKLORISDEAD = 7,
DATA_VEKLOR_DEATH = 8,
@@ -60,6 +60,7 @@ enum Creatures
NPC_SKERAM = 15263,
NPC_VEM = 15544,
NPC_KRI = 15511,
NPC_YAUJ = 15543,
NPC_VEKLOR = 15276,
NPC_VEKNILASH = 15275,
NPC_OURO = 15517,
@@ -80,14 +80,14 @@ enum AuriayaEvents
EVENT_ENRAGE = 10,
};
enum AuriayaSounds
enum Texts
{
SOUND_AGGRO = 15473,
SOUND_SLAY1 = 15474,
SOUND_SLAY2 = 15475,
SOUND_DEATH = 15476,
SOUND_BERSERK = 15477,
SOUND_WOUND = 15478,
SAY_AGGRO = 0,
SAY_SLAY = 1,
SAY_BERSERK = 2,
EMOTE_DEATH = 3,
EMOTE_FEAR = 4,
EMOTE_DEFFENDER = 5,
};
enum Misc
@@ -186,26 +186,16 @@ public:
summons.DoZoneInCombat(NPC_SANCTUM_SENTRY);
me->Yell("Some things are better left alone!", LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_AGGRO);
Talk(SAY_AGGRO);
me->setActive(true);
}
void KilledUnit(Unit* /*victim*/) override
void KilledUnit(Unit* victim) override
{
if (urand(0, 2))
if (victim->GetTypeId() != TYPEID_PLAYER || urand(0, 2))
return;
if (urand(0, 1))
{
me->Yell("The secret dies with you!", LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_SLAY1);
}
else
{
me->Yell("There is no escape!", LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_SLAY2);
}
Talk(SAY_SLAY);
}
void JustDied(Unit* /*killer*/) override
@@ -216,8 +206,7 @@ public:
EntryCheckPredicate pred(NPC_FERAL_DEFENDER);
summons.DoAction(ACTION_DESPAWN_ADDS, pred);
summons.DespawnAll();
me->TextEmote("Auriaya screams in agony.", nullptr, true);
me->PlayDirectSound(SOUND_DEATH);
Talk(EMOTE_DEATH);
}
void DoAction(int32 param) override
@@ -240,7 +229,7 @@ public:
switch (events.ExecuteEvent())
{
case EVENT_SUMMON_FERAL_DEFENDER:
me->TextEmote("Auriaya begins to activate Feral Defender.", nullptr, true);
Talk(EMOTE_DEFFENDER);
me->CastSpell(me, SPELL_ACTIVATE_FERAL_DEFENDER, true);
me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_INTERRUPT_CAST, true);
events.ScheduleEvent(EVENT_REMOVE_IMMUNE, 3000);
@@ -249,7 +238,7 @@ public:
me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_INTERRUPT_CAST, false);
break;
case EVENT_TERRIFYING_SCREECH:
me->TextEmote("Auriaya begins to cast Terrifying Screech.", nullptr, true);
Talk(EMOTE_FEAR);
me->CastSpell(me, SPELL_TERRIFYING_SCREECH, false);
events.RepeatEvent(35000);
break;
@@ -273,8 +262,7 @@ public:
break;
}
case EVENT_ENRAGE:
me->TextEmote("You waste my time!", nullptr, true);
me->PlayDirectSound(SOUND_BERSERK);
Talk(SAY_BERSERK);
me->CastSpell(me, SPELL_ENRAGE, true);
break;
}
@@ -183,25 +183,26 @@ enum FreyaEvents
EVENT_DETONATING_LASHER_FLAME_LASH = 55,
};
enum FreyaSounds
enum Texts
{
// STONEBARK
SOUND_STONEBARK_AGGRO = 15500,
SOUND_STONEBARK_SLAY1 = 15501,
SOUND_STONEBARK_SLAY2 = 15502,
SOUND_STONEBARK_DEATH = 15503,
// Elder Brightleaf / Elder Ironbranch / Elder Stonebark
SAY_ELDER_AGGRO = 0,
SAY_ELDER_SLAY = 1,
SAY_ELDER_DEATH = 2,
// IRONBRANCH
SOUND_IRONBRANCH_AGGRO = 15493,
SOUND_IRONBRANCH_SLAY1 = 15494,
SOUND_IRONBRANCH_SLAY2 = 15495,
SOUND_IRONBRANCH_DEATH = 15496,
// BRIGHTLEAF
SOUND_BRIGHTLEAF_AGGRO = 15483,
SOUND_BRIGHTLEAF_SLAY1 = 15485,
SOUND_BRIGHTLEAF_SLAY2 = 15486,
SOUND_BRIGHTLEAF_DEATH = 15487,
// Freya
SAY_AGGRO = 0,
SAY_AGGRO_WITH_ELDER = 1,
SAY_SLAY = 2,
SAY_DEATH = 3,
SAY_BERSERK = 4,
SAY_SUMMON_CONSERVATOR = 5,
SAY_SUMMON_TRIO = 6,
SAY_SUMMON_LASHERS = 7,
EMOTE_LIFEBINDERS_GIFT = 8,
EMOTE_ALLIES_OF_NATURE = 9,
EMOTE_GROUND_TREMOR = 10,
EMOTE_IRON_ROOTS = 11,
};
enum FreyaNPCs
@@ -224,20 +225,6 @@ enum FreyaNPCs
NPC_DETONATING_LASHER = 32918,
};
enum FreyaSouns
{
SOUND_AGGRO = 15526,
SOUND_ELDERS = 15527,
SOUND_CONSERVATOR = 15528,
SOUND_SLAY1 = 15529,
SOUND_SLAY2 = 15530,
SOUND_DEATH = 15531,
SOUND_BERSERK = 15532,
SOUND_TRIO = 15533,
SOUND_DETONATING = 15534,
SOUND_ASKHELP = 15535,
};
enum Misc
{
ACTION_REMOVE_10_STACK = 10,
@@ -322,32 +309,21 @@ public:
if (victim->GetTypeId() != TYPEID_PLAYER || urand(0, 2))
return;
if (urand(0, 1))
{
me->Yell("Forgive me.", LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_SLAY1);
}
else
{
me->Yell("From your death springs life anew!", LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_SLAY2);
}
Talk(SAY_SLAY);
}
void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
// kaboom!
if (damage >= me->GetHealth())
{
me->Yell("His hold on me dissipates. I can see clearly once more. Thank you, heroes.", LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_DEATH);
Talk(SAY_DEATH);
damage = 0;
me->SetReactState(REACT_PASSIVE);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetFaction(FACTION_FRIENDLY);
me->SetHealth(me->GetMaxHealth());
me->CombatStop();
me->RemoveAllAuras();
me->AttackStop();
events.Reset();
summons.DespawnAll();
@@ -360,7 +336,7 @@ public:
continue;
if (Creature* e = ObjectAccessor::GetCreature(*me, _elderGUID[i]))
Unit::Kill(e, e);
e->DespawnOrUnsummon();
++_elderCount;
}
@@ -397,12 +373,12 @@ public:
void SpawnWave()
{
_waveNumber = _waveNumber == 1 ? 3 : _waveNumber - 1;
Talk(EMOTE_ALLIES_OF_NATURE);
// Wave of three
if (_waveNumber == 1)
{
me->Yell("Children, assist me!", LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_TRIO);
Talk(SAY_SUMMON_TRIO);
me->SummonCreature(NPC_ANCIENT_WATER_SPIRIT, me->GetPositionX() + urand(5, 15), me->GetPositionY() + urand(5, 15), me->GetMapHeight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()));
me->SummonCreature(NPC_STORM_LASHER, me->GetPositionX() + urand(5, 15), me->GetPositionY() + urand(5, 15), me->GetMapHeight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()));
me->SummonCreature(NPC_SNAPLASHER, me->GetPositionX() + urand(5, 15), me->GetPositionY() + urand(5, 15), me->GetMapHeight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()));
@@ -410,15 +386,13 @@ public:
// Ancient Conservator
else if (_waveNumber == 2)
{
me->Yell("Eonar, your servant requires aid!", LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_CONSERVATOR);
Talk(SAY_SUMMON_CONSERVATOR);
me->SummonCreature(NPC_ANCIENT_CONSERVATOR, me->GetPositionX() + urand(5, 15), me->GetPositionY() + urand(5, 15), me->GetMapHeight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()), 0, TEMPSUMMON_CORPSE_DESPAWN);
}
// Detonating Lashers
else if (_waveNumber == 3)
{
me->Yell("The swarm of the elements shall overtake you!", LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_DETONATING);
Talk(SAY_SUMMON_LASHERS);
for (uint8 i = 0; i < 10; ++i)
me->SummonCreature(NPC_DETONATING_LASHER, me->GetPositionX() + urand(5, 20), me->GetPositionY() + urand(5, 20), me->GetMapHeight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()), 0, TEMPSUMMON_CORPSE_DESPAWN);
}
@@ -553,13 +527,11 @@ public:
if (_elderGUID[0] || _elderGUID[1] || _elderGUID[2])
{
me->Yell("Elders, grant me your strength!", LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_ELDERS);
Talk(SAY_AGGRO_WITH_ELDER);
}
else
{
me->Yell("The Conservatory must be protected!", LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_AGGRO);
Talk(SAY_AGGRO);
}
}
@@ -595,6 +567,7 @@ public:
break;
case EVENT_FREYA_LIFEBINDER:
{
Talk(EMOTE_LIFEBINDERS_GIFT);
events.RepeatEvent(45000);
float x, y, z;
for (uint8 i = 0; i < 10; ++i)
@@ -643,15 +616,16 @@ public:
break;
}
case EVENT_FREYA_BERSERK:
me->Yell("You have strayed too far, wasted too much time!", LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_BERSERK);
Talk(SAY_BERSERK);
me->CastSpell(me, SPELL_BERSERK, true);
break;
case EVENT_FREYA_GROUND_TREMOR:
Talk(EMOTE_GROUND_TREMOR);
me->CastSpell(me, SPELL_GROUND_TREMOR_FREYA, false);
events.RepeatEvent(25000 + urand(0, 10000));
break;
case EVENT_FREYA_IRON_ROOT:
Talk(EMOTE_IRON_ROOTS);
me->CastCustomSpell(SPELL_IRON_ROOTS_FREYA, SPELLVALUE_MAX_TARGETS, 1, me, false);
events.RepeatEvent(45000 + urand(0, 10000));
break;
@@ -706,24 +680,14 @@ public:
if (urand(0, 1))
return;
if (urand(0, 1))
{
me->TextEmote("Angry roar");
me->PlayDirectSound(SOUND_STONEBARK_SLAY1);
}
else
{
me->Yell("Such a waste.", LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_STONEBARK_SLAY2);
}
Talk(SAY_ELDER_SLAY);
}
void JustDied(Unit* killer) override
{
if (killer && me->GetEntry() == killer->GetEntry())
return;
me->Yell("Matron, flee! They are ruthless....", LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_STONEBARK_DEATH);
Talk(SAY_ELDER_DEATH);
// Lumberjacked
if (me->GetInstanceScript())
@@ -737,8 +701,8 @@ public:
events.ScheduleEvent(EVENT_STONEBARK_GROUND_TREMOR, 5000);
events.ScheduleEvent(EVENT_STONEBARK_PETRIFIED_BARK, 20000);
me->Yell("This place will serve as your graveyard.", LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_STONEBARK_AGGRO);
if (!me->HasAura(SPELL_DRAINED_OF_POWER)) // Prevents speech if combat is initiated by hardmode activation
Talk(SAY_ELDER_AGGRO);
}
void DamageTaken(Unit*, uint32& damage, DamageEffectType damageType, SpellSchoolMask damageSchoolMask) override
@@ -812,24 +776,14 @@ public:
if (urand(0, 1))
return;
if (urand(0, 1))
{
me->Yell("Fertilizer.", LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_BRIGHTLEAF_SLAY1);
}
else
{
me->Yell("Your corpse will nourish the soil!", LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_BRIGHTLEAF_SLAY2);
}
Talk(SAY_ELDER_SLAY);
}
void JustDied(Unit* killer) override
{
if (killer && me->GetEntry() == killer->GetEntry())
return;
me->Yell("Matron, one has fallen!", LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_BRIGHTLEAF_DEATH);
Talk(SAY_ELDER_DEATH);
// Lumberjacked
if (me->GetInstanceScript())
@@ -843,8 +797,8 @@ public:
events.ScheduleEvent(EVENT_BRIGHTLEAF_SOLAR_FLARE, 5000);
events.ScheduleEvent(EVENT_BRIGHTLEAF_UNSTABLE_SUN_BEAM, 8000);
me->Yell("Matron, the Conservatory has been breached!", LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_BRIGHTLEAF_AGGRO);
if (!me->HasAura(SPELL_DRAINED_OF_POWER)) // Prevents speech if combat is initiated by hardmode activation
Talk(SAY_ELDER_AGGRO);
}
void UpdateAI(uint32 diff) override
@@ -933,24 +887,14 @@ public:
if (urand(0, 1))
return;
if (urand(0, 1))
{
me->Yell("I return you whence you came!", LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_IRONBRANCH_SLAY1);
}
else
{
me->Yell("BEGONE!", LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_IRONBRANCH_SLAY2);
}
Talk(SAY_ELDER_SLAY);
}
void JustDied(Unit* killer) override
{
if (killer && me->GetEntry() == killer->GetEntry())
return;
me->Yell("Freya! They come for you.", LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_IRONBRANCH_DEATH);
Talk(SAY_ELDER_DEATH);
// Lumberjacked
if (me->GetInstanceScript())
@@ -964,8 +908,8 @@ public:
events.ScheduleEvent(EVENT_IRONBRANCH_IRON_ROOT, 15000);
events.ScheduleEvent(EVENT_IRONBRANCH_THORN_SWARM, 3000);
me->Yell("Mortals have no place here!", LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_IRONBRANCH_AGGRO);
if (!me->HasAura(SPELL_DRAINED_OF_POWER)) // Prevents speech if combat is initiated by hardmode activation
Talk(SAY_ELDER_AGGRO);
}
void UpdateAI(uint32 diff) override
@@ -57,26 +57,17 @@
#define NPC_SCORCHED_GROUND 33123
#define NPC_WATER_TRIGGER 22515
#define TEXT_AGGRO "Insolent whelps! Your blood will temper the weapons used to reclaim this world!"
#define TEXT_ACTIVATE_CONSTRUCT "Arise, soldiers of the Iron Crucible! The Makers' will be done!"
#define TEXT_SCORCH_1 "Let the inferno consume you!"
#define TEXT_SCORCH_2 "BURN! Burn in the makers fire!"
#define TEXT_SLAG_POT "I will burn away your impurities!"
#define TEXT_SLAY_1 "More scraps for the scrapheap!"
#define TEXT_SLAY_2 "Your bones will serve as kindling!"
#define TEXT_BERSERK "Let it be finished!"
#define TEXT_DEATH "I. Have. Failed."
#define TEXT_FLAME_JETS "Ignis The Furnace Master begins to cast Flame Jets!"
#define SOUND_AGGRO 15564
#define SOUND_ACTIVATE_CONSTRUCT 15565
#define SOUND_SLAG_POT 15566
#define SOUND_SCORCH_1 15567
#define SOUND_SCORCH_2 15568
#define SOUND_SLAY_1 15569
#define SOUND_SLAY_2 15570
#define SOUND_BERSERK 15571
#define SOUND_DEATH 15572
enum Texts
{
SAY_AGGRO = 0,
SAY_SUMMON = 1,
SAY_SLAG_POT = 2,
SAY_SCORCH = 3,
SAY_SLAY = 4,
SAY_BERSERK = 5,
SAY_DEATH = 6,
EMOTE_JETS = 7,
};
#define ACHIEV_STOKIN_THE_FURNACE_EVENT 20951
@@ -260,8 +251,7 @@ public:
events.ScheduleEvent(EVENT_SPELL_FLAME_JETS, 32000);
events.ScheduleEvent(EVENT_GRAB, 25000);
me->Yell(TEXT_AGGRO, LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_AGGRO);
Talk(SAY_AGGRO);
DoZoneInCombat();
if( InstanceScript* m_pInstance = me->GetInstanceScript() )
@@ -296,24 +286,15 @@ public:
me->setActive(false);
}
void KilledUnit(Unit* /*victim*/) override
void KilledUnit(Unit* victim) override
{
if( rand() % 2 )
{
me->Yell(TEXT_SLAY_1, LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_SLAY_1);
}
else
{
me->Yell(TEXT_SLAY_2, LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_SLAY_2);
}
if (victim->GetTypeId() == TYPEID_PLAYER)
Talk(SAY_SLAY);
}
void JustDied(Unit* /*killer*/) override
{
me->Yell(TEXT_DEATH, LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_DEATH);
Talk(SAY_DEATH);
if( me->GetInstanceScript() )
me->GetInstanceScript()->SetData(TYPE_IGNIS, DONE);
@@ -357,27 +338,18 @@ public:
case 0:
break;
case EVENT_ACTIVATE_CONSTRUCT:
Talk(SAY_SUMMON);
me->CastCustomSpell(SPELL_ACTIVATE_CONSTRUCT, SPELLVALUE_MAX_TARGETS, 1, (Unit*)nullptr, false);
if (++counter >= 20)
{
me->Yell(TEXT_BERSERK, LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_BERSERK);
Talk(SAY_BERSERK);
me->CastSpell(me, SPELL_BERSERK, true);
break;
}
events.RepeatEvent(RAID_MODE(40000, 30000));
break;
case EVENT_SPELL_SCORCH:
if( rand() % 2 )
{
me->Yell(TEXT_SCORCH_1, LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_SCORCH_1);
}
else
{
me->Yell(TEXT_SCORCH_2, LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_SCORCH_2);
}
Talk(SAY_SCORCH);
me->SetControlled(true, UNIT_STATE_ROOT);
me->DisableRotate(true);
me->SendMovementFlagUpdate();
@@ -390,7 +362,7 @@ public:
me->DisableRotate(false);
break;
case EVENT_SPELL_FLAME_JETS:
me->TextEmote(TEXT_FLAME_JETS, nullptr, true);
Talk(EMOTE_JETS);
me->CastSpell(me->GetVictim(), S_FLAME_JETS, false);
events.RepeatEvent(25000);
break;
@@ -429,8 +401,7 @@ public:
int8 pos = urand(0, playerGUIDs.size() - 1);
if( Player* pTarget = ObjectAccessor::GetPlayer(*me, playerGUIDs.at(pos)) )
{
me->Yell(TEXT_SLAG_POT, LANG_UNIVERSAL);
me->PlayDirectSound(SOUND_SLAG_POT);
Talk(SAY_SLAG_POT);
me->CastSpell(pTarget, SPELL_GRAB, false);
}
}
@@ -25,72 +25,68 @@
#include "WaypointMgr.h"
#include "ulduar.h"
#define SPELL_FLAMEBUFFET_10 64016
#define SPELL_FLAMEBUFFET_25 64023
#define S_FLAMEBUFFET RAID_MODE(SPELL_FLAMEBUFFET_10, SPELL_FLAMEBUFFET_25)
#define SPELL_FIREBALL 63815
#define SPELL_WINGBUFFET 62666
#define SPELL_FLAMEBREATH_10 63317
#define SPELL_FLAMEBREATH_25 64021
#define S_FLAMEBREATH RAID_MODE(SPELL_FLAMEBREATH_10, SPELL_FLAMEBREATH_25)
#define SPELL_FUSEARMOR 64771
#define SPELL_DEVOURINGFLAME 63236
#define SPELL_BERSERK 47008
enum Spells
{
// Razorscale
SPELL_FLAMEBUFFET_10 = 64016,
SPELL_FLAMEBUFFET_25 = 64023,
SPELL_FIREBALL = 63815,
SPELL_WINGBUFFET = 62666,
SPELL_FLAMEBREATH_10 = 63317,
SPELL_FLAMEBREATH_25 = 64021,
SPELL_FUSEARMOR = 64771,
SPELL_FUSED_ARMOR = 64774, // Applied on 5th stack of SPELL_FUSEARMOR
SPELL_DEVOURINGFLAME = 63236,
SPELL_BERSERK = 47008,
#define SPELL_CHAIN_1 49679
#define SPELL_CHAIN_2 49682
#define SPELL_CHAIN_3 49683
#define SPELL_CHAIN_4 49684
#define SPELL_LAUNCH_CHAIN 62505
//#define SPELL_HARPOON_SHOT_BUFF 62509
//#define SPELL_HARPOON_FIRE_STATE 62696
// Haproons
SPELL_CHAIN_1 = 49679,
SPELL_CHAIN_2 = 49682,
SPELL_CHAIN_3 = 49683,
SPELL_CHAIN_4 = 49684,
SPELL_LAUNCH_CHAIN = 62505,
// Dark Rune Sentinel
SPELL_WHIRLWIND = 63808,
SPELL_BATTLE_SHOUT_10 = 46763,
SPELL_BATTLE_SHOUT_25 = 64062,
// Dark Rune Guardian
SPELL_STORMSTRIKE_DMG = 65971,
SPELL_STORMSTRIKE_DEBUFF = 64757,
// Dark Rune Watcher
SPELL_LIGHTINGBOLT_10 = 63809,
SPELL_LIGHTINGBOLT_25 = 64696,
SPELL_CHAINLIGHTNING_10 = 64758,
SPELL_CHAINLIGHTNING_25 = 64759,
};
#define SPELL_FLAMEBUFFET RAID_MODE(SPELL_FLAMEBUFFET_10, SPELL_FLAMEBUFFET_25)
#define SPELL_FLAMEBREATH RAID_MODE(SPELL_FLAMEBREATH_10, SPELL_FLAMEBREATH_25)
#define SPELL_BATTLE_SHOUT RAID_MODE(SPELL_BATTLE_SHOUT_10, SPELL_BATTLE_SHOUT_25)
#define SPELL_LIGHTINGBOLT RAID_MODE(SPELL_LIGHTINGBOLT_10, SPELL_LIGHTINGBOLT_25)
#define SPELL_CHAINLIGHTNING RAID_MODE(SPELL_CHAINLIGHTNING_10, SPELL_CHAINLIGHTNING_25)
#define REQ_CHAIN_COUNT RAID_MODE(2, 4)
#define SPELL_DEVOURINGFLAME_SUMMON 63308
//#define SPELL_DEVOURINGFLAME_GROUNDAURA_10 64709
//#define SPELL_DEVOURINGFLAME_GROUNDAURA_25 64734
//#define S_DEVOURINGFLAME_GROUNDAURA RAID_MODE(SPELL_DEVOURINGFLAME_GROUNDAURA_10, SPELL_DEVOURINGFLAME_GROUNDAURA_25)
//#define NPC_DEVOURINGFLAME 34188
#define SPELL_STORMSTRIKE 51876
#define SPELL_WHIRLWIND 63808
#define SPELL_LIGHTINGBOLT 63809
#define SPELL_CHAINLIGHTNING 64758
#define NPC_DARK_RUNE_SENTINEL 33846
#define NPC_DARK_RUNE_GUARDIAN 33388
#define NPC_DARK_RUNE_WATCHER 33453
#define NPC_EXPEDITION_ENGINEER 33287
#define NPC_EXPEDITION_COMMANDER 33210
//#define NPC_EXPEDITION_DEFENDER 33816
//#define NPC_EXPEDITION_TRAPPER 33259
#define NPC_RAZORSCALE 33186
//#define NPC_HARPOON_FIRE_STATE 33282
#define GO_DRILL 195305
#define GO_HARPOON_GUN_1 194519
#define GO_HARPOON_GUN_2 194541
#define GO_HARPOON_GUN_3 194542
#define GO_HARPOON_GUN_4 194543
#define GO_BROKEN_HARPOON 194565
#define TEXT_GOSSIP_ACTION "We are ready to help!"
#define TEXT_EE_AGGRO "Give us a moment to prepare to build the turrets."
#define TEXT_EE_MOVE_OUT "Ready to move out, keep those dwarves off of our backs!"
#define TEXT_EE_FIRES_OUT "Fires out! Let's rebuild those turrets!"
#define TEXT_TURRET_READY "Harpoon Turret is ready for use!"
#define TEXT_DEEP_BREATH "Razorscale takes a deep breath..."
#define TEXT_GROUNDED_PERMANENTLY "Razorscale grounded permanently!"
#define CORDS_GROUND 588.0f, -166.0f, 391.1f
#define CORDS_AIR 588.0f, -178.0f, 490.0f
#define REPAIR_POINTS 25
enum eSay
enum NPCs
{
SAY_COMMANDER_INTRO = 0,
SAY_COMMANDER_GROUND = 1,
SAY_COMMANDER_AGGRO = 2
NPC_DARK_RUNE_SENTINEL = 33846,
NPC_DARK_RUNE_GUARDIAN = 33388,
NPC_DARK_RUNE_WATCHER = 33453,
NPC_EXPEDITION_ENGINEER = 33287,
NPC_EXPEDITION_COMMANDER = 33210,
NPC_RAZORSCALE_CONTROLLER = 33233, // Trigger Creature
};
enum GOs
{
GO_DRILL = 195305,
GO_HARPOON_GUN_1 = 194519,
GO_HARPOON_GUN_2 = 194541,
GO_HARPOON_GUN_3 = 194542,
GO_HARPOON_GUN_4 = 194543,
GO_BROKEN_HARPOON = 194565,
};
enum eEvents
@@ -113,11 +109,40 @@ enum eEvents
EVENT_SPELL_FLAME_BUFFET,
};
enum eMisc
enum Texts
{
POINT_RAZORSCALE_INIT = 1
// Razorscale
EMOTE_PERMA_GROUND = 0,
EMOTE_BREATH = 1,
EMOTE_BERSERK = 2,
// Expedition Commander
SAY_COMMANDER_AGGRO = 0,
SAY_COMMANDER_GROUND_PHASE = 1,
SAY_COMMANDER_ENGINEERS_DEAD = 2, // Should be called when all engineers are dead, currently unused
// Expedition Engineer
SAY_EE_AGGRO = 0,
SAY_EE_START_REPAIR = 1,
SAY_EE_REBUILD_TURRETS = 2,
// Harpoon
EMOTE_HARPOON = 0,
};
enum Misc
{
POINT_RAZORSCALE_INIT = 1,
REPAIR_POINTS = 25,
// Expedition Commander Gossip
GOSSIP_MENU_START_ENCOUNTER = 10314,
NPC_TEXT_COMMANDER = 40100,
};
const Position CORDS_GROUND = {588.0f, -166.0f, 391.1f};
const Position CORDS_AIR = {588.0f, -178.0f, 490.0f};
class boss_razorscale : public CreatureScript
{
public:
@@ -161,6 +186,11 @@ public:
for (uint8 i = 0; i < 3; ++i)
ExpeditionEngineerGUIDs[i].Clear();
// Show gossip icon if previously hidden
if (Creature* commander = ObjectAccessor::GetCreature(*me, CommanderGUID))
if (!commander->HasNpcFlag(UNIT_NPC_FLAG_GOSSIP))
commander->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP);
CommanderGUID.Clear();
bGroundPhase = false;
flyTimes = 0;
@@ -195,7 +225,7 @@ public:
break;
ExpeditionEngineerGUIDs[i] = (*itr)->GetGUID();
if (!i)
(*itr)->Yell(TEXT_EE_AGGRO, LANG_UNIVERSAL);
(*itr)->AI()->Talk(SAY_EE_AGGRO);
++i;
}
if (Creature* c = me->FindNearestCreature(NPC_EXPEDITION_COMMANDER, 300.0f, true))
@@ -260,7 +290,7 @@ public:
if( count >= REQ_CHAIN_COUNT )
{
if (Creature* commander = ObjectAccessor::GetCreature(*me, CommanderGUID))
commander->AI()->Talk(SAY_COMMANDER_GROUND);
commander->AI()->Talk(SAY_COMMANDER_GROUND_PHASE);
me->InterruptNonMeleeSpells(true);
events.CancelEvent(EVENT_SPELL_FIREBALL);
@@ -348,8 +378,8 @@ public:
case 0:
break;
case EVENT_ENRAGE:
Talk(EMOTE_BERSERK);
me->CastSpell(me, SPELL_BERSERK, true);
events.RepeatEvent(600000);
break;
case EVENT_COMMANDER_SAY_AGGRO:
if (Creature* commander = ObjectAccessor::GetCreature(*me, CommanderGUID))
@@ -360,7 +390,7 @@ public:
if (Creature* c = ObjectAccessor::GetCreature(*me, ExpeditionEngineerGUIDs[i]))
{
if (!i)
c->Yell(TEXT_EE_MOVE_OUT, LANG_UNIVERSAL);
c->AI()->Talk(SAY_EE_START_REPAIR);
c->AI()->SetData(1, 0); // start repairing
}
break;
@@ -454,12 +484,12 @@ public:
}
break;
case EVENT_WARN_DEEP_BREATH:
me->TextEmote(TEXT_DEEP_BREATH, nullptr, true);
Talk(EMOTE_BREATH);
me->RemoveAura(62794);
events.ScheduleEvent(EVENT_PHASE2_FLAME_BREATH, 2500);
break;
case EVENT_PHASE2_FLAME_BREATH:
me->CastSpell(me, S_FLAMEBREATH, true);
me->CastSpell(me, SPELL_FLAMEBREATH, true);
events.ScheduleEvent(EVENT_FLY_UP, 2000);
break;
case EVENT_FLY_UP:
@@ -485,6 +515,7 @@ public:
if( (me->GetHealth() * 100) / me->GetMaxHealth() < 50 ) // start phase 3
{
Talk(EMOTE_PERMA_GROUND);
me->SetControlled(false, UNIT_STATE_ROOT);
me->DisableRotate(false);
DoResetThreat();
@@ -527,12 +558,12 @@ public:
if (Creature* c = ObjectAccessor::GetCreature(*me, ExpeditionEngineerGUIDs[i]))
{
if (!i)
c->Yell(TEXT_EE_FIRES_OUT, LANG_UNIVERSAL);
c->AI()->Talk(SAY_EE_REBUILD_TURRETS);
c->AI()->SetData(1, 0); // start repairing
}
break;
case EVENT_SPELL_FLAME_BREATH:
me->CastSpell(me->GetVictim(), S_FLAMEBREATH, false);
me->CastSpell(me->GetVictim(), SPELL_FLAMEBREATH, false);
events.RepeatEvent(20000);
break;
case EVENT_SPELL_DEVOURING_FLAME_GROUND:
@@ -546,14 +577,14 @@ public:
me->CastSpell(victim, SPELL_FUSEARMOR, false);
if (Aura* aur = victim->GetAura(SPELL_FUSEARMOR))
if (aur->GetStackAmount() == 5)
victim->CastSpell(victim, 64774, true);
victim->CastSpell(victim, SPELL_FUSED_ARMOR, true);
events.RepeatEvent(10000);
break;
}
events.RepeatEvent(2000);
break;
case EVENT_SPELL_FLAME_BUFFET:
me->CastSpell(me->GetVictim(), S_FLAMEBUFFET, false);
me->CastSpell(me->GetVictim(), SPELL_FLAMEBUFFET, false);
events.RepeatEvent(7000);
break;
}
@@ -619,8 +650,8 @@ public:
if (!razorscale || razorscale->IsInCombat())
return true;
AddGossipItemFor(player, GOSSIP_ICON_CHAT, TEXT_GOSSIP_ACTION, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
SendGossipMenuFor(player, 40100, creature);
AddGossipItemFor(player, GOSSIP_MENU_START_ENCOUNTER, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
SendGossipMenuFor(player, NPC_TEXT_COMMANDER, creature);
return true;
}
@@ -638,6 +669,9 @@ public:
Creature* razorscale = ObjectAccessor::GetCreature(*creature, instance->GetGuidData(TYPE_RAZORSCALE));
if (razorscale && !razorscale->IsInCombat())
{
// Do not show gossip icon if encounter is in progress
creature->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP);
// reset npcs NPC_HARPOON_FIRE_STATE
for (uint8 i = 0; i < 4; ++i)
if (Creature* hfs = ObjectAccessor::GetCreature(*creature, instance->GetGuidData(DATA_HARPOON_FIRE_STATE_1 + i)))
@@ -679,7 +713,7 @@ public:
return;
_introSpoken = true;
Talk(SAY_COMMANDER_INTRO);
//Talk(SAY_COMMANDER_INTRO); // No source leads to showing any text messages, perhaps only SOUND ID 15647 is played?
}
private:
@@ -758,7 +792,8 @@ public:
if( GameObject* wh = me->SummonGameObject(GetHarpoonGunIdForThisHFS(), me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 3 * M_PI / 2, 0.0f, 0.0f, 0.0f, 0.0f, 0) )
{
me->RemoveGameObject(wh, false);
me->TextEmote(TEXT_TURRET_READY, nullptr, true);
if (Creature* cr = me->SummonCreature(NPC_RAZORSCALE_CONTROLLER, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 5000))
cr->AI()->Talk(EMOTE_HARPOON);
}
}
}
@@ -987,9 +1022,9 @@ public:
else timer2 -= diff;
if (timer2 == 0 && me->GetVictim() && me->IsWithinMeleeRange(me->GetVictim()))
{
me->CastSpell(me->GetVictim(), 65971, true);
me->CastSpell(me->GetVictim(), 65971, true); // me->CastSpell(me->GetVictim(), 65972, true); // cast the same twice cus second one requires setting offhand damage
me->CastSpell(me->GetVictim(), 64757, true);
me->CastSpell(me->GetVictim(), SPELL_STORMSTRIKE_DMG, true);
me->CastSpell(me->GetVictim(), SPELL_STORMSTRIKE_DMG, true); // cast the same twice cus second one requires setting offhand damage
me->CastSpell(me->GetVictim(), SPELL_STORMSTRIKE_DEBUFF, true);
timer2 = urand(8000, 10000);
return;
}
@@ -1034,7 +1069,7 @@ public:
if( timer1 <= diff )
{
me->CastSpell(me->GetVictim(), RAID_MODE(64758, 64759), false);
me->CastSpell(me->GetVictim(), SPELL_CHAINLIGHTNING, false);
timer1 = urand(10000, 12000);
return;
}
@@ -1043,7 +1078,7 @@ public:
if (timer2 <= diff)
{
me->CastSpell(me->GetVictim(), RAID_MODE(63809, 64696), false);
me->CastSpell(me->GetVictim(), SPELL_LIGHTINGBOLT, false);
timer2 = 4000;
return;
}
@@ -1090,7 +1125,7 @@ public:
if( timer1 <= diff )
{
me->CastSpell(me, RAID_MODE(46763, 64062), false);
me->CastSpell(me, SPELL_BATTLE_SHOUT, false);
timer1 = urand(15000, 20000);
}
else
@@ -1100,7 +1135,7 @@ public:
else timer2 -= diff;
if (timer2 == 0 && me->GetVictim() && me->IsWithinMeleeRange(me->GetVictim()))
{
me->CastSpell(me, 63808, false);
me->CastSpell(me, SPELL_WHIRLWIND, false);
timer2 = urand(10000, 12000);
}
@@ -91,17 +91,20 @@ enum NPCs
NPC_PILE_TRIGGER = 33337,
};
enum Sounds
enum Texts
{
XT_SOUND_AGGRO = 15724,
XT_SOUND_HEART_OPEN = 15725,
XT_SOUND_HEART_CLOSED = 15726,
XT_SOUND_TANTARUM = 15727,
XT_SOUND_SLAY1 = 15728,
XT_SOUND_SLAY2 = 15729,
XT_SOUND_ENRAGE = 15730,
XT_SOUND_DEATH = 15731,
XT_SOUND_SUMMON = 15732,
SAY_AGGRO = 0,
SAY_HEART_OPENED = 1,
SAY_HEART_CLOSED = 2,
SAY_TYMPANIC_TANTRUM = 3,
SAY_SLAY = 4,
SAY_BERSERK = 5,
SAY_DEATH = 6,
SAY_SUMMON = 7,
EMOTE_HEART_OPENED = 8,
EMOTE_HEART_CLOSED = 9,
EMOTE_TYMPANIC_TANTRUM = 10,
EMOTE_SCRAPBOT = 11,
};
enum Misc
@@ -203,8 +206,7 @@ public:
RescheduleEvents(); // Other events are scheduled here
me->setActive(true);
me->Yell("New toys? For me? I promise I won't break them this time!", LANG_UNIVERSAL);
me->PlayDirectSound(XT_SOUND_AGGRO);
Talk(SAY_AGGRO);
if (m_pInstance)
{
@@ -223,23 +225,13 @@ public:
{
if (victim->GetTypeId() == TYPEID_PLAYER && !urand(0, 2))
{
if (urand(0, 1))
{
me->Yell("I... I think I broke it.", LANG_UNIVERSAL);
me->PlayDirectSound(XT_SOUND_SLAY1);
}
else
{
me->Yell("I guess it doesn't bend that way.", LANG_UNIVERSAL);
me->PlayDirectSound(XT_SOUND_SLAY2);
}
Talk(SAY_SLAY);
}
}
void JustDied(Unit* /*killer*/) override
{
me->Yell("You are bad... Toys... Very... Baaaaad!", LANG_UNIVERSAL);
me->PlayDirectSound(XT_SOUND_DEATH);
Talk(SAY_DEATH);
if (m_pInstance)
{
@@ -279,7 +271,7 @@ public:
me->CastSpell(me, SPELL_HEARTBREAK, true);
me->TextEmote("XT-002 Deconstructor's heart is severed from his body.", nullptr, true);
Talk(EMOTE_HEART_CLOSED);
events.ScheduleEvent(EVENT_REMOVE_EMOTE, 4000);
return;
}
@@ -329,8 +321,7 @@ public:
me->SetControlled(true, UNIT_STATE_STUNNED);
me->SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_STAND_STATE, UNIT_STAND_STATE_SUBMERGED); // submerge with animation
me->Yell("So tired. I will rest for just a moment!", LANG_UNIVERSAL);
me->PlayDirectSound(XT_SOUND_HEART_OPEN);
Talk(SAY_HEART_OPENED);
events.CancelEventGroup(1);
events.ScheduleEvent(EVENT_START_SECOND_PHASE, 5000);
@@ -355,21 +346,19 @@ public:
events.ScheduleEvent(EVENT_GRAVITY_BOMB, 10000, 1);
break;
case EVENT_TYMPANIC_TANTARUM:
me->TextEmote("XT-002 Deconstructor begins to cause the earth to quake.", nullptr, true);
me->Yell("NO! NO! NO! NO! NO!", LANG_UNIVERSAL);
me->PlayDirectSound(XT_SOUND_TANTARUM);
Talk(EMOTE_TYMPANIC_TANTRUM);
Talk(SAY_TYMPANIC_TANTRUM);
me->CastSpell(me, SPELL_TYMPANIC_TANTARUM, true);
events.RepeatEvent(60000);
return;
case EVENT_ENRAGE:
me->Yell("I'm tired of these toys. I don't want to play anymore!", LANG_UNIVERSAL);
me->PlayDirectSound(XT_SOUND_ENRAGE);
Talk(SAY_BERSERK);
me->CastSpell(me, SPELL_XT002_ENRAGE, true);
break;
// Animation events
case EVENT_START_SECOND_PHASE:
me->TextEmote("XT-002 Deconstructor's heart is exposed and leaking energy.", nullptr, true);
Talk(EMOTE_HEART_OPENED);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
if (Unit* heart = me->GetVehicleKit() ? me->GetVehicleKit()->GetPassenger(HEART_VEHICLE_SEAT) : nullptr)
heart->GetAI()->DoAction(ACTION_AWAKEN_HEART);
@@ -383,8 +372,7 @@ public:
return;
}
me->Yell("I'm ready to play!", LANG_UNIVERSAL);
me->PlayDirectSound(XT_SOUND_HEART_CLOSED);
Talk(SAY_HEART_CLOSED);
me->SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_STAND_STATE, UNIT_STAND_STATE_STAND); // emerge
// Hide heart
@@ -619,7 +607,7 @@ public:
}
if (!urand(0, 2))
me->TextEmote("XT-002 Deconstructor consumes scrap bot to repair himself.", nullptr, true);
pXT002->AI()->Talk(EMOTE_SCRAPBOT);
me->DespawnOrUnsummon(1);
}
@@ -28,11 +28,14 @@ enum Misc
// TEXTS
SAY_AGGRO = 0,
SAY_KILL = 1,
EMOTE_RANGE = 2,
SAY_DEATH = 3,
SAY_DRAKE_DEATH = 5,
SAY_DRAKE_BREATH = 6,
// EMOTES
EMOTE_DEEP_BREATH = 0,
EMOTE_RANGE = 1,
// SPELLS
SPELL_CRUSH_N = 50234,
SPELL_CRUSH_H = 59330,
@@ -41,9 +44,9 @@ enum Misc
SPELL_WHIRLWIND_N = 50228,
SPELL_WHIRLWIND_H = 50228,
SPELL_FLAME_VISUAL = 47592,
SPELL_FLAME_BREATH_N = 47579,
SPELL_FLAME_BREATH_H = 60020,
SPELL_FREEZING_CLOUD_VISUAL = 47592,
SPELL_FREEZING_CLOUD_N = 47579,
SPELL_FREEZING_CLOUD_H = 60020,
SPELL_LAUNCH_HARPOON = 48642,
@@ -95,8 +98,6 @@ static Position SkadiPosition[] =
{490.76f, -517.389f, 123.368f, 0.0f}
};
#define EMOTE_IN_RANGE "Skadi the Ruthless is within range of the harpoon launchers"
enum phase
{
PHASE_NONE,
@@ -323,8 +324,8 @@ public:
void SpellHitTarget(Unit* target, SpellInfo const* spellInfo) override
{
if (spellInfo->Id == 47593) // SPELL_FLAME_VISUAL trigger
target->CastSpell(target, me->GetMap()->IsHeroic() ? SPELL_FLAME_BREATH_H : SPELL_FLAME_BREATH_N, true);
if (spellInfo->Id == 47593) // SPELL_FREEZING_CLOUD_VISUAL trigger
target->CastSpell(target, me->GetMap()->IsHeroic() ? SPELL_FREEZING_CLOUD_H : SPELL_FREEZING_CLOUD_N, true);
}
void SpawnFlameTriggers(uint8 point)
@@ -341,13 +342,13 @@ public:
{
Creature* cr;
if ((cr = me->SummonCreature(NPC_BREATH_TRIGGER, 483, -484.9f, 105, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000)))
cr->CastSpell(cr, cr->GetMap()->IsHeroic() ? SPELL_FLAME_BREATH_H : SPELL_FLAME_BREATH_N, true);
cr->CastSpell(cr, cr->GetMap()->IsHeroic() ? SPELL_FREEZING_CLOUD_H : SPELL_FREEZING_CLOUD_N, true);
if ((cr = me->SummonCreature(NPC_BREATH_TRIGGER, 471.0f, -484.7f, 105, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000)))
cr->CastSpell(cr, cr->GetMap()->IsHeroic() ? SPELL_FLAME_BREATH_H : SPELL_FLAME_BREATH_N, true);
cr->CastSpell(cr, cr->GetMap()->IsHeroic() ? SPELL_FREEZING_CLOUD_H : SPELL_FREEZING_CLOUD_N, true);
for (uint8 j = 0; j < 7; j++)
if ((cr = me->SummonCreature(NPC_BREATH_TRIGGER, 477.0f, -507.0f + (j * 3), 105.0f, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000)))
cr->CastSpell(cr, cr->GetMap()->IsHeroic() ? SPELL_FLAME_BREATH_H : SPELL_FLAME_BREATH_N, true);
cr->CastSpell(cr, cr->GetMap()->IsHeroic() ? SPELL_FREEZING_CLOUD_H : SPELL_FREEZING_CLOUD_N, true);
}
}
@@ -357,15 +358,14 @@ public:
{
case 0:
case 1:
me->RemoveAurasDueToSpell(SPELL_FLAME_VISUAL);
me->RemoveAurasDueToSpell(SPELL_FREEZING_CLOUD_VISUAL);
me->SetFacingTo(M_PI * 2);
break;
case 2:
case 3:
if (m_pInstance)
m_pInstance->SetData(SKADI_IN_RANGE, 1);
me->TextEmote(EMOTE_IN_RANGE, nullptr, true);
Talk(EMOTE_RANGE);
me->SetFacingTo(M_PI);
break;
}
@@ -466,8 +466,9 @@ public:
me->GetMotionMaster()->MovePoint(targetPoint, SkadiPosition[targetPoint].GetPositionX(), SkadiPosition[targetPoint].GetPositionY(), SkadiPosition[targetPoint].GetPositionZ());
if (targetPoint <= 1)
{
Talk(EMOTE_DEEP_BREATH);
SpawnFlameTriggers(targetPoint);
me->CastSpell(me, SPELL_FLAME_VISUAL, false);
me->CastSpell(me, SPELL_FREEZING_CLOUD_VISUAL, false);
}
if (m_pInstance)
@@ -1472,6 +1472,48 @@ public:
}
};
enum OnslaughtGryphon
{
SPELL_DELIVER_GRYPHON = 54420,
SPELL_ONSLAUGHT_GRYPHON = 49641,
NPC_CAPTURED_ONSLAUGHT_GRYPHON = 29415,
SEAT_PLAYER = 0
};
class spell_deliver_gryphon : public SpellScript
{
PrepareSpellScript(spell_deliver_gryphon);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_DELIVER_GRYPHON, SPELL_ONSLAUGHT_GRYPHON });
}
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
{
if (Unit* caster = GetCaster())
{
if (Vehicle* gryphon = caster->GetVehicleKit())
{
if (Unit* player = gryphon->GetPassenger(SEAT_PLAYER))
{
player->ExitVehicle();
player->RemoveAurasDueToSpell(VEHICLE_SPELL_PARACHUTE);
player->RemoveAurasDueToSpell(SPELL_ONSLAUGHT_GRYPHON);
player->SummonCreature(NPC_CAPTURED_ONSLAUGHT_GRYPHON, 7434.7f, 4213.3f, 316.52f, 3.88f, TEMPSUMMON_TIMED_DESPAWN, 1 * MINUTE * IN_MILLISECONDS);
}
}
}
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_deliver_gryphon::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
// Theirs
/*######
## npc_guardian_pavilion
@@ -2159,6 +2201,7 @@ void AddSC_icecrown()
new spell_anti_air_rocket_bomber();
new npc_infra_green_bomber_generic();
new spell_onslaught_or_call_bone_gryphon();
RegisterSpellScript(spell_deliver_gryphon);
// Theirs
new npc_guardian_pavilion();
@@ -28,13 +28,17 @@
#define QUEST_SUMMON_AHUNE 11691
#define ITEM_MAGMA_TOTEM 34953
#define AHUNE_DEFAULT_MODEL 23344
#define TEXT_RETREAT "Ahune Retreats. His defenses diminish."
#define TEXT_RESURFACE "Ahune will soon resurface."
const Position AhuneSummonPos = {-97.3473f, -233.139f, -1.27587f, M_PI / 2};
const Position TotemPos[3] = { {-115.141f, -143.317f, -2.09467f, 4.92772f}, {-120.178f, -144.398f, -2.23786f, 4.92379f}, {-125.277f, -145.463f, -1.95209f, 4.97877f} };
const Position MinionSummonPos = {-97.154404f, -204.382675f, -1.19f, M_PI / 2};
enum Text
{
EMOTE_RETREAT = 0,
EMOTE_RESURFACE = 1,
};
enum EventSpells
{
SPELL_STARTING_BEAM = 46593,
@@ -192,7 +196,7 @@ public:
events.RescheduleEvent(EVENT_SUBMERGE, 10000);
break;
case EVENT_SUBMERGE:
me->TextEmote(TEXT_RETREAT, nullptr, true);
Talk(EMOTE_RETREAT);
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->CastSpell(me, SPELL_SUBMERGE_0, true);
me->CastSpell(me, SPELL_SELF_STUN, true);
@@ -205,7 +209,7 @@ public:
events.RescheduleEvent(EVENT_EMERGE_WARNING, 20000);
break;
case EVENT_EMERGE_WARNING:
me->TextEmote(TEXT_RESURFACE, nullptr, true);
Talk(EMOTE_RESURFACE);
break;
case EVENT_COMBAT_EMERGE:
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
+52 -61
View File
@@ -22,6 +22,7 @@
#include <deque>
#include <filesystem>
#include <set>
#include <unordered_map>
#ifdef _WIN32
#include "direct.h"
@@ -62,8 +63,13 @@ typedef struct
uint32 id;
} map_id;
map_id* map_ids;
uint16* LiqType;
struct LiquidTypeEntry
{
uint8 SoundBank;
};
std::vector<map_id> map_ids;
std::unordered_map<uint32, LiquidTypeEntry> LiquidTypes;
#define MAX_PATH_LENGTH 128
char output_path[MAX_PATH_LENGTH] = ".";
char input_path[MAX_PATH_LENGTH] = ".";
@@ -271,7 +277,7 @@ uint32 ReadMapDBC()
}
size_t map_count = dbc.getRecordCount();
map_ids = new map_id[map_count];
map_ids.resize(map_count);
for (uint32 x = 0; x < map_count; ++x)
{
map_ids[x].id = dbc.getRecord(x).getUInt(0);
@@ -291,15 +297,13 @@ void ReadLiquidTypeTableDBC()
exit(1);
}
size_t liqTypeCount = dbc.getRecordCount();
size_t liqTypeMaxId = dbc.getMaxId();
LiqType = new uint16[liqTypeMaxId + 1];
memset(LiqType, 0xff, (liqTypeMaxId + 1) * sizeof(uint16));
for (uint32 x = 0; x < dbc.getRecordCount(); ++x)
{
LiquidTypeEntry& liquidType = LiquidTypes[dbc.getRecord(x).getUInt(0)];
liquidType.SoundBank = dbc.getRecord(x).getUInt(3);
}
for (uint32 x = 0; x < liqTypeCount; ++x)
LiqType[dbc.getRecord(x).getUInt(0)] = dbc.getRecord(x).getUInt(3);
printf("Done! (%u LiqTypes loaded)\n", (uint32)liqTypeCount);
printf("Done! (%lu LiquidTypes loaded)\n", LiquidTypes.size());
}
//
@@ -357,7 +361,6 @@ struct map_heightHeader
#define MAP_LIQUID_TYPE_SLIME 0x08
#define MAP_LIQUID_TYPE_DARK_WATER 0x10
#define MAP_LIQUID_TYPE_WMO_WATER 0x20
#define MAP_LIQUID_NO_TYPE 0x0001
#define MAP_LIQUID_NO_HEIGHT 0x0002
@@ -365,7 +368,8 @@ struct map_heightHeader
struct map_liquidHeader
{
uint32 fourcc;
uint16 flags;
uint8 flags;
uint8 liquidFlags;
uint16 liquidType;
uint8 offsetX;
uint8 offsetY;
@@ -720,73 +724,57 @@ bool ConvertADT(std::string const& inputPath, std::string const& outputPath, int
adt_MH2O* h2o = adt.a_grid->getMH2O();
if (h2o)
{
for (int i = 0; i < ADT_CELLS_PER_GRID; i++)
for (int32 i = 0; i < ADT_CELLS_PER_GRID; i++)
{
for (int j = 0; j < ADT_CELLS_PER_GRID; j++)
for (int32 j = 0; j < ADT_CELLS_PER_GRID; j++)
{
adt_liquid_header* h = h2o->getLiquidData(i, j);
adt_liquid_instance const* h = h2o->GetLiquidInstance(i,j);
if (!h)
continue;
int count = 0;
uint64 show = h2o->getLiquidShowMap(h);
for (int y = 0; y < h->height; y++)
adt_liquid_attributes attrs = h2o->GetLiquidAttributes(i, j);
int32 count = 0;
uint64 existsMask = h2o->GetLiquidExistsBitmap(h);
for (int32 y = 0; y < h->GetHeight(); y++)
{
int cy = i * ADT_CELL_SIZE + y + h->yOffset;
for (int x = 0; x < h->width; x++)
int32 cy = i * ADT_CELL_SIZE + y + h->GetOffsetY();
for (int32 x = 0; x < h->GetWidth(); x++)
{
int cx = j * ADT_CELL_SIZE + x + h->xOffset;
if (show & 1)
int32 cx = j * ADT_CELL_SIZE + x + h->GetOffsetX();
if (existsMask & 1)
{
liquid_show[cy][cx] = true;
++count;
}
show >>= 1;
existsMask >>= 1;
}
}
liquid_entry[i][j] = h->liquidType;
switch (LiqType[h->liquidType])
liquid_entry[i][j] = h->LiquidType;
switch (LiquidTypes.at(h->LiquidType).SoundBank)
{
case LIQUID_TYPE_WATER:
liquid_flags[i][j] |= MAP_LIQUID_TYPE_WATER;
break;
case LIQUID_TYPE_OCEAN:
liquid_flags[i][j] |= MAP_LIQUID_TYPE_OCEAN;
break;
case LIQUID_TYPE_MAGMA:
liquid_flags[i][j] |= MAP_LIQUID_TYPE_MAGMA;
break;
case LIQUID_TYPE_SLIME:
liquid_flags[i][j] |= MAP_LIQUID_TYPE_SLIME;
break;
case LIQUID_TYPE_WATER: liquid_flags[i][j] |= MAP_LIQUID_TYPE_WATER; break;
case LIQUID_TYPE_OCEAN: liquid_flags[i][j] |= MAP_LIQUID_TYPE_OCEAN; if (attrs.Deep) liquid_flags[i][j] |= MAP_LIQUID_TYPE_DARK_WATER; break;
case LIQUID_TYPE_MAGMA: liquid_flags[i][j] |= MAP_LIQUID_TYPE_MAGMA; break;
case LIQUID_TYPE_SLIME: liquid_flags[i][j] |= MAP_LIQUID_TYPE_SLIME; break;
default:
printf("\nCan't find Liquid type %u for map %s\nchunk %d,%d\n", h->liquidType, inputPath.c_str(), i, j);
printf("\nCan't find Liquid type %u for map %s\nchunk %d,%d\n", h->LiquidType, inputPath.c_str(), i, j);
break;
}
// Dark water detect
if (LiqType[h->liquidType] == LIQUID_TYPE_OCEAN)
{
uint8* lm = h2o->getLiquidLightMap(h);
if (!lm)
liquid_flags[i][j] |= MAP_LIQUID_TYPE_DARK_WATER;
}
if (!count && liquid_flags[i][j])
printf("Wrong liquid detect in MH2O chunk");
float* height = h2o->getLiquidHeightMap(h);
int pos = 0;
for (int y = 0; y <= h->height; y++)
int32 pos = 0;
for (int32 y = 0; y <= h->GetHeight(); y++)
{
int cy = i * ADT_CELL_SIZE + y + h->yOffset;
for (int x = 0; x <= h->width; x++)
int cy = i * ADT_CELL_SIZE + y + h->GetOffsetY();
for (int32 x = 0; x <= h->GetWidth(); x++)
{
int cx = j * ADT_CELL_SIZE + x + h->xOffset;
if (height)
liquid_height[cy][cx] = height[pos];
else
liquid_height[cy][cx] = h->heightLevel1;
int32 cx = j * ADT_CELL_SIZE + x + h->GetOffsetX();
liquid_height[cy][cx] = h2o->GetLiquidHeight(h, pos);
pos++;
}
}
@@ -796,13 +784,14 @@ bool ConvertADT(std::string const& inputPath, std::string const& outputPath, int
//============================================
// Pack liquid data
//============================================
uint8 type = liquid_flags[0][0];
uint16 firstLiquidType = liquid_entry[0][0];
uint8 firstLiquidFlag = liquid_flags[0][0];
bool fullType = false;
for (int y = 0; y < ADT_CELLS_PER_GRID; y++)
{
for (int x = 0; x < ADT_CELLS_PER_GRID; x++)
{
if (liquid_flags[y][x] != type)
if (liquid_entry[y][x] != firstLiquidType || liquid_flags[y][x] != firstLiquidFlag)
{
fullType = true;
y = ADT_CELLS_PER_GRID;
@@ -814,7 +803,7 @@ bool ConvertADT(std::string const& inputPath, std::string const& outputPath, int
map_liquidHeader liquidHeader;
// no water data (if all grid have 0 liquid type)
if (type == 0 && !fullType)
if (firstLiquidFlag == 0 && !fullType)
{
// No liquid data
map.liquidMapOffset = 0;
@@ -873,7 +862,10 @@ bool ConvertADT(std::string const& inputPath, std::string const& outputPath, int
liquidHeader.flags |= MAP_LIQUID_NO_TYPE;
if (liquidHeader.flags & MAP_LIQUID_NO_TYPE)
liquidHeader.liquidType = type;
{
liquidHeader.liquidFlags = firstLiquidFlag;
liquidHeader.liquidType = firstLiquidType;
}
else
map.liquidMapSize += sizeof(liquid_entry) + sizeof(liquid_flags);
@@ -1020,7 +1012,6 @@ void ExtractMapsFromMpq(uint32 build)
}
}
printf("\n");
delete[] map_ids;
}
bool ExtractFile( char const* mpq_name, std::string const& filename )
+106 -51
View File
@@ -177,21 +177,36 @@ public:
}
};
#define ADT_LIQUID_HEADER_FULL_LIGHT 0x01
#define ADT_LIQUID_HEADER_NO_HIGHT 0x02
struct adt_liquid_header
enum class LiquidVertexFormatType : uint16
{
uint16 liquidType; // Index from LiquidType.dbc
uint16 formatFlags;
float heightLevel1;
float heightLevel2;
uint8 xOffset;
uint8 yOffset;
uint8 width;
uint8 height;
uint32 offsData2a;
uint32 offsData2b;
HeightDepth = 0,
HeightTextureCoord = 1,
Depth = 2,
};
struct adt_liquid_instance
{
uint16 LiquidType; // Index from LiquidType.db2
LiquidVertexFormatType LiquidVertexFormat;
float MinHeightLevel;
float MaxHeightLevel;
uint8 OffsetX;
uint8 OffsetY;
uint8 Width;
uint8 Height;
uint32 OffsetExistsBitmap;
uint32 OffsetVertexData;
uint8 GetOffsetX() const { return OffsetX; }
uint8 GetOffsetY() const { return OffsetY; }
uint8 GetWidth() const { return Width; }
uint8 GetHeight() const { return Height; }
};
struct adt_liquid_attributes
{
uint64 Fishable;
uint64 Deep;
};
//
@@ -209,59 +224,99 @@ public:
struct adt_LIQUID
{
uint32 offsData1;
uint32 OffsetInstances;
uint32 used;
uint32 offsData2;
uint32 OffsetAttributes;
} liquid[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID];
bool prepareLoadedData();
adt_liquid_header* getLiquidData(int x, int y)
adt_liquid_instance const* GetLiquidInstance(int32 x, int32 y) const
{
if (liquid[x][y].used && liquid[x][y].offsData1)
return (adt_liquid_header*)((uint8*)this + 8 + liquid[x][y].offsData1);
if (liquid[x][y].used && liquid[x][y].OffsetInstances)
return (adt_liquid_instance *)((uint8*)this + 8 + liquid[x][y].OffsetInstances);
return nullptr;
}
float* getLiquidHeightMap(adt_liquid_header* h)
adt_liquid_attributes GetLiquidAttributes(int32 x, int32 y) const
{
if (h->formatFlags & ADT_LIQUID_HEADER_NO_HIGHT)
return nullptr;
if (h->offsData2b)
return (float*)((uint8*)this + 8 + h->offsData2b);
return nullptr;
}
uint8* getLiquidLightMap(adt_liquid_header* h)
{
if (h->formatFlags & ADT_LIQUID_HEADER_FULL_LIGHT)
return nullptr;
if (h->offsData2b)
if (liquid[x][y].used)
{
if (h->formatFlags & ADT_LIQUID_HEADER_NO_HIGHT)
return (uint8*)((uint8*)this + 8 + h->offsData2b);
return (uint8*)((uint8*)this + 8 + h->offsData2b + (h->width + 1) * (h->height + 1) * 4);
if (liquid[x][y].OffsetAttributes)
return *((adt_liquid_attributes *)((uint8*)this + 8 + liquid[x][y].OffsetAttributes));
return { 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF };
}
return { 0, 0 };
}
uint16 GetLiquidType(adt_liquid_instance const* h) const
{
if (h->LiquidVertexFormat == LiquidVertexFormatType::Depth)
return 2;
return h->LiquidType;
}
float GetLiquidHeight(adt_liquid_instance const* h, int32 pos) const
{
if (!h->OffsetVertexData)
return 0.0f;
switch (h->LiquidVertexFormat)
{
case LiquidVertexFormatType::HeightDepth:
case LiquidVertexFormatType::HeightTextureCoord:
return ((float const*)((uint8*)this + 8 + h->OffsetVertexData))[pos];
case LiquidVertexFormatType::Depth:
return 0.0f;
default:
break;
}
return 0.0f;
}
int8 GetLiquidDepth(adt_liquid_instance const* h, int32 pos) const
{
if (!h->OffsetVertexData)
return -1;
switch (h->LiquidVertexFormat)
{
case LiquidVertexFormatType::HeightDepth:
return ((int8 const*)((int8 const*)this + 8 + h->OffsetVertexData + (h->GetWidth() + 1) * (h->GetHeight() + 1) * 4))[pos];
case LiquidVertexFormatType::HeightTextureCoord:
return 0;
case LiquidVertexFormatType::Depth:
return ((int8 const*)((uint8*)this + 8 + h->OffsetVertexData))[pos];
default:
break;
}
return 0;
}
uint16 const* GetLiquidTextureCoordMap(adt_liquid_instance const* h, int32 pos) const
{
if (!h->OffsetVertexData)
return nullptr;
switch (h->LiquidVertexFormat)
{
case LiquidVertexFormatType::HeightDepth:
case LiquidVertexFormatType::Depth:
return nullptr;
case LiquidVertexFormatType::HeightTextureCoord:
return (uint16 const*)((uint8 const*)this + 8 + h->OffsetVertexData + 4 * ((h->GetWidth() + 1) * (h->GetHeight() + 1) + pos));
default:
break;
}
return nullptr;
}
uint32* getLiquidFullLightMap(adt_liquid_header* h)
uint64 GetLiquidExistsBitmap(adt_liquid_instance const* h) const
{
if (!(h->formatFlags & ADT_LIQUID_HEADER_FULL_LIGHT))
return nullptr;
if (h->offsData2b)
{
if (h->formatFlags & ADT_LIQUID_HEADER_NO_HIGHT)
return (uint32*)((uint8*)this + 8 + h->offsData2b);
return (uint32*)((uint8*)this + 8 + h->offsData2b + (h->width + 1) * (h->height + 1) * 4);
}
return nullptr;
}
uint64 getLiquidShowMap(adt_liquid_header* h)
{
if (h->offsData2a)
return *((uint64*)((uint8*)this + 8 + h->offsData2a));
if (h->OffsetExistsBitmap)
return *((uint64 *)((uint8*)this + 8 + h->OffsetExistsBitmap));
else
return 0xFFFFFFFFFFFFFFFFuLL;
}
+9 -4
View File
@@ -634,10 +634,15 @@ namespace MMAP
Tile& tile = tiles[x + y * TILES_PER_MAP];
// Calculate the per tile bounding box.
tileCfg.bmin[0] = config.bmin[0] + float(x * config.tileSize - config.borderSize) * config.cs;
tileCfg.bmin[2] = config.bmin[2] + float(y * config.tileSize - config.borderSize) * config.cs;
tileCfg.bmax[0] = config.bmin[0] + float((x + 1) * config.tileSize + config.borderSize) * config.cs;
tileCfg.bmax[2] = config.bmin[2] + float((y + 1) * config.tileSize + config.borderSize) * config.cs;
tileCfg.bmin[0] = config.bmin[0] + x * float(config.tileSize * config.cs);
tileCfg.bmin[2] = config.bmin[2] + y * float(config.tileSize * config.cs);
tileCfg.bmax[0] = config.bmin[0] + (x + 1) * float(config.tileSize * config.cs);
tileCfg.bmax[2] = config.bmin[2] + (y + 1) * float(config.tileSize * config.cs);
tileCfg.bmin[0] -= tileCfg.borderSize * tileCfg.cs;
tileCfg.bmin[2] -= tileCfg.borderSize * tileCfg.cs;
tileCfg.bmax[0] += tileCfg.borderSize * tileCfg.cs;
tileCfg.bmax[2] += tileCfg.borderSize * tileCfg.cs;
// build heightfield
tile.solid = rcAllocHeightfield();
+2 -1
View File
@@ -20,6 +20,7 @@
#include "Timer.h"
#include "DBCFileLoader.h"
#include "PathCommon.h"
#include "Util.h"
#include <boost/filesystem.hpp>
#include <unordered_map>
@@ -341,6 +342,6 @@ int main(int argc, char** argv)
builder.buildMaps({});
if (!silent)
printf("Finished. MMAPS were built in %u ms!\n", GetMSTimeDiffToNow(start));
printf("Finished. MMAPS were built in %s\n", secsToTimeString(GetMSTimeDiffToNow(start) / 1000).c_str());
return 0;
}
+69 -55
View File
@@ -60,7 +60,8 @@ struct map_heightHeader
struct map_liquidHeader
{
uint32 fourcc;
uint16 flags;
uint8 flags;
uint8 liquidFlags;
uint16 liquidType;
uint8 offsetX;
uint8 offsetY;
@@ -75,7 +76,6 @@ struct map_liquidHeader
#define MAP_LIQUID_TYPE_MAGMA 0x04
#define MAP_LIQUID_TYPE_SLIME 0x08
#define MAP_LIQUID_TYPE_DARK_WATER 0x10
#define MAP_LIQUID_TYPE_WMO_WATER 0x20
uint32 GetLiquidFlags(uint32 liquidId);
@@ -172,8 +172,10 @@ namespace MMAP
// data used later
uint16 holes[16][16];
memset(holes, 0, sizeof(holes));
uint8 liquid_type[16][16];
memset(liquid_type, 0, sizeof(liquid_type));
uint16 liquid_entry[16][16];
memset(liquid_entry, 0, sizeof(liquid_entry));
uint8 liquid_flags[16][16];
memset(liquid_flags, 0, sizeof(liquid_flags));
G3D::Array<int> ltriangles;
G3D::Array<int> ttriangles;
@@ -284,75 +286,87 @@ namespace MMAP
float* liquid_map = nullptr;
if (!(lheader.flags & MAP_LIQUID_NO_TYPE))
if (fread(liquid_type, sizeof(liquid_type), 1, mapFile) != 1)
{
if (fread(liquid_entry, sizeof(liquid_entry), 1, mapFile) != 1)
printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n");
if (fread(liquid_flags, sizeof(liquid_flags), 1, mapFile) != 1)
printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n");
}
else
{
std::fill_n(&liquid_entry[0][0], 16 * 16, lheader.liquidType);
std::fill_n(&liquid_flags[0][0], 16 * 16, lheader.liquidFlags);
}
if (!(lheader.flags & MAP_LIQUID_NO_HEIGHT))
{
uint32 toRead = lheader.width * lheader.height;
liquid_map = new float [toRead];
if (fread(liquid_map, sizeof(float), toRead, mapFile) != toRead)
{
printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n");
delete[] liquid_map;
liquid_map = nullptr;
}
}
if (liquid_map)
int count = meshData.liquidVerts.size() / 3;
float xoffset = (float(tileX)-32)*GRID_SIZE;
float yoffset = (float(tileY)-32)*GRID_SIZE;
float coord[3];
int row, col;
// generate coordinates
if (!(lheader.flags & MAP_LIQUID_NO_HEIGHT))
{
int count = meshData.liquidVerts.size() / 3;
float xoffset = (float(tileX) - 32) * GRID_SIZE;
float yoffset = (float(tileY) - 32) * GRID_SIZE;
float coord[3];
int row, col;
// generate coordinates
if (!(lheader.flags & MAP_LIQUID_NO_HEIGHT))
int j = 0;
for (int i = 0; i < V9_SIZE_SQ; ++i)
{
int j = 0;
for (int i = 0; i < V9_SIZE_SQ; ++i)
row = i / V9_SIZE;
col = i % V9_SIZE;
if (row < lheader.offsetY || row >= lheader.offsetY + lheader.height ||
col < lheader.offsetX || col >= lheader.offsetX + lheader.width)
{
row = i / V9_SIZE;
col = i % V9_SIZE;
if (row < lheader.offsetY || row >= lheader.offsetY + lheader.height ||
col < lheader.offsetX || col >= lheader.offsetX + lheader.width)
{
// dummy vert using invalid height
meshData.liquidVerts.append((xoffset + col * GRID_PART_SIZE) * -1, INVALID_MAP_LIQ_HEIGHT, (yoffset + row * GRID_PART_SIZE) * -1);
continue;
}
getLiquidCoord(i, j, xoffset, yoffset, coord, liquid_map);
meshData.liquidVerts.append(coord[0]);
meshData.liquidVerts.append(coord[2]);
meshData.liquidVerts.append(coord[1]);
j++;
// dummy vert using invalid height
meshData.liquidVerts.append((xoffset+col*GRID_PART_SIZE)*-1, INVALID_MAP_LIQ_HEIGHT, (yoffset+row*GRID_PART_SIZE)*-1);
continue;
}
getLiquidCoord(i, j, xoffset, yoffset, coord, liquid_map);
meshData.liquidVerts.append(coord[0]);
meshData.liquidVerts.append(coord[2]);
meshData.liquidVerts.append(coord[1]);
j++;
}
else
}
else
{
for (int i = 0; i < V9_SIZE_SQ; ++i)
{
for (int i = 0; i < V9_SIZE_SQ; ++i)
{
row = i / V9_SIZE;
col = i % V9_SIZE;
meshData.liquidVerts.append((xoffset + col * GRID_PART_SIZE) * -1, lheader.liquidLevel, (yoffset + row * GRID_PART_SIZE) * -1);
}
row = i / V9_SIZE;
col = i % V9_SIZE;
meshData.liquidVerts.append((xoffset+col*GRID_PART_SIZE)*-1, lheader.liquidLevel, (yoffset+row*GRID_PART_SIZE)*-1);
}
}
delete [] liquid_map;
delete[] liquid_map;
int indices[] = { 0, 0, 0 };
int loopStart = 0, loopEnd = 0, loopInc = 0, triInc = BOTTOM - TOP;
getLoopVars(portion, loopStart, loopEnd, loopInc);
int indices[] = { 0, 0, 0 };
int loopStart = 0, loopEnd = 0, loopInc = 0, triInc = BOTTOM-TOP;
getLoopVars(portion, loopStart, loopEnd, loopInc);
// generate triangles
for (int i = loopStart; i < loopEnd; i += loopInc)
for (int j = TOP; j <= BOTTOM; j += triInc)
{
getHeightTriangle(i, Spot(j), indices, true);
ltriangles.append(indices[2] + count);
ltriangles.append(indices[1] + count);
ltriangles.append(indices[0] + count);
}
// generate triangles
for (int i = loopStart; i < loopEnd; i += loopInc)
{
for (int j = TOP; j <= BOTTOM; j += triInc)
{
getHeightTriangle(i, Spot(j), indices, true);
ltriangles.append(indices[2] + count);
ltriangles.append(indices[1] + count);
ltriangles.append(indices[0] + count);
}
}
}
@@ -399,7 +413,7 @@ namespace MMAP
}
else
{
liquidType = getLiquidType(i, liquid_type);
liquidType = getLiquidType(i, liquid_flags);
if (liquidType & MAP_LIQUID_TYPE_DARK_WATER)
{
// players should not be here, so logically neither should creatures
@@ -708,7 +722,7 @@ namespace MMAP
copyIndices(tempTriangles, meshData.solidTris, offset, isM2);
// now handle liquid data
if (liquid)
if (liquid && liquid->GetFlagsStorage())
{
std::vector<G3D::Vector3> liqVerts;
std::vector<int> liqTris;
+1 -85
View File
@@ -22,91 +22,7 @@
#include "mpq_libmpq04.h"
#include "wmo.h"
#define TILESIZE (533.33333f)
#define CHUNKSIZE ((TILESIZE) / 16.0f)
#define UNITSIZE (CHUNKSIZE / 8.0f)
class Liquid;
typedef struct
{
float x;
float y;
float z;
} svec;
struct vec
{
double x;
double y;
double z;
};
struct triangle
{
vec v[3];
};
typedef struct
{
float v9[16 * 8 + 1][16 * 8 + 1];
float v8[16 * 8][16 * 8];
} Cell;
typedef struct
{
double v9[9][9];
double v8[8][8];
uint16 area_id;
//Liquid *lq;
float waterlevel[9][9];
uint8 flag;
} chunk;
typedef struct
{
chunk ch[16][16];
} mcell;
struct MapChunkHeader
{
uint32 flags;
uint32 ix;
uint32 iy;
uint32 nLayers;
uint32 nDoodadRefs;
uint32 ofsHeight;
uint32 ofsNormal;
uint32 ofsLayer;
uint32 ofsRefs;
uint32 ofsAlpha;
uint32 sizeAlpha;
uint32 ofsShadow;
uint32 sizeShadow;
uint32 areaid;
uint32 nMapObjRefs;
uint32 holes;
uint16 s1;
uint16 s2;
uint32 d1;
uint32 d2;
uint32 d3;
uint32 predTex;
uint32 nEffectDoodad;
uint32 ofsSndEmitters;
uint32 nSndEmitters;
uint32 ofsLiquid;
uint32 sizeLiquid;
float zpos;
float xpos;
float ypos;
uint32 textureId;
uint32 props;
uint32 effectId;
};
#pragma pack(push, 1)
namespace ADT
{
struct MDDF
@@ -127,7 +43,7 @@ namespace ADT
Vec3D Rotation;
AaBox3D Bounds;
uint16 Flags;
uint16 DoodadSet; // can be larger than number of doodad sets in WMO
uint16 DoodadSet;
uint16 NameSet;
uint16 Scale;
};
+1 -1
View File
@@ -163,7 +163,7 @@ bool ExtractSingleWmo(std::string& fname)
string s = groupFileName;
WMOGroup fgroup(s);
if (!fgroup.open())
if (!fgroup.open(&froot))
{
printf("Could not open all Group file for: %s\n", plain_name);
file_ok = false;
+2 -2
View File
@@ -24,8 +24,8 @@
namespace VMAP
{
const char VMAP_MAGIC[] = "VMAP_4.6";
const char RAW_VMAP_MAGIC[] = "VMAP046"; // used in extracted vmap files with raw data
const char VMAP_MAGIC[] = "VMAP_4.7";
const char RAW_VMAP_MAGIC[] = "VMAP047"; // used in extracted vmap files with raw data
}
enum ModelFlags
+65 -64
View File
@@ -30,7 +30,7 @@
WMORoot::WMORoot(std::string const& filename)
: filename(filename), color(0), nTextures(0), nGroups(0), nPortals(0), nLights(0),
nDoodadNames(0), nDoodadDefs(0), nDoodadSets(0), RootWMOID(0), liquidType(0)
nDoodadNames(0), nDoodadDefs(0), nDoodadSets(0), RootWMOID(0), flags(0)
{
memset(bbcorn1, 0, sizeof(bbcorn1));
memset(bbcorn2, 0, sizeof(bbcorn2));
@@ -71,7 +71,7 @@ bool WMORoot::open()
f.read(&RootWMOID, 4);
f.read(bbcorn1, 12);
f.read(bbcorn2, 12);
f.read(&liquidType, 4);
f.read(&flags, 4);
}
else if (!strcmp(fourcc, "MODS"))
{
@@ -158,14 +158,14 @@ WMOGroup::WMOGroup(std::string const& filename) :
filename(std::move(filename)), MOPY(nullptr), MOVI(nullptr), MoviEx(nullptr), MOVT(nullptr), MOBA(nullptr), MobaEx(nullptr),
hlq(nullptr), LiquEx(nullptr), LiquBytes(nullptr), groupName(0), descGroupName(0), mogpFlags(0),
moprIdx(0), moprNItems(0), nBatchA(0), nBatchB(0), nBatchC(0), fogIdx(0),
liquidType(0), groupWMOID(0), mopy_size(0), moba_size(0), LiquEx_size(0),
groupLiquid(0), groupWMOID(0), mopy_size(0), moba_size(0), LiquEx_size(0),
nVertices(0), nTriangles(0), liquflags(0)
{
memset(bbcorn1, 0, sizeof(bbcorn1));
memset(bbcorn2, 0, sizeof(bbcorn2));
}
bool WMOGroup::open()
bool WMOGroup::open(WMORoot* rootWMO)
{
MPQFile f(filename.c_str());
if (f.isEof ())
@@ -202,8 +202,19 @@ bool WMOGroup::open()
f.read(&nBatchB, 2);
f.read(&nBatchC, 4);
f.read(&fogIdx, 4);
f.read(&liquidType, 4);
f.read(&groupLiquid, 4);
f.read(&groupWMOID, 4);
// according to WoW.Dev Wiki:
if (rootWMO->flags & 4)
groupLiquid = GetLiquidTypeId(groupLiquid);
else if (groupLiquid == 15)
groupLiquid = 0;
else
groupLiquid = GetLiquidTypeId(groupLiquid + 1);
if (groupLiquid)
liquflags |= 2;
}
else if (!strcmp(fourcc, "MOPY"))
{
@@ -252,6 +263,19 @@ bool WMOGroup::open()
LiquBytes = new char[nLiquBytes];
f.read(LiquBytes, nLiquBytes);
// Determine legacy liquid type
if (!groupLiquid)
{
for (int i = 0; i < hlq->xtiles * hlq->ytiles; ++i)
{
if ((LiquBytes[i] & 0xF) != 15)
{
groupLiquid = GetLiquidTypeId((LiquBytes[i] & 0xF) + 1);
break;
}
}
}
/* std::ofstream llog("Buildings/liquid.log", ios_base::out | ios_base::app);
llog << filename;
llog << "\nbbox: " << bbcorn1[0] << ", " << bbcorn1[1] << ", " << bbcorn1[2] << " | " << bbcorn2[0] << ", " << bbcorn2[1] << ", " << bbcorn2[2];
@@ -423,78 +447,55 @@ int WMOGroup::ConvertToVMAPGroupWmo(FILE* output, WMORoot* rootWMO, bool precise
}
//------LIQU------------------------
if (LiquEx_size != 0)
if (liquflags & 3)
{
int LIQU_h[] = { 0x5551494C, static_cast<int>(sizeof(WMOLiquidHeader) + LiquEx_size) + hlq->xtiles * hlq->ytiles }; // "LIQU"
int LIQU_totalSize = sizeof(uint32);
if (liquflags & 1)
{
LIQU_totalSize += sizeof(WMOLiquidHeader);
LIQU_totalSize += LiquEx_size / sizeof(WMOLiquidVert) * sizeof(float);
LIQU_totalSize += hlq->xtiles * hlq->ytiles;
}
int LIQU_h[] = { 0x5551494C, LIQU_totalSize };// "LIQU"
fwrite(LIQU_h, 4, 2, output);
// according to WoW.Dev Wiki:
uint32 liquidEntry;
if (rootWMO->liquidType & 4)
liquidEntry = liquidType;
else if (liquidType == 15)
liquidEntry = 0;
else
liquidEntry = liquidType + 1;
if (!liquidEntry)
{
int v1; // edx@1
int v2; // eax@1
v1 = hlq->xtiles * hlq->ytiles;
v2 = 0;
if (v1 > 0)
{
while ((LiquBytes[v2] & 0xF) == 15)
{
++v2;
if (v2 >= v1)
break;
}
if (v2 < v1 && (LiquBytes[v2] & 0xF) != 15)
liquidEntry = (LiquBytes[v2] & 0xF) + 1;
}
}
if (liquidEntry && liquidEntry < 21)
{
switch ((liquidEntry - 1) & 3)
{
case 0:
liquidEntry = ((mogpFlags & 0x80000) != 0) + 13;
break;
case 1:
liquidEntry = 14;
break;
case 2:
liquidEntry = 19;
break;
case 3:
liquidEntry = 20;
break;
}
}
hlq->type = liquidEntry;
/* std::ofstream llog("Buildings/liquid.log", ios_base::out | ios_base::app);
llog << filename;
llog << ":\nliquidEntry: " << liquidEntry << " type: " << hlq->type << " (root:" << rootWMO->liquidType << " group:" << liquidType << ")\n";
llog.close(); */
fwrite(hlq, sizeof(WMOLiquidHeader), 1, output);
// only need height values, the other values are unknown anyway
for (uint32 i = 0; i < LiquEx_size / sizeof(WMOLiquidVert); ++i)
fwrite(&LiquEx[i].height, sizeof(float), 1, output);
// todo: compress to bit field
fwrite(LiquBytes, 1, hlq->xtiles * hlq->ytiles, output);
fwrite(&groupLiquid, sizeof(uint32), 1, output);
if (liquflags & 1)
{
fwrite(hlq, sizeof(WMOLiquidHeader), 1, output);
// only need height values, the other values are unknown anyway
for (uint32 i = 0; i < LiquEx_size / sizeof(WMOLiquidVert); ++i)
fwrite(&LiquEx[i].height, sizeof(float), 1, output);
// todo: compress to bit field
fwrite(LiquBytes, 1, hlq->xtiles * hlq->ytiles, output);
}
}
return nColTriangles;
}
uint32 WMOGroup::GetLiquidTypeId(uint32 liquidTypeId)
{
if (liquidTypeId < 21 && liquidTypeId)
{
switch (((static_cast<uint8>(liquidTypeId) - 1) & 3))
{
case 0: return ((mogpFlags & 0x80000) != 0) + 13;
case 1: return 14;
case 2: return 19;
case 3: return 20;
default: break;
}
}
return liquidTypeId;
}
WMOGroup::~WMOGroup()
{
delete [] MOPY;
+5 -7
View File
@@ -26,9 +26,6 @@
#include "vec3d.h"
#include "loadlib/loadlib.h"
#define TILESIZE (533.33333f)
#define CHUNKSIZE ((TILESIZE) / 16.0f)
// MOPY flags
enum MopyFlags
{
@@ -84,7 +81,7 @@ private:
std::string filename;
public:
unsigned int color;
uint32 nTextures, nGroups, nPortals, nLights, nDoodadNames, nDoodadDefs, nDoodadSets, RootWMOID, liquidType;
uint32 nTextures, nGroups, nPortals, nLights, nDoodadNames, nDoodadDefs, nDoodadSets, RootWMOID, flags;
float bbcorn1[3];
float bbcorn2[3];
@@ -105,7 +102,7 @@ struct WMOLiquidHeader
float pos_x;
float pos_y;
float pos_z;
short type;
short material;
};
struct WMOLiquidVert
@@ -141,7 +138,7 @@ public:
uint16 moprNItems;
uint16 nBatchA;
uint16 nBatchB;
uint32 nBatchC, fogIdx, liquidType, groupWMOID;
uint32 nBatchC, fogIdx, groupLiquid, groupWMOID;
int mopy_size, moba_size;
int LiquEx_size;
@@ -154,8 +151,9 @@ public:
WMOGroup(std::string const& filename);
~WMOGroup();
bool open();
bool open(WMORoot* rootWMO);
int ConvertToVMAPGroupWmo(FILE* output, WMORoot* rootWMO, bool preciseVectorData);
uint32 GetLiquidTypeId(uint32 liquidTypeId);
};
namespace MapObject