mirror of
https://gitlab.com/Scribble/gdlenhanced.git
synced 2026-08-17 14:23:04 -04:00
308 lines
9.4 KiB
C++
308 lines
9.4 KiB
C++
#include <StdAfx.h>
|
|
#include "SpellProjectile.h"
|
|
#include "World.h"
|
|
#include "Monster.h"
|
|
#include "CombatFormulas.h"
|
|
#include "Player.h"
|
|
|
|
const float MAX_SPELL_PROJECTILE_LIFETIME = 30.0f;
|
|
|
|
CSpellProjectile::CSpellProjectile(const SpellCastData &scd, uint32_t target_id)//, unsigned int damage)
|
|
{
|
|
m_CachedSpellCastData = scd;
|
|
m_SourceID = scd.source_id;
|
|
m_TargetID = target_id;
|
|
//m_Damage = damage;
|
|
|
|
m_fSpawnTime = Timer::cur_time;
|
|
m_fDestroyTime = FLT_MAX;
|
|
|
|
m_fFriction = 1.0f;
|
|
m_fElasticity = 0.0f;
|
|
|
|
m_DefaultScript = 0x5A;
|
|
m_DefaultScriptIntensity = 1.0f;
|
|
|
|
m_fEffectMod = max(0.0, min(1.0, ((scd.power_level_of_power_component - 1.0) / 15.0)));
|
|
|
|
SetItemType(ITEM_TYPE::TYPE_SELF);
|
|
|
|
m_Qualities.SetBool(STUCK_BOOL, TRUE);
|
|
m_Qualities.SetBool(ATTACKABLE_BOOL, TRUE);
|
|
m_Qualities.SetBool(UI_HIDDEN_BOOL, TRUE);
|
|
|
|
SetInitialPhysicsState(INELASTIC_PS | SCRIPTED_COLLISION_PS | REPORT_COLLISIONS_PS | MISSILE_PS | LIGHTING_ON_PS | PATHCLIPPED_PS | ALIGNPATH_PS);
|
|
}
|
|
|
|
CSpellProjectile::~CSpellProjectile()
|
|
{
|
|
}
|
|
|
|
void CSpellProjectile::Tick()
|
|
{
|
|
if (!m_bDestroyMe)
|
|
{
|
|
if (!InValidCell() || (m_fDestroyTime <= Timer::cur_time))
|
|
{
|
|
MarkForDestroy();
|
|
}
|
|
// not destroyed yet and distance/time exceeded
|
|
else if (m_fDestroyTime > Timer::cur_time + 10 && (m_Position.distance(m_CachedSpellCastData.initial_cast_position) > m_CachedSpellCastData.max_range || (m_fSpawnTime + MAX_SPELL_PROJECTILE_LIFETIME - 1) <= Timer::cur_time))
|
|
{
|
|
HandleExplode();
|
|
m_fDestroyTime = Timer::cur_time + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CSpellProjectile::PostSpawn()
|
|
{
|
|
CWeenieObject::PostSpawn();
|
|
|
|
EmitEffect(PS_Launch, m_fEffectMod);
|
|
m_fSpawnTime = Timer::cur_time;
|
|
}
|
|
|
|
void CSpellProjectile::HandleExplode()
|
|
{
|
|
EmitEffect(PS_Explode, m_fEffectMod);
|
|
|
|
// INELASTIC_PS | SCRIPTED_COLLISION_PS | REPORT_COLLISIONS_PS | MISSILE_PS | LIGHTING_ON_PS | PATHCLIPPED_PS | ALIGNPATH_PS
|
|
set_state(/*0x128374*/
|
|
INELASTIC_PS | SCRIPTED_COLLISION_PS | MISSILE_PS | PATHCLIPPED_PS | ALIGNPATH_PS | ETHEREAL_PS | IGNORE_COLLISIONS_PS | NODRAW_PS | CLOAKED_PS, TRUE);
|
|
|
|
// no more REPORT_COLLISIONS_PS and no more LIGHTING_ON_PS
|
|
}
|
|
|
|
BOOL CSpellProjectile::DoCollision(const class EnvCollisionProfile &prof)
|
|
{
|
|
Movement_UpdatePos();
|
|
HandleExplode();
|
|
m_fDestroyTime = Timer::cur_time + 1.0;
|
|
|
|
return CWeenieObject::DoCollision(prof);
|
|
}
|
|
|
|
BOOL CSpellProjectile::DoCollision(const class AtkCollisionProfile &prof)
|
|
{
|
|
CWeenieObject *pHit = g_pWorld->FindWithinPVS(this, prof.id);
|
|
if (pHit && (!m_TargetID || m_TargetID == pHit->GetID()) && (pHit->GetID() != m_SourceID))
|
|
{
|
|
if (pHit->m_Qualities.m_WeenieType == CombatPet_WeenieType)
|
|
return 1;
|
|
CWeenieObject *pSource = NULL;
|
|
if (m_SourceID)
|
|
pSource = g_pWorld->FindObject(m_SourceID);
|
|
if (!pSource)
|
|
return 1;
|
|
|
|
pHit->m_Qualities.SetInstanceID(CURRENT_ATTACKER_IID, m_SourceID);
|
|
|
|
uint32_t currentEnemy;
|
|
if (pHit->m_Qualities.InqInstanceID(CURRENT_ENEMY_IID, currentEnemy))
|
|
{
|
|
if(currentEnemy == 0)
|
|
pHit->m_Qualities.SetInstanceID(CURRENT_ENEMY_IID, m_SourceID);
|
|
}
|
|
|
|
|
|
|
|
if (!pHit->ImmuneToDamage(pSource))
|
|
{
|
|
Movement_UpdatePos();
|
|
// EmitEffect(5, 0.8f);
|
|
HandleExplode();
|
|
m_fDestroyTime = Timer::cur_time + 0.5;
|
|
|
|
if (pSource && pHit && pSource->AsPlayer() && pHit->AsPlayer())
|
|
{
|
|
pSource->AsPlayer()->UpdatePKActivity();
|
|
pHit->AsPlayer()->UpdatePKActivity();
|
|
}
|
|
|
|
if (m_CachedSpellCastData.spell_id == 1783 || m_CachedSpellCastData.spell_id == 1784 || m_CachedSpellCastData.spell_id == 1785 || m_CachedSpellCastData.spell_id == 1786
|
|
|| m_CachedSpellCastData.spell_id == 1787 || m_CachedSpellCastData.spell_id == 1788 || m_CachedSpellCastData.spell_id == 1789 || m_CachedSpellCastData.spell_id == 5331)
|
|
{
|
|
if (m_CachedSpellCastData.caster_id != m_CachedSpellCastData.source_id)
|
|
{
|
|
CWeenieObject* casterObj = g_pWorld->FindObject(m_CachedSpellCastData.caster_id);
|
|
|
|
if (casterObj && casterObj->IsCloak())
|
|
{
|
|
int wieldDiff = 0;
|
|
if (casterObj->m_Qualities.InqInt(WIELD_DIFFICULTY_INT, wieldDiff))
|
|
{
|
|
m_CachedSpellCastData.current_skill = wieldDiff * 3;
|
|
}
|
|
else
|
|
{
|
|
m_CachedSpellCastData.current_skill = 150;
|
|
SERVER_ERROR << "Cloak found missing WieldDiff";
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// try to resist
|
|
bool bResisted = false;
|
|
|
|
if (m_CachedSpellCastData.spell->_bitfield & Resistable_SpellIndex)
|
|
{
|
|
if (pSource != pHit)
|
|
{
|
|
if (pHit->TryMagicResist(m_CachedSpellCastData.current_skill))
|
|
{
|
|
pHit->EmitSound(Sound_ResistSpell, 1.0f, false);
|
|
|
|
if (pSource)
|
|
{
|
|
pHit->SendText(csprintf("You resist the spell cast by %s", pSource->GetName().c_str()), LTT_MAGIC);
|
|
pSource->SendText(csprintf("%s resists your spell", pHit->GetName().c_str()), LTT_MAGIC);
|
|
pHit->OnResistSpell(pSource);
|
|
if (pSource->AsPlayer())
|
|
pSource->AsPlayer()->HandleAetheriaProc(0);
|
|
}
|
|
bResisted = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bResisted)
|
|
{
|
|
CWeenieObject *wand = g_pWorld->FindObject(m_CachedSpellCastData.wand_id);
|
|
if (isEnchantProjectile)
|
|
{
|
|
ProjectileEnchantmentSpellEx *meta = (ProjectileEnchantmentSpellEx *)m_CachedSpellCastData.spellEx->_meta_spell._spell;
|
|
Enchantment enchant;
|
|
enchant._id = m_CachedSpellCastData.spell_id | ((uint32_t)m_CachedSpellCastData.serial << (uint32_t)16);
|
|
enchant.m_SpellSetID = 0; // ???
|
|
enchant._spell_category = m_CachedSpellCastData.spellEx->_category; // meta->_spellCategory;
|
|
enchant._power_level = m_CachedSpellCastData.spell->_power;
|
|
enchant._start_time = Timer::cur_time;
|
|
int buffDuration = 1;
|
|
enchant._duration = meta->_duration;
|
|
enchant._caster = pSource->GetID();
|
|
enchant._degrade_modifier = meta->_degrade_modifier;
|
|
enchant._degrade_limit = meta->_degrade_limit;
|
|
enchant._last_time_degraded = -1.0;
|
|
enchant._smod = meta->_smod;
|
|
enchant._dtype = meta->_etype;
|
|
enchant._duration += 0.25; // Handle extra time so all ticks happen
|
|
|
|
if (pHit->AsMonster() && !pHit->AsPlayer())
|
|
{
|
|
int currentDotValue = pHit->m_Qualities.GetInt(NETHER_OVER_TIME_INT, 0);
|
|
pHit->m_Qualities.SetInt(NETHER_OVER_TIME_INT, (currentDotValue + enchant._smod.val));
|
|
}
|
|
pHit->HandleAggro(pSource);
|
|
|
|
pHit->m_Qualities.UpdateEnchantment(&enchant);
|
|
pHit->NotifyEnchantmentUpdated(&enchant);
|
|
|
|
if (m_CachedSpellCastData.spell_id < 5754 || m_CachedSpellCastData.spell_id > 5756)
|
|
{
|
|
if (pSource->AsPlayer())
|
|
pSource->AsPlayer()->SendText(csprintf("You cast %s on %s", m_CachedSpellCastData.spell->_name.c_str(), pHit->GetName().c_str()), LTT_MAGIC);
|
|
|
|
if (pHit->AsPlayer())
|
|
pHit->AsPlayer()->SendText(csprintf("%s cast %s on you", pSource->GetName().c_str(), m_CachedSpellCastData.spell->_name.c_str()), LTT_MAGIC);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
int preVarianceDamage;
|
|
double baseDamage;
|
|
if (isLifeProjectile)
|
|
{
|
|
preVarianceDamage = selfDrainedAmount;
|
|
baseDamage = selfDrainedAmount * selfDrainedDamageRatio;
|
|
}
|
|
else
|
|
{
|
|
ProjectileSpellEx *meta = (ProjectileSpellEx *)m_CachedSpellCastData.spellEx->_meta_spell._spell;
|
|
double minDamage = (double)meta->_baseIntensity;
|
|
double maxDamage = (double)minDamage + meta->_variance;
|
|
|
|
preVarianceDamage = maxDamage;
|
|
baseDamage = Random::RollDice(minDamage, maxDamage);
|
|
|
|
if (wand)
|
|
{
|
|
double wandElemDamMod = wand->GetElementalDamage();
|
|
double elementalDamageMod = wand->InqDamageType() == InqDamageType() ? wandElemDamMod : 1.0;
|
|
|
|
if (pSource->AsPlayer() && pHit->AsPlayer()) //pvp
|
|
elementalDamageMod = ((elementalDamageMod - 1.0) / 2.0) + 1.0;
|
|
baseDamage *= elementalDamageMod;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
DamageEventData dmgEvent;
|
|
dmgEvent.source = pSource;
|
|
dmgEvent.target = pHit;
|
|
dmgEvent.weapon = wand;
|
|
dmgEvent.damage_form = DF_MAGIC;
|
|
dmgEvent.damage_type = InqDamageType();
|
|
dmgEvent.hit_quadrant = DAMAGE_QUADRANT::DQ_UNDEF; //should spells have hit quadrants?
|
|
dmgEvent.attackSkill = m_CachedSpellCastData.spell->InqSkillForSpell();
|
|
dmgEvent.attackSkillLevel = m_CachedSpellCastData.current_skill;
|
|
dmgEvent.preVarianceDamage = preVarianceDamage;
|
|
dmgEvent.baseDamage = baseDamage;
|
|
|
|
dmgEvent.isProjectileSpell = true;
|
|
dmgEvent.spell_name = m_CachedSpellCastData.spell->_name;
|
|
|
|
CalculateCriticalHitData(&dmgEvent, &m_CachedSpellCastData);
|
|
dmgEvent.wasCrit = (Random::GenFloat(0.0, 1.0) < dmgEvent.critChance) ? true : false;
|
|
|
|
double angle = pSource->HeadingFrom(pHit, false);
|
|
|
|
CalculateAttackConditions(&dmgEvent, 0.0f, angle);
|
|
|
|
CalculateDamage(&dmgEvent, &m_CachedSpellCastData);
|
|
|
|
pHit->TryToDealDamage(dmgEvent);
|
|
|
|
if (pSource && pSource->AsPlayer())
|
|
{
|
|
// update the target's health on the casting player asap
|
|
((CPlayerWeenie*)pSource)->RefreshTargetHealth();
|
|
}
|
|
}
|
|
if (pSource && pSource->AsPlayer())
|
|
pSource->AsPlayer()->HandleAetheriaProc(pHit->GetID());
|
|
}
|
|
}
|
|
}
|
|
|
|
return CWeenieObject::DoCollision(prof);
|
|
}
|
|
|
|
BOOL CSpellProjectile::DoCollision(const class ObjCollisionProfile &prof)
|
|
{
|
|
Movement_UpdatePos();
|
|
// EmitEffect(5, 0.8f);
|
|
HandleExplode();
|
|
m_fDestroyTime = Timer::cur_time + 1.0;
|
|
|
|
return CWeenieObject::DoCollision(prof);
|
|
}
|
|
|
|
void CSpellProjectile::DoCollisionEnd(uint32_t object_id)
|
|
{
|
|
CWeenieObject::DoCollisionEnd(object_id);
|
|
}
|
|
|
|
void CSpellProjectile::makeLifeProjectile(int iSelfDrainedAmount, float fSelfDrainedDamageRatio)
|
|
{
|
|
isLifeProjectile = true;
|
|
selfDrainedAmount = iSelfDrainedAmount;
|
|
selfDrainedDamageRatio = fSelfDrainedDamageRatio;
|
|
}
|
|
|
|
|
|
|