From 191aa575f853a2db6afd8160745e499324939726 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 27 Nov 2014 22:12:13 -0500 Subject: [PATCH 1/9] Projectiles (ie Arrows) fired from an archery attacks will do damage upon actually hitting the target, instead of instantly when fired. Consistent with live. Optional SQL added to disable this. Throwing will be implemented in a future update. --- changelog.txt | 5 + common/ruletypes.h | 1 + .../2014_11_27_ProjectileDmgOnImpact.sql | 1 + zone/client_process.cpp | 2 + zone/common.h | 14 + zone/mob.cpp | 15 + zone/mob.h | 9 +- zone/special_attacks.cpp | 402 ++++++++++++------ 8 files changed, 323 insertions(+), 126 deletions(-) create mode 100644 utils/sql/git/optional/2014_11_27_ProjectileDmgOnImpact.sql diff --git a/changelog.txt b/changelog.txt index b05fc26ee..c05951538 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,10 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- + +== 11/27/2014 == +Kayen: Projectiles (ie Arrows) fired from archery will now do damage upon impact instead of instantly (consistent w/ live). +Optional SQL: utils/sql/git/optional/2014_11_27_ProjectileDmgOnImpact.sql + == 11/24/2014 == Trevius: Spells that modify model size are now limited to 2 size adjustments from the base size. Trevius: Fix to prevent Mercenaries from being set as Group Leader. diff --git a/common/ruletypes.h b/common/ruletypes.h index b3e9b4f1c..8ff077809 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -419,6 +419,7 @@ RULE_INT ( Combat, ArcheryBonusChance, 50) RULE_INT ( Combat, BerserkerFrenzyStart, 35) RULE_INT ( Combat, BerserkerFrenzyEnd, 45) RULE_BOOL ( Combat, OneProcPerWeapon, true) //If enabled, One proc per weapon per round +RULE_BOOL ( Combat, ProjectileDmgOnImpact, true) //If enabled, projectiles (ie arrows) will hit on impact, instead of instantly. RULE_CATEGORY_END() RULE_CATEGORY( NPC ) diff --git a/utils/sql/git/optional/2014_11_27_ProjectileDmgOnImpact.sql b/utils/sql/git/optional/2014_11_27_ProjectileDmgOnImpact.sql new file mode 100644 index 000000000..ce7e16905 --- /dev/null +++ b/utils/sql/git/optional/2014_11_27_ProjectileDmgOnImpact.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:ProjectileDmgOnImpact', 'true', 'If enabled, projectiles (ie arrows) will hit on impact, instead of instantly.'); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index c07a73aea..40b7420d3 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -579,6 +579,8 @@ bool Client::Process() { viral_timer_counter = 0; } + ProjectileAttack(); + if(projectile_timer.Check()) SpellProjectileEffect(); diff --git a/zone/common.h b/zone/common.h index ea299ab11..d1aad5e40 100644 --- a/zone/common.h +++ b/zone/common.h @@ -459,6 +459,20 @@ struct Shielders_Struct { uint16 shielder_bonus; }; +typedef struct +{ + uint16 increment; + uint16 hit_increment; + uint16 target_id; + int32 wpn_dmg; + float origin_x; + float origin_y; + float origin_z; + uint32 ranged_id; + uint32 ammo_id; + uint8 skill; +} tProjatk; + //eventually turn this into a typedef and //make DoAnim take it instead of int, to enforce its use. enum { //type arguments to DoAnim diff --git a/zone/mob.cpp b/zone/mob.cpp index 35a019252..3b74d2417 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -287,6 +287,21 @@ Mob::Mob(const char* in_name, for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_z[i] = 0; } projectile_timer.Disable(); + ActiveProjectileATK = false; + for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) + { + ProjectileAtk[i].increment = 0; + ProjectileAtk[i].hit_increment = 0; + ProjectileAtk[i].target_id = 0; + ProjectileAtk[i].wpn_dmg = 0; + ProjectileAtk[i].origin_x = 0.0f; + ProjectileAtk[i].origin_y = 0.0f; + ProjectileAtk[i].origin_z = 0.0f; + ProjectileAtk[i].ranged_id = 0; + ProjectileAtk[i].ammo_id = 0; + ProjectileAtk[i].skill = 0; + } + memset(&itembonuses, 0, sizeof(StatBonuses)); memset(&spellbonuses, 0, sizeof(StatBonuses)); memset(&aabonuses, 0, sizeof(StatBonuses)); diff --git a/zone/mob.h b/zone/mob.h index 525cbb2e8..df694aae9 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -724,7 +724,11 @@ public: virtual void DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, int32 min_damage = 1, int32 hate_override = -1, int ReuseTime = 10, bool HitChance=false, bool CanAvoid=true); virtual void DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const Item_Struct* item=nullptr, uint16 weapon_damage=0, int16 chance_mod=0,int16 focus=0, int ReuseTime=0); virtual void DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes skillinuse, int16 chance_mod=0, int16 focus=0, bool CanRiposte=false, int ReuseTime=0); - virtual void DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const ItemInst* Ammo=nullptr, uint16 weapon_damage=0, int16 chance_mod=0, int16 focus=0, int ReuseTime=0); + virtual void DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const ItemInst* Ammo=nullptr, uint16 weapon_damage=0, int16 chance_mod=0, int16 focus=0, int ReuseTime=0, uint32 range_id=0, uint32 ammo_id=0, const Item_Struct *AmmoItem=nullptr); + bool TryProjectileAttack(Mob* other, const Item_Struct *item, SkillUseTypes skillInUse, uint16 weapon_dmg, const ItemInst* RangeWeapon, const ItemInst* Ammo); + void ProjectileAttack(); + inline bool HasProjectileAttack() const { return ActiveProjectileATK; } + inline void SetProjectileAttack(bool value) { ActiveProjectileATK = value; } bool CanDoSpecialAttack(Mob *other); bool Flurry(ExtraAttackOptions *opts); bool Rampage(ExtraAttackOptions *opts); @@ -1096,6 +1100,9 @@ protected: uint8 projectile_increment[MAX_SPELL_PROJECTILE]; float projectile_x[MAX_SPELL_PROJECTILE], projectile_y[MAX_SPELL_PROJECTILE], projectile_z[MAX_SPELL_PROJECTILE]; + bool ActiveProjectileATK; + tProjatk ProjectileAtk[MAX_SPELL_PROJECTILE]; + float rewind_x; float rewind_y; float rewind_z; diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 3625c9180..879515d20 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -789,8 +789,8 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) { return; } - SendItemAnimation(GetTarget(), AmmoItem, SkillArchery); - DoArcheryAttackDmg(GetTarget(), RangeWeapon, Ammo); + //Shoots projectile and/or applies the archery damage + DoArcheryAttackDmg(GetTarget(), RangeWeapon, Ammo,0,0,0,0,0,0, AmmoItem); //EndlessQuiver AA base1 = 100% Chance to avoid consumption arrow. int ChanceAvoidConsume = aabonuses.ConsumeProjectile + itembonuses.ConsumeProjectile + spellbonuses.ConsumeProjectile; @@ -806,147 +806,206 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) { CommonBreakInvisible(); } -void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const ItemInst* Ammo, uint16 weapon_damage, int16 chance_mod, int16 focus, int ReuseTime) { - if (!CanDoSpecialAttack(other)) +void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const ItemInst* Ammo, uint16 weapon_damage, int16 chance_mod, int16 focus, int ReuseTime, + uint32 range_id, uint32 ammo_id, const Item_Struct *AmmoItem) { + + if ((other == nullptr || + ((IsClient() && CastToClient()->dead) || + (other->IsClient() && other->CastToClient()->dead)) || + HasDied() || + (!IsAttackAllowed(other)) || + (other->GetInvul() || + other->GetSpecialAbility(IMMUNE_MELEE)))) + { return; + } - if (!other->CheckHitChance(this, SkillArchery, MainPrimary, chance_mod)) { + const ItemInst* _RangeWeapon = nullptr; + const ItemInst* _Ammo = nullptr; + const Item_Struct* ammo_lost = nullptr; + + /* + If LaunchProjectile is false this function will do archery damage on target, + otherwise it will shoot the projectile at the target, once the projectile hits target + this function is then run again to do the damage portion + */ + bool LaunchProjectile = false; + bool ProjectileMiss = false; + + if (RuleB(Combat, ProjectileDmgOnImpact)){ + + if (AmmoItem) + LaunchProjectile = true; + else{ + /* + Item sync check on projectile landing. + Weapon damage is already calculated so this only affects procs! + Ammo proc check will use database to find proc if you used up your last ammo. + If you change range item mid projectile flight, you loose your chance to proc from bow (Deal with it!). + */ + + if (!RangeWeapon && !Ammo && range_id && ammo_id){ + + if (weapon_damage == 0) + ProjectileMiss = true; //This indicates that MISS was originally calculated. + + if (IsClient()){ + + _RangeWeapon = CastToClient()->m_inv[MainRange]; + if (!_RangeWeapon || _RangeWeapon->GetItem()->ID != range_id) + RangeWeapon = nullptr; + else + RangeWeapon = _RangeWeapon; + + _Ammo = CastToClient()->m_inv[MainAmmo]; + if (!_Ammo || _Ammo->GetItem()->ID != ammo_id) + ammo_lost = database.GetItem(ammo_id); + else + Ammo = _Ammo; + } + } + } + } + else if (AmmoItem) + SendItemAnimation(other, AmmoItem, SkillArchery); + + if (ProjectileMiss || !other->CheckHitChance(this, SkillArchery, MainPrimary, chance_mod)) { mlog(COMBAT__RANGED, "Ranged attack missed %s.", other->GetName()); - other->Damage(this, 0, SPELL_UNKNOWN, SkillArchery); + + if (LaunchProjectile){ + TryProjectileAttack(other, AmmoItem, SkillArchery, 0, RangeWeapon, Ammo); + return; + } + else + other->Damage(this, 0, SPELL_UNKNOWN, SkillArchery); } else { mlog(COMBAT__RANGED, "Ranged attack hit %s.", other->GetName()); + bool HeadShot = false; + uint32 HeadShot_Dmg = TryHeadShot(other, SkillArchery); + if (HeadShot_Dmg) + HeadShot = true; - bool HeadShot = false; - uint32 HeadShot_Dmg = TryHeadShot(other, SkillArchery); - if (HeadShot_Dmg) - HeadShot = true; + int32 hate = 0; + int32 TotalDmg = 0; + int16 WDmg = 0; + int16 ADmg = 0; + if (!weapon_damage){ + WDmg = GetWeaponDamage(other, RangeWeapon); + ADmg = GetWeaponDamage(other, Ammo); + } + else + WDmg = weapon_damage; - int32 TotalDmg = 0; - int16 WDmg = 0; - int16 ADmg = 0; - if (!weapon_damage){ - WDmg = GetWeaponDamage(other, RangeWeapon); - ADmg = GetWeaponDamage(other, Ammo); - } - else - WDmg = weapon_damage; + if (LaunchProjectile){//1: Shoot the Projectile once we calculate weapon damage. + TryProjectileAttack(other, AmmoItem, SkillArchery, WDmg, RangeWeapon, Ammo); + return; + } - if (focus) //From FcBaseEffects - WDmg += WDmg*focus/100; + if (focus) //From FcBaseEffects + WDmg += WDmg*focus/100; - if((WDmg > 0) || (ADmg > 0)) { - if(WDmg < 0) - WDmg = 0; - if(ADmg < 0) - ADmg = 0; - uint32 MaxDmg = (RuleR(Combat, ArcheryBaseDamageBonus)*(WDmg+ADmg)*GetDamageTable(SkillArchery)) / 100; - int32 hate = ((WDmg+ADmg)); - - if (HeadShot) - MaxDmg = HeadShot_Dmg; - - uint16 bonusArcheryDamageModifier = aabonuses.ArcheryDamageModifier + itembonuses.ArcheryDamageModifier + spellbonuses.ArcheryDamageModifier; - - MaxDmg += MaxDmg*bonusArcheryDamageModifier / 100; - - mlog(COMBAT__RANGED, "Bow DMG %d, Arrow DMG %d, Max Damage %d.", WDmg, ADmg, MaxDmg); - - bool dobonus = false; - if(GetClass() == RANGER && GetLevel() > 50) - { - int bonuschance = RuleI(Combat, ArcheryBonusChance); - - bonuschance = mod_archery_bonus_chance(bonuschance, RangeWeapon); - - if( !RuleB(Combat, UseArcheryBonusRoll) || (MakeRandomInt(1, 100) < bonuschance) ) - { - if(RuleB(Combat, ArcheryBonusRequiresStationary)) - { - if(other->IsNPC() && !other->IsMoving() && !other->IsRooted()) - { - dobonus = true; - } - } - else - { - dobonus = true; - } - } - - if(dobonus) - { - MaxDmg *= 2; - hate *= 2; - MaxDmg = mod_archery_bonus_damage(MaxDmg, RangeWeapon); - - mlog(COMBAT__RANGED, "Ranger. Double damage success roll, doubling damage to %d", MaxDmg); - Message_StringID(MT_CritMelee, BOW_DOUBLE_DAMAGE); - } - } - - if (MaxDmg == 0) - MaxDmg = 1; - - if(RuleB(Combat, UseIntervalAC)) - TotalDmg = MaxDmg; - else - TotalDmg = MakeRandomInt(1, MaxDmg); - - int minDmg = 1; - if(GetLevel() > 25){ - //twice, for ammo and weapon - TotalDmg += (2*((GetLevel()-25)/3)); - minDmg += (2*((GetLevel()-25)/3)); - minDmg += minDmg * GetMeleeMinDamageMod_SE(SkillArchery) / 100; - hate += (2*((GetLevel()-25)/3)); - } - - if (!HeadShot) - other->AvoidDamage(this, TotalDmg, false); - - other->MeleeMitigation(this, TotalDmg, minDmg); - if(TotalDmg > 0) - { - ApplyMeleeDamageBonus(SkillArchery, TotalDmg); - TotalDmg += other->GetFcDamageAmtIncoming(this, 0, true, SkillArchery); - TotalDmg += (itembonuses.HeroicDEX / 10) + (TotalDmg * other->GetSkillDmgTaken(SkillArchery) / 100) + GetSkillDmgAmt(SkillArchery); - - TotalDmg = mod_archery_damage(TotalDmg, dobonus, RangeWeapon); - - TryCriticalHit(other, SkillArchery, TotalDmg); - other->AddToHateList(this, hate, 0, false); - CheckNumHitsRemaining(NUMHIT_OutgoingHitSuccess); - } - } - else - TotalDmg = -5; + if((WDmg > 0) || (ADmg > 0)) { + if(WDmg < 0) + WDmg = 0; + if(ADmg < 0) + ADmg = 0; + uint32 MaxDmg = (RuleR(Combat, ArcheryBaseDamageBonus)*(WDmg+ADmg)*GetDamageTable(SkillArchery)) / 100; + hate = ((WDmg+ADmg)); if (HeadShot) - entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, FATAL_BOW_SHOT, GetName()); - - other->Damage(this, TotalDmg, SPELL_UNKNOWN, SkillArchery); - - if (TotalDmg > 0 && HasSkillProcSuccess() && GetTarget() && other && !other->HasDied()){ - if (ReuseTime) - TrySkillProc(other, SkillArchery, ReuseTime); - else - TrySkillProc(other, SkillArchery, 0, true, MainRange); + MaxDmg = HeadShot_Dmg; + + uint16 bonusArcheryDamageModifier = aabonuses.ArcheryDamageModifier + itembonuses.ArcheryDamageModifier + spellbonuses.ArcheryDamageModifier; + + MaxDmg += MaxDmg*bonusArcheryDamageModifier / 100; + + mlog(COMBAT__RANGED, "Bow DMG %d, Arrow DMG %d, Max Damage %d.", WDmg, ADmg, MaxDmg); + + bool dobonus = false; + if(GetClass() == RANGER && GetLevel() > 50){ + + int bonuschance = RuleI(Combat, ArcheryBonusChance); + bonuschance = mod_archery_bonus_chance(bonuschance, RangeWeapon); + + if( !RuleB(Combat, UseArcheryBonusRoll) || (MakeRandomInt(1, 100) < bonuschance)){ + if(RuleB(Combat, ArcheryBonusRequiresStationary)){ + if(other->IsNPC() && !other->IsMoving() && !other->IsRooted()) + dobonus = true; + } + else + dobonus = true; + } + + if(dobonus){ + MaxDmg *= 2; + hate *= 2; + MaxDmg = mod_archery_bonus_damage(MaxDmg, RangeWeapon); + + mlog(COMBAT__RANGED, "Ranger. Double damage success roll, doubling damage to %d", MaxDmg); + Message_StringID(MT_CritMelee, BOW_DOUBLE_DAMAGE); + } } + + if (MaxDmg == 0) + MaxDmg = 1; + + if(RuleB(Combat, UseIntervalAC)) + TotalDmg = MaxDmg; + else + TotalDmg = MakeRandomInt(1, MaxDmg); + + int minDmg = 1; + if(GetLevel() > 25){ + //twice, for ammo and weapon + TotalDmg += (2*((GetLevel()-25)/3)); + minDmg += (2*((GetLevel()-25)/3)); + minDmg += minDmg * GetMeleeMinDamageMod_SE(SkillArchery) / 100; + hate += (2*((GetLevel()-25)/3)); + } + + if (!HeadShot) + other->AvoidDamage(this, TotalDmg, false); + + other->MeleeMitigation(this, TotalDmg, minDmg); + if(TotalDmg > 0){ + CommonOutgoingHitSuccess(other, TotalDmg, SkillArchery); + TotalDmg = mod_archery_damage(TotalDmg, dobonus, RangeWeapon); + } + } + else + TotalDmg = -5; + + if (HeadShot) + entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, FATAL_BOW_SHOT, GetName()); + + other->AddToHateList(this, hate, 0, false); + other->Damage(this, TotalDmg, SPELL_UNKNOWN, SkillArchery); + + //Skill Proc Success + if (TotalDmg > 0 && HasSkillProcSuccess() && other && !other->HasDied()){ + if (ReuseTime) + TrySkillProc(other, SkillArchery, ReuseTime); + else + TrySkillProc(other, SkillArchery, 0, true, MainRange); + } } - //try proc on hits and misses - if((RangeWeapon != nullptr) && GetTarget() && other && !other->HasDied()){ + if (LaunchProjectile) + return;//Shouldn't reach this point, but just in case. + + //Weapon Proc + if(!RangeWeapon && other && !other->HasDied()) TryWeaponProc(RangeWeapon, other, MainRange); - } - //Arrow procs because why not? - if((Ammo != NULL) && GetTarget() && other && !other->HasDied()) - { - TryWeaponProc(Ammo, other, MainRange); - } + //Ammo Proc + if (ammo_lost) + TryWeaponProc(nullptr, ammo_lost, other, MainRange); + else if(Ammo && other && !other->HasDied()) + TryWeaponProc(Ammo, other, MainRange); - if (HasSkillProcs() && GetTarget() && other && !other->HasDied()){ + //Skill Proc + if (HasSkillProcs() && other && !other->HasDied()){ if (ReuseTime) TrySkillProc(other, SkillArchery, ReuseTime); else @@ -954,6 +1013,99 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item } } +bool Mob::TryProjectileAttack(Mob* other, const Item_Struct *item, SkillUseTypes skillInUse, uint16 weapon_dmg, const ItemInst* RangeWeapon, const ItemInst* Ammo){ + + if (!other) + return false; + + int slot = -1; + + //Make sure there is an avialable slot. + for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { + if (ProjectileAtk[i].target_id == 0){ + slot = i; + break; + } + } + + if (slot < 0) + return false; + + float distance = other->CalculateDistance(GetX(), GetY(), GetZ()); + float hit = 60.0f + (distance / 1.8f); //Calcuation: 60 = Animation Lag, 1.8 = Speed modifier for speed of (4) + + ProjectileAtk[slot].increment = 1; + ProjectileAtk[slot].hit_increment = hit; //This projected hit time if target does NOT MOVE + ProjectileAtk[slot].target_id = other->GetID(); + ProjectileAtk[slot].wpn_dmg = weapon_dmg; + ProjectileAtk[slot].origin_x = GetX(); + ProjectileAtk[slot].origin_y = GetY(); + ProjectileAtk[slot].origin_z = GetZ(); + ProjectileAtk[slot].ranged_id = RangeWeapon->GetItem()->ID; + ProjectileAtk[slot].ammo_id = Ammo->GetItem()->ID; + ProjectileAtk[slot].skill = skillInUse; + + SetProjectileAttack(true); + + if(item) + SendItemAnimation(other, item, skillInUse); + else if (IsNPC()) + ProjectileAnimation(other, 0,false,0,0,0,0,CastToNPC()->GetAmmoIDfile(),skillInUse); + + return true; +} + + +void Mob::ProjectileAttack() +{ + if (!HasProjectileAttack()) + return;; + + Mob* target = nullptr; + bool disable = true; + + for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { + + if (ProjectileAtk[i].increment == 0){ + continue; + } + + disable = false; + Mob* target = entity_list.GetMobID(ProjectileAtk[i].target_id); + + float distance = 0.0f; + + if (target && IsMoving()){ //Only recalculate hit increment if target moving + distance = target->CalculateDistance(ProjectileAtk[i].origin_x, ProjectileAtk[i].origin_y, ProjectileAtk[i].origin_z); + float hit = 60.0f + (distance / 1.8f); //Calcuation: 60 = Animation Lag, 1.8 = Speed modifier for speed of (4) + ProjectileAtk[i].hit_increment = static_cast(hit); + } + + if (ProjectileAtk[i].hit_increment <= ProjectileAtk[i].increment){ + + if (ProjectileAtk[i].skill == SkillArchery) + DoArcheryAttackDmg(target, nullptr, nullptr,ProjectileAtk[i].wpn_dmg,0,0,0,ProjectileAtk[i].ranged_id, ProjectileAtk[i].ammo_id); + + ProjectileAtk[i].increment = 0; + ProjectileAtk[i].target_id = 0; + ProjectileAtk[i].wpn_dmg = 0; + ProjectileAtk[i].origin_x = 0.0f; + ProjectileAtk[i].origin_y = 0.0f; + ProjectileAtk[i].origin_z = 0.0f; + ProjectileAtk[i].ranged_id = 0; + ProjectileAtk[i].ammo_id = 0; + ProjectileAtk[i].skill = 0; + } + + else { + ProjectileAtk[i].increment++; + } + } + + if (disable) + SetProjectileAttack(false); +} + void NPC::RangedAttack(Mob* other) { From 6b45b2bc527d9079a3dd8bd8a8b2a152c5fa2a49 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 27 Nov 2014 23:14:49 -0500 Subject: [PATCH 2/9] Fix for better ammo slot sync check. --- zone/common.h | 1 + zone/mob.cpp | 1 + zone/mob.h | 4 ++-- zone/special_attacks.cpp | 15 ++++++++------- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/zone/common.h b/zone/common.h index d1aad5e40..c4f643525 100644 --- a/zone/common.h +++ b/zone/common.h @@ -470,6 +470,7 @@ typedef struct float origin_z; uint32 ranged_id; uint32 ammo_id; + int ammo_slot; uint8 skill; } tProjatk; diff --git a/zone/mob.cpp b/zone/mob.cpp index 3b74d2417..398bffd3d 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -299,6 +299,7 @@ Mob::Mob(const char* in_name, ProjectileAtk[i].origin_z = 0.0f; ProjectileAtk[i].ranged_id = 0; ProjectileAtk[i].ammo_id = 0; + ProjectileAtk[i].ammo_slot = 0; ProjectileAtk[i].skill = 0; } diff --git a/zone/mob.h b/zone/mob.h index df694aae9..63e687854 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -724,8 +724,8 @@ public: virtual void DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, int32 min_damage = 1, int32 hate_override = -1, int ReuseTime = 10, bool HitChance=false, bool CanAvoid=true); virtual void DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const Item_Struct* item=nullptr, uint16 weapon_damage=0, int16 chance_mod=0,int16 focus=0, int ReuseTime=0); virtual void DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes skillinuse, int16 chance_mod=0, int16 focus=0, bool CanRiposte=false, int ReuseTime=0); - virtual void DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const ItemInst* Ammo=nullptr, uint16 weapon_damage=0, int16 chance_mod=0, int16 focus=0, int ReuseTime=0, uint32 range_id=0, uint32 ammo_id=0, const Item_Struct *AmmoItem=nullptr); - bool TryProjectileAttack(Mob* other, const Item_Struct *item, SkillUseTypes skillInUse, uint16 weapon_dmg, const ItemInst* RangeWeapon, const ItemInst* Ammo); + virtual void DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const ItemInst* Ammo=nullptr, uint16 weapon_damage=0, int16 chance_mod=0, int16 focus=0, int ReuseTime=0, uint32 range_id=0, uint32 ammo_id=0, const Item_Struct *AmmoItem=nullptr, int AmmoSlot=0); + bool TryProjectileAttack(Mob* other, const Item_Struct *item, SkillUseTypes skillInUse, uint16 weapon_dmg, const ItemInst* RangeWeapon, const ItemInst* Ammo, int AmmoSlot); void ProjectileAttack(); inline bool HasProjectileAttack() const { return ActiveProjectileATK; } inline void SetProjectileAttack(bool value) { ActiveProjectileATK = value; } diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 879515d20..10456e2fa 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -790,7 +790,7 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) { } //Shoots projectile and/or applies the archery damage - DoArcheryAttackDmg(GetTarget(), RangeWeapon, Ammo,0,0,0,0,0,0, AmmoItem); + DoArcheryAttackDmg(GetTarget(), RangeWeapon, Ammo,0,0,0,0,0,0, AmmoItem, ammo_slot); //EndlessQuiver AA base1 = 100% Chance to avoid consumption arrow. int ChanceAvoidConsume = aabonuses.ConsumeProjectile + itembonuses.ConsumeProjectile + spellbonuses.ConsumeProjectile; @@ -807,7 +807,7 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) { } void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const ItemInst* Ammo, uint16 weapon_damage, int16 chance_mod, int16 focus, int ReuseTime, - uint32 range_id, uint32 ammo_id, const Item_Struct *AmmoItem) { + uint32 range_id, uint32 ammo_id, const Item_Struct *AmmoItem, int AmmoSlot) { if ((other == nullptr || ((IsClient() && CastToClient()->dead) || @@ -857,7 +857,7 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite else RangeWeapon = _RangeWeapon; - _Ammo = CastToClient()->m_inv[MainAmmo]; + _Ammo = CastToClient()->m_inv[AmmoSlot]; if (!_Ammo || _Ammo->GetItem()->ID != ammo_id) ammo_lost = database.GetItem(ammo_id); else @@ -873,7 +873,7 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite mlog(COMBAT__RANGED, "Ranged attack missed %s.", other->GetName()); if (LaunchProjectile){ - TryProjectileAttack(other, AmmoItem, SkillArchery, 0, RangeWeapon, Ammo); + TryProjectileAttack(other, AmmoItem, SkillArchery, 0, RangeWeapon, Ammo, AmmoSlot); return; } else @@ -898,7 +898,7 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite WDmg = weapon_damage; if (LaunchProjectile){//1: Shoot the Projectile once we calculate weapon damage. - TryProjectileAttack(other, AmmoItem, SkillArchery, WDmg, RangeWeapon, Ammo); + TryProjectileAttack(other, AmmoItem, SkillArchery, WDmg, RangeWeapon, Ammo, AmmoSlot); return; } @@ -1013,7 +1013,7 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite } } -bool Mob::TryProjectileAttack(Mob* other, const Item_Struct *item, SkillUseTypes skillInUse, uint16 weapon_dmg, const ItemInst* RangeWeapon, const ItemInst* Ammo){ +bool Mob::TryProjectileAttack(Mob* other, const Item_Struct *item, SkillUseTypes skillInUse, uint16 weapon_dmg, const ItemInst* RangeWeapon, const ItemInst* Ammo, int AmmoSlot){ if (!other) return false; @@ -1043,6 +1043,7 @@ bool Mob::TryProjectileAttack(Mob* other, const Item_Struct *item, SkillUseTypes ProjectileAtk[slot].origin_z = GetZ(); ProjectileAtk[slot].ranged_id = RangeWeapon->GetItem()->ID; ProjectileAtk[slot].ammo_id = Ammo->GetItem()->ID; + ProjectileAtk[slot].ammo_slot = 0; ProjectileAtk[slot].skill = skillInUse; SetProjectileAttack(true); @@ -1084,7 +1085,7 @@ void Mob::ProjectileAttack() if (ProjectileAtk[i].hit_increment <= ProjectileAtk[i].increment){ if (ProjectileAtk[i].skill == SkillArchery) - DoArcheryAttackDmg(target, nullptr, nullptr,ProjectileAtk[i].wpn_dmg,0,0,0,ProjectileAtk[i].ranged_id, ProjectileAtk[i].ammo_id); + DoArcheryAttackDmg(target, nullptr, nullptr,ProjectileAtk[i].wpn_dmg,0,0,0,ProjectileAtk[i].ranged_id, ProjectileAtk[i].ammo_id, nullptr, ProjectileAtk[i].ammo_slot); ProjectileAtk[i].increment = 0; ProjectileAtk[i].target_id = 0; From 28ad768c0ad87b755e939fef1359150021007400 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 28 Nov 2014 01:05:57 -0500 Subject: [PATCH 3/9] fixes --- zone/mob.h | 2 +- zone/special_attacks.cpp | 32 ++++++++++++++++++-------------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/zone/mob.h b/zone/mob.h index 63e687854..373650661 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -722,7 +722,7 @@ public: int32 ReduceAllDamage(int32 damage); virtual void DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, int32 min_damage = 1, int32 hate_override = -1, int ReuseTime = 10, bool HitChance=false, bool CanAvoid=true); - virtual void DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const Item_Struct* item=nullptr, uint16 weapon_damage=0, int16 chance_mod=0,int16 focus=0, int ReuseTime=0); + virtual void DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const Item_Struct* item=nullptr, uint16 weapon_damage=0, int16 chance_mod=0,int16 focus=0, int ReuseTime=0, uint32 ammo_id=0, int AmmoSlot=0); virtual void DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes skillinuse, int16 chance_mod=0, int16 focus=0, bool CanRiposte=false, int ReuseTime=0); virtual void DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const ItemInst* Ammo=nullptr, uint16 weapon_damage=0, int16 chance_mod=0, int16 focus=0, int ReuseTime=0, uint32 range_id=0, uint32 ammo_id=0, const Item_Struct *AmmoItem=nullptr, int AmmoSlot=0); bool TryProjectileAttack(Mob* other, const Item_Struct *item, SkillUseTypes skillInUse, uint16 weapon_dmg, const ItemInst* RangeWeapon, const ItemInst* Ammo, int AmmoSlot); diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 10456e2fa..04f2b199a 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -199,7 +199,7 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) { SetAttackTimer(); ThrowingAttack(GetTarget()); if (CheckDoubleRangedAttack()) - RangedAttack(GetTarget(), true); + ThrowingAttack(GetTarget(), true); return; } //ranged attack (archery) @@ -1074,18 +1074,18 @@ void Mob::ProjectileAttack() disable = false; Mob* target = entity_list.GetMobID(ProjectileAtk[i].target_id); - float distance = 0.0f; - - if (target && IsMoving()){ //Only recalculate hit increment if target moving - distance = target->CalculateDistance(ProjectileAtk[i].origin_x, ProjectileAtk[i].origin_y, ProjectileAtk[i].origin_z); + if (target && target->IsMoving()){ //Only recalculate hit increment if target moving + float distance = target->CalculateDistance(ProjectileAtk[i].origin_x, ProjectileAtk[i].origin_y, ProjectileAtk[i].origin_z); float hit = 60.0f + (distance / 1.8f); //Calcuation: 60 = Animation Lag, 1.8 = Speed modifier for speed of (4) ProjectileAtk[i].hit_increment = static_cast(hit); } if (ProjectileAtk[i].hit_increment <= ProjectileAtk[i].increment){ - if (ProjectileAtk[i].skill == SkillArchery) - DoArcheryAttackDmg(target, nullptr, nullptr,ProjectileAtk[i].wpn_dmg,0,0,0,ProjectileAtk[i].ranged_id, ProjectileAtk[i].ammo_id, nullptr, ProjectileAtk[i].ammo_slot); + if (target){ + if (ProjectileAtk[i].skill == SkillArchery) + DoArcheryAttackDmg(target, nullptr, nullptr,ProjectileAtk[i].wpn_dmg,0,0,0,ProjectileAtk[i].ranged_id, ProjectileAtk[i].ammo_id, nullptr, ProjectileAtk[i].ammo_slot); + } ProjectileAtk[i].increment = 0; ProjectileAtk[i].target_id = 0; @@ -1323,7 +1323,7 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 else if(DistNoRootNoZ(*GetTarget()) < (RuleI(Combat, MinRangedAttackDist)*RuleI(Combat, MinRangedAttackDist))){ return; } - + Shout("SLOT = %i", ammo_slot); if(!IsAttackAllowed(GetTarget()) || IsCasting() || IsSitting() || @@ -1345,7 +1345,7 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 CommonBreakInvisible(); } -void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item_Struct* item, uint16 weapon_damage, int16 chance_mod,int16 focus, int ReuseTime) +void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item_Struct* item, uint16 weapon_damage, int16 chance_mod,int16 focus, int ReuseTime, uint32 ammo_id, int AmmoSlot) { if (!CanDoSpecialAttack(other)) return; @@ -1358,8 +1358,12 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite int16 WDmg = 0; - if (!weapon_damage && item != nullptr) - WDmg = GetWeaponDamage(other, item); + if (!weapon_damage){ + if (IsClient() && RangeWeapon) + WDmg = GetWeaponDamage(other, RangeWeapon); + else if (item) + WDmg = GetWeaponDamage(other, item); + } else WDmg = weapon_damage; @@ -1396,7 +1400,7 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite other->AddToHateList(this, 2*WDmg, 0, false); other->Damage(this, TotalDmg, SPELL_UNKNOWN, SkillThrowing); - if (TotalDmg > 0 && HasSkillProcSuccess() && GetTarget() && other && !other->HasDied()){ + if (TotalDmg > 0 && HasSkillProcSuccess() && other && !other->HasDied()){ if (ReuseTime) TrySkillProc(other, SkillThrowing, ReuseTime); else @@ -1404,10 +1408,10 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite } } - if((RangeWeapon != nullptr) && GetTarget() && other && (other->GetHP() > -10)) + if((RangeWeapon != nullptr) && other && (other->GetHP() > -10)) TryWeaponProc(RangeWeapon, other, MainRange); - if (HasSkillProcs() && GetTarget() && other && !other->HasDied()){ + if (HasSkillProcs() && other && !other->HasDied()){ if (ReuseTime) TrySkillProc(other, SkillThrowing, ReuseTime); else From ac0933719ae39ced2c2da17ae8ad710cf6054d30 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 28 Nov 2014 05:42:36 -0500 Subject: [PATCH 4/9] Sanity check added before calculating distance. --- zone/common.h | 1 + zone/mob.cpp | 1 + zone/special_attacks.cpp | 11 ++++++++--- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/zone/common.h b/zone/common.h index c4f643525..267831368 100644 --- a/zone/common.h +++ b/zone/common.h @@ -468,6 +468,7 @@ typedef struct float origin_x; float origin_y; float origin_z; + float sanitycheck; uint32 ranged_id; uint32 ammo_id; int ammo_slot; diff --git a/zone/mob.cpp b/zone/mob.cpp index 398bffd3d..b4645b115 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -297,6 +297,7 @@ Mob::Mob(const char* in_name, ProjectileAtk[i].origin_x = 0.0f; ProjectileAtk[i].origin_y = 0.0f; ProjectileAtk[i].origin_z = 0.0f; + ProjectileAtk[i].sanitycheck = 0.0f; ProjectileAtk[i].ranged_id = 0; ProjectileAtk[i].ammo_id = 0; ProjectileAtk[i].ammo_slot = 0; diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 04f2b199a..5f8373cac 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -1075,9 +1075,14 @@ void Mob::ProjectileAttack() Mob* target = entity_list.GetMobID(ProjectileAtk[i].target_id); if (target && target->IsMoving()){ //Only recalculate hit increment if target moving - float distance = target->CalculateDistance(ProjectileAtk[i].origin_x, ProjectileAtk[i].origin_y, ProjectileAtk[i].origin_z); - float hit = 60.0f + (distance / 1.8f); //Calcuation: 60 = Animation Lag, 1.8 = Speed modifier for speed of (4) - ProjectileAtk[i].hit_increment = static_cast(hit); + + //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].sanitycheck != target->GetX() * target->GetY()){ + ProjectileAtk[i].sanitycheck = target->GetX() * target->GetY(); + float distance = target->CalculateDistance(ProjectileAtk[i].origin_x, ProjectileAtk[i].origin_y, ProjectileAtk[i].origin_z); + float hit = 60.0f + (distance / 1.8f); //Calcuation: 60 = Animation Lag, 1.8 = Speed modifier for speed of (4) + ProjectileAtk[i].hit_increment = static_cast(hit); + } } if (ProjectileAtk[i].hit_increment <= ProjectileAtk[i].increment){ From 67863e364b48b5c4e0ea5b99dda0e006a9de5c72 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 28 Nov 2014 05:46:23 -0500 Subject: [PATCH 5/9] fix --- zone/special_attacks.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 5f8373cac..38cbceb3b 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -1098,8 +1098,10 @@ void Mob::ProjectileAttack() ProjectileAtk[i].origin_x = 0.0f; ProjectileAtk[i].origin_y = 0.0f; ProjectileAtk[i].origin_z = 0.0f; + ProjectileAtk[i].sanitycheck = 0.0f; ProjectileAtk[i].ranged_id = 0; ProjectileAtk[i].ammo_id = 0; + ProjectileAtk[i].ammo_slot = 0; ProjectileAtk[i].skill = 0; } From 3d83f647bf9400e952885f05d755c5cac1e3163d Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 28 Nov 2014 07:08:28 -0500 Subject: [PATCH 6/9] Implemented projectile on impact for Throwing --- zone/mob.h | 2 +- zone/special_attacks.cpp | 108 ++++++++++++++++++++++++++++++--------- 2 files changed, 86 insertions(+), 24 deletions(-) diff --git a/zone/mob.h b/zone/mob.h index 373650661..a0fcf2d79 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -722,7 +722,7 @@ public: int32 ReduceAllDamage(int32 damage); virtual void DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, int32 min_damage = 1, int32 hate_override = -1, int ReuseTime = 10, bool HitChance=false, bool CanAvoid=true); - virtual void DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const Item_Struct* item=nullptr, uint16 weapon_damage=0, int16 chance_mod=0,int16 focus=0, int ReuseTime=0, uint32 ammo_id=0, int AmmoSlot=0); + virtual void DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const Item_Struct* AmmoItem=nullptr, uint16 weapon_damage=0, int16 chance_mod=0,int16 focus=0, int ReuseTime=0, uint32 range_id=0, int AmmoSlot=0); virtual void DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes skillinuse, int16 chance_mod=0, int16 focus=0, bool CanRiposte=false, int ReuseTime=0); virtual void DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const ItemInst* Ammo=nullptr, uint16 weapon_damage=0, int16 chance_mod=0, int16 focus=0, int ReuseTime=0, uint32 range_id=0, uint32 ammo_id=0, const Item_Struct *AmmoItem=nullptr, int AmmoSlot=0); bool TryProjectileAttack(Mob* other, const Item_Struct *item, SkillUseTypes skillInUse, uint16 weapon_dmg, const ItemInst* RangeWeapon, const ItemInst* Ammo, int AmmoSlot); diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 38cbceb3b..48bf2aaba 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -852,16 +852,14 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite if (IsClient()){ _RangeWeapon = CastToClient()->m_inv[MainRange]; - if (!_RangeWeapon || _RangeWeapon->GetItem()->ID != range_id) - RangeWeapon = nullptr; - else - RangeWeapon = _RangeWeapon; + if (_RangeWeapon && !_RangeWeapon->GetItem() && _RangeWeapon->GetItem()->ID == range_id) + RangeWeapon = _RangeWeapon; _Ammo = CastToClient()->m_inv[AmmoSlot]; - if (!_Ammo || _Ammo->GetItem()->ID != ammo_id) - ammo_lost = database.GetItem(ammo_id); - else + if (_Ammo && _Ammo->GetItem() && _Ammo->GetItem()->ID == ammo_id) Ammo = _Ammo; + else + ammo_lost = database.GetItem(ammo_id); } } } @@ -1035,14 +1033,19 @@ bool Mob::TryProjectileAttack(Mob* other, const Item_Struct *item, SkillUseTypes float hit = 60.0f + (distance / 1.8f); //Calcuation: 60 = Animation Lag, 1.8 = Speed modifier for speed of (4) ProjectileAtk[slot].increment = 1; - ProjectileAtk[slot].hit_increment = hit; //This projected hit time if target does NOT MOVE + ProjectileAtk[slot].hit_increment = static_cast(hit); //This projected hit time if target does NOT MOVE ProjectileAtk[slot].target_id = other->GetID(); ProjectileAtk[slot].wpn_dmg = weapon_dmg; ProjectileAtk[slot].origin_x = GetX(); ProjectileAtk[slot].origin_y = GetY(); ProjectileAtk[slot].origin_z = GetZ(); - ProjectileAtk[slot].ranged_id = RangeWeapon->GetItem()->ID; - ProjectileAtk[slot].ammo_id = Ammo->GetItem()->ID; + + if (RangeWeapon && RangeWeapon->GetItem()) + ProjectileAtk[slot].ranged_id = RangeWeapon->GetItem()->ID; + + if (Ammo && Ammo->GetItem()) + ProjectileAtk[slot].ammo_id = Ammo->GetItem()->ID; + ProjectileAtk[slot].ammo_slot = 0; ProjectileAtk[slot].skill = skillInUse; @@ -1077,8 +1080,9 @@ void Mob::ProjectileAttack() 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 (ProjectileAtk[i].sanitycheck != target->GetX() * target->GetY()){ - ProjectileAtk[i].sanitycheck = target->GetX() * target->GetY(); + float _sanitycheck = target->GetX() * target->GetY(); + if (ProjectileAtk[i].sanitycheck != _sanitycheck){ + ProjectileAtk[i].sanitycheck = _sanitycheck; float distance = target->CalculateDistance(ProjectileAtk[i].origin_x, ProjectileAtk[i].origin_y, ProjectileAtk[i].origin_z); float hit = 60.0f + (distance / 1.8f); //Calcuation: 60 = Animation Lag, 1.8 = Speed modifier for speed of (4) ProjectileAtk[i].hit_increment = static_cast(hit); @@ -1090,6 +1094,8 @@ void Mob::ProjectileAttack() if (target){ if (ProjectileAtk[i].skill == SkillArchery) DoArcheryAttackDmg(target, nullptr, nullptr,ProjectileAtk[i].wpn_dmg,0,0,0,ProjectileAtk[i].ranged_id, ProjectileAtk[i].ammo_id, nullptr, ProjectileAtk[i].ammo_slot); + else if (ProjectileAtk[i].skill == SkillThrowing) + DoThrowingAttackDmg(target, nullptr, nullptr,ProjectileAtk[i].wpn_dmg,0,0,0, ProjectileAtk[i].ranged_id, ProjectileAtk[i].ammo_slot); } ProjectileAtk[i].increment = 0; @@ -1330,7 +1336,7 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 else if(DistNoRootNoZ(*GetTarget()) < (RuleI(Combat, MinRangedAttackDist)*RuleI(Combat, MinRangedAttackDist))){ return; } - Shout("SLOT = %i", ammo_slot); + if(!IsAttackAllowed(GetTarget()) || IsCasting() || IsSitting() || @@ -1341,8 +1347,6 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 (GetAppearance() == eaDead)){ return; } - //send item animation, also does the throw animation - SendItemAnimation(GetTarget(), item, SkillThrowing); DoThrowingAttackDmg(GetTarget(), RangeWeapon, item); @@ -1352,14 +1356,62 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 CommonBreakInvisible(); } -void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item_Struct* item, uint16 weapon_damage, int16 chance_mod,int16 focus, int ReuseTime, uint32 ammo_id, int AmmoSlot) +void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item_Struct* AmmoItem, uint16 weapon_damage, int16 chance_mod,int16 focus, int ReuseTime, uint32 range_id, int AmmoSlot) { - if (!CanDoSpecialAttack(other)) + if ((other == nullptr || + ((IsClient() && CastToClient()->dead) || + (other->IsClient() && other->CastToClient()->dead)) || + HasDied() || + (!IsAttackAllowed(other)) || + (other->GetInvul() || + other->GetSpecialAbility(IMMUNE_MELEE)))) + { return; + } - if (!other->CheckHitChance(this, SkillThrowing, MainPrimary, chance_mod)){ + const ItemInst* _RangeWeapon = nullptr; + const Item_Struct* ammo_lost = nullptr; + + /* + If LaunchProjectile is false this function will do archery damage on target, + otherwise it will shoot the projectile at the target, once the projectile hits target + this function is then run again to do the damage portion + */ + bool LaunchProjectile = false; + bool ProjectileMiss = false; + + if (RuleB(Combat, ProjectileDmgOnImpact)){ + + if (AmmoItem) + LaunchProjectile = true; + else{ + if (!RangeWeapon && range_id){ + + if (weapon_damage == 0) + ProjectileMiss = true; //This indicates that MISS was originally calculated. + + if (IsClient()){ + + _RangeWeapon = CastToClient()->m_inv[AmmoSlot]; + if (_RangeWeapon && _RangeWeapon->GetItem() && _RangeWeapon->GetItem()->ID != range_id) + RangeWeapon = _RangeWeapon; + else + ammo_lost = database.GetItem(range_id); + } + } + } + } + else if (AmmoItem) + SendItemAnimation(other, AmmoItem, SkillThrowing); + + if (ProjectileMiss || !other->CheckHitChance(this, SkillThrowing, MainPrimary, chance_mod)){ mlog(COMBAT__RANGED, "Ranged attack missed %s.", other->GetName()); - other->Damage(this, 0, SPELL_UNKNOWN, SkillThrowing); + if (LaunchProjectile){ + TryProjectileAttack(other, AmmoItem, SkillThrowing, 0, RangeWeapon, nullptr, AmmoSlot); + return; + } + else + other->Damage(this, 0, SPELL_UNKNOWN, SkillThrowing); } else { mlog(COMBAT__RANGED, "Throwing attack hit %s.", other->GetName()); @@ -1368,8 +1420,13 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite if (!weapon_damage){ if (IsClient() && RangeWeapon) WDmg = GetWeaponDamage(other, RangeWeapon); - else if (item) - WDmg = GetWeaponDamage(other, item); + else if (AmmoItem) + WDmg = GetWeaponDamage(other, AmmoItem); + + if (LaunchProjectile){ + TryProjectileAttack(other, AmmoItem, SkillThrowing, WDmg, RangeWeapon, nullptr, AmmoSlot); + return; + } } else WDmg = weapon_damage; @@ -1415,7 +1472,13 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite } } - if((RangeWeapon != nullptr) && other && (other->GetHP() > -10)) + if (LaunchProjectile) + return; + + //Throwing item Proc + if (ammo_lost) + TryWeaponProc(nullptr, ammo_lost, other, MainRange); + else if(RangeWeapon && other && !other->HasDied()) TryWeaponProc(RangeWeapon, other, MainRange); if (HasSkillProcs() && other && !other->HasDied()){ @@ -1424,7 +1487,6 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite else TrySkillProc(other, SkillThrowing, 0, false, MainRange); } - } void Mob::SendItemAnimation(Mob *to, const Item_Struct *item, SkillUseTypes skillInUse) { From ad2fd9e4d5415f89b4baa17dc1a4029216af4c03 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 29 Nov 2014 21:10:51 -0500 Subject: [PATCH 7/9] update to projectile move check --- zone/common.h | 3 ++- zone/mob.cpp | 3 ++- zone/special_attacks.cpp | 10 +++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/zone/common.h b/zone/common.h index 267831368..ddb7b4b0a 100644 --- a/zone/common.h +++ b/zone/common.h @@ -468,7 +468,8 @@ typedef struct float origin_x; float origin_y; float origin_z; - float sanitycheck; + float tlast_x; + float tlast_y; uint32 ranged_id; uint32 ammo_id; int ammo_slot; diff --git a/zone/mob.cpp b/zone/mob.cpp index 7261aeb46..58aafa1cb 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -297,7 +297,8 @@ Mob::Mob(const char* in_name, ProjectileAtk[i].origin_x = 0.0f; ProjectileAtk[i].origin_y = 0.0f; ProjectileAtk[i].origin_z = 0.0f; - ProjectileAtk[i].sanitycheck = 0.0f; + ProjectileAtk[i].tlast_x = 0.0f; + ProjectileAtk[i].tlast_y = 0.0f; ProjectileAtk[i].ranged_id = 0; ProjectileAtk[i].ammo_id = 0; ProjectileAtk[i].ammo_slot = 0; diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index c73a2cb11..0ba5ebe09 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -1077,11 +1077,10 @@ void Mob::ProjectileAttack() 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. - float _sanitycheck = target->GetX() * target->GetY(); - if (ProjectileAtk[i].sanitycheck != _sanitycheck){ - ProjectileAtk[i].sanitycheck = _sanitycheck; + 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 = 60.0f + (distance / 1.8f); //Calcuation: 60 = Animation Lag, 1.8 = Speed modifier for speed of (4) ProjectileAtk[i].hit_increment = static_cast(hit); @@ -1103,7 +1102,8 @@ void Mob::ProjectileAttack() ProjectileAtk[i].origin_x = 0.0f; ProjectileAtk[i].origin_y = 0.0f; ProjectileAtk[i].origin_z = 0.0f; - ProjectileAtk[i].sanitycheck = 0.0f; + ProjectileAtk[i].tlast_x = 0.0f; + ProjectileAtk[i].tlast_y = 0.0f; ProjectileAtk[i].ranged_id = 0; ProjectileAtk[i].ammo_id = 0; ProjectileAtk[i].ammo_slot = 0; From e04496188b118e56ce2dbc4a5d7a0ee4ffde6f63 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 30 Nov 2014 01:43:51 -0500 Subject: [PATCH 8/9] Spell Projectiles have been revamped to use new system. --- zone/client_process.cpp | 3 --- zone/common.h | 1 + zone/mob.cpp | 53 ++-------------------------------------- zone/mob.h | 17 ++++--------- zone/mob_ai.cpp | 1 - zone/npc.cpp | 3 +-- zone/special_attacks.cpp | 36 +++++++++++++++------------ zone/spell_effects.cpp | 47 ++++++++++++++++++++--------------- 8 files changed, 57 insertions(+), 104 deletions(-) diff --git a/zone/client_process.cpp b/zone/client_process.cpp index dfadde437..c2e3ae1f3 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -566,9 +566,6 @@ bool Client::Process() { } ProjectileAttack(); - - if(projectile_timer.Check()) - SpellProjectileEffect(); if(spellbonuses.GravityEffect == 1) { if(gravity_timer.Check()) diff --git a/zone/common.h b/zone/common.h index ddb7b4b0a..477d5b876 100644 --- a/zone/common.h +++ b/zone/common.h @@ -474,6 +474,7 @@ typedef struct uint32 ammo_id; int ammo_slot; uint8 skill; + float speed_mod; } tProjatk; //eventually turn this into a typedef and diff --git a/zone/mob.cpp b/zone/mob.cpp index 58aafa1cb..afb9889fe 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -279,14 +279,6 @@ Mob::Mob(const char* in_name, casting_spell_inventory_slot = 0; target = 0; - for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_spell_id[i] = 0; } - for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_target_id[i] = 0; } - for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_increment[i] = 0; } - for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_x[i] = 0; } - for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_y[i] = 0; } - for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_z[i] = 0; } - projectile_timer.Disable(); - ActiveProjectileATK = false; for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { @@ -303,6 +295,8 @@ Mob::Mob(const char* in_name, ProjectileAtk[i].ammo_id = 0; ProjectileAtk[i].ammo_slot = 0; ProjectileAtk[i].skill = 0; + ProjectileAtk[i].speed_mod = 0.0f; + } memset(&itembonuses, 0, sizeof(StatBonuses)); @@ -4253,49 +4247,6 @@ bool Mob::TryReflectSpell(uint32 spell_id) return false; } -void Mob::SpellProjectileEffect() -{ - bool time_disable = false; - - for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { - - if (projectile_increment[i] == 0){ - continue; - } - - Mob* target = entity_list.GetMobID(projectile_target_id[i]); - - float dist = 0; - - if (target) - dist = target->CalculateDistance(projectile_x[i], projectile_y[i], projectile_z[i]); - - int increment_end = 0; - increment_end = static_cast(dist / 10) - 1; //This pretty accurately determines end time for speed for 1.5 and timer of 250 ms - - if (increment_end <= projectile_increment[i]){ - - if (target && IsValidSpell(projectile_spell_id[i])) - SpellOnTarget(projectile_spell_id[i], target, false, true, spells[projectile_spell_id[i]].ResistDiff, true); - - projectile_spell_id[i] = 0; - projectile_target_id[i] = 0; - projectile_x[i] = 0, projectile_y[i] = 0, projectile_z[i] = 0; - projectile_increment[i] = 0; - time_disable = true; - } - - else { - projectile_increment[i]++; - time_disable = false; - } - } - - if (time_disable) - projectile_timer.Disable(); -} - - void Mob::DoGravityEffect() { Mob *caster = nullptr; diff --git a/zone/mob.h b/zone/mob.h index 49c37d783..0e04c43b1 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -237,8 +237,7 @@ public: uint16 CastingSpellID() const { return casting_spell_id; } bool DoCastingChecks(); bool TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier); - void SpellProjectileEffect(); - bool TrySpellProjectile(Mob* spell_target, uint16 spell_id); + bool TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed = 1.5f); void ResourceTap(int32 damage, uint16 spell_id); void TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker); bool CheckSpellCategory(uint16 spell_id, int category_id, int effect_id); @@ -729,10 +728,10 @@ public: int32 ReduceAllDamage(int32 damage); virtual void DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, int32 min_damage = 1, int32 hate_override = -1, int ReuseTime = 10, bool HitChance=false, bool CanAvoid=true); - virtual void DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const Item_Struct* AmmoItem=nullptr, uint16 weapon_damage=0, int16 chance_mod=0,int16 focus=0, int ReuseTime=0, uint32 range_id=0, int AmmoSlot=0); + virtual void DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const Item_Struct* AmmoItem=nullptr, uint16 weapon_damage=0, int16 chance_mod=0,int16 focus=0, int ReuseTime=0, uint32 range_id=0, int AmmoSlot=0, float speed = 4.0f); virtual void DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes skillinuse, int16 chance_mod=0, int16 focus=0, bool CanRiposte=false, int ReuseTime=0); - virtual void DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const ItemInst* Ammo=nullptr, uint16 weapon_damage=0, int16 chance_mod=0, int16 focus=0, int ReuseTime=0, uint32 range_id=0, uint32 ammo_id=0, const Item_Struct *AmmoItem=nullptr, int AmmoSlot=0); - bool TryProjectileAttack(Mob* other, const Item_Struct *item, SkillUseTypes skillInUse, uint16 weapon_dmg, const ItemInst* RangeWeapon, const ItemInst* Ammo, int AmmoSlot); + virtual void DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const ItemInst* Ammo=nullptr, uint16 weapon_damage=0, int16 chance_mod=0, int16 focus=0, int ReuseTime=0, uint32 range_id=0, uint32 ammo_id=0, const Item_Struct *AmmoItem=nullptr, int AmmoSlot=0, float speed= 4.0f); + bool TryProjectileAttack(Mob* other, const Item_Struct *item, SkillUseTypes skillInUse, uint16 weapon_dmg, const ItemInst* RangeWeapon, const ItemInst* Ammo, int AmmoSlot, float speed); void ProjectileAttack(); inline bool HasProjectileAttack() const { return ActiveProjectileATK; } inline void SetProjectileAttack(bool value) { ActiveProjectileATK = value; } @@ -851,7 +850,7 @@ public: // HP Event inline int GetNextHPEvent() const { return nexthpevent; } void SetNextHPEvent( int hpevent ); - void SendItemAnimation(Mob *to, const Item_Struct *item, SkillUseTypes skillInUse); + void SendItemAnimation(Mob *to, const Item_Struct *item, SkillUseTypes skillInUse, float velocity= 4.0); inline int& GetNextIncHPEvent() { return nextinchpevent; } void SetNextIncHPEvent( int inchpevent ); @@ -1101,12 +1100,6 @@ protected: uint8 bardsong_slot; uint32 bardsong_target_id; - Timer projectile_timer; - uint32 projectile_spell_id[MAX_SPELL_PROJECTILE]; - uint16 projectile_target_id[MAX_SPELL_PROJECTILE]; - uint8 projectile_increment[MAX_SPELL_PROJECTILE]; - float projectile_x[MAX_SPELL_PROJECTILE], projectile_y[MAX_SPELL_PROJECTILE], projectile_z[MAX_SPELL_PROJECTILE]; - bool ActiveProjectileATK; tProjatk ProjectileAtk[MAX_SPELL_PROJECTILE]; diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 614ba984d..30f435dfa 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -597,7 +597,6 @@ void Mob::AI_ShutDown() { tic_timer.Disable(); mana_timer.Disable(); spellend_timer.Disable(); - projectile_timer.Disable(); rewind_timer.Disable(); bindwound_timer.Disable(); stunned_timer.Disable(); diff --git a/zone/npc.cpp b/zone/npc.cpp index b5716e845..17ab9ed48 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -670,8 +670,7 @@ bool NPC::Process() viral_timer_counter = 0; } - if(projectile_timer.Check()) - SpellProjectileEffect(); + ProjectileAttack(); if(spellbonuses.GravityEffect == 1) { if(gravity_timer.Check()) diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 0ba5ebe09..481599bac 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -596,7 +596,6 @@ void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime) for (int i = 0; i < EmuConstants::ITEM_COMMON_SIZE; ++i) { ItemInst *aug = wpn->GetAugment(i); - if(aug) { backstab_dmg += aug->GetItem()->BackstabDmg; } @@ -806,7 +805,7 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) { } void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const ItemInst* Ammo, uint16 weapon_damage, int16 chance_mod, int16 focus, int ReuseTime, - uint32 range_id, uint32 ammo_id, const Item_Struct *AmmoItem, int AmmoSlot) { + uint32 range_id, uint32 ammo_id, const Item_Struct *AmmoItem, int AmmoSlot, float speed) { if ((other == nullptr || ((IsClient() && CastToClient()->dead) || @@ -870,7 +869,7 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite mlog(COMBAT__RANGED, "Ranged attack missed %s.", other->GetName()); if (LaunchProjectile){ - TryProjectileAttack(other, AmmoItem, SkillArchery, 0, RangeWeapon, Ammo, AmmoSlot); + TryProjectileAttack(other, AmmoItem, SkillArchery, 0, RangeWeapon, Ammo, AmmoSlot, speed); return; } else @@ -895,7 +894,7 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite WDmg = weapon_damage; if (LaunchProjectile){//1: Shoot the Projectile once we calculate weapon damage. - TryProjectileAttack(other, AmmoItem, SkillArchery, WDmg, RangeWeapon, Ammo, AmmoSlot); + TryProjectileAttack(other, AmmoItem, SkillArchery, WDmg, RangeWeapon, Ammo, AmmoSlot, speed); return; } @@ -1010,7 +1009,7 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite } } -bool Mob::TryProjectileAttack(Mob* other, const Item_Struct *item, SkillUseTypes skillInUse, uint16 weapon_dmg, const ItemInst* RangeWeapon, const ItemInst* Ammo, int AmmoSlot){ +bool Mob::TryProjectileAttack(Mob* other, const Item_Struct *item, SkillUseTypes skillInUse, uint16 weapon_dmg, const ItemInst* RangeWeapon, const ItemInst* Ammo, int AmmoSlot, float speed){ if (!other) return false; @@ -1024,12 +1023,15 @@ bool Mob::TryProjectileAttack(Mob* other, const Item_Struct *item, SkillUseTypes break; } } - + speed = 2.0f; + Shout("Speed %.2f", speed); if (slot < 0) return false; + float speed_mod = speed * 0.45f; + float distance = other->CalculateDistance(GetX(), GetY(), GetZ()); - float hit = 60.0f + (distance / 1.8f); //Calcuation: 60 = Animation Lag, 1.8 = Speed modifier for speed of (4) + float hit = 60.0f + (distance / speed_mod); //Calcuation: 60 = Animation Lag, 1.8 = Speed modifier for speed of (4) ProjectileAtk[slot].increment = 1; ProjectileAtk[slot].hit_increment = static_cast(hit); //This projected hit time if target does NOT MOVE @@ -1047,13 +1049,14 @@ bool Mob::TryProjectileAttack(Mob* other, const Item_Struct *item, SkillUseTypes ProjectileAtk[slot].ammo_slot = 0; ProjectileAtk[slot].skill = skillInUse; + ProjectileAtk[slot].speed_mod = speed_mod; SetProjectileAttack(true); if(item) - SendItemAnimation(other, item, skillInUse); + SendItemAnimation(other, item, skillInUse, speed); else if (IsNPC()) - ProjectileAnimation(other, 0,false,0,0,0,0,CastToNPC()->GetAmmoIDfile(),skillInUse); + ProjectileAnimation(other, 0,false,speed,0,0,0,CastToNPC()->GetAmmoIDfile(),skillInUse); return true; } @@ -1082,7 +1085,7 @@ void Mob::ProjectileAttack() 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 = 60.0f + (distance / 1.8f); //Calcuation: 60 = Animation Lag, 1.8 = Speed modifier for speed of (4) + float hit = 60.0f + (distance / ProjectileAtk[i].speed_mod); //Calcuation: 60 = Animation Lag, 1.8 = Speed modifier for speed of (4) ProjectileAtk[i].hit_increment = static_cast(hit); } } @@ -1094,6 +1097,8 @@ void Mob::ProjectileAttack() DoArcheryAttackDmg(target, nullptr, nullptr,ProjectileAtk[i].wpn_dmg,0,0,0,ProjectileAtk[i].ranged_id, ProjectileAtk[i].ammo_id, nullptr, ProjectileAtk[i].ammo_slot); else if (ProjectileAtk[i].skill == SkillThrowing) DoThrowingAttackDmg(target, nullptr, nullptr,ProjectileAtk[i].wpn_dmg,0,0,0, ProjectileAtk[i].ranged_id, ProjectileAtk[i].ammo_slot); + else if (ProjectileAtk[i].skill == SkillConjuration && IsValidSpell(ProjectileAtk[i].wpn_dmg)) + SpellOnTarget(ProjectileAtk[i].wpn_dmg, target, false, true, spells[ProjectileAtk[i].wpn_dmg].ResistDiff, true); } ProjectileAtk[i].increment = 0; @@ -1108,6 +1113,7 @@ void Mob::ProjectileAttack() ProjectileAtk[i].ammo_id = 0; ProjectileAtk[i].ammo_slot = 0; ProjectileAtk[i].skill = 0; + ProjectileAtk[i].speed_mod = 0.0f; } else { @@ -1355,7 +1361,7 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 CommonBreakInvisible(); } -void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item_Struct* AmmoItem, uint16 weapon_damage, int16 chance_mod,int16 focus, int ReuseTime, uint32 range_id, int AmmoSlot) +void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item_Struct* AmmoItem, uint16 weapon_damage, int16 chance_mod,int16 focus, int ReuseTime, uint32 range_id, int AmmoSlot, float speed) { if ((other == nullptr || ((IsClient() && CastToClient()->dead) || @@ -1406,7 +1412,7 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite if (ProjectileMiss || !other->CheckHitChance(this, SkillThrowing, MainPrimary, chance_mod)){ mlog(COMBAT__RANGED, "Ranged attack missed %s.", other->GetName()); if (LaunchProjectile){ - TryProjectileAttack(other, AmmoItem, SkillThrowing, 0, RangeWeapon, nullptr, AmmoSlot); + TryProjectileAttack(other, AmmoItem, SkillThrowing, 0, RangeWeapon, nullptr, AmmoSlot, speed); return; } else @@ -1423,7 +1429,7 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite WDmg = GetWeaponDamage(other, AmmoItem); if (LaunchProjectile){ - TryProjectileAttack(other, AmmoItem, SkillThrowing, WDmg, RangeWeapon, nullptr, AmmoSlot); + TryProjectileAttack(other, AmmoItem, SkillThrowing, WDmg, RangeWeapon, nullptr, AmmoSlot, speed); return; } } @@ -1488,7 +1494,7 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite } } -void Mob::SendItemAnimation(Mob *to, const Item_Struct *item, SkillUseTypes skillInUse) { +void Mob::SendItemAnimation(Mob *to, const Item_Struct *item, SkillUseTypes skillInUse, float velocity) { EQApplicationPacket *outapp = new EQApplicationPacket(OP_SomeItemPacketMaybe, sizeof(Arrow_Struct)); Arrow_Struct *as = (Arrow_Struct *) outapp->pBuffer; as->type = 1; @@ -1516,7 +1522,7 @@ void Mob::SendItemAnimation(Mob *to, const Item_Struct *item, SkillUseTypes skil Arc causes the object to form an arc in motion. A value too high will */ - as->velocity = 4.0; + as->velocity = velocity; //these angle and tilt used together seem to make the arrow/knife throw as straight as I can make it diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 620d2669c..79fe70e00 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -6400,17 +6400,16 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama return false; } -bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id){ +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. -It was changed at some point to use an actual projectile as done here (opposed to a particle effect in classic) - -The projectile graphic appears to be that of 'Ball of Sunlight' ID 80648 and will be visible to anyone in SoF+ -There is no LOS check to prevent a bolt from being cast. If you don't have LOS your bolt simply goes into whatever barrier and you lose your mana. If there is LOS the bolt will lock onto your target and the damage is applied when it hits the target. -If your target moves the bolt moves with it in any direction or angle (consistent with other projectiles). - -The way this is written once a bolt is cast a timer checks the distance from the initial cast to the target repeatedly - and calculates at what predicted time the bolt should hit that target in client_process (therefore accounting for any target movement). + -The way this is written once a bolt is cast a the distance from the initial cast to the target repeatedly + check and if target is moving recalculates at what predicted time the bolt should hit that target in client_process When bolt hits its predicted point the damage is then done to target. Note: Projectile speed of 1 takes 3 seconds to go 100 distance units. Calculations are based on this constant. Live Bolt speed: Projectile speed of X takes 5 seconds to go 300 distance units. @@ -6422,31 +6421,41 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id){ return false; uint8 anim = spells[spell_id].CastingAnim; - int bolt_id = -1; + int slot = -1; //Make sure there is an avialable bolt to be cast. for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { - if (projectile_spell_id[i] == 0){ - bolt_id = i; + if (ProjectileAtk[slot].target_id == 0){ + slot = i; break; } } - if (bolt_id < 0) + if (slot < 0) return false; if (CheckLosFN(spell_target)) { - projectile_spell_id[bolt_id] = spell_id; - projectile_target_id[bolt_id] = spell_target->GetID(); - projectile_x[bolt_id] = GetX(), projectile_y[bolt_id] = GetY(), projectile_z[bolt_id] = GetZ(); - projectile_increment[bolt_id] = 1; - projectile_timer.Start(250); + float speed_mod = speed * 0.45f; //Constant for adjusting speeds to match calculated impact time. + float distance = spell_target->CalculateDistance(GetX(), GetY(), GetZ()); + float hit = 60.0f + (distance / speed_mod); + + ProjectileAtk[slot].increment = 1; + ProjectileAtk[slot].hit_increment = static_cast(hit); //This projected hit time if target does NOT MOVE + ProjectileAtk[slot].target_id = spell_target->GetID(); + ProjectileAtk[slot].wpn_dmg = spell_id; //Store spell_id in weapon damage field + ProjectileAtk[slot].origin_x = GetX(); + ProjectileAtk[slot].origin_y = GetY(); + ProjectileAtk[slot].origin_z = GetZ(); + ProjectileAtk[slot].skill = SkillConjuration; + ProjectileAtk[slot].speed_mod = speed_mod; + + 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, 1.5,0,0,0, spells[spell_id].player_1); + ProjectileAnimation(spell_target,0, false, speed,0,0,0, spells[spell_id].player_1); } //This allows limited support for server using older spell files that do not contain data for bolt graphics. @@ -6456,19 +6465,17 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id){ if (IsClient()){ if (CastToClient()->GetClientVersionBit() <= 4) //Titanium needs alternate graphic. - ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_Titanium)), false, 1.5); + ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_Titanium)), false, speed); else - ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_SOF)), false, 1.5); + ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_SOF)), false, speed); } else - ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_NPC)), false, 1.5); - + ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_NPC)), false, speed); } - //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, 1.5); + ProjectileAnimation(spell_target,0, 1, speed); } if (spells[spell_id].CastingAnim == 64) From 8dfd61bbcf71e1447a784615413df1e335c21105 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 30 Nov 2014 01:58:23 -0500 Subject: [PATCH 9/9] fix --- zone/mob.cpp | 1 - zone/special_attacks.cpp | 3 +-- zone/spell_effects.cpp | 4 ++-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index afb9889fe..e56f3839c 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -296,7 +296,6 @@ Mob::Mob(const char* in_name, ProjectileAtk[i].ammo_slot = 0; ProjectileAtk[i].skill = 0; ProjectileAtk[i].speed_mod = 0.0f; - } memset(&itembonuses, 0, sizeof(StatBonuses)); diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 481599bac..cf6a83c80 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -1023,8 +1023,7 @@ bool Mob::TryProjectileAttack(Mob* other, const Item_Struct *item, SkillUseTypes break; } } - speed = 2.0f; - Shout("Speed %.2f", speed); + if (slot < 0) return false; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 79fe70e00..d3ef1145d 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -6425,7 +6425,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[slot].target_id == 0){ + if (ProjectileAtk[i].target_id == 0){ slot = i; break; } @@ -6433,7 +6433,7 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed){ if (slot < 0) return false; - + if (CheckLosFN(spell_target)) { float speed_mod = speed * 0.45f; //Constant for adjusting speeds to match calculated impact time.