mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-12 13:41:31 +00:00
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:
parent
792a3b1443
commit
5d92d484a1
@ -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;
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user