eluna-scripts-ornfelt/lua/WowEmulationScriptPack/C++/RandomizedItems/RI_CreatureScaleScripts.cpp
2024-09-23 07:59:23 +02:00

888 lines
41 KiB
C++

#include "Map.h"
#include "Player.h"
#include "Creature.h"
#include "GameTime.h"
#include "ScriptMgr.h"
#include "TemporarySummon.h"
#include "PassiveAI.h"
#include "GridNotifiers.h"
#include "SpellMgr.h"
#include "GameEventCallbacks.h"
#include "ObjectExtension.cpp"
#include "RI_CreatureScaleScripts.h"
#include "SpellAuras.h"
#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif
#ifndef MAX
#define MAX(a,b) ((a)<(b)?(b):(a))
#endif
#define RESISTANCE_FOR_MAX_MITIGATION 700
#define RESISTANCE_MAX_DMG_MITIGATION 0.8f
enum DifficultyScriptTypes
{
RI_DS_NOT_USED_DUMMY_VALUE,
RI_DS_MIN_DIFF_ANYSCRIPT,
RI_DS_FROST_NOVA_ON_STRUCK,
RI_DS_LIGHTNING_ON_STRUCK,
RI_DS_FIRE_RAIN_ON_STRUCK,
RI_DS_MOSTER_MOVE_SPEED,
RI_DS_EXPLODE,
RI_DS_DECREASE_PLAYER_HEALTH_PERIODICALLY,
RI_DS_TOXIC_POOL,
RI_DS_LINKED_HEALTH,
RI_DS_RANDOM_IMMUNITY,
RI_DS_REANIMATE,
RI_DS_ENGULFED_IN_FLAME, // shroud of sorrow
RI_DS_FALL_ASLEEP, // standing in 1 place makes you fall asleep
RI_DS_CRIT_CHANCE,
RI_DS_RANDOM_MECHANIC_IMMUNITY,
RI_DS_LOOSE_GOLD_ON_DEATH,
RI_DS_REFLECT_SPELLS,
RI_DS_ELEMENTAL_DMG,
RI_DS_LAVA_POOL,
RI_DS_REDUCED_DAMAGE_TAKEN,
RI_DS_PERIODIC_DISPEL,
RI_DS_REFLECT_DMG,
RI_EMPOWER_ON_HIT,
RI_DS_FRENZIED_REGENERATION,
RI_DS_FIRE_CHAINS,
RI_DS_MAX_SCRIPTS,
};
int RI_DS_REQUIRED_DIFF[RI_DS_MAX_SCRIPTS];
#define PLAYER_ITEM_COUNT 11
#define MONSTER_DIIFCULTY_MULTIPLIER (PLAYER_ITEM_COUNT / 2) // expect half of the items get upgraded to half potential. Mobs scale in both dmg and HP !
#define MONSTER_DIIFCULTY_MULTIPLIER_HP (MONSTER_DIIFCULTY_MULTIPLIER/2)
bool CheckShouldTriggerCreatureScript(Map *m, DifficultyScriptTypes Type, Creature *c = NULL, Player *p = NULL)
{
if (m == NULL)
return false;
int64 *InstanceScalePlayer = m->GetExtension<int64>(OE_MAP_MOB_DIFFICULTY_SCALER);
if (InstanceScalePlayer == NULL)
return false;
if (c != NULL)
{
if (p != NULL)
{
if (p->IsFriendlyTo(c) == true)
return false;
}
//toxic pool should not trigger any other scripts
if(c->GetEntry()==15925)
return false;
if (c->IsCritter())
return false;
}
if (*InstanceScalePlayer >= RI_DS_REQUIRED_DIFF[Type])
return true;
return false;
}
void RI_DS_MapUpdate(void *p, void *)
{
CP_MAP_PERIODIC_UPDATE *params = PointerCast(CP_MAP_PERIODIC_UPDATE, p);
if (params->map == NULL)
return;
//only trigger for maps that are scaled
if (CheckShouldTriggerCreatureScript(params->map, RI_DS_DECREASE_PLAYER_HEALTH_PERIODICALLY) == true)
{
//check if enough time has passed to decrease player health
int64 *NextHealthDecreaseStamp = params->map->GetCreateIn64Extension(OE_MAP_RI_DS_HEALTH_DECREASE_STORE);
if (*NextHealthDecreaseStamp < GameTime::GetGameTimeMS())
{
*NextHealthDecreaseStamp = GameTime::GetGameTimeMS() + 10000;
Map::PlayerList const& PlayerList = params->map->GetPlayers();
for (Map::PlayerList::const_iterator itr = PlayerList.begin(); itr != PlayerList.end(); ++itr)
itr->GetSource()->EnvironmentalDamage(DAMAGE_DROWNING, 5 * itr->GetSource()->GetHealth() / 100);
}
}
if (CheckShouldTriggerCreatureScript(params->map, RI_DS_FRENZIED_REGENERATION) == true)
{
int64 *NextHealthDecreaseStamp = params->map->GetCreateIn64Extension(OE_MAP_RI_DS_HEALTH_INCREASE_STORE);
if (*NextHealthDecreaseStamp < GameTime::GetGameTimeMS())
{
*NextHealthDecreaseStamp = GameTime::GetGameTimeMS() + 2000;
//every 10 minutes since the instance have been spawned, mobs will regenerate 1% health
uint64 SecondsSinceCreate = GameTime::GetGameTime() - params->map->GetCreateIn64Extension(OE_MAP_CREATE_TIME, false, GameTime::GetGameTime())[0];
float RestorePCT = SecondsSinceCreate / 60.0f / 10.0f / 100.0f;
Map::CreatureBySpawnIdContainer const& CreatureList = params->map->GetCreatureBySpawnIdStore();
for (auto itr = CreatureList.begin(); itr != CreatureList.end(); ++itr)
{
if (itr->second == NULL || itr->second->IsAlive() == false)
continue;
int32 HealthMissing = itr->second->GetMaxHealth() - itr->second->GetHealth();
if (HealthMissing <= 0)
continue;
int32 RestoreAmt = itr->second->GetMaxHealth() * RestorePCT;
itr->second->ModifyHealth(RestoreAmt);
}
}
}
}
uint32 GetMinResistance(Unit *u, SpellSchoolMask mask)
{
if (mask == 0)
return 0;
uint32 resist = 0x00FFFFFF;
for (int32 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
if ((mask & (1 << i)) && resist > u->GetResistance(SpellSchools(i)))
{
if( i == 0 )
resist = u->GetResistance(SpellSchools(i)) / 100;
else
resist = u->GetResistance(SpellSchools(i));
}
// resist value will never be negative here
return uint32(resist);
}
void RI_OnHitTarget(void *p, void *Context)
{
CP_DMG_DONE_RECEIVED *params = PointerCast(CP_DMG_DONE_RECEIVED, p);
if (params->OriDamage <= 0 || params->Attacker == NULL || params->Victim == NULL)
return;
Player *PlayerAttacker = params->Attacker->ToPlayer();
if (PlayerAttacker == NULL)
PlayerAttacker = params->Attacker->GetCharmerOrOwnerPlayerOrPlayerItself();
Player *PlayerTarget = params->Victim->ToPlayer();
if (PlayerTarget == NULL)
PlayerTarget = params->Victim->GetCharmerOrOwnerPlayerOrPlayerItself();
//if player attacks the creature
if (PlayerAttacker != NULL && PlayerTarget == NULL)
{
if (CheckShouldTriggerCreatureScript(params->Attacker->GetMap(), RI_DS_MIN_DIFF_ANYSCRIPT, params->Victim->ToCreature(), PlayerAttacker) == false)
return;
//check if we need to trigger any of the scripts
if (CheckShouldTriggerCreatureScript(params->Attacker->GetMap(), RI_DS_FROST_NOVA_ON_STRUCK) == true)
{
int64 *NextTrigger = params->Attacker->GetCreateIn64Extension(OE_CREATURE_FROST_NOVA_COOLDOWN);
if (*NextTrigger < GameTime::GetGameTimeMS())
{
*NextTrigger = GameTime::GetGameTimeMS() + 2000;
int SpellId;
if (params->Victim->getLevel() < 85 / 4 * 1)
SpellId = 122;
else if (params->Victim->getLevel() < 85 / 4 * 2)
SpellId = 865;
else if (params->Victim->getLevel() < 85 / 4 * 3)
SpellId = 6131;
else
SpellId = 10230;
params->Victim->CastSpell(params->Attacker, SpellId, true);
}
}
//check if we need to trigger any of the scripts
if (CheckShouldTriggerCreatureScript(params->Attacker->GetMap(), RI_DS_LIGHTNING_ON_STRUCK) == true)
{
int64 *NextTrigger = params->Attacker->GetCreateIn64Extension(OE_CREATURE_CHAIN_LIGHTNING_COOLDOWN);
if (*NextTrigger < GameTime::GetGameTimeMS())
{
*NextTrigger = GameTime::GetGameTimeMS() + 2500;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(421);
Spell* spell = new Spell(params->Victim, spellInfo, TRIGGERED_FULL_MASK);
spell->m_targets.SetUnitTarget(params->Attacker);
spell->SetSpellValue(SPELLVALUE_BASE_POINT0, int32(params->Attacker->GetMaxHealth() * (0.5f / 100.0f))); // 0.5%
spell->cast(true);
}
}
//check if we need to trigger any of the scripts
if (CheckShouldTriggerCreatureScript(params->Attacker->GetMap(), RI_DS_FIRE_RAIN_ON_STRUCK) == true)
{
int64 *NextTrigger = params->Attacker->GetCreateIn64Extension(OE_CREATURE_FIRE_RAIN_COOLDOWN);
if (*NextTrigger < GameTime::GetGameTimeMS())
{
*NextTrigger = GameTime::GetGameTimeMS() + 3000;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(5740); //5740,26573
Spell* spell = new Spell(params->Victim, spellInfo, TRIGGERED_FULL_MASK);
spell->m_targets.SetUnitTarget(params->Attacker);
spell->m_targets.SetSrc(params->Victim->GetPosition());
spell->m_targets.SetDst(params->Attacker->GetPosition());
spell->SetSpellValue(SPELLVALUE_BASE_POINT0, int32(params->Victim->GetMaxHealth() * (1.f / 100.0f))); // 0.5%
spell->cast(true);
}
}
//check if we need to trigger any of the scripts
if (CheckShouldTriggerCreatureScript(params->Attacker->GetMap(), RI_DS_LINKED_HEALTH) == true)
{
auto TargetList = params->Attacker->GetThreatManager().GetThreatenedByMeList();
size_t TargetCount = TargetList.size();
if (TargetCount > 1)
{
int DmgPart = params->GetDamage() / (int)TargetCount; // each victim will receive same amount of dmg
int32 SharedAmount = 0;
//other targets will receive dmg as well
for (auto itr = TargetList.begin(); itr != TargetList.end(); itr++)
{
if (itr->first == params->Victim->GetGUID())
continue;
Unit *u = params->Attacker->GetMap()->GetCreature(itr->first);
if (u == NULL)
continue;
if ((int)u->GetHealth() > DmgPart)
{
u->ModifyHealth(-DmgPart);
SharedAmount += DmgPart;
}
}
params->FlatMods -= SharedAmount; // original target will only receive 1 part of dmg
}
}
if (CheckShouldTriggerCreatureScript(params->Attacker->GetMap(), RI_DS_REFLECT_SPELLS) == true && rand32() % 100 < 5)
{
//pick a random reflection AURA
const static uint32 ReflectSpells[] = { 51758,51763,51764,51766,22857 };
uint32 PickedSpellId = ReflectSpells[rand32() % _countof(ReflectSpells)];
params->Victim->CastSpell(params->Victim, PickedSpellId, TRIGGERED_FULL_MASK);
}
}
}
void RI_OnDamageTaken(void *p, void *Context)
{
CP_DMG_DONE_RECEIVED *params = PointerCast(CP_DMG_DONE_RECEIVED, p);
if (params->OriDamage <= 0 || params->Attacker == NULL || params->Victim == NULL)
return;
Player *PlayerAttacker = params->Attacker->ToPlayer();
if (PlayerAttacker == NULL)
PlayerAttacker = params->Attacker->GetCharmerOrOwnerPlayerOrPlayerItself();
Player *PlayerTarget = params->Victim->ToPlayer();
if (PlayerTarget == NULL)
PlayerTarget = params->Victim->GetCharmerOrOwnerPlayerOrPlayerItself();
//if player attacks the creature
if (PlayerAttacker != NULL && PlayerTarget == NULL)
{
if (CheckShouldTriggerCreatureScript(params->Attacker->GetMap(), RI_DS_REDUCED_DAMAGE_TAKEN) == true)
{
int64 *InstanceScale = params->Attacker->GetMap()->GetExtension<int64>(OE_MAP_MOB_DIFFICULTY_SCALER);
if (InstanceScale[0] > 0)
{
float OriginalDamage = (float)params->GetDamage();
float DifficultyScaler = ((float)InstanceScale[0]) / 1000.0f * RESISTANCE_MAX_DMG_MITIGATION; // at 1000% this would mean a loss of 80% dmg
float DmgReduction = OriginalDamage * DifficultyScaler;
uint32 PlayerListSize = params->Attacker->GetMap()->GetPlayers().getSize();
for (uint32 i = 1; i < PlayerListSize; i++)
{
float RemainingDmg = OriginalDamage - DmgReduction;
DmgReduction += RemainingDmg * 0.75f; //loose 75% of damage for every extra player
}
params->FlatMods -= (int32)(DmgReduction);
}
}
}
if (PlayerAttacker == NULL && PlayerTarget != NULL)
{
//mod damage based on map setting
int64 *InstanceScale = params->Attacker->GetMap()->GetExtension<int64>(OE_MAP_MOB_DIFFICULTY_SCALER);
if (InstanceScale != NULL && *InstanceScale > 0)
{
float PCTScaler = (float)*InstanceScale;
if (PCTScaler > 200)
{
PCTScaler = (PCTScaler) / 100.0f * (PCTScaler / 3.0f) / 100.0f; // 100 = 0, 200 = 0, 300 = 3, 400 = 5, 500 = 8, 600 = 12
params->FlatMods += (uint32)(params->OriDamage * PCTScaler);
}
//mitigation pct based reduction
uint32 Resistance = 0;
if (params->sp == NULL)
Resistance = GetMinResistance(params->Victim, params->Attacker->GetMeleeDamageSchoolMask());
else
Resistance = GetMinResistance(params->Victim, params->sp->GetSchoolMask());
Resistance = MIN(RESISTANCE_FOR_MAX_MITIGATION, Resistance);
float DmgReduction = Resistance / RESISTANCE_FOR_MAX_MITIGATION * RESISTANCE_MAX_DMG_MITIGATION; // max 80% dmg reduction for 500 resistance
params->FlatMods -= (int32)(params->GetDamage() * (1.0f - DmgReduction));
}
}
}
#define TIMEOUT_PREVIOUS_CONDITION_CHECKS 30000
#define DISTANCE_MOVE_SQ 0.1f
class FallAsleepStore
{
public:
FallAsleepStore()
{
NextPositionCheckStamp = 0;
MountId = 0;
}
uint32 NextPositionCheckStamp;
uint32 MountId;
Position pos;
};
void PeriodicCheckPlayerFallAsleep(void *p, void *)
{
Player *player = (Player*)p;
FallAsleepStore *ms = player->GetCreateExtension<FallAsleepStore>(OE_PLAYER_FALL_ASLEEP_STORE);
uint32 TickNow = GameTime::GetGameTimeMS();
//if it is a good condition, than we should start counting seconds and distances
if (ms->NextPositionCheckStamp > TickNow)
return;
ms->NextPositionCheckStamp = TickNow + TIMEOUT_PREVIOUS_CONDITION_CHECKS;
//only trigger if we are standing in 1 place
float dist = ms->pos.GetExactDist2dSq(player->GetPosition());
//remember position
ms->pos = player->GetPosition();
if (dist >= DISTANCE_MOVE_SQ)
return;
//self cast asleep
player->CastSpell(player, 34801, TRIGGERED_FULL_MASK);
}
void RI_DS_OnPlayerMapChange(void *p, void *)
{
CP_MAP_CHANGED *params = PointerCast(CP_MAP_CHANGED, p);
if (CheckShouldTriggerCreatureScript(params->NewMap, RI_DS_FROST_NOVA_ON_STRUCK) == true)
params->Player->BroadcastMessage("ScaledInstance : Frost Nova on struck - map mod activated");
if (CheckShouldTriggerCreatureScript(params->NewMap, RI_DS_RANDOM_IMMUNITY) == true)
params->Player->BroadcastMessage("ScaledInstance : Random dmg school immunity - map mod activated");
if (CheckShouldTriggerCreatureScript(params->NewMap, RI_DS_REANIMATE) == true)
params->Player->BroadcastMessage("ScaledInstance : Killed creatures reannimate - map mod activated");
if (CheckShouldTriggerCreatureScript(params->NewMap, RI_DS_FALL_ASLEEP) == true)
params->Player->BroadcastMessage("ScaledInstance : Standing makes you fall asleep - map mod activated");
if (CheckShouldTriggerCreatureScript(params->NewMap, RI_DS_ENGULFED_IN_FLAME) == true)
params->Player->BroadcastMessage("ScaledInstance : Mobs gain AOE aura - map mod activated");
if (CheckShouldTriggerCreatureScript(params->NewMap, RI_DS_LIGHTNING_ON_STRUCK) == true)
params->Player->BroadcastMessage("ScaledInstance : Lightning on struck - map mod activated");
if (CheckShouldTriggerCreatureScript(params->NewMap, RI_DS_FIRE_RAIN_ON_STRUCK) == true)
params->Player->BroadcastMessage("ScaledInstance : Rain of Fire on struck - map mod activated");
if (CheckShouldTriggerCreatureScript(params->NewMap, RI_DS_EXPLODE) == true)
params->Player->BroadcastMessage("ScaledInstance : Explode on death - map mod activated");
if (CheckShouldTriggerCreatureScript(params->NewMap, RI_DS_MOSTER_MOVE_SPEED) == true)
params->Player->BroadcastMessage("ScaledInstance : Extra Monster speed - map mod activated");
if (CheckShouldTriggerCreatureScript(params->NewMap, RI_DS_DECREASE_PLAYER_HEALTH_PERIODICALLY) == true)
params->Player->BroadcastMessage("ScaledInstance : Sudden Death - map mod activated");
if (CheckShouldTriggerCreatureScript(params->NewMap, RI_DS_TOXIC_POOL) == true)
params->Player->BroadcastMessage("ScaledInstance : Toxic pool - map mod activated");
if (CheckShouldTriggerCreatureScript(params->NewMap, RI_DS_LINKED_HEALTH) == true)
params->Player->BroadcastMessage("ScaledInstance : Linked Health - map mod activated");
if (CheckShouldTriggerCreatureScript(params->NewMap, RI_DS_CRIT_CHANCE) == true)
params->Player->BroadcastMessage("ScaledInstance : Mob crit chance - map mod activated");
if (CheckShouldTriggerCreatureScript(params->NewMap, RI_DS_RANDOM_MECHANIC_IMMUNITY) == true)
params->Player->BroadcastMessage("ScaledInstance : Random mechanic immunity - map mod activated");
if (CheckShouldTriggerCreatureScript(params->NewMap, RI_DS_LOOSE_GOLD_ON_DEATH) == true)
params->Player->BroadcastMessage("ScaledInstance : Death Toll - map mod activated");
if (CheckShouldTriggerCreatureScript(params->NewMap, RI_DS_REFLECT_SPELLS) == true)
params->Player->BroadcastMessage("ScaledInstance : Reflection - map mod activated");
if (CheckShouldTriggerCreatureScript(params->NewMap, RI_DS_ELEMENTAL_DMG) == true)
params->Player->BroadcastMessage("ScaledInstance : Elemental - map mod activated");
if (CheckShouldTriggerCreatureScript(params->NewMap, RI_DS_LAVA_POOL) == true)
params->Player->BroadcastMessage("ScaledInstance : Lava pool - map mod activated");
if (CheckShouldTriggerCreatureScript(params->NewMap, RI_DS_FIRE_CHAINS) == true)
params->Player->BroadcastMessage("ScaledInstance : Arcane chains - map mod activated");
if (CheckShouldTriggerCreatureScript(params->NewMap, RI_DS_REDUCED_DAMAGE_TAKEN) == true)
params->Player->BroadcastMessage("ScaledInstance : Tough - map mod activated");
if (CheckShouldTriggerCreatureScript(params->NewMap, RI_DS_FRENZIED_REGENERATION) == true)
params->Player->BroadcastMessage("ScaledInstance : Health Regen - map mod activated");
if (CheckShouldTriggerCreatureScript(params->NewMap, RI_DS_FALL_ASLEEP) == true)
params->Player->RegisterCallbackFunc(CALLBACK_TYPE_PLAYER_PERIODIC_TICK, PeriodicCheckPlayerFallAsleep);
else
params->Player->UnRegisterCallbackFunc(CALLBACK_TYPE_PLAYER_PERIODIC_TICK, PeriodicCheckPlayerFallAsleep);
}
bool CanUnitBeFireChainTarget(Unit *u, Player *p)
{
if (u->GetChannelObjectGuid().Empty != false)
return false;
if (u->GetUInt32Value(UNIT_CHANNEL_SPELL) != 0)
return false;
if (u->GetExtension<uint64>(OE_CREATURE_FIRE_CHAIN_MATE) != NULL)
return false;
if (u->isTargetableForAttack(false) == false)
return false;
if (p != NULL && u->IsValidAttackTarget(p) == false)
return false;
return true;
}
void OnCreatureUpdateFireChain(void *pCreature, void *)
{
Creature *Owner = (Creature *)pCreature;
int64 *NextUpdateStamp = Owner->GetCreateIn64Extension(OE_CREATURE_FIRE_CHAIN_NEXT_UPDATE_STAMP);
//no need to spam this
if (*NextUpdateStamp > GameTime::GetGameTimeMS())
return;
*NextUpdateStamp = GameTime::GetGameTimeMS() + 2000;
Creature *ChainedTarget = NULL;
if (Owner->GetMap() != NULL)
{
ObjectGuid MateGuid(*Owner->GetExtension<uint64>(OE_CREATURE_FIRE_CHAIN_MATE));
ChainedTarget = Owner->GetMap()->GetCreature(MateGuid);
}
if (ChainedTarget == NULL || Owner->IsAlive() == false || ChainedTarget->IsAlive() == false)
{
//remove the chain from us and our mate
Owner->SetChannelObjectGuid(ObjectGuid());
Owner->SetUInt32Value(UNIT_CHANNEL_SPELL, 0);
if (ChainedTarget != NULL)
{
ChainedTarget->SetChannelObjectGuid(ObjectGuid());
ChainedTarget->SetUInt32Value(UNIT_CHANNEL_SPELL, 0);
}
//unregister this callback. Suicide call !
Owner->UnRegisterCallbackFunc(CALLBACK_TYPE_CREATURE_UPDATE, OnCreatureUpdateFireChain);
return;
}
float Divider = (Owner->GetPositionX() - ChainedTarget->GetPositionX());
if (Divider == 0.f)
Divider = 0.000001f;
float LineSloap = (Owner->GetPositionY() - ChainedTarget->GetPositionY()) / Divider;// line will be : y - Owner->GetPositionY() = LineSloap(x - Owner->GetPositionX() )
float MinX = MIN(Owner->GetPositionX(), ChainedTarget->GetPositionX()) + 1.f;
float MaxX = MAX(Owner->GetPositionX(), ChainedTarget->GetPositionX()) - 1.f;
float MinY = MIN(Owner->GetPositionY(), ChainedTarget->GetPositionY()) + 1.f;
float MaxY = MAX(Owner->GetPositionY(), ChainedTarget->GetPositionY()) - 1.f;
//get nearby players, apply dmg
const float Radius = 30.0f;
Map::PlayerList const& PlayerList = Owner->GetMap()->GetPlayers();
for (Map::PlayerList::const_iterator itr = PlayerList.begin(); itr != PlayerList.end(); ++itr)
{
//is this player crossing the line between the 2 mobs
Player *p = (itr->GetSource());
float PlayerDistanceFromLine = LineSloap * (p->GetPositionX() - Owner->GetPositionX()) - (p->GetPositionY() - Owner->GetPositionY());
if ( abs(PlayerDistanceFromLine) > 1.5f)
continue;
//are we between the 2 mobs ?
if (p->GetPositionX() < MinX || p->GetPositionX() > MaxX)
continue;
if (p->GetPositionY() < MinY || p->GetPositionY() > MaxY)
continue;
//deal the damage
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(51021);
Spell* spell = new Spell(Owner, spellInfo, (TriggerCastFlags)(TRIGGERED_FULL_MASK | TRIGGERED_IGNORE_CAST_TIME | TRIGGERED_IGNORE_MOVE_CHECK));
spell->m_targets.SetUnitTarget(p);
spell->SetSpellValue(SPELLVALUE_BASE_POINT0, p->GetMaxHealth() * 10 / 100);
spell->cast(true);
}
}
void RI_DS_OnMobSpawn(void *p, void *)
{
CP_CREATURE_SPAWN *params = PointerCast(CP_CREATURE_SPAWN, p);
if (params->Spawn == NULL || params->map == NULL)
return;
Player *plr = NULL;
if( params->map->GetPlayers().begin() != params->map->GetPlayers().end())
plr = params->map->GetPlayers().begin()->GetSource();
if (CheckShouldTriggerCreatureScript(params->map, RI_DS_MIN_DIFF_ANYSCRIPT, params->Spawn, plr) == false)
return;
if (CheckShouldTriggerCreatureScript(params->Spawn->GetMap(), RI_DS_MOSTER_MOVE_SPEED, params->Spawn) == true)
{
params->Spawn->SetObjectScale(0.75f);
for (UnitMoveType i = MOVE_WALK; i <= MOVE_PITCH_RATE; i = (UnitMoveType)(i + 1))
params->Spawn->SetSpeedRate(i, 1.5f);
params->Spawn->ApplyAttackTimePercentMod(BASE_ATTACK, -50, true);
params->Spawn->ApplyAttackTimePercentMod(OFF_ATTACK, -50, true);
params->Spawn->ApplyAttackTimePercentMod(RANGED_ATTACK, -50, true);
}
if (CheckShouldTriggerCreatureScript(params->Spawn->GetMap(), RI_DS_RANDOM_IMMUNITY, params->Spawn) == true)
{
int PickedImmunity = rand32() % MAX_SPELL_SCHOOL;
static uint32 const placeholderSpellId = std::numeric_limits<uint32>::max();
params->Spawn->ApplySpellImmune(placeholderSpellId, IMMUNITY_SCHOOL, PickedImmunity, true);
if (PickedImmunity == SPELL_SCHOOL_FIRE)
params->Spawn->CastSpell(params->Spawn, 34333, TRIGGERED_FULL_MASK);
if (PickedImmunity == SPELL_SCHOOL_FROST)
params->Spawn->CastSpell(params->Spawn, 34334, TRIGGERED_FULL_MASK);
if (PickedImmunity == SPELL_SCHOOL_HOLY)
params->Spawn->CastSpell(params->Spawn, 34336, TRIGGERED_FULL_MASK);
if (PickedImmunity == SPELL_SCHOOL_NATURE)
params->Spawn->CastSpell(params->Spawn, 34335, TRIGGERED_FULL_MASK);
if (PickedImmunity == SPELL_SCHOOL_NORMAL)
params->Spawn->CastSpell(params->Spawn, 34337, TRIGGERED_FULL_MASK);
if (PickedImmunity == SPELL_SCHOOL_SHADOW)
params->Spawn->CastSpell(params->Spawn, 34338, TRIGGERED_FULL_MASK);
if (PickedImmunity == SPELL_SCHOOL_ARCANE)
params->Spawn->CastSpell(params->Spawn, 34331, TRIGGERED_FULL_MASK);
}
if (CheckShouldTriggerCreatureScript(params->Spawn->GetMap(), RI_DS_RANDOM_MECHANIC_IMMUNITY, params->Spawn) == true)
{
int PickedImmunity = rand32() % MAX_MECHANIC;
static uint32 const placeholderSpellId = std::numeric_limits<uint32>::max();
params->Spawn->ApplySpellImmune(placeholderSpellId, IMMUNITY_MECHANIC, PickedImmunity, true);
PickedImmunity = rand32() % MAX_MECHANIC;
params->Spawn->ApplySpellImmune(placeholderSpellId, IMMUNITY_MECHANIC, PickedImmunity, true);
}
if (CheckShouldTriggerCreatureScript(params->Spawn->GetMap(), RI_DS_ENGULFED_IN_FLAME, params->Spawn) == true)
{
params->Spawn->CastSpell(params->Spawn, 50589, TRIGGERED_FULL_MASK);
Aura *a = params->Spawn->GetAura(50589);
if (a != NULL && a->GetDuration() != -1)
a->SetDuration(10 * 60 * 60 * 1000);
}
if (CheckShouldTriggerCreatureScript(params->Spawn->GetMap(), RI_DS_CRIT_CHANCE, params->Spawn) == true)
{
params->Spawn->m_baseSpellCritChance += 50;
params->Spawn->m_baseCritChance += 50;
}
if (CheckShouldTriggerCreatureScript(params->Spawn->GetMap(), RI_DS_ELEMENTAL_DMG, params->Spawn) == true)
{
int PickedSchool = rand32() % MAX_SPELL_SCHOOL;
params->Spawn->SetMeleeDamageSchool(SpellSchools(PickedSchool));
}
if (CheckShouldTriggerCreatureScript(params->Spawn->GetMap(), RI_DS_FIRE_CHAINS, params->Spawn) == true)
{
// only chain max 10% of creatures
// int64 *CreaturesSpawned = params->Spawn->GetMap()->GetCreateIn64Extension(OE_CREATURE_FIRE_CHAIN_TOTAL_CREATURES_SPAWNED);
// int64 *CreaturesChained = params->Spawn->GetMap()->GetCreateIn64Extension(OE_CREATURE_FIRE_CHAIN_CREATURES_FIRECHAINED);
// *CreaturesSpawned += 1;
// if (*CreaturesSpawned * 10 / 100 > *CreaturesChained)
{
Player *AnyPlayer = NULL;
if( params->Spawn->GetMap()->GetPlayers().begin() != params->Spawn->GetMap()->GetPlayers().end())
AnyPlayer = params->Spawn->GetMap()->GetPlayers().begin()->GetSource();
if (CanUnitBeFireChainTarget(params->Spawn, AnyPlayer) == true)
{
// search for a mate to chain up with
const float Radius = 20.0f;
std::list<Unit*> NearbyUnits;
Trinity::AnyFriendlyUnitInObjectRangeCheck u_check(params->Spawn, params->Spawn, Radius);
Trinity::UnitListSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(params->Spawn, NearbyUnits, u_check);
Cell::VisitAllObjects(params->Spawn, searcher, Radius);
//search to have some minimum radius between the 2
Unit *BestMatch = NULL;
float MinDistance = 0.0f;
for (std::list<Unit*>::iterator itr = NearbyUnits.begin(); itr != NearbyUnits.end(); itr++)
{
if (CanUnitBeFireChainTarget((*itr), AnyPlayer) == false)
continue;
if (abs((*itr)->GetPositionZ() - params->Spawn->GetPositionZ()) > 5.0f)
continue;
float Dist = (*itr)->GetDistance2d(params->Spawn);
if (Dist < 5.f)
continue;
if (Dist > MinDistance)
{
MinDistance = Dist;
BestMatch = (*itr);
}
}
//we found a creature we can link each other with
if (BestMatch != NULL)
{
*params->Spawn->GetCreateExtension<uint64>(OE_CREATURE_FIRE_CHAIN_MATE) = BestMatch->GetGUID().GetRawValue();
*BestMatch->GetCreateExtension<uint64>(OE_CREATURE_FIRE_CHAIN_MATE) = params->Spawn->GetGUID().GetRawValue();
params->Spawn->SetChannelObjectGuid(BestMatch->GetGUID());
BestMatch->SetChannelObjectGuid(params->Spawn->GetGUID());
params->Spawn->SetUInt32Value(UNIT_CHANNEL_SPELL, 689);
BestMatch->SetUInt32Value(UNIT_CHANNEL_SPELL, 689);
// *CreaturesChained += 1;
params->Spawn->RegisterCallbackFunc(CALLBACK_TYPE_CREATURE_UPDATE, OnCreatureUpdateFireChain, NULL);
}
}
}
}
//scale health and resistances
int64 *InstanceScale = params->map->GetCreateIn64Extension(OE_MAP_MOB_DIFFICULTY_SCALER, true);
if (InstanceScale != NULL)
{
int64 PCTScaler = *InstanceScale;
if (PCTScaler > 0)
{
int64 MaxHealth = params->Spawn->GetCreateHealth();
if(PCTScaler < 600 )
MaxHealth += MaxHealth * PCTScaler / 100 * MONSTER_DIIFCULTY_MULTIPLIER_HP; // !!! note that we already scaled health once. 3*2*2 = 12, 4*2*2=16, 5*2*2=20
else
MaxHealth += MaxHealth * PCTScaler / 100 * PCTScaler / 100; // 6*6=36, 7*7=49, 8*8=64, 9*9=81 ( 6*5=30, 7*6=42, 8*7=56, 9*8=72 )
if (MaxHealth > 0x0FFFFFFF)
MaxHealth = 0x0FFFFFFF;
// params->Spawn->SetCreateHealth((uint32)MaxHealth);
params->Spawn->SetMaxHealth((uint32)MaxHealth);
params->Spawn->SetHealth((uint32)MaxHealth);
for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++)
{
if (i == SPELL_SCHOOL_NORMAL)
params->Spawn->SetResistance(SpellSchools(i), (int32)PCTScaler * 10);
else
params->Spawn->SetResistance(SpellSchools(i), (int32)PCTScaler);
}
}
}
}
struct trigger_periodicToxicAI : public NullCreatureAI
{
trigger_periodicToxicAI(Creature* creature) : NullCreatureAI(creature)
{
interval = 2000;
timer = interval;
DespawnTimer = 2 * 60 * 1000;
}
int32 timer, interval, DespawnTimer;
void UpdateAI(uint32 diff) override
{
//we should never die
me->SetHealth(me->GetMaxHealth());
if (timer <= (int32)diff)
{
//get nearby players, apply toxic dmg
const float Radius = 3.0f;
const float HealthPCTDmg = 0.05f;
std::list<Player*> players;
Trinity::AnyPlayerInObjectRangeCheck checker(me, Radius);
Trinity::PlayerListSearcher<Trinity::AnyPlayerInObjectRangeCheck> searcher(me, players, checker);
Cell::VisitWorldObjects(me, searcher, Radius);
for (std::list<Player*>::iterator itr = players.begin(); itr != players.end(); itr++)
{
//nature esistance will decrease this damage
uint32 Res = MIN(RESISTANCE_FOR_MAX_MITIGATION,(*itr)->GetResistance(SPELL_SCHOOL_NATURE));
float ResPct = Res / RESISTANCE_FOR_MAX_MITIGATION * RESISTANCE_MAX_DMG_MITIGATION;
uint32 Dmg = (uint32)(ResPct * HealthPCTDmg * (*itr)->GetHealth());
(*itr)->EnvironmentalDamage(DAMAGE_SLIME, Dmg);
}
timer = interval;
}
else
timer -= diff;
DespawnTimer -= diff;
if (DespawnTimer <= 0)
me->DespawnOrUnsummon();
}
};
struct trigger_periodicLavaAI : public NullCreatureAI
{
trigger_periodicLavaAI(Creature* creature) : NullCreatureAI(creature)
{
interval = 1000;
timer = interval;
DespawnTimer = 2 * 60 * 1000;
}
int32 timer, interval, DespawnTimer;
void UpdateAI(uint32 diff) override
{
//we should never die
me->SetHealth(me->GetMaxHealth());
if (timer <= (int32)diff)
{
//get nearby players, apply toxic dmg
const float Radius = 1.5f;
const float HealthPCTDmg = 0.10f;
std::list<Player*> players;
Trinity::AnyPlayerInObjectRangeCheck checker(me, Radius);
Trinity::PlayerListSearcher<Trinity::AnyPlayerInObjectRangeCheck> searcher(me, players, checker);
Cell::VisitWorldObjects(me, searcher, Radius);
for (std::list<Player*>::iterator itr = players.begin(); itr != players.end(); itr++)
{
//nature esistance will decrease this damage
uint32 Res = MIN(RESISTANCE_FOR_MAX_MITIGATION, (*itr)->GetResistance(SPELL_SCHOOL_FIRE));
float ResPct = Res / RESISTANCE_FOR_MAX_MITIGATION * RESISTANCE_MAX_DMG_MITIGATION;
uint32 Dmg = (uint32)(ResPct * HealthPCTDmg * (*itr)->GetMaxHealth());
(*itr)->EnvironmentalDamage(DAMAGE_LAVA, Dmg);
}
timer = interval;
}
else
timer -= diff;
DespawnTimer -= diff;
if (DespawnTimer <= 0)
me->DespawnOrUnsummon();
}
};
struct trigger_ExpoldeAI : public NullCreatureAI
{
trigger_ExpoldeAI(Creature* creature) : NullCreatureAI(creature)
{
timer = 5000;
}
int32 timer;
int32 Damage;
void UpdateAI(uint32 diff) override
{
//we should never die
me->SetHealth(me->GetMaxHealth());
if (timer <= (int32)diff)
{
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
me->ClearUnitState(UNIT_STATE_UNATTACKABLE);
me->ClearUnitState(UNIT_STATE_CANNOT_AUTOATTACK);
me->ClearUnitState(UNIT_STATE_NOT_MOVE);
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(8349);
Spell* spell = new Spell(me, spellInfo, TRIGGERED_FULL_MASK);
spell->m_targets.SetUnitTarget(me);
spell->m_targets.SetSrc(me->GetPosition());
spell->m_targets.SetDst(me->GetPosition());
spell->SetSpellValue(SPELLVALUE_BASE_POINT0, Damage); // 3% - this will be scaled by difficulty scaler to up to 20% at this point
spell->cast(true);
me->DespawnOrUnsummon();
}
else
timer -= diff;
}
};
class TC_GAME_API RI_DS_OnCreatureDie : public PlayerScript
{
public:
RI_DS_OnCreatureDie() : PlayerScript("RI_DS_OnCreatureDie") {}
void OnCreatureKill(Player* killer, Creature* killed)
{
if (killed == NULL)
return;
Player *PlayerTarget = killed->ToPlayer();
if (PlayerTarget == NULL)
PlayerTarget = killed->GetCharmerOrOwnerPlayerOrPlayerItself();
if (PlayerTarget != NULL)
return; //do not scale pets
if (CheckShouldTriggerCreatureScript(killer->GetMap(), RI_DS_MIN_DIFF_ANYSCRIPT, killed, killer) == false)
return;
if (CheckShouldTriggerCreatureScript(killed->GetMap(), RI_DS_TOXIC_POOL) == true && (rand32() % 100 < 50))
{
TempSummon* summon = killed->GetMap()->SummonCreature(15925, killed->GetPosition(), nullptr, 2 * 60 * 1000);
if (summon)
{
summon->SetTempSummonType(TEMPSUMMON_TIMED_DESPAWN);
summon->SetMaxHealth(666666);
summon->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
summon->AddUnitState(UNIT_STATE_UNATTACKABLE);
summon->AddUnitState(UNIT_STATE_CANNOT_AUTOATTACK);
summon->AddUnitState(UNIT_STATE_NOT_MOVE);
summon->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH);
summon->SetObjectScale(3.0f);
summon->SetLevel(MAX_LEVEL);
CreatureAI *cai = new trigger_periodicToxicAI(summon);
summon->SetAI(cai);
}
}
else if (CheckShouldTriggerCreatureScript(killed->GetMap(), RI_DS_LAVA_POOL) == true)
{
TempSummon* summon = killed->GetMap()->SummonCreature(15925, killed->GetPosition(), nullptr, 2 * 60 * 1000);
if (summon)
{
summon->SetTempSummonType(TEMPSUMMON_TIMED_DESPAWN);
summon->SetMaxHealth(666666);
summon->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
summon->AddUnitState(UNIT_STATE_UNATTACKABLE);
summon->AddUnitState(UNIT_STATE_CANNOT_AUTOATTACK);
summon->AddUnitState(UNIT_STATE_NOT_MOVE);
summon->SetDisplayId(4754);
summon->SetLevel(MAX_LEVEL);
summon->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH);
summon->SetObjectScale(2.0f);
CreatureAI *cai = new trigger_periodicLavaAI(summon);
summon->SetAI(cai);
}
}
if (CheckShouldTriggerCreatureScript(killed->GetMap(), RI_DS_EXPLODE) == true)
{
TempSummon* summon = killed->GetMap()->SummonCreature(15925, killed->GetPosition(), nullptr, 2 * 60 * 1000);
if (summon)
{
summon->SetTempSummonType(TEMPSUMMON_TIMED_DESPAWN);
summon->SetMaxHealth(666666);
summon->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
summon->AddUnitState(UNIT_STATE_UNATTACKABLE);
summon->AddUnitState(UNIT_STATE_CANNOT_AUTOATTACK);
summon->AddUnitState(UNIT_STATE_NOT_MOVE);
summon->SetDisplayId(6888);
summon->SetObjectScale(1.25f);
summon->SetLevel(MAX_LEVEL);
trigger_ExpoldeAI *cai = new trigger_ExpoldeAI(summon);
cai->Damage = killer->GetMaxHealth() * 10 / 100;
summon->SetAI(cai);
}
}
if (CheckShouldTriggerCreatureScript(killed->GetMap(), RI_DS_REANIMATE) == true)
{
if (killed->ToTempSummon() == NULL && killed->IsDungeonBoss() == false && killed->IsCritter() == false && killed->GetCharmerGUID() == ObjectGuid::Empty && killed->GetCharm() == NULL)
{
TempSummon* summon = killed->GetMap()->SummonCreature(killed->GetEntry(), killed->GetPosition(), nullptr, 2 * 60 * 1000);
if (summon)
{
summon->SetTempSummonType(TEMPSUMMON_TIMED_DESPAWN);
summon->GetThreatManager().AddThreat(killer, 1);
//TriggerCallbackFunctions(CALLBACK_TYPE_CREATURE_SPAWN, &CP_CREATURE_SPAWN(summon, killed->GetMap()));
}
}
}
}
};
class TC_GAME_API RI_DS_PlayerScripts : public PlayerScript
{
public:
RI_DS_PlayerScripts() : PlayerScript("RI_DS_PlayerScripts") {}
void OnPlayerKilledByCreature(Creature* killer, Player* killed)
{
if (CheckShouldTriggerCreatureScript(killed->GetMap(), RI_DS_LOOSE_GOLD_ON_DEATH) == true)
{
killed->ModifyMoney(killed->GetMoney() / 2);
}
}
};
void RI_RegisterMapHandleScritps()
{
//list of difficulties required for scripts to trigger
RI_DS_REQUIRED_DIFF[RI_DS_MIN_DIFF_ANYSCRIPT] = 101;
RI_DS_REQUIRED_DIFF[RI_DS_FROST_NOVA_ON_STRUCK] = 100;
RI_DS_REQUIRED_DIFF[RI_DS_LIGHTNING_ON_STRUCK] = 200;
RI_DS_REQUIRED_DIFF[RI_DS_CRIT_CHANCE] = 300;
RI_DS_REQUIRED_DIFF[RI_DS_REFLECT_SPELLS] = 350;
RI_DS_REQUIRED_DIFF[RI_DS_FIRE_RAIN_ON_STRUCK] = 400;
RI_DS_REQUIRED_DIFF[RI_DS_ENGULFED_IN_FLAME] = 450;
RI_DS_REQUIRED_DIFF[RI_DS_REANIMATE] = 500;
RI_DS_REQUIRED_DIFF[RI_DS_LOOSE_GOLD_ON_DEATH] = 550;
RI_DS_REQUIRED_DIFF[RI_DS_ELEMENTAL_DMG] = 550;
RI_DS_REQUIRED_DIFF[RI_DS_EXPLODE] = 600;
RI_DS_REQUIRED_DIFF[RI_DS_FALL_ASLEEP] = 650;
RI_DS_REQUIRED_DIFF[RI_DS_REDUCED_DAMAGE_TAKEN] = 650;
RI_DS_REQUIRED_DIFF[RI_DS_MOSTER_MOVE_SPEED] = 700;
RI_DS_REQUIRED_DIFF[RI_DS_DECREASE_PLAYER_HEALTH_PERIODICALLY] = 750;
RI_DS_REQUIRED_DIFF[RI_DS_RANDOM_MECHANIC_IMMUNITY] = 850;
RI_DS_REQUIRED_DIFF[RI_DS_RANDOM_IMMUNITY] = 850;
RI_DS_REQUIRED_DIFF[RI_DS_TOXIC_POOL] = 900;
RI_DS_REQUIRED_DIFF[RI_DS_LINKED_HEALTH] = 950;
RI_DS_REQUIRED_DIFF[RI_DS_LAVA_POOL] = 1000;
RI_DS_REQUIRED_DIFF[RI_DS_FIRE_CHAINS] = 1000;
RI_DS_REQUIRED_DIFF[RI_DS_FRENZIED_REGENERATION] = 1000;
//will call sub functions to handle difficulty scripts
RegisterCallbackFunction(CALLBACK_TYPE_DMG_DONE, RI_OnHitTarget, NULL);
RegisterCallbackFunction(CALLBACK_TYPE_DMG_RECEIVED, RI_OnDamageTaken, NULL);
RegisterCallbackFunction(CALLBACK_TYPE_MAP_PERIODIC_UPDATE, RI_DS_MapUpdate, NULL);
RegisterCallbackFunction(CALLBACK_TYPE_PLAYER_MAP_CHANGE, RI_DS_OnPlayerMapChange, NULL);
RegisterCallbackFunction(CALLBACK_TYPE_CREATURE_SPAWN, RI_DS_OnMobSpawn, NULL);
//used by toxic pools
new RI_DS_OnCreatureDie();
new RI_DS_PlayerScripts();
}