/*====================================================================== /*QUAKED misc_targetdummy (0 .5 .5) (-32 -32 -24) (32 32 64) KNIGHT WIZARD OGRE HELL_KNIGHT ZOMBIE DEMON SHALRATH SHAMBLER Target practice dummy ------- KEYS -------- noise1 : Targetnumber tracking weapon used noise2 : Targetnumber tracking enemy distance noise3 : Targetnumber tracking damage noise4 : Targetnumber tracking % efficency wait : Impact marker removal time (def=2) -------- SPAWNFLAGS -------- KNIGHT : Knight model WIZARD : Wizard model OGRE : Ogre model HELL_KNIGHT : Hell Knight model ZOMBIE : Zombie model DEMON : Demon model SHALRATH : Shalrath model SHAMBLER : Shambler model ------- NOTES -------- Target practice dummy ======================================================================*/ float MISC_TARGKNIGHT = 1; float MISC_TARGWIZARD = 2; float MISC_TARGOGRE = 4; float MISC_TARGHELLKNIGHT = 8; float MISC_TARGZOMBIE = 16; float MISC_TARGDEMON = 32; float MISC_TARGSHALRATH = 64; float MISC_TARGSHAMBLER = 128; //---------------------------------------------------------------------- void(float tvalue, string tdstr) misc_targetdummy_trigger = { local entity tdfind, tself, tother; if (tdstr == "") return; self.targetnumber = tvalue; tdfind = find(world, targetname, tdstr); if (tdfind.classtype == CT_TARGETNUMBER) { tself = self; tother = other; self = tdfind; other = tself; self.use(); self = tself; other = tother; } }; //---------------------------------------------------------------------- void() misc_targetdummy_stand = { // Cycle around standing animation self.frame = self.frame + 1; if (self.frame == self.pos1_y) self.frame = self.pos1_x; self.nextthink = time + 0.1; // Check for any accumulating damage totals if (self.attack_finished < time) { dprint("Wpn ("); if (self.weapon & IT_SHOTGUN) dprint("Shotgun"); else if (self.weapon & IT_SUPER_SHOTGUN && self.moditems & IT_UPGRADE_SSG) dprint("Shotgun Upgrade"); else if (self.weapon & IT_SUPER_SHOTGUN) dprint("Super Shotgun"); else if (self.weapon & IT_NAILGUN) dprint("Nail Gun"); else if (self.weapon & IT_SUPER_NAILGUN) dprint("Super Nail Gun"); else if (self.weapon & IT_GRENADE_LAUNCHER) dprint("Grenade Launcher"); else if (self.weapon & IT_ROCKET_LAUNCHER) dprint("Rocket Launcher"); else if (self.weapon & IT_LIGHTNING && self.moditems & IT_UPGRADE_LG) dprint("Lightning Upgrade"); else if (self.weapon & IT_LIGHTNING) dprint("Lightning Gun"); else dprint("Unknown"); dprint(") Dist ("); dprint(ftos(self.enemydist)); dprint(") Dmg ("); dprint(ftos(self.dmg)); dprint(") % ("); if (self.weapon & IT_SHOTGUN || self.weapon & IT_SUPER_SHOTGUN) { if (self.weapon & IT_SHOTGUN) self.cnt = self.dmg / 28; else if (self.moditems & IT_UPGRADE_SSG) self.cnt = self.dmg / 84; else self.cnt = self.dmg / 56; self.cnt = self.cnt * 100; } else self.cnt = 100; dprint(ftos(self.cnt)); dprint(")\n"); misc_targetdummy_trigger(self.weapon, self.noise1); misc_targetdummy_trigger(self.enemydist, self.noise2); misc_targetdummy_trigger(self.dmg, self.noise3); misc_targetdummy_trigger(self.cnt, self.noise4); // Reset health, damage timer and total self.health = self.max_health; self.attack_finished = LARGE_TIMER; self.dmg = 0; } }; //---------------------------------------------------------------------- void(entity inflictor, entity attacker, float damage) misc_targetdummy_pain = { // want all damage to go through pain function, reset timer self.pain_finished = 0; // Play a pain sound every second if (self.pain_check < time) { sound (self, CHAN_VOICE, self.pain_sound, 1, ATTN_NORM); self.pain_check = time + 1; } // If the attacker is a player, record details if (attacker.flags & FL_CLIENT) { self.weapon = attacker.weapon; self.items = attacker.items; self.moditems = attacker.moditems; self.enemydist = range_distance(attacker, TRUE); } else self.weapon = self.enemydist = 0; // The damage value is different depending on projectile type if (inflictor.classgroup == CG_PROJSHELLS) self.dmg = damage; else self.dmg = self.dmg + damage; // reset timer for accumulating damage over time self.attack_finished = time + 0.5; // Spawn a marker to show impact location // Will remove self after certain amount of time newmis = spawn(); newmis.classtype = CT_DEVMARKER; newmis.movetype = MOVETYPE_NONE; newmis.solid = SOLID_NOT; setmodel(newmis, MODEL_IMPACT); // Randomize between 8 yellow skins newmis.skin = rint(random()*7); // Take projectile impact location setorigin(newmis, inflictor.origin); setsize (newmis, VEC_ORIGIN, VEC_ORIGIN); newmis.think = SUB_Remove; newmis.nextthink = time + self.wait; }; //---------------------------------------------------------------------- void() misc_targetdummy = { // Always reset Ammo Resistance to be consistent self.resist_shells = self.resist_nails = 0; self.resist_rockets = self.resist_cells = 0; if (self.spawnflags & MISC_TARGKNIGHT) { self.mdl = "progs/mon_knight.mdl"; self.headmdl = "progs/targd_short.mdl"; self.pain_sound = "knight/khurt.wav"; self.t_width = FALSE; self.bboxtype = BBOX_SHORT; self.pos1 = '0 9 0'; // stand1-9 } else if (self.spawnflags & MISC_TARGWIZARD) { self.mdl = "progs/mon_wizard.mdl"; self.headmdl = "progs/targd_tall.mdl"; self.pain_sound = "wizard/wpain.wav"; self.t_width = FALSE; self.bboxtype = BBOX_TALL; self.pos1 = '0 8 0'; // hover1-8 } else if (self.spawnflags & MISC_TARGOGRE) { self.mdl = "progs/ogre.mdl"; self.headmdl = "progs/targd_wide.mdl"; self.pain_sound = "ogre/ogpain1.wav"; self.t_width = TRUE; self.bboxtype = BBOX_WIDE; self.pos1 = '0 9 0'; // stand1-9 } else if (self.spawnflags & MISC_TARGHELLKNIGHT) { self.mdl = "progs/mon_hknight.mdl"; self.headmdl = "progs/targd_tall.mdl"; self.pain_sound = "hknight/pain1.wav"; self.t_width = FALSE; self.bboxtype = BBOX_TALL; self.pos1 = '0 9 0'; // stand1-9 } else if (self.spawnflags & MISC_TARGZOMBIE) { self.mdl = "progs/mon_zombie.mdl"; self.headmdl = "progs/targd_short.mdl"; self.pain_sound = "zombie/z_pain.wav"; self.t_width = FALSE; self.bboxtype = BBOX_SHORT; self.pos1 = '0 15 0'; // stand 1-15 } else if (self.spawnflags & MISC_TARGDEMON) { self.mdl = "progs/demon.mdl"; self.headmdl = "progs/targd_wide.mdl"; self.pain_sound = "demon/dpain1.wav"; self.t_width = TRUE; self.bboxtype = BBOX_WIDE; self.pos1 = '0 13 0'; // stand 1-13 } else if (self.spawnflags & MISC_TARGSHALRATH) { self.mdl = "progs/mon_shalrath.mdl"; self.headmdl = "progs/targd_wide.mdl"; self.pain_sound = "shalrath/pain.wav"; self.t_width = TRUE; self.bboxtype = BBOX_WIDE; self.pos1 = '0 1 0'; // stand 1 } else if (self.spawnflags & MISC_TARGSHAMBLER) { self.mdl = "progs/shambler.mdl"; self.headmdl = "progs/targd_massive.mdl"; self.pain_sound = "shambler/shurt2.wav"; self.t_width = FALSE; self.bboxtype = BBOX_MASSIVE; self.pos1 = '0 17 0'; // stand1-17 self.resist_rockets = 0.5; } else { remove(self); return; } precache_model (self.mdl); precache_model (self.headmdl); precache_sound (self.pain_sound); precache_model (MODEL_IMPACT); // Impact model marker self.classtype = CT_TARGETDUMMY; self.classgroup = CG_MONSTERS; self.health = self.max_health = 100000; self.solid = SOLID_SLIDEBOX; self.movetype = MOVETYPE_NONE; self.movespeed = 0; // Never move monster if (self.wait < 1) self.wait = 2; // impact marker removal time self.pain_finished = 0; // Always reset this self.pain_flinch = 1; // Always pass flinch test self.dmg = 0; // Accumulated damage per frame self.attack_finished = LARGE_TIMER; // Reset timer for checks self.view_ofs = '0 0 24'; // Default monster offset self.takedamage = DAMAGE_AIM; // Always take damage monster_bbox(); // Setup bounding box setmodel(self, self.mdl); // Display model setsize (self, self.bbmins, self.bbmaxs); // Update bbox // Spawn projectile bounding box visual marker self.attachment = spawn(); self.attachment.solid = SOLID_NOT; self.attachment.movetype = MOVETYPE_NONE; setmodel(self.attachment, self.headmdl); setorigin(self.attachment, self.origin-self.view_ofs); self.attachment.alpha = 0.3; // Spawn Hull2 bounding box visual marker if (self.t_width) { self.noise = "progs/targd_hull2.mdl"; precache_model(self.noise); self.attachment2 = spawn(); self.attachment2.solid = SOLID_NOT; self.attachment2.movetype = MOVETYPE_NONE; setmodel(self.attachment2, self.noise); setorigin(self.attachment2, self.origin-self.view_ofs); self.attachment2.alpha = 0.15; } // Intercept all AI states self.th_pain = misc_targetdummy_pain; self.th_stand = misc_targetdummy_stand; self.th_walk = misc_targetdummy_stand; self.th_run = misc_targetdummy_stand; self.th_melee = misc_targetdummy_stand; // Stand around and wait for the pain! self.think = self.th_stand; self.nextthink = time + 0.5 + random(); }; /*====================================================================== /*QUAKED misc_targetnumber (.5 .5 0) (-4 -4 -8) (4 4 8) x target number to display ingame ------- KEYS -------- targetname : required for numbers to updated pos1 : X= top digit quantity, Y=Lower digit quantity -------- SPAWNFLAGS -------- ------- NOTES -------- target number to display ingame ======================================================================*/ void() misc_targetnumber_dispno = { local float str_counter, str_pow, str_result, str_low; str_counter = self.pos1_x; // Upper number length str_pow = pow10(self.pos1_x); // Setup initial base 10 power for checking // If number larger than fixed length? - truncate number if (self.count > str_pow) self.count = self.count - (floor(self.count / str_pow) * str_pow); // Loop through lower part of number while(str_counter > 0) { // Move down one pow digit str_pow = str_pow / 10; // Divide number by pow to get divider str_result = floor(self.count / str_pow); if (str_counter == 4) self.tno8.skin = str_result; else if (str_counter == 3) self.tno7.skin = str_result; else if (str_counter == 2) self.tno6.skin = str_result; else if (str_counter == 1) self.tno5.skin = str_result; // remove top part of number, decrease str length counter self.count = self.count - (str_result * str_pow); str_counter = str_counter - 1; } // Shift the lower part of the number upwards str_low = str_counter = self.pos1_y; str_pow = pow10(self.pos1_y); while (str_low > 0) { self.count = self.count * 10; str_low = str_low - 1; } // Loop through lower part of number while(str_counter > 0) { // Move down one pow digit str_pow = str_pow / 10; // Divide number by pow to get divider str_result = floor(self.count / str_pow); if (str_counter == 3) self.tno3.skin = str_result; else if (str_counter == 2) self.tno2.skin = str_result; else if (str_counter == 1) self.tno1.skin = str_result; // remove top part of number, decrease str length counter self.count = self.count - (str_result * str_pow); str_counter = str_counter - 1; } }; //---------------------------------------------------------------------- void() misc_targetnumber_use = { self.count = fabs(other.targetnumber); misc_targetnumber_dispno(); }; //---------------------------------------------------------------------- entity(float offset, float startno) misc_targetnumber_spawn = { local entity tnoent; tnoent = spawn(); tnoent.classtype = CT_TARGETNUMBER; tnoent.solid = SOLID_NOT; tnoent.movetype = MOVETYPE_NONE; makevectors(self.angles); setmodel(tnoent, self.mdl); setorigin(tnoent, self.origin + v_right*(8*offset)); setsize(tnoent, VEC_ORIGIN, VEC_ORIGIN); tnoent.skin = startno; tnoent.angles = self.angles; return tnoent; }; //---------------------------------------------------------------------- void() misc_targetnumber = { self.mdl = "progs/misc_digit.mdl"; precache_model (self.mdl); self.classtype = CT_TARGETNUMBER; self.solid = SOLID_NOT; self.movetype = MOVETYPE_NONE; // Setup lower part of number (1-3 decimal places, 4=dot always) if (self.pos1_y > 0) { if (self.pos1_y == 1) self.tno1 = misc_targetnumber_spawn(0, 0); else if (self.pos1_y == 2) { self.tno1 = misc_targetnumber_spawn(0, 0); self.tno2 = misc_targetnumber_spawn(1, 0); } else { self.pos1_y = 3; self.tno1 = misc_targetnumber_spawn(0, 0); self.tno2 = misc_targetnumber_spawn(1, 0); self.tno3 = misc_targetnumber_spawn(2, 0); } // Decimal place is always tno4 self.tno4 = misc_targetnumber_spawn(self.pos1_y, 10); self.pos1_z = self.pos1_y +1; } else self.pos1_z = 0; // Setup upper part of number (1-4 digits only, min 1 digit) if (self.pos1_x < 1) self.pos1_x = 1; if (self.pos1_x == 1) self.tno5 = misc_targetnumber_spawn(self.pos1_z, 0); else if (self.pos1_x == 2) { self.tno5 = misc_targetnumber_spawn(self.pos1_z, 0); self.tno6 = misc_targetnumber_spawn(self.pos1_z+1, 0); } else if (self.pos1_x == 3) { self.tno5 = misc_targetnumber_spawn(self.pos1_z, 0); self.tno6 = misc_targetnumber_spawn(self.pos1_z+1, 0); self.tno7 = misc_targetnumber_spawn(self.pos1_z+2, 0); } else { self.pos1_x = 4; self.tno5 = misc_targetnumber_spawn(self.pos1_z, 0); self.tno6 = misc_targetnumber_spawn(self.pos1_z+1, 0); self.tno7 = misc_targetnumber_spawn(self.pos1_z+2, 0); self.tno8 = misc_targetnumber_spawn(self.pos1_z+3, 0); } // Setup trigger function for number update if (self.targetname != "") self.use = misc_targetnumber_use; };