2019-07-07 20:56:15 +00:00
|
|
|
#include <StdAfx.h>
|
2018-03-29 17:01:57 -04:00
|
|
|
#include "WeenieObject.h"
|
|
|
|
|
#include "Ammunition.h"
|
|
|
|
|
#include "Player.h"
|
|
|
|
|
#include "World.h"
|
|
|
|
|
#include "CombatFormulas.h"
|
|
|
|
|
|
|
|
|
|
CAmmunitionWeenie::CAmmunitionWeenie()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CAmmunitionWeenie::~CAmmunitionWeenie()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CAmmunitionWeenie::ApplyQualityOverrides()
|
|
|
|
|
{
|
|
|
|
|
CWeenieObject::ApplyQualityOverrides();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CAmmunitionWeenie::MakeIntoMissile()
|
|
|
|
|
{
|
|
|
|
|
int stackSize = 0;
|
|
|
|
|
if (m_Qualities.InqInt(STACK_SIZE_INT, stackSize))
|
|
|
|
|
{
|
|
|
|
|
SetStackSize(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_Qualities.SetInstanceID(WIELDER_IID, 0);
|
|
|
|
|
m_Qualities.SetInstanceID(CONTAINER_IID, 0);
|
|
|
|
|
_cachedHasOwner = false;
|
|
|
|
|
|
|
|
|
|
m_Qualities.SetInt(CURRENT_WIELDED_LOCATION_INT, 0);
|
|
|
|
|
m_Qualities.SetInt(PARENT_LOCATION_INT, 0);
|
|
|
|
|
|
|
|
|
|
m_PhysicsState = INELASTIC_PS | GRAVITY_PS | PATHCLIPPED_PS | ALIGNPATH_PS | MISSILE_PS | REPORT_COLLISIONS_PS;
|
|
|
|
|
SetInitialPhysicsState(m_PhysicsState);
|
|
|
|
|
|
|
|
|
|
SetPlacementFrame(Placement::MissileFlight, FALSE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CAmmunitionWeenie::MakeIntoAmmo()
|
|
|
|
|
{
|
|
|
|
|
set_state(INELASTIC_PS | GRAVITY_PS | IGNORE_COLLISIONS_PS | ETHEREAL_PS, m_bWorldIsAware ? TRUE : FALSE);
|
|
|
|
|
SetInitialPhysicsState(m_PhysicsState);
|
|
|
|
|
|
|
|
|
|
SetPlacementFrame(Placement::Resting, TRUE);
|
|
|
|
|
|
2018-04-20 18:59:55 -05:00
|
|
|
_timeToRot = Timer::cur_time + 1.0;
|
2018-03-29 17:01:57 -04:00
|
|
|
_beganRot = false;
|
|
|
|
|
m_Qualities.SetFloat(TIME_TO_ROT_FLOAT, _timeToRot);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CAmmunitionWeenie::PostSpawn()
|
|
|
|
|
{
|
|
|
|
|
CWeenieObject::PostSpawn();
|
|
|
|
|
|
|
|
|
|
if (m_PhysicsState & MISSILE_PS)
|
|
|
|
|
{
|
|
|
|
|
EmitEffect(PS_Launch, 0.0f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CAmmunitionWeenie::HandleNonTargetCollision()
|
|
|
|
|
{
|
|
|
|
|
Movement_UpdatePos();
|
|
|
|
|
MakeIntoAmmo();
|
|
|
|
|
_targetID = 0;
|
|
|
|
|
|
2018-07-12 14:41:21 -07:00
|
|
|
if (CWeenieObject *source = g_pWorld->FindObject(_sourceID))
|
2018-03-29 17:01:57 -04:00
|
|
|
{
|
|
|
|
|
if (source->AsPlayer())
|
|
|
|
|
source->SendText("Your missile attack hit the environment.", LTT_DEFAULT);
|
|
|
|
|
else
|
2018-07-12 14:41:21 -07:00
|
|
|
MarkForDestroy();
|
2018-03-29 17:01:57 -04:00
|
|
|
|
|
|
|
|
EmitSound(Sound_Collision, 1.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// set the collision angle to be the heading of the object?
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CAmmunitionWeenie::HandleTargetCollision()
|
|
|
|
|
{
|
2018-07-12 14:41:21 -07:00
|
|
|
MarkForDestroy();
|
2018-03-29 17:01:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL CAmmunitionWeenie::DoCollision(const class EnvCollisionProfile &prof)
|
|
|
|
|
{
|
|
|
|
|
HandleNonTargetCollision();
|
|
|
|
|
return CWeenieObject::DoCollision(prof);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL CAmmunitionWeenie::DoCollision(const class AtkCollisionProfile &prof)
|
|
|
|
|
{
|
|
|
|
|
bool targetCollision = false;
|
|
|
|
|
|
2018-07-12 14:41:21 -07:00
|
|
|
CWeenieObject *pHit = g_pWorld->FindWithinPVS(this, prof.id);
|
2018-03-29 17:01:57 -04:00
|
|
|
if (pHit && (!_targetID || _targetID == pHit->GetID()) && (pHit->GetID() != _sourceID) && (pHit->GetID() != _launcherID))
|
|
|
|
|
{
|
2018-07-12 14:41:21 -07:00
|
|
|
CWeenieObject *pSource = g_pWorld->FindObject(_sourceID);
|
2018-03-29 17:01:57 -04:00
|
|
|
|
2020-05-17 00:49:05 -07:00
|
|
|
pHit->m_Qualities.SetInstanceID(CURRENT_ATTACKER_IID, _sourceID);
|
|
|
|
|
|
|
|
|
|
uint32_t currentEnemy;
|
|
|
|
|
if (pHit->m_Qualities.InqInstanceID(CURRENT_ENEMY_IID, currentEnemy))
|
|
|
|
|
{
|
|
|
|
|
if(currentEnemy == 0)
|
|
|
|
|
pHit->m_Qualities.SetInstanceID(CURRENT_ENEMY_IID, _sourceID);
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-29 17:01:57 -04:00
|
|
|
if (!pHit->ImmuneToDamage(pSource))
|
|
|
|
|
{
|
|
|
|
|
targetCollision = true;
|
|
|
|
|
|
|
|
|
|
int preVarianceDamage;
|
|
|
|
|
float variance;
|
|
|
|
|
|
2018-07-12 14:41:21 -07:00
|
|
|
CWeenieObject *weapon = g_pWorld->FindObject(_launcherID);
|
2018-07-12 18:49:32 -05:00
|
|
|
|
|
|
|
|
// For thrown weaps, the weapon no longer exists on the last missile in a stack. So for these we will just use the cloned ammo weenie itself.
|
|
|
|
|
if (!weapon && m_Qualities.GetInt(DEFAULT_COMBAT_STYLE_INT, 0) == ThrownWeapon_CombatStyle)
|
|
|
|
|
weapon = g_pWorld->FindObject(id);
|
|
|
|
|
|
2018-03-29 17:01:57 -04:00
|
|
|
if (weapon)
|
|
|
|
|
{
|
|
|
|
|
bool bEvaded = false;
|
|
|
|
|
|
2019-07-07 20:56:15 +00:00
|
|
|
uint32_t missileDefense = 0;
|
2018-03-29 17:01:57 -04:00
|
|
|
if (pHit->InqSkill(MISSILE_DEFENSE_SKILL, missileDefense, FALSE) && missileDefense > 0)
|
|
|
|
|
{
|
2019-09-29 01:52:19 +00:00
|
|
|
if (pHit->TryAttackEvade((uint32_t)(_weaponSkillLevel * (_attackPower + 0.5f)), STypeSkill::MISSILE_DEFENSE_SKILL))
|
2018-03-29 17:01:57 -04:00
|
|
|
{
|
|
|
|
|
pHit->OnEvadeAttack(pSource);
|
|
|
|
|
|
|
|
|
|
// send evasion message
|
|
|
|
|
BinaryWriter attackerEvadeEvent;
|
2019-07-07 20:56:15 +00:00
|
|
|
attackerEvadeEvent.Write<uint32_t>(0x01B3);
|
2018-03-29 17:01:57 -04:00
|
|
|
attackerEvadeEvent.WriteString(pHit->GetName());
|
2018-07-16 16:42:34 -05:00
|
|
|
if (pSource != nullptr)
|
|
|
|
|
pSource->SendNetMessage(&attackerEvadeEvent, PRIVATE_MSG, TRUE, FALSE);
|
|
|
|
|
|
2018-03-29 17:01:57 -04:00
|
|
|
|
|
|
|
|
BinaryWriter attackedEvadeEvent;
|
2019-07-07 20:56:15 +00:00
|
|
|
attackedEvadeEvent.Write<uint32_t>(0x01B4);
|
2018-07-16 16:42:34 -05:00
|
|
|
if (pSource != nullptr)
|
|
|
|
|
attackedEvadeEvent.WriteString(pSource->GetName());
|
|
|
|
|
else
|
|
|
|
|
attackedEvadeEvent.WriteString("Unknown");
|
2018-03-29 17:01:57 -04:00
|
|
|
pHit->SendNetMessage(&attackedEvadeEvent, PRIVATE_MSG, TRUE, FALSE);
|
|
|
|
|
bEvaded = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!bEvaded)
|
|
|
|
|
{
|
|
|
|
|
EmitSound(Sound_Collision, 1.0f);
|
|
|
|
|
|
2018-07-16 16:42:34 -05:00
|
|
|
if (pSource != nullptr && pSource && pHit && pSource->AsPlayer() && pHit->AsPlayer())
|
2018-06-14 18:52:55 -07:00
|
|
|
{
|
|
|
|
|
pSource->AsPlayer()->UpdatePKActivity();
|
|
|
|
|
pHit->AsPlayer()->UpdatePKActivity();
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-19 15:59:08 -06:00
|
|
|
bool isThrownWeapon = (weapon->InqIntQuality(DEFAULT_COMBAT_STYLE_INT, 0) == ThrownWeapon_CombatStyle);
|
|
|
|
|
|
|
|
|
|
preVarianceDamage = isThrownWeapon ? weapon->GetAttackDamage() : GetAttackDamage();
|
2018-03-29 17:01:57 -04:00
|
|
|
variance = InqFloatQuality(DAMAGE_VARIANCE_FLOAT, 0.0f);
|
|
|
|
|
|
2019-09-22 06:57:10 +00:00
|
|
|
//int elementalDamageBonus = weapon->InqDamageType() == InqDamageType() || InqDamageType() == BASE_DAMAGE_TYPE ? weapon->InqIntQuality(ELEMENTAL_DAMAGE_BONUS_INT, 0) : 0;
|
2018-04-01 15:03:58 -04:00
|
|
|
double damageMod = weapon->InqFloatQuality(DAMAGE_MOD_FLOAT, 1.0);
|
|
|
|
|
|
2019-09-22 06:57:10 +00:00
|
|
|
//preVarianceDamage += elementalDamageBonus;
|
2018-04-01 15:03:58 -04:00
|
|
|
preVarianceDamage *= damageMod;
|
|
|
|
|
|
2018-03-29 17:01:57 -04:00
|
|
|
DamageEventData dmgEvent;
|
|
|
|
|
dmgEvent.source = pSource;
|
|
|
|
|
dmgEvent.target = pHit;
|
|
|
|
|
dmgEvent.weapon = weapon;
|
|
|
|
|
dmgEvent.damage_form = DF_MISSILE;
|
2018-06-16 00:14:42 -05:00
|
|
|
|
|
|
|
|
if (InqDamageType() != BASE_DAMAGE_TYPE)
|
|
|
|
|
dmgEvent.damage_type = InqDamageType();
|
|
|
|
|
else if (!weapon->InqDamageType())
|
|
|
|
|
dmgEvent.damage_type = PIERCE_DAMAGE_TYPE;
|
|
|
|
|
else
|
|
|
|
|
dmgEvent.damage_type = weapon->InqDamageType();
|
|
|
|
|
|
2019-04-22 22:20:18 +00:00
|
|
|
dmgEvent.hit_quadrant = (DAMAGE_QUADRANT)prof.location;
|
2018-03-29 17:01:57 -04:00
|
|
|
dmgEvent.attackSkill = _weaponSkill;
|
|
|
|
|
dmgEvent.attackSkillLevel = _weaponSkillLevel;
|
|
|
|
|
dmgEvent.preVarianceDamage = preVarianceDamage;
|
|
|
|
|
dmgEvent.baseDamage = preVarianceDamage * (1.0f - Random::GenFloat(0.0f, variance));
|
|
|
|
|
|
2018-06-17 15:51:05 -05:00
|
|
|
CalculateCriticalHitData(&dmgEvent, NULL);
|
|
|
|
|
dmgEvent.wasCrit = (Random::GenFloat(0.0, 1.0) < dmgEvent.critChance) ? true : false;
|
|
|
|
|
if (dmgEvent.wasCrit)
|
|
|
|
|
{
|
|
|
|
|
dmgEvent.baseDamage = dmgEvent.preVarianceDamage;//Recalculate baseDamage with no variance (uses max dmg on weapon)
|
|
|
|
|
}
|
2019-04-23 01:26:25 +00:00
|
|
|
|
|
|
|
|
CalculateAttackConditions(&dmgEvent, _attackPower, HeadingFrom(pHit, false));
|
2019-04-22 22:19:31 +00:00
|
|
|
|
2018-07-03 15:42:54 -05:00
|
|
|
//cast on strike
|
2018-07-11 04:53:32 +00:00
|
|
|
if (weapon->InqDIDQuality(PROC_SPELL_DID, 0))
|
2018-07-03 15:42:54 -05:00
|
|
|
{
|
2018-07-11 04:53:32 +00:00
|
|
|
double procChance = weapon->InqFloatQuality(PROC_SPELL_RATE_FLOAT, 0.0f);
|
2018-07-03 15:42:54 -05:00
|
|
|
|
|
|
|
|
bool proc = (Random::GenFloat(0.0, 1.0) < procChance) ? true : false;
|
|
|
|
|
|
2018-07-11 04:53:32 +00:00
|
|
|
if (proc && _targetID)
|
2018-07-03 15:42:54 -05:00
|
|
|
{
|
2019-07-07 20:56:15 +00:00
|
|
|
uint32_t targetid = _targetID;
|
|
|
|
|
uint32_t procspell = weapon->InqDIDQuality(PROC_SPELL_DID, 0);
|
2018-07-03 15:42:54 -05:00
|
|
|
|
2018-07-11 04:53:32 +00:00
|
|
|
weapon->TryCastSpell(targetid, procspell);
|
2018-07-03 15:42:54 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-22 22:19:09 -07:00
|
|
|
if (pSource && pSource->AsPlayer())
|
2020-05-16 18:39:12 -07:00
|
|
|
pSource->AsPlayer()->HandleAetheriaProc(pHit->GetID());
|
|
|
|
|
|
2019-07-21 06:58:50 -07:00
|
|
|
uint32_t dirtyFighting = 0;
|
|
|
|
|
if (pSource && pSource->AsPlayer() && pSource->InqSkill(DIRTY_FIGHTING_SKILL, dirtyFighting, FALSE) && dirtyFighting > 0)
|
|
|
|
|
{
|
|
|
|
|
double skillCalc = (double)dirtyFighting / (double)dmgEvent.attackSkillLevel;
|
|
|
|
|
double chanceOfDfEffect = .25 * min(1.0, skillCalc);
|
|
|
|
|
double procRoll = Random::GenFloat(0.0, 1.0);
|
|
|
|
|
bool proc = (procRoll < chanceOfDfEffect) ? true : false;
|
|
|
|
|
SKILL_ADVANCEMENT_CLASS sac = SKILL_ADVANCEMENT_CLASS::UNTRAINED_SKILL_ADVANCEMENT_CLASS;
|
|
|
|
|
bool hasQuality = pSource->m_Qualities.InqSkillAdvancementClass((STypeSkill)DIRTY_FIGHTING_SKILL, sac);
|
|
|
|
|
uint32_t targetid = pHit->GetID();
|
|
|
|
|
//_weenie->AsPlayer()->SendText(csprintf("Attack Skill: %u DF Skill: %u Skill Calc: %f Proc %f", dmgEvent.attackSkillLevel, dirtyFighting, skillCalc, procRoll), LTT_COMBAT);
|
|
|
|
|
if (proc)
|
|
|
|
|
{
|
|
|
|
|
if (dmgEvent.hit_quadrant & DAMAGE_QUADRANT::DQ_HIGH)
|
|
|
|
|
{
|
|
|
|
|
if (sac == SPECIALIZED_SKILL_ADVANCEMENT_CLASS)
|
|
|
|
|
{
|
|
|
|
|
dmgEvent.weapon->TryCastSpell(targetid, 5938); //Blinding Assault
|
|
|
|
|
dmgEvent.weapon->TryCastSpell(targetid, 5941); //Traumatic Assault
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
dmgEvent.weapon->TryCastSpell(targetid, 5942); //Blinding Blow
|
|
|
|
|
dmgEvent.weapon->TryCastSpell(targetid, 5945); //Traumatic Blow
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (dmgEvent.hit_quadrant & DAMAGE_QUADRANT::DQ_MEDIUM)
|
|
|
|
|
{
|
|
|
|
|
if (sac == SPECIALIZED_SKILL_ADVANCEMENT_CLASS)
|
|
|
|
|
{
|
|
|
|
|
dmgEvent.weapon->TryCastSpell(targetid, 5939); //Bleeding Assault
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
dmgEvent.weapon->TryCastSpell(targetid, 5943); //Bleeding Blow
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (dmgEvent.hit_quadrant & DAMAGE_QUADRANT::DQ_LOW)
|
|
|
|
|
{
|
|
|
|
|
if (sac == SPECIALIZED_SKILL_ADVANCEMENT_CLASS)
|
|
|
|
|
{
|
|
|
|
|
dmgEvent.weapon->TryCastSpell(targetid, 5940); //Unbalancing Assault
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
dmgEvent.weapon->TryCastSpell(targetid, 5944); //Unbalancing Blow
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SERVER_ERROR << "Unknown error on Dirty Fighting";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-07-03 16:44:24 -05:00
|
|
|
CalculateDamage(&dmgEvent);
|
2018-07-16 16:42:34 -05:00
|
|
|
if (pSource != nullptr)
|
|
|
|
|
pSource->TryToDealDamage(dmgEvent);
|
2018-03-29 17:01:57 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (targetCollision)
|
|
|
|
|
HandleTargetCollision();
|
|
|
|
|
else
|
|
|
|
|
HandleNonTargetCollision();
|
|
|
|
|
|
|
|
|
|
return CWeenieObject::DoCollision(prof);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL CAmmunitionWeenie::DoCollision(const class ObjCollisionProfile &prof)
|
|
|
|
|
{
|
|
|
|
|
HandleNonTargetCollision();
|
|
|
|
|
return CWeenieObject::DoCollision(prof);
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-07 20:56:15 +00:00
|
|
|
void CAmmunitionWeenie::DoCollisionEnd(uint32_t object_id)
|
2018-03-29 17:01:57 -04:00
|
|
|
{
|
|
|
|
|
CWeenieObject::DoCollisionEnd(object_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|