PDA

View Full Version : Plugin::ArcheryAttack()


nenelan
02-23-2010, 10:14 PM
Going to post it for now. May need more work, as I was unable to test the complete functionality for Hitting of All clients (couldn't set it up to connect more than one).

What this plugin will do is allow a mob to do a custom archery attack against either, Current Target, All Targets on hatelist, or a random target on the mob's hatelist.

Certain variables are settable in the main plugin file.

Plugin requires RandomRange() from the following link:
http://www.eqemulator.org/forums/showthread.php?t=30421

Here are the steps to add this plugin to your server:

1. Open notepad, or whatever text editor you prefer.

2. Copy and paste the following code into Notepad:


# Usage: plugin::ArcheryAttack(MeanDamage, Variance, TargNumber);
# MeanDamage - average damage of the attack
# Variance - +- % of mean as a range.
# TargNumber - 1: Current target, 2: All Targets on Hatelist, 3: Random Target from Hatelist
# Requires most current globals.pl and randomrange.pl plugins.

sub ArcheryAttack {

## Variables, set these to customize how the script works

$ACDiv = 75; # 35% shielding (max default) at 2625 AC with ACDiv of 75
$PCTMiss = 5; # Percent chance to miss outright
$PCTCrit = 5; # Percent chance to critically hit
$MaxShield = 35; # Max Shielding percent
$ThroatDamage = 1.25; # Following variables edit the damage
$HeadDamage = 1.1; # Based on where the arrow is striking
$ChestDamage = 1.0; # Feel free to edit these to
$ArmDamage = 0.9; # Suit your needs.
$LegDamage = 0.9;
$HandDamage = 0.75;
$FootDamage = 0.75;

## End Variables

$PCTMiss = 100-$PCTMiss+1;

$npc = plugin::val('$npc');
$entity_list = plugin::val('entity_list');

my $MeanDamage = $_[0];
my $Variance = $_[1];
my $TargNumber = $_[2];
my $MobName = $npc->GetCleanName();

my $MinDamage = int ( $MeanDamage * (1-($Variance/100)) );
my $MaxDamage = int ( $MeanDamage * (1+($Variance/100)) );
$HitDmg = plugin::RandomRange($MinDamage, $MaxDamage);


if ($TargNumber == 1) {
$targ = $npc->GetHateTop();
$hitname = $targ->GetName();
$targid = $targ->GetID();
$targid = $entity_list->GetClientByID($targid);
LocDmg();
quest::emote("sights and releases an arrow at his most infuriating enemy, $hitname.");
quest::doanim(9);
ApplyDamage();
}

if ($TargNumber == 2) {
quest::emote("begins to a fire off a furious barrage of arrows, aiming at everyone around him!");
quest::doanim(9);
LocDmg();
HitAllClients();
}

if ($TargNumber == 3) {
$target = $npc->GetHateRandom();
$targid = $target->GetID();
$targid = $entity_list->GetClientByID($targid);
$hitname = $target->GetName();
LocDmg();
quest::emote("looses an arrow at $hitname at random.");
quest::doanim(9);
ApplyDamage();
}
}

sub LocDmg {

$HitLoc = quest::ChooseRandom("head","head","leg","arm","chest","chest","throat","throat","hand","foot");

CritOrMiss();

if ($HitLoc eq "head") {
$HitDmg = int($HitDmg*$HeadDamage);
}

if ($HitLoc eq "leg") {
$HitDmg = int($HitDmg*$LegDamage);
}

if ($HitLoc eq "arm") {
$HitDmg = int($HitDmg*$ArmDamage);
}

if ($HitLoc eq "throat") {
$HitDmg = int($HitDmg*$ThroatDamage);
}

if ($HitLoc eq "hand" ) {
$HitDmg = int($HitDmg*$HandDamage);
}

if ($HitLoc eq "foot") {
$HitDmg = int($HitDmg*$FootDamage);
}

if ($HitLoc eq "chest") {
$HitDmg = int($HitDmg*$ChestDamage);
}
}


sub HitAllClients {
my @hatelist = $npc->GetHateList();
foreach $ent (@hatelist)
{
if($ent)
{
my $h_ent = $ent->GetEnt();
if($h_ent)
{
$targid = $h_ent->GetID();
$targid = $entity_list->GetClientByID($targid);
ApplyDamage();
}
}
}
}

sub ApplyDamage {
if ($HitDmg != 0) {

$pac = $targid->CastToMob()->GetAC();

$shielding = $pac/$ACDiv;


if ($shielding > $MaxShield) {
$shielding = $MaxShield;
}

$shielding = $shielding/100;
$HitDmg = int ($HitDmg * (1-$shielding));
$targid->Damage($npc,$HitDmg,0,4);
if ($DidCrit eq "crit") {
$targid->Message(13,"You have been CRITICALLY STRUCK by an arrow in the $HitLoc for $HitDmg points of non-melee damage!");
}
else {
$targid->Message(13,"You have been struck by an arrow in the $HitLoc for $HitDmg points of non-melee damage!");
}
}
else {
$targid->Message(13,"You breathe a sigh of relief as the arrow narrowly misses you!");
}
}

sub CritOrMiss {

$didmiss = int(rand(100));
if ($didmiss > $PCTMiss) {
$HitDmg = 0;
}
if ($didmiss <= $PCTCrit) {
$DidCrit = "crit";
$HitDmg = $HitDmg*2;
}

else {
$DidCrit = "no";
}
}

3. Save that file to your server /plugins/ folder and name it "ArcheryAttack.pl".

4. Look over the variables in the ArcheryAttack.pl that you just saved, can edit a few different variables to customize how it works in accordance with what you want.

5. Do a #questreload and the new plugin should be ready for use

Here is an example script:


sub EVENT_SPAWN {
quest::stoptimer("attack");
}
sub EVENT_COMBAT {
if ($combat_state == 1) {
quest::settimer("attack",10);
}
if ($combat_state == 0) {
quest::stoptimer("attack");
}
}

sub EVENT_TIMER {

plugin::ArcheryAttack(1000, 10, 1);


}

Every 10 seconds, the example script will fire an arrow at the the main tank. This arrow will have a base damage in the range of 900-1100, which is then further modified by where the arrow will strike. The arrow will then check to see if it crits or misses, and doubles damage on a crit, or set to 0 if it misses. The damage is then applied to the player through an invisible message, and a custom message is sent to the player telling them where they were struck by the arrow, and for how much.

Usage for this Plugin? Can set up a mob to have three timers once aggroed, one for the Single Target, one for the Hit all, one for hit random target on a permarooted mob and you now have a ranged fight. Give the mob high min/max damage to encourage people to stay out of melee. Was more developed as a way to see if I could do something like this.

Current caveat, I could not find a way to pull a player's Shielding, so I used AC to simulate (poorly) a player's shielding. AC / 75 = Shielding Percent. If anyone has any ideas to get around that, please let me know!

trevius
02-24-2010, 10:30 PM
That looks like a pretty fun plugin for custom servers wanting to simulate ranged attacks. I have actually been wanting to add one in that simulates it more like you might see on Live using the following commands and more:

CheckLoSToLoc(x, y, z, [mob_size])
ProjectileAnim(mob, item_id, [IsArrow?, speed, angle, tilt, arc])
DoSpecialAttackDamage()

Though, I may have to go even further and try to export some of the damage calculating functions from mob.cpp to perl so I can use the actual damage calculations for the NPC as if it was meleeing the client(s). I will have to look into it, but maybe some of these functions could be exported to allow for some cool flexibility:

bool Mob::CheckHitChance(Mob* other, SkillType skillinuse, int Hand)
bool Mob::AvoidDamage(Mob* other, sint32 &damage)
void Mob::MeleeMitigation(Mob *attacker, sint32 &damage, sint32 minhit)
bool NPC::Attack(Mob* other, int Hand, bool bRiposte)
sint32 Mob::ReduceDamage(sint32 damage)
sint32 Mob::ReduceMagicalDamage(sint32 damage)

I also think it would be nice to have an option for setting timers to allow it to set the timer in milliseconds instead of seconds. A simple optional bool at the end of the command should work just fine. You could then adjust timers to happen at any interval you could possibly want :)

Or, maybe I should just do it the right way and try to just write it all in the source code so Ranger class NPCs with Bows in their loot table or with a Bow weapon model equiped will use ranged attacks automatically (maybe with a rule option to enable). I think KLS has some ranged NPC code started already, so it might just mean finishing that code off. I think the hard part will be making it so they don't constantly run toward the character, as I think they should just get within a certain range and check LoS and shoot from that position until they are out of range or lose their LoS to the target.

nenelan
02-24-2010, 11:02 PM
Hmm. Your option is much better. I'm still rather new to the whole perl scripting side of things, so it was a challenge I made myself to see if I could do something a bit more advanced than "give me this item".
That being said, I did get a nice encounter working with this plugin, however, I will be the first to admit, it has its share of redundancy in the code, it is somewhat sloppy, and could be streamlined.
I admit I am very thrilled with the prospect of ranger (or any bow using npcs) using a bow if they have one in their loot table/model equipped. That's something that even live hasn't done yet!

krujo81
12-27-2010, 08:23 PM
kinda old i know but has anyone finshed anything 100%?. Because Necro swarm pet aa are supose to be archers yet they melee lol.

Ill take a look at this code and see if i cant use it but am rather new to this stuff myself

Randymarsh9
01-30-2011, 09:03 PM
Yeah is this functional? I noticed there is a rule in the DB about npc archery damage now, but I haven't found a way to make them actually use bows.

Caryatis
01-30-2011, 09:59 PM
Add special attack 'Y' to NPCs and they will use archery if their target is out of melee range. The rule is a multiplier of their normal dmg since we dont have values for ranged npc dmg in the db yet.