Too lazy to comment
This commit is contained in:
512
mod_xj18/my_progs/ai_combat.qc
Normal file
512
mod_xj18/my_progs/ai_combat.qc
Normal file
@@ -0,0 +1,512 @@
|
||||
/*======================================================================
|
||||
COMBAT FUNCTIONS
|
||||
|
||||
Returns true if the inflictor can directly damage the target.
|
||||
Used for explosions and melee attacks.
|
||||
======================================================================*/
|
||||
void(entity targ, entity attacker) ClientObituary;
|
||||
|
||||
//-----------------------------------------------------------
|
||||
float(entity targ, entity inflictor) CanDamage =
|
||||
{
|
||||
// Any entity marked with no damage is automatically excluded
|
||||
if (targ.takedamage == DAMAGE_NO) return FALSE;
|
||||
|
||||
// bmodels need special checking because their origin is 0,0,0
|
||||
// Not all bmodels use push movetype, use bsporigin instead
|
||||
// if (targ.movetype == MOVETYPE_PUSH) {
|
||||
if (targ.bsporigin == TRUE) {
|
||||
traceline(inflictor.origin, 0.5 * (targ.absmin + targ.absmax), TRUE, inflictor);
|
||||
if (trace_fraction == 1) return TRUE;
|
||||
if (trace_ent == targ) return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Trace around the target to see if it can hit
|
||||
traceline(inflictor.origin, targ.origin, TRUE, inflictor);
|
||||
if (trace_fraction == 1) return TRUE;
|
||||
traceline(inflictor.origin, targ.origin + '15 15 0', TRUE, inflictor);
|
||||
if (trace_fraction == 1) return TRUE;
|
||||
traceline(inflictor.origin, targ.origin + '-15 -15 0', TRUE, inflictor);
|
||||
if (trace_fraction == 1) return TRUE;
|
||||
traceline(inflictor.origin, targ.origin + '-15 15 0', TRUE, inflictor);
|
||||
if (trace_fraction == 1) return TRUE;
|
||||
traceline(inflictor.origin, targ.origin + '15 -15 0', TRUE, inflictor);
|
||||
if (trace_fraction == 1) return TRUE;
|
||||
|
||||
return FALSE;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------
|
||||
void(entity targ, entity attacker) Killed =
|
||||
{
|
||||
local entity oself;
|
||||
|
||||
// Switch self for death functions
|
||||
oself = self; self = targ;
|
||||
self.activate = attacker; // Always keep track of what did damage
|
||||
|
||||
// func bmodels, triggers, breakables etc
|
||||
// Horrible hack for pushables; They switch movetype, hard to catch
|
||||
if (self.movetype == MOVETYPE_PUSH || self.movetype == MOVETYPE_NONE ||
|
||||
self.classtype == CT_FUNCPUSHABLE) {
|
||||
self.th_die ();
|
||||
}
|
||||
else {
|
||||
if (!self.deadflag && (self.flags & FL_MONSTER || self.flags & FL_CLIENT) ) {
|
||||
// Object is dying, stop re-triggering
|
||||
self.deadflag = DEAD_DYING;
|
||||
self.enemy = attacker; // Enemy dealing final blow
|
||||
if (self.enemy.classtype == CT_WORLD) self.enemy = self;
|
||||
|
||||
// bump the monster counter
|
||||
if (self.flags & FL_MONSTER) {
|
||||
// If a monster marked with no monster count dies
|
||||
// update HUD totals to reflect death properly
|
||||
if (self.nomonstercount) {
|
||||
total_monsters = total_monsters + 1;
|
||||
update_hud_totals(HUD_MONSTERS);
|
||||
}
|
||||
// Update global monster death totals
|
||||
killed_monsters = killed_monsters + 1;
|
||||
WriteByte (MSG_ALL, SVC_KILLEDMONSTER);
|
||||
}
|
||||
|
||||
ClientObituary(self, attacker);
|
||||
self.touch = SUB_Null;
|
||||
|
||||
if (self.flags & FL_MONSTER) monster_death_use();
|
||||
self.th_die ();
|
||||
}
|
||||
// Completely fuckup setup for the oldone boss with a weird target
|
||||
// field and mess of triggers around it! This elseif is just an
|
||||
// exception so it works, anyway no one fights the oldone!
|
||||
else if (!self.deadflag && self.classtype == CT_MONIDSHUB) {
|
||||
self.deadflag = DEAD_DEAD;
|
||||
killed_monsters = killed_monsters + 1;
|
||||
WriteByte (MSG_ALL, SVC_KILLEDMONSTER);
|
||||
self.th_die ();
|
||||
}
|
||||
}
|
||||
// Restore self
|
||||
self = oself;
|
||||
};
|
||||
|
||||
/*======================================================================
|
||||
T_Damage
|
||||
The damage is coming from inflictor, but get mad at attacker
|
||||
This should be the only function that ever reduces health.
|
||||
Parameters:
|
||||
targ = entity receiving damage (monster)
|
||||
inflictor = entity causing damage (bullets)
|
||||
attacker = parent entity of inflictor (player firing bullets)
|
||||
damage = quantity (before modifiers, quad etc)
|
||||
checkarmor = can bypass armour (DAMARMOR, NOARMOR)
|
||||
======================================================================*/
|
||||
void(entity targ, entity inflictor, entity attacker, float damage, float checkarmor) T_Damage =
|
||||
{
|
||||
local vector dir;
|
||||
local entity oldself;
|
||||
local float save, take, flinch;
|
||||
|
||||
// Cannot hurt the world or enemy targets (fake flying markers)
|
||||
if (targ.classtype == CT_WORLD) return;
|
||||
if (targ.classtype == CT_ENEMYTARGET) return;
|
||||
|
||||
// Is the monster dying and going through death animation?
|
||||
if (targ.flags & FL_MONSTER && targ.health < 1) {
|
||||
if (targ.health > targ.gibhealth) {
|
||||
// SSG projectiles need to do more damage (higher chance of gib)
|
||||
if (inflictor.classtype == CT_PROJ_SSG) take = 1 + random()*0.25;
|
||||
// Lowered the chance of extra gibs from other weapons
|
||||
// way to much gibbing was happening to all monsters
|
||||
else take = 1;
|
||||
|
||||
// Keep taking health off after death
|
||||
targ.health = targ.health - (damage*take);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If target cannot take damage, no point doing damage
|
||||
if (targ.takedamage == DAMAGE_NO) return;
|
||||
|
||||
// Check for breakable/pushables being immune to monster damage
|
||||
// This check is present in all projectiles functions instead
|
||||
// Its not really necessary here because its about monsters
|
||||
// if (ai_immunedamage(attacker, targ)) return;
|
||||
|
||||
// used by buttons and triggers to set activator for target firing
|
||||
damage_attacker = attacker;
|
||||
|
||||
// check for quad damage powerup on the attacker
|
||||
if (attacker.super_damage_finished > 0) damage = damage * 4;
|
||||
|
||||
// co-op team play damage avoidance
|
||||
if ( (teamplay == 1) && (targ.team > 0) && (targ.team == attacker.team) ) return;
|
||||
|
||||
// Poor zombies, they have so many exceptional ways to die!
|
||||
if (targ.classgroup == CG_ZOMBIE) {
|
||||
// If a zombie falls into slime or lava, instant death
|
||||
if (targ.watertype == CONTENT_SLIME || targ.watertype == CONTENT_LAVA) {
|
||||
damage = fabs(targ.gibhealth * 4);
|
||||
//dprint("\b[T_DAMAGE]\b Instant death, in slime or lava\n");
|
||||
}
|
||||
// Exploding boils always destroy zombies
|
||||
if (attacker.classtype == CT_MONBOIL) damage = fabs(targ.gibhealth * 2);
|
||||
}
|
||||
|
||||
// Are monsters infighting and is there a damage modifier active?
|
||||
if (attacker.infightextra && targ.flags & FL_MONSTER)
|
||||
damage = damage * attacker.infightextra;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Check for plasma burn/explosion on fatal hit
|
||||
if (inflictor.classtype == CT_PROJ_PLASMA && attacker.flags & FL_CLIENT) {
|
||||
if ((targ.health-damage < 0) && attacker.plasma_burn < time && random() < 0.2) {
|
||||
// Stop the plasma burn effect not happening alot at once
|
||||
attacker.plasma_burn = time + 4 + random()*4;
|
||||
// Don't add anymore damage if quad active
|
||||
if (attacker.super_damage_finished == 0)
|
||||
damage = (targ.health - targ.gibhealth);
|
||||
// Big blue sprite explosion + particles
|
||||
particle_explode(targ.origin-'0 0 16', 50+random()*50, 2, PARTICLE_BURST_BLUE, PARTICLE_BURST_UPWARD);
|
||||
SpawnExplosion(EXPLODE_PLASMA_BIG, inflictor.origin+'0 0 8', SOUND_REXP3);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Check for monster/breakable extra damage key condition
|
||||
// The key is usually checked with ai_damagebreakable function
|
||||
// Needs to be checked with projectiles as well
|
||||
if (targ.classgroup == CG_BREAKABLE && attacker.flags & FL_MONSTER) {
|
||||
if (targ.brkmondmg > 0) damage = damage + targ.brkmondmg;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// save damage based on the target's armor level
|
||||
//----------------------------------------------------------------------
|
||||
save = 0;
|
||||
if (checkarmor == DAMARMOR) {
|
||||
save = ceil(targ.armortype*damage);
|
||||
if (save >= targ.armorvalue) {
|
||||
save = targ.armorvalue;
|
||||
targ.armortype = 0; // lost all armor
|
||||
targ.items = targ.items - (targ.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3));
|
||||
}
|
||||
// Reduce armour based on save forumla above
|
||||
targ.armorvalue = targ.armorvalue - save;
|
||||
// CEIL function will round up any fractional damage
|
||||
// TBH it is better to round up than round down
|
||||
take = ceil(damage-save);
|
||||
}
|
||||
else take = damage;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// add to the damage total for clients, which will be sent as a single
|
||||
// message at the end of the frame
|
||||
// FIXME: remove after combining shotgun blasts?
|
||||
//----------------------------------------------------------------------
|
||||
if (targ.flags & FL_CLIENT) {
|
||||
targ.dmg_take = targ.dmg_take + take;
|
||||
targ.dmg_save = targ.dmg_save + save;
|
||||
targ.dmg_inflictor = inflictor;
|
||||
}
|
||||
|
||||
// figure momentum add
|
||||
if ( (inflictor != world) && (targ.movetype == MOVETYPE_WALK) ) {
|
||||
dir = targ.origin - (inflictor.absmin + inflictor.absmax) * 0.5;
|
||||
dir = normalize(dir);
|
||||
targ.velocity = targ.velocity + dir*damage*8;
|
||||
}
|
||||
|
||||
// check for godmode or invincibility
|
||||
if (targ.flags & FL_GODMODE || targ.invincible_finished >= time) {
|
||||
if (targ.invincible_sound < time) {
|
||||
sound (targ, CHAN_ITEM, SOUND_ARTPENT3, 1, ATTN_NORM);
|
||||
targ.invincible_sound = time + 2;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// do the damage
|
||||
//----------------------------------------------------------------------
|
||||
targ.health = targ.health - take;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Check monster turret is to be released based on % health
|
||||
// Fire any targets (once only) when this condition is met
|
||||
//----------------------------------------------------------------------
|
||||
if (targ.turrethealth > 0 && targ.movespeed < 0) {
|
||||
if (targ.health < (targ.max_health * targ.turrethealth)) {
|
||||
if (targ.turrettarget != "") {
|
||||
// Fire all targets and remove trigger string
|
||||
trigger_strs(targ.turrettarget, attacker);
|
||||
targ.turrettarget = "";
|
||||
}
|
||||
// Release turret, free movement
|
||||
targ.movespeed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// When the SG projectile system is active each monster needs to keep
|
||||
//track of cumulative damage so that pain flinches work correctly
|
||||
// AmmoShells classgroup is only active when SG projectiles are fired
|
||||
//----------------------------------------------------------------------
|
||||
// Is the Shotguns projectile system active?
|
||||
if (inflictor.classgroup == CG_PROJSHELLS) {
|
||||
// Does the frame damage acculator need to be reset?
|
||||
if (targ.dmgtimeframe <= time) {
|
||||
targ.dmgtimeframe = time + 0.1;
|
||||
targ.dmgcombined = 0;
|
||||
}
|
||||
// Accumulate damage until next fromae
|
||||
targ.dmgcombined = targ.dmgcombined + take;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// When the SG projectile system is active ZOMBIES need to know if
|
||||
// there has been enough damage over a single frame to die or not
|
||||
// AmmoShells classgroup is only active when SG projectiles are fired
|
||||
//----------------------------------------------------------------------
|
||||
if (targ.classgroup == CG_ZOMBIE) {
|
||||
// Zombies MUST ALWAYS RUN their pain function!?!
|
||||
self.pain_finished = 0;
|
||||
// Horrible Hack - Launch_Bullet sets the weapon flag on projectiles
|
||||
// Only register damage against zombies if SSG upgrade or Quad!
|
||||
if (inflictor.classgroup == CG_PROJSHELLS && inflictor.weapon) {
|
||||
// Has the accumulator gone over the max health?
|
||||
if (targ.dmgcombined > targ.max_health) {
|
||||
targ.health = targ.gibhealth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
// Axe upgrade will gib enemies if they are killed with the axe
|
||||
// A bit excessive on the gibbing, disabled for the moment
|
||||
if (attacker.moditems & IT_SHADAXE && attacker.weapon == IT_AXE) {
|
||||
if (targ.health < 1 && targ.gibhealth) targ.health = targ.gibhealth;
|
||||
} */
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Has the monster died?
|
||||
//----------------------------------------------------------------------
|
||||
if (targ.health < 1) {
|
||||
Killed (targ, attacker);
|
||||
return;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Switch self (foundtarget and pain function need self correct)
|
||||
// self restored before end of function
|
||||
oldself = self;
|
||||
self = targ;
|
||||
//----------------------------------------------------------------------
|
||||
// react to the damage - only if a monster
|
||||
// No reaction if attacker is world (env trap, door, etc)
|
||||
//----------------------------------------------------------------------
|
||||
if ( (self.flags & FL_MONSTER) && attacker != world) {
|
||||
// Is the current attacker not self and not current enemy target?
|
||||
if (self != attacker && attacker != SUB_entEnemyTarget()) {
|
||||
if ( self.noinfighting == TRUE && attacker.flags & FL_MONSTER
|
||||
|| attacker.noinfighting == TRUE) {
|
||||
// Prevent monsters from infighting
|
||||
// but let them react to the player
|
||||
}
|
||||
else {
|
||||
// If previous switch target dead, focus on new target
|
||||
if (self.switchattacker.health < 1) self.switchtimer = 0;
|
||||
// Is the attacker a different classgroup and switching target available?
|
||||
if ( self.classgroup != attacker.classgroup && self.switchtimer < time) {
|
||||
// Prevent the monster from rapidily switching targets
|
||||
if (self.switchattacker != attacker) self.switchtimer = time + self.switchoverride;
|
||||
else self.switchtimer = time + ( (self.switchoverride + random()) * 0.5);
|
||||
// Switch to new target
|
||||
self.switchattacker = attacker;
|
||||
|
||||
// Shift previous enemy to old enemy ready for new target
|
||||
// Only switch if the new enemy is the player! (id logic)
|
||||
// Check for new enemy target system before switching
|
||||
if (SUB_flagsEnemyTarget(FL_CLIENT)) self.oldenemy = SUB_entEnemyTarget();
|
||||
if (self.enemy.classtype == CT_ENEMYTARGET) SUB_switchEnemyTarget();
|
||||
self.enemy = attacker; // attack the attacker!
|
||||
FoundTarget (); // Run+turn towards new enemy
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self = oldself;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Check for ammo type resistance and immunity to pain
|
||||
// Targetdummies rely on the pain function to display stats
|
||||
if (targ.classtype != CT_TARGETDUMMY) {
|
||||
if (targ.resist_shells > 0 && inflictor.classgroup == CG_PROJSHELLS) return;
|
||||
else if (targ.resist_nails > 0 && inflictor.classgroup == CG_PROJNAILS) return;
|
||||
else if (targ.resist_rockets > 0 && inflictor.classgroup == CG_PROJROCKETS) return;
|
||||
else if (targ.resist_cells > 0 && inflictor.classgroup == CG_PROJCELLS) return;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Switch self (foundtarget and pain function need self correct)
|
||||
// self restored before end of function
|
||||
oldself = self;
|
||||
self = targ;
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
if (self.th_pain && self.pain_finished < time) {
|
||||
// Save pain finished state for later
|
||||
self.pain_finstate = self.pain_finished;
|
||||
|
||||
// The damage from projectile shotguns trinkle at the monster
|
||||
// which means it cannot trigger the flinch function
|
||||
// Let the combined damage over 1 frame accumulate first
|
||||
// Exception - Zombies don't flinch, they must run pain function
|
||||
if (inflictor.classgroup == CG_PROJSHELLS && targ.classgroup != CG_ZOMBIE) {
|
||||
// Demon, Shambler, Shalrath and Wizard all have high flinch values
|
||||
if (self.pain_flinch > 40) flinch = 40;
|
||||
else flinch = self.pain_flinch + 1;
|
||||
if (self.dmgcombined < flinch) {self = oldself; return;}
|
||||
}
|
||||
|
||||
// Block pain re-trigger event
|
||||
self.pain_finished = time + self.pain_timeout;
|
||||
|
||||
// Ignoring pain from other monsters? (infighting)
|
||||
if (self.pain_ignore && attacker.flags & FL_MONSTER)
|
||||
self.pain_finished = self.pain_finished + 1 + random();
|
||||
else {
|
||||
// If accumulated pain is higher, use that instead
|
||||
// Added classgroup exception otherwise damage qty is wrong
|
||||
if (inflictor.classgroup == CG_PROJSHELLS && take < self.dmgcombined)
|
||||
self.th_pain (inflictor, attacker, self.dmgcombined);
|
||||
else self.th_pain (inflictor, attacker, take);
|
||||
|
||||
// nightmare mode monsters don't go into pain frames often
|
||||
if (skill == SKILL_NIGHTMARE && targ.classgroup != CG_ZOMBIE) self.pain_finished = time + 5;
|
||||
|
||||
// Exceptions to pain not being blocked (even nightmare!)
|
||||
// If the monster is in slime or lava always go into pain
|
||||
if (self.watertype == CONTENT_SLIME || self.watertype == CONTENT_LAVA)
|
||||
self.pain_finished = time + 0.3;
|
||||
}
|
||||
}
|
||||
self = oldself;
|
||||
};
|
||||
|
||||
/*======================================================================
|
||||
T_RadiusDamage
|
||||
|
||||
inflictor -(self) = projectile
|
||||
attacker - (self.owner) = monster/player OR (self) = direct attack
|
||||
ignore - (other) = Original target OR (world) = no exceptions
|
||||
======================================================================*/
|
||||
void(entity inflictor, entity attacker, float damage, entity ignore, float checkclass) T_RadiusDamage =
|
||||
{
|
||||
local float points;
|
||||
local entity head;
|
||||
local vector org, imporg;
|
||||
|
||||
// Cannot hurt the world or enemy targets (fake flying markers)
|
||||
if (attacker.noradiusdmg) return;
|
||||
if (attacker.classtype == CT_WORLD) return;
|
||||
if (attacker.classtype == CT_ENEMYTARGET) return;
|
||||
|
||||
// check if inflictor is a bmodel (different origin location)
|
||||
if (inflictor.bsporigin) imporg = bmodel_origin(inflictor);
|
||||
else imporg = inflictor.origin;
|
||||
|
||||
head = findradius(imporg, damage+40);
|
||||
|
||||
while (head) {
|
||||
if (head != ignore) {
|
||||
// This is used mostly for wraiths so when they die they don't
|
||||
// kill each other, can't get the code logic to work so its
|
||||
// setup as a dead exception path
|
||||
if (checkclass == IGNORECLASS && head.classtype == ignore.classtype) { }
|
||||
else {
|
||||
// Check for breakable/pushable monster immunity
|
||||
// Do nothing, radius damage has no miss effect
|
||||
if (ai_immunedamage(attacker, head)) {
|
||||
}
|
||||
// Check for any breakables which are prone to explosive damage
|
||||
else if (ai_foundbreakable(attacker, head,TRUE) && head.brktrigmissile !=0) {
|
||||
trigger_ent(head, attacker);
|
||||
}
|
||||
else {
|
||||
// Can be damaged and NOT immune to radius (splash) damage
|
||||
if (head.takedamage > 0 && head.noradiusdmg == 0) {
|
||||
// This formula is using bounding box size which means
|
||||
// the damage amount is lower if the monster is larger
|
||||
// It should really be based on bounding box edge
|
||||
// not monster origin
|
||||
org = head.origin + (head.mins + head.maxs)*0.5;
|
||||
points = 0.5*vlen (imporg - org);
|
||||
if (points < 0) points = 0;
|
||||
points = damage - points;
|
||||
|
||||
// Half damage if caught by own explosion
|
||||
if (head == attacker) points = points * 0.5;
|
||||
|
||||
if (points > 0 && head.health > 0) {
|
||||
// Need CanDamage to check for anything blocking LoS
|
||||
// It will do several traces (source->target) to check
|
||||
// for any blocking entities (lifts, doors, walls etc)
|
||||
if (CanDamage (head, inflictor)) {
|
||||
// Splash damage is part of rocket resistance
|
||||
if (head.resist_rockets > 0)
|
||||
points = Resist_Damage(head, IT_ROCKETS, points);
|
||||
|
||||
// Zombies should really be blown from explosives
|
||||
// especially from ogre grenades right under their feet
|
||||
// Check for glancing damage or if too far away
|
||||
if (head.classgroup == CG_ZOMBIE && points > (head.health*0.25))
|
||||
points = DAMAGE_ZOMBIECLASS;
|
||||
// Any damage left? Check for poison as well
|
||||
if (points > 0) T_Damage (head, inflictor, attacker, points, DAMARMOR);
|
||||
if (attacker.poisonous) PoisonDeBuff(head);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
head = head.chain;
|
||||
}
|
||||
};
|
||||
|
||||
/*======================================================================
|
||||
T_BeamDamage (never used, leftover dev crap!)
|
||||
======================================================================
|
||||
void(entity attacker, float damage) T_BeamDamage =
|
||||
{
|
||||
local float points;
|
||||
local entity head;
|
||||
|
||||
// Cannot hurt the world or enemy targets (fake flying markers)
|
||||
if (attacker.classtype == CT_WORLD) return;
|
||||
if (attacker.classtype == CT_ENEMYTARGET) return;
|
||||
|
||||
head = findradius(attacker.origin, damage+40);
|
||||
|
||||
while (head) {
|
||||
if (head.takedamage) {
|
||||
points = 0.5*vlen (attacker.origin - head.origin);
|
||||
if (points < 0) points = 0;
|
||||
points = damage - points;
|
||||
if (head == attacker) points = points * 0.5;
|
||||
if (points > 0) {
|
||||
if (CanDamage (head, attacker)) {
|
||||
if (head.resist_rockets > 0)
|
||||
points = points * head.resist_rockets;
|
||||
T_Damage (head, attacker, attacker, points,DAMARMOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
head = head.chain;
|
||||
}
|
||||
}; */
|
||||
|
||||
Reference in New Issue
Block a user