-
Notifications
You must be signed in to change notification settings - Fork 66
Combat Interaction in RuneScript
The following code examples does not necessarily have to reflect the code for 2004scape. This is only to explain the general process of how this works. A typical combat interaction begins when a player does an op2/ap2 action.
- opnpc trigger only runs when the player does the action.
- ai_queue only runs one time when set.
- npc modes runs every tick when set.
Here is a script example for initiating the interaction in op range. This has an active player pointer.
[opnpc2,_]
// this means the npc is not avail to fight i.e dead
if (npc_stat(hitpoints) = 0) {
return;
}
// this means we are currently unavailable to fight
if (%action_delay > map_clock) {
p_opnpc(2); // TODO p_apnpc()
return;
}
~p_combat_stat; // update combat varps before swinging
// flinching
if (add(%npc_action_delay, 8) < map_clock) {
def_int $attackspeed = npc_param(attackrate);
%npc_action_delay = add(map_clock, add(divide($attackspeed, 2), 1));
}
if (~combat_hit_chance = true) {
}
anim(%com_attackanim, 0); // our attack animation
sound_synth(%com_attacksound, 0, 0); // our attack sound
npc_queue(1, 0); // queue the npc on ai_queue1 for retaliation
npc_queue(2, 0); // queue the npc on ai_queue2 for damage
npc_anim(npc_param(defend_anim), 0); // delay npc this tick
sound_synth(npc_param(defend_sound), 0, 20); // delay 1 client tick for the hit queue
%npc_retaliation_pid = uid;
// set the skill clock depending on the weapon attack rate
def_obj $weapon = inv_getobj(worn, ^wearpos_rhand);
%action_delay = add(map_clock, oc_param($weapon, attackrate)); // set our action delay for our weapon speed
p_opnpc(2);
When you set npc_queue(1, 0);
, this is queueing an ai_queue1
on the npc with a delay of 0 (1 tick).
ai_queue1
is specifically only used for combat retaliation for an npc by searching for a target and starting the combat ai mode.
This does not have an active player pointer.
[ai_queue1,_]
if (finduid(%npc_retaliation_pid) = true) { // give player pointer
npc_setmode(opplayer2); // set ai mode to opplayer2
}
When you set npc_queue(2, 0);
, this is queueing an ai_queue2
on the npc with a delay of 0 (1 tick).
ai_queue2
is specifically only used for applying damage on an npc and handling what happens to that npc when they take damage.
This does not have an active player pointer.
[ai_queue2,_]
def_int $damage = 100; // the amount of damage to apply
// if the npc is already dead
if (npc_stat(hitpoints) = 0) {
return;
}
// damage the npc
if ($damage = 0) {
npc_damage(^hitmark_block, 0);
} else {
npc_damage(^hitmark_damage, $damage);
}
if (npc_stat(hitpoints) > 0) {
return;
}
// if the npc died from this damage
npc_setmode(none);
npc_queue(3, 0);
When you set npc_queue(3, 0);
, this is queueing an ai_queue3
on the npc with a delay of 0 (1 tick).
ai_queue3
is specifically only used for making an npc die and remove from the world.
This does not have an active player pointer.
[ai_queue3,_]
npc_anim(npc_param(death_anim), 0); // make the npc do a human death animation.
npc_delay(1); // delay the npc 2 ticks (how long the death animation should be)
npc_del; // remove the npc from the world
if (finduid(npc_findhero) = true) { // gives pointer to player
obj_add(npc_coord, npc_param(death_drop), 1, 200); // drop the item
}
This is a general example of an ai_opplayer2
which runs every tick when set on the npc mode.
ai_opplayer2
is specifically used for an npc to defend from an enemy in combat for op range interactions.
This is how you would code for an npc attacking the active player back for example.
These types of npc modes has an active player pointer.
[ai_opplayer2,_]
if (%npc_action_delay > map_clock) return;
npc_anim(npc_param(attack_anim), 0);
anim(%com_defendanim, 0);
sound_synth(npc_param(attack_sound), 0, 0);
~npc_meleeattack;
// perform a basic melee attack from the npc to the player.
[proc,npc_meleeattack]
def_int $attack_roll;
def_int $maxhit_roll;
$attack_roll, $maxhit_roll = ~npc_meleedamage;
if (randominc($attack_roll) > randominc(~player_combat_defence_stat(npc_param(damagetype)))) {
~playerhit_n_melee(randominc($maxhit_roll), npc_param(attackrate));
return;
}
~playerhit_n_melee(0, npc_param(attackrate));
[proc,playerhit_n_melee](int $damage, int $delay)
~damage_self($damage);
queue(playerhit_n_retaliate, 0, npc_uid); // this should be a queue* command
%npc_action_delay = add(map_clock, $delay);
%combat_logout_delay = add(map_clock, 17); // 10 seconds
[queue,playerhit_n_retaliate](npc_uid $nid)
def_boolean $retaliate = true;
if ($retaliate = true & p_finduid(uid) = true & npc_finduid($nid) = true) {
// npc flinches player
if (%action_delay < map_clock) {
%action_delay = add(map_clock, divide(npc_param(attackrate), 2));
}
p_opnpc(2);
}
[opnpc2,count_draynor]
gosub(player_melee_attack);
if (npc_stat(hitpoints) = 0) {
@count_draynor_use_stake;
}
[ai_queue3,count_draynor] // do nothing
[ai_queue4,count_draynor] gosub(npc_death);
[ai_opplayer2,count_draynor] @count_draynor_attack;
[label,count_draynor_attack]
gosub(npc_default_attack);
if (inv_total(inv, garlic) > 0 & random(16) = 0) {
// TODO garlic effects
mes("The vampire seems to weaken.");
}
[label,count_draynor_use_stake]
if (inv_total(inv, stake) = 0) {
mes("The vampire seems to regenerate!");
npc_statheal(hitpoints, npc_basestat(hitpoints), 0);
npc_setmode(opplayer2);
return;
}
if (inv_total(inv, hammer) = 0) {
mes("You're unable to push the stake far enough in!");
mes("The vampire seems to regenerate!");
npc_statheal(hitpoints, npc_basestat(hitpoints), 0);
npc_setmode(opplayer2);
return;
}
p_delay(1);
mes("You hammer the stake into the vampire's chest!");
inv_del(inv, stake, 1);
anim(human_stake, 0);
npc_setmode(none);
npc_queue(4, 0);
queue(quest_vampire_complete, 3);