Projectiles Update (#1468)

Fixed spell projectiles whose angle was not being calculated correctly.

Significantly improved all projectile timings. At least up to range of 300 should see more accurate timings for damage/effects occurring upon projectile impact. Will be noticed most significantly for all spell projectiles and for longer range archery.
This commit is contained in:
KayenEQ 2021-07-26 22:20:13 -04:00 committed by GitHub
parent 792a3b1443
commit 5d92d484a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 89 additions and 31 deletions

View File

@ -929,11 +929,31 @@ bool Mob::TryProjectileAttack(Mob *other, const EQ::ItemData *item, EQ::skills::
if (slot < 0)
return false;
float speed_mod = speed;
float distance_mod = 0.0f;
float distance = other->CalculateDistance(GetX(), GetY(), GetZ());
float hit =
1200.0f + (10 * distance / speed_mod); // Calcuation: 60 = Animation Lag, 1.8 = Speed modifier for speed of (4)
/*
New Distance Mod constant (7/25/21 update), modifier is needed to adjust slower speeds to have correct impact times at short distances.
We use archery 4.0 speed as a baseline for the forumla. At speed 1.5 at 50 pct distance mod is needed, where as speed 4.0 there is no modifer.
Therefore, we derive out our modifer as follows. distance_mod = (speed - 4) * ((50 - 0)/(1.5-4)). The ratio there is -20.0f. distance_mod = (speed - 4) * -20.0f
For distances >125 we use different modifier, this was all meticulously tested by eye to get the best possible outcome for projectile impact times. Not perfect though.
*/
if (distance <= 125.0f) {
if (speed != 4.0f) { //Standard functions will always be 4.0f for archery.
distance_mod = (speed - 4.0f) * -20.0f;
distance += distance * distance_mod / 100.0f;
}
}
else if (distance > 125.0f && distance <= 200.0f)
distance = 3.14f * (distance / 2.0f); //Get distance of arc to better reflect projectile path length
else if (distance > 200.0f) {
distance = distance * 1.30f; //Add 30% to base distance if over 200 range to tighten up hit timing.
distance = 3.14f * (distance / 2.0f); //Get distance of arc to better reflect projectile path length
}
float hit = 1200.0f + (10 * distance / speed);
ProjectileAtk[slot].increment = 1;
ProjectileAtk[slot].hit_increment = static_cast<uint16>(hit); // This projected hit time if target does NOT MOVE
@ -951,14 +971,12 @@ bool Mob::TryProjectileAttack(Mob *other, const EQ::ItemData *item, EQ::skills::
ProjectileAtk[slot].ammo_slot = 0;
ProjectileAtk[slot].skill = skillInUse;
ProjectileAtk[slot].speed_mod = speed_mod;
ProjectileAtk[slot].speed_mod = speed;
SetProjectileAttack(true);
if (item)
SendItemAnimation(other, item, skillInUse, speed);
// else if (IsNPC())
// ProjectileAnimation(other, 0,false,speed,0,0,0,CastToNPC()->GetAmmoIDfile(),skillInUse);
return true;
}
@ -978,23 +996,40 @@ void Mob::ProjectileAttack()
disable = false;
Mob *target = entity_list.GetMobID(ProjectileAtk[i].target_id);
if (target && target->IsMoving()) { // Only recalculate hit increment if target moving
// Due to frequency that we need to check increment the targets position variables may not be
// updated even if moving. Do a simple check before calculating distance.
if (target && target->IsMoving()) {
/*
Only recalculate hit increment if target is moving.
Due to frequency that we need to check increment the targets position variables may not be
updated even if moving. Do a simple check before calculating distance.
*/
if (ProjectileAtk[i].tlast_x != target->GetX() || ProjectileAtk[i].tlast_y != target->GetY()) {
ProjectileAtk[i].tlast_x = target->GetX();
ProjectileAtk[i].tlast_y = target->GetY();
float distance = target->CalculateDistance(
ProjectileAtk[i].origin_x, ProjectileAtk[i].origin_y, ProjectileAtk[i].origin_z);
float hit = 1200.0f + (10 * distance / ProjectileAtk[i].speed_mod); // Calcuation: 60 =
// Animation Lag, 1.8 =
// Speed modifier for speed
// of (4)
//Recalculate from the original location the projectile was fired in relation to the current targets location.
float distance = target->CalculateDistance(ProjectileAtk[i].origin_x, ProjectileAtk[i].origin_y, ProjectileAtk[i].origin_z);
float distance_mod = 0.0f;
if (distance <= 125.0f) {
distance_mod = (ProjectileAtk[i].speed_mod - 4.0f) * -20.0f;
distance += distance * distance_mod / 100.0f;
}
else if (distance > 125.0f && distance <= 200.0f)
distance = 3.14f * (distance / 2.0f); //Get distance of arc to better reflect projectile path length
else if (distance > 200.0f) {
distance = distance * 1.30f; //Add 30% to base distance if over 200 range to tighten up hit timing.
distance = 3.14f * (distance / 2.0f); //Get distance of arc to better reflect projectile path length
}
float hit = 1200.0f + (10 * distance / ProjectileAtk[i].speed_mod);
ProjectileAtk[i].hit_increment = static_cast<uint16>(hit);
}
}
// We hit I guess?
// Check if we hit.
if (ProjectileAtk[i].hit_increment <= ProjectileAtk[i].increment) {
if (target) {
if (IsNPC()) {
@ -1496,7 +1531,7 @@ void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, f
speed = 4.0;
}
if(!angle) {
angle = CalculateHeadingToTarget(to->GetX(), to->GetY()) * 2;
angle = CalculateHeadingToTarget(to->GetX(), to->GetY());
}
if(!tilt) {
tilt = 125;

View File

@ -6995,7 +6995,7 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama
return false;
}
bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed){
bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed) {
/*For mage 'Bolt' line and other various spells.
-This is mostly accurate for how the modern clients handle this effect.
@ -7020,7 +7020,7 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed){
//Make sure there is an avialable bolt to be cast.
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) {
if (ProjectileAtk[i].target_id == 0){
if (ProjectileAtk[i].target_id == 0) {
slot = i;
break;
}
@ -7029,11 +7029,35 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed){
if (slot < 0)
return false;
float arc = 0.0f;
float distance_mod = 0.0f;
if (CheckLosFN(spell_target)) {
float speed_mod = speed; //Constant for adjusting speeds to match calculated impact time.
float distance = spell_target->CalculateDistance(GetX(), GetY(), GetZ());
float hit = 1200.0f + (10 * distance / speed_mod);
/*
New Distance Mod constant (7/25/21 update), modifier is needed to adjust slower speeds to have correct impact times at short distances.
We use archery 4.0 speed as a baseline for the forumla. At speed 1.5 at 50 pct distance mod is needed, where as speed 4.0 there is no modifer.
Therefore, we derive out our modifer as follows. distance_mod = (speed - 4) * ((50 - 0)/(1.5-4)). The ratio there is -20.0f. distance_mod = (speed - 4) * -20.0f
For distances >125 we use different modifier, this was all meticulously tested by eye to get the best possible outcome for projectile impact times. Not perfect though.
*/
if (distance <= 125.0f) {
distance_mod = (speed - 4.0f) * -20.0f;
distance += distance * distance_mod / 100.0f;
}
else if (distance > 125.0f && distance <= 200.0f)
distance = 3.14f * (distance / 2.0f); //Get distance of arc to better reflect projectile path length
else if (distance > 200.0f) {
arc = 50.0f - ((distance - 200.0f) * 0.266f); //Arc angle gets drastically larger if >200 distance, lets lower it down gradually for better effect.
arc = std::max(arc, 20.0f); //No lower than 20 arc
distance = distance * 1.30f; //Add 30% to base distance if over 200 range to tighten up hit timing.
distance = 3.14f * (distance / 2.0f); //Get distance of arc to better reflect projectile path length
}
float hit = 1200.0f + (10 * distance / speed);
ProjectileAtk[slot].increment = 1;
ProjectileAtk[slot].hit_increment = static_cast<uint16>(hit); //This projected hit time if target does NOT MOVE
@ -7043,34 +7067,33 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed){
ProjectileAtk[slot].origin_y = GetY();
ProjectileAtk[slot].origin_z = GetZ();
ProjectileAtk[slot].skill = EQ::skills::SkillConjuration;
ProjectileAtk[slot].speed_mod = speed_mod;
ProjectileAtk[slot].speed_mod = speed;
SetProjectileAttack(true);
}
//This will use the correct graphic as defined in the player_1 field of spells_new table. Found in UF+ spell files.
if (RuleB(Spells, UseLiveSpellProjectileGFX)) {
ProjectileAnimation(spell_target,0, false, speed,0,0,0, spells[spell_id].player_1);
ProjectileAnimation(spell_target, 0, false, speed, 0.0f, 0.0f, arc, spells[spell_id].player_1);
}
//This allows limited support for server using older spell files that do not contain data for bolt graphics.
else {
//Only use fire graphic for fire spells.
if (spells[spell_id].resisttype == RESIST_FIRE) {
if (IsClient()){
if (IsClient()) {
if (CastToClient()->ClientVersionBit() <= 4) //Titanium needs alternate graphic.
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_Titanium)), false, speed);
ProjectileAnimation(spell_target, (RuleI(Spells, FRProjectileItem_Titanium)), false, speed, 0.0f, 0.0f, arc);
else
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_SOF)), false, speed);
ProjectileAnimation(spell_target, (RuleI(Spells, FRProjectileItem_SOF)), false, speed, 0.0f, 0.0f, arc);
}
else
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_NPC)), false, speed);
ProjectileAnimation(spell_target, (RuleI(Spells, FRProjectileItem_NPC)), false, speed, 0.0f, 0.0f, arc);
}
//Default to an arrow if not using a mage bolt (Use up to date spell file and enable above rules for best results)
else
ProjectileAnimation(spell_target,0, 1, speed);
ProjectileAnimation(spell_target, 0, 1, speed, 0.0f, 0.0f, arc);
}
if (spells[spell_id].CastingAnim == 64)