Merge pull request #319 from KayenEQ/Development

NPC ranged attack projectiles will now do damage on impact.
This commit is contained in:
KayenEQ 2014-12-17 12:00:03 -05:00
commit 4f242f01c1
2 changed files with 105 additions and 82 deletions

View File

@ -155,6 +155,7 @@ public:
virtual void RangedAttack(Mob* other); virtual void RangedAttack(Mob* other);
virtual void ThrowingAttack(Mob* other) { } virtual void ThrowingAttack(Mob* other) { }
int32 GetNumberOfAttacks() const { return attack_count; } int32 GetNumberOfAttacks() const { return attack_count; }
void DoRangedAttackDmg(Mob* other, bool Launch=true, int16 damage_mod=0, int16 chance_mod=0, SkillUseTypes skill=SkillArchery, float speed=4.0f, const char *IDFile = nullptr);
bool DatabaseCastAccepted(int spell_id); bool DatabaseCastAccepted(int spell_id);
bool IsFactionListAlly(uint32 other_faction); bool IsFactionListAlly(uint32 other_faction);

View File

@ -1062,8 +1062,8 @@ bool Mob::TryProjectileAttack(Mob* other, const Item_Struct *item, SkillUseTypes
if(item) if(item)
SendItemAnimation(other, item, skillInUse, speed); SendItemAnimation(other, item, skillInUse, speed);
else if (IsNPC()) //else if (IsNPC())
ProjectileAnimation(other, 0,false,speed,0,0,0,CastToNPC()->GetAmmoIDfile(),skillInUse); //ProjectileAnimation(other, 0,false,speed,0,0,0,CastToNPC()->GetAmmoIDfile(),skillInUse);
return true; return true;
} }
@ -1100,6 +1100,12 @@ void Mob::ProjectileAttack()
if (ProjectileAtk[i].hit_increment <= ProjectileAtk[i].increment){ if (ProjectileAtk[i].hit_increment <= ProjectileAtk[i].increment){
if (target){ if (target){
if (IsNPC())
CastToNPC()->DoRangedAttackDmg(target, false, ProjectileAtk[i].wpn_dmg,0, static_cast<SkillUseTypes>(ProjectileAtk[i].skill));
else
{
if (ProjectileAtk[i].skill == SkillArchery) 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); 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) else if (ProjectileAtk[i].skill == SkillThrowing)
@ -1107,6 +1113,7 @@ void Mob::ProjectileAttack()
else if (ProjectileAtk[i].skill == SkillConjuration && IsValidSpell(ProjectileAtk[i].wpn_dmg)) 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); SpellOnTarget(ProjectileAtk[i].wpn_dmg, target, false, true, spells[ProjectileAtk[i].wpn_dmg].ResistDiff, true);
} }
}
ProjectileAtk[i].increment = 0; ProjectileAtk[i].increment = 0;
ProjectileAtk[i].target_id = 0; ProjectileAtk[i].target_id = 0;
@ -1190,14 +1197,8 @@ void NPC::RangedAttack(Mob* other)
attacks = attacks > 0 ? attacks : 1; attacks = attacks > 0 ? attacks : 1;
for(int i = 0; i < attacks; ++i) { for(int i = 0; i < attacks; ++i) {
//if we have SPECATK_RANGED_ATK set then we range attack without weapon or ammo
const Item_Struct* weapon = nullptr;
const Item_Struct* ammo = nullptr;
if(!GetSpecialAbility(SPECATK_RANGED_ATK)) if(!GetSpecialAbility(SPECATK_RANGED_ATK))
{
//find our bow and ammo return if we can't find them...
return; return;
}
int sa_min_range = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 4); //Min Range of NPC attack int sa_min_range = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 4); //Min Range of NPC attack
int sa_max_range = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 1); //Max Range of NPC attack int sa_max_range = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 1); //Max Range of NPC attack
@ -1211,17 +1212,12 @@ void NPC::RangedAttack(Mob* other)
if (sa_min_range) if (sa_min_range)
min_range = static_cast<float>(sa_min_range); min_range = static_cast<float>(sa_min_range);
mlog(COMBAT__RANGED, "Calculated bow range to be %.1f", max_range);
max_range *= max_range; max_range *= max_range;
if(DistNoRootNoZ(*other) > max_range) { if(DistNoRoot(*other) > max_range)
mlog(COMBAT__RANGED, "Ranged attack out of range...%.2f vs %.2f", DistNoRootNoZ(*other), max_range);
//target is out of range, client does a message
return; return;
} else if(DistNoRoot(*other) < (min_range * min_range))
else if(DistNoRootNoZ(*other) < (min_range * min_range))
return; return;
if(!other || !IsAttackAllowed(other) || if(!other || !IsAttackAllowed(other) ||
IsCasting() || IsCasting() ||
DivineAura() || DivineAura() ||
@ -1232,33 +1228,59 @@ void NPC::RangedAttack(Mob* other)
return; return;
} }
SkillUseTypes skillinuse = SkillArchery;
skillinuse = static_cast<SkillUseTypes>(GetRangedSkill());
if(!ammo && !GetAmmoIDfile())
ammo = database.GetItem(8005);
if(ammo)
SendItemAnimation(other, ammo, SkillArchery);
else
ProjectileAnimation(other, 0,false,0,0,0,0,GetAmmoIDfile(),skillinuse);
FaceTarget(other); FaceTarget(other);
if (!other->CheckHitChance(this, skillinuse, MainRange, GetSpecialAbilityParam(SPECATK_RANGED_ATK, 2))) DoRangedAttackDmg(other);
CommonBreakInvisible();
}
}
void NPC::DoRangedAttackDmg(Mob* other, bool Launch, int16 damage_mod, int16 chance_mod, SkillUseTypes skill, float speed, const char *IDFile) {
if ((other == nullptr ||
(other->HasDied())) ||
HasDied() ||
(!IsAttackAllowed(other)) ||
(other->GetInvul() ||
other->GetSpecialAbility(IMMUNE_MELEE)))
{ {
mlog(COMBAT__RANGED, "Ranged attack missed %s.", other->GetName()); return;
other->Damage(this, 0, SPELL_UNKNOWN, skillinuse); }
SkillUseTypes skillInUse = static_cast<SkillUseTypes>(GetRangedSkill());
if (skill != skillInUse)
skillInUse = skill;
if (Launch)
{
const char *ammo = "IT10";
if (IDFile != nullptr)
ammo = IDFile;
else if (GetAmmoIDfile())
ammo = GetAmmoIDfile();
ProjectileAnimation(other, 0,false,speed,0,0,0,ammo,skillInUse);
if (RuleB(Combat, ProjectileDmgOnImpact))
{
TryProjectileAttack(other, nullptr, skillInUse, damage_mod, nullptr, nullptr, 0, speed);
return;
}
}
if (!chance_mod)
chance_mod = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 2);
if (!other->CheckHitChance(this, skillInUse, MainRange, chance_mod))
{
other->Damage(this, 0, SPELL_UNKNOWN, skillInUse);
} }
else else
{ {
int16 WDmg = GetWeaponDamage(other, weapon);
int16 ADmg = GetWeaponDamage(other, ammo);
int32 TotalDmg = 0; int32 TotalDmg = 0;
if(WDmg > 0 || ADmg > 0)
{
mlog(COMBAT__RANGED, "Ranged attack hit %s.", other->GetName());
int32 MaxDmg = max_dmg * RuleR(Combat, ArcheryNPCMultiplier); // should add a field to npc_types int32 MaxDmg = max_dmg * RuleR(Combat, ArcheryNPCMultiplier); // should add a field to npc_types
int32 MinDmg = min_dmg * RuleR(Combat, ArcheryNPCMultiplier); int32 MinDmg = min_dmg * RuleR(Combat, ArcheryNPCMultiplier);
@ -1267,14 +1289,17 @@ void NPC::RangedAttack(Mob* other)
else else
TotalDmg = zone->random.Int(MinDmg, MaxDmg); TotalDmg = zone->random.Int(MinDmg, MaxDmg);
TotalDmg += TotalDmg * GetSpecialAbilityParam(SPECATK_RANGED_ATK, 3) / 100; //Damage modifier
if (!damage_mod)
damage_mod = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 3);//Damage modifier
TotalDmg += TotalDmg * damage_mod / 100;
other->AvoidDamage(this, TotalDmg, false); other->AvoidDamage(this, TotalDmg, false);
other->MeleeMitigation(this, TotalDmg, MinDmg); other->MeleeMitigation(this, TotalDmg, MinDmg);
if (TotalDmg > 0)
CommonOutgoingHitSuccess(other, TotalDmg, skillinuse);
}
if (TotalDmg > 0)
CommonOutgoingHitSuccess(other, TotalDmg, skillInUse);
else else
TotalDmg = -5; TotalDmg = -5;
@ -1283,10 +1308,10 @@ void NPC::RangedAttack(Mob* other)
else else
other->AddToHateList(this, 0, 0, false); other->AddToHateList(this, 0, 0, false);
other->Damage(this, TotalDmg, SPELL_UNKNOWN, skillinuse); other->Damage(this, TotalDmg, SPELL_UNKNOWN, skillInUse);
if (TotalDmg > 0 && HasSkillProcSuccess() && GetTarget() && !other->HasDied()) if (TotalDmg > 0 && HasSkillProcSuccess() && !other->HasDied())
TrySkillProc(other, skillinuse, 0, true, MainRange); TrySkillProc(other, skillInUse, 0, true, MainRange);
} }
//try proc on hits and misses //try proc on hits and misses
@ -1294,10 +1319,7 @@ void NPC::RangedAttack(Mob* other)
TrySpellProc(nullptr, (const Item_Struct*)nullptr, other, MainRange); TrySpellProc(nullptr, (const Item_Struct*)nullptr, other, MainRange);
if (HasSkillProcs() && other && !other->HasDied()) if (HasSkillProcs() && other && !other->HasDied())
TrySkillProc(other, skillinuse, 0, false, MainRange); TrySkillProc(other, skillInUse, 0, false, MainRange);
CommonBreakInvisible();
}
} }
uint16 Mob::GetThrownDamage(int16 wDmg, int32& TotalDmg, int& minDmg) { uint16 Mob::GetThrownDamage(int16 wDmg, int32& TotalDmg, int& minDmg) {