3 const vector SOLDIER_MIN = '-16 -16 -30';
4 const vector SOLDIER_MAX = '16 16 32';
7 string SOLDIER_MODEL = "models/monsters/soldier.zym";
13 float autocvar_g_monster_soldier;
14 float autocvar_g_monster_soldier_health;
15 float autocvar_g_monster_soldier_melee_damage;
16 float autocvar_g_monster_soldier_speed_walk;
17 float autocvar_g_monster_soldier_speed_run;
18 float autocvar_g_monster_soldier_ammo;
19 float autocvar_g_monster_soldier_weapon_laser_chance;
20 float autocvar_g_monster_soldier_weapon_shotgun_chance;
21 float autocvar_g_monster_soldier_weapon_machinegun_chance;
22 float autocvar_g_monster_soldier_weapon_rocketlauncher_chance;
23 float autocvar_g_monster_soldier_attack_uzi_bullets;
24 float autocvar_g_monster_soldier_attack_uzi_damage;
25 float autocvar_g_monster_soldier_attack_uzi_force;
26 float autocvar_g_monster_soldier_attack_shotgun_damage;
27 float autocvar_g_monster_soldier_attack_shotgun_force;
28 float autocvar_g_monster_soldier_attack_shotgun_spread;
29 float autocvar_g_monster_soldier_attack_shotgun_bullets;
30 float autocvar_g_monster_soldier_attack_rocket_damage;
31 float autocvar_g_monster_soldier_attack_rocket_edgedamage;
32 float autocvar_g_monster_soldier_attack_rocket_radius;
33 float autocvar_g_monster_soldier_attack_rocket_force;
34 float autocvar_g_monster_soldier_attack_rocket_lifetime;
35 float autocvar_g_monster_soldier_attack_rocket_speed;
36 float autocvar_g_monster_soldier_attack_laser_damage;
37 float autocvar_g_monster_soldier_attack_laser_edgedamage;
38 float autocvar_g_monster_soldier_attack_laser_radius;
39 float autocvar_g_monster_soldier_attack_laser_force;
42 const float soldier_anim_die1 = 0;
43 const float soldier_anim_die2 = 1;
44 const float soldier_anim_draw = 2;
45 const float soldier_anim_duck = 3;
46 const float soldier_anim_duckwalk = 4;
47 const float soldier_anim_duckjump = 5;
48 const float soldier_anim_duckidle = 6;
49 const float soldier_anim_idle = 7;
50 const float soldier_anim_jump = 8;
51 const float soldier_anim_pain1 = 9;
52 const float soldier_anim_pain2 = 10;
53 const float soldier_anim_shoot = 11;
54 const float soldier_anim_taunt = 12;
55 const float soldier_anim_run = 13;
56 const float soldier_anim_runbackwards = 14;
57 const float soldier_anim_strafeleft = 15;
58 const float soldier_anim_straferight = 16;
59 const float soldier_anim_dead1 = 17;
60 const float soldier_anim_dead2 = 18;
61 const float soldier_anim_forwardright = 19;
62 const float soldier_anim_forwardleft = 20;
63 const float soldier_anim_backright = 21;
64 const float soldier_anim_backleft = 22;
68 self.think = soldier_think;
69 self.nextthink = time + self.ticrate;
72 self.nextthink = self.delay;
74 if(time < self.attack_finished_single)
75 monster_move(0, 0, 0, soldier_anim_shoot, soldier_anim_shoot, soldier_anim_shoot);
77 monster_move(autocvar_g_monster_soldier_speed_run, autocvar_g_monster_soldier_speed_walk, 50, soldier_anim_run, soldier_anim_run, soldier_anim_idle);
80 void soldier_reload ()
82 self.monster_delayedattack = func_null; // out of ammo, don't keep attacking
84 monsters_setframe(soldier_anim_draw);
85 self.attack_finished_single = time + 2;
86 self.currentammo = autocvar_g_monster_soldier_ammo;
87 sound (self, CH_SHOTS, "weapons/reload.wav", VOL_BASE, ATTN_LARGE);
91 void soldier_uzi_fire ()
93 self.currentammo -= 1;
94 if(self.currentammo <= 0)
100 self.grunt_cycles += 1;
102 if(self.grunt_cycles > autocvar_g_monster_soldier_attack_uzi_bullets)
104 self.monster_delayedattack = func_null;
109 W_SetupShot (self, autocvar_g_antilag_bullets && 18000 >= autocvar_g_antilag_bullets, 0, "weapons/uzi_fire.wav", CH_WEAPON_A, autocvar_g_monster_soldier_attack_uzi_damage);
110 fireBallisticBullet(w_shotorg, w_shotdir, 0.02, 18000, 5, autocvar_g_monster_soldier_attack_uzi_damage, autocvar_g_monster_soldier_attack_uzi_force, DEATH_MONSTER_MARINE, 0, 1, 115);
111 endFireBallisticBullet();
113 self.delay = time + 0.1;
114 self.monster_delayedattack = soldier_uzi_fire;
117 void soldier_rocket_explode()
119 self.event_damage = func_null;
120 self.takedamage = DAMAGE_NO;
122 pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);
123 sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
125 RadiusDamage (self, self.realowner, autocvar_g_monster_soldier_attack_rocket_damage, autocvar_g_monster_soldier_attack_rocket_edgedamage, autocvar_g_monster_soldier_attack_rocket_radius, world, autocvar_g_monster_soldier_attack_rocket_force, self.projectiledeathtype, other);
130 void soldier_rocket_touch()
134 soldier_rocket_explode();
137 void soldier_rocket_think()
139 self.nextthink = time;
142 soldier_rocket_explode();
147 void soldier_rocket_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
149 if (self.health <= 0)
152 if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
153 return; // g_projectiles_damage says to halt
155 self.health -= damage;
156 self.angles = vectoangles(self.velocity);
158 if (self.health <= 0)
159 W_PrepareExplosionByDamage(attacker, soldier_rocket_explode);
162 void soldier_rocket_fire()
166 W_SetupShot_ProjectileSize (self, '-3 -3 -3', '3 3 3', FALSE, 5, "weapons/rocket_fire.wav", CH_WEAPON_A, autocvar_g_monster_soldier_attack_rocket_damage);
169 missile.owner = missile.realowner = self;
170 missile.classname = "rocket";
171 missile.bot_dodge = TRUE;
172 missile.bot_dodgerating = autocvar_g_monster_soldier_attack_rocket_damage * 2; // * 2 because it can be detonated inflight which makes it even more dangerous
174 missile.takedamage = DAMAGE_YES;
176 missile.event_damage = soldier_rocket_damage;
177 missile.damagedbycontents = TRUE;
179 missile.movetype = MOVETYPE_FLY;
180 PROJECTILE_MAKETRIGGER(missile);
181 missile.projectiledeathtype = DEATH_MONSTER_MARINE;
182 setsize (missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot
184 setorigin (missile, w_shotorg - v_forward * 3); // move it back so it hits the wall at the right point
185 W_SetupProjectileVelocity(missile, autocvar_g_monster_soldier_attack_rocket_speed, 0);
186 missile.angles = vectoangles (missile.velocity);
188 missile.touch = soldier_rocket_touch;
189 missile.think = soldier_rocket_think;
190 missile.nextthink = time;
191 missile.cnt = time + autocvar_g_monster_soldier_attack_rocket_lifetime;
192 missile.flags = FL_PROJECTILE;
193 missile.missile_flags = MIF_SPLASH;
195 CSQCProjectile(missile, TRUE, PROJECTILE_ROCKET, FALSE);
198 void soldier_shotgun_fire()
201 W_SetupShot (self, autocvar_g_antilag_bullets && 18000 >= autocvar_g_antilag_bullets, 5, "weapons/shotgun_fire.wav", CH_WEAPON_A, autocvar_g_monster_soldier_attack_shotgun_damage * autocvar_g_monster_soldier_attack_shotgun_bullets);
202 for (sc = 0;sc < autocvar_g_monster_soldier_attack_shotgun_bullets;sc = sc + 1)
203 fireBallisticBullet(w_shotorg, w_shotdir, autocvar_g_monster_soldier_attack_shotgun_spread, 18000, 5, autocvar_g_monster_soldier_attack_shotgun_damage, autocvar_g_monster_soldier_attack_shotgun_force, DEATH_MONSTER_MARINE, 0, 1, 115);
204 endFireBallisticBullet();
207 void soldier_laser_touch()
211 self.event_damage = func_null;
212 RadiusDamage (self, self.realowner, autocvar_g_monster_soldier_attack_laser_damage, autocvar_g_monster_soldier_attack_laser_edgedamage, autocvar_g_monster_soldier_attack_laser_radius, world, autocvar_g_monster_soldier_attack_laser_force, self.projectiledeathtype, other);
217 void soldier_laser_fire()
221 W_SetupShot_Dir(self, v_forward, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, autocvar_g_monster_soldier_attack_laser_damage);
224 missile.owner = missile.realowner = self;
225 missile.classname = "laserbolt";
226 PROJECTILE_MAKETRIGGER(missile);
227 missile.projectiledeathtype = DEATH_MONSTER_MARINE;
229 setorigin (missile, w_shotorg);
230 setsize(missile, '0 0 0', '0 0 0');
232 W_SETUPPROJECTILEVELOCITY(missile, g_monster_soldier_attack_laser);
233 missile.angles = vectoangles(missile.velocity);
234 missile.touch = soldier_laser_touch;
236 missile.flags = FL_PROJECTILE;
237 missile.missile_flags = MIF_SPLASH;
238 missile.movetype = MOVETYPE_FLY;
240 missile.think = SUB_Remove;
241 missile.nextthink = time + 5;
243 CSQCProjectile(missile, TRUE, PROJECTILE_LASER, TRUE);
246 float soldier_attack()
248 monsters_setframe(soldier_anim_shoot);
249 makevectors(self.angles);
251 if(self.currentammo <= 0)
257 self.grunt_cycles = 0;
261 case WEP_ROCKET_LAUNCHER:
263 self.currentammo -= 1;
264 self.attack_finished_single = time + 0.8;
265 soldier_rocket_fire();
270 self.currentammo -= 1;
271 self.attack_finished_single = time + 0.8;
272 soldier_shotgun_fire();
277 self.attack_finished_single = time + 0.8;
278 self.delay = time + 0.1;
279 self.monster_delayedattack = soldier_uzi_fire;
284 self.attack_finished_single = time + 0.8;
285 soldier_laser_fire();
289 return FALSE; // no weapon?
293 void soldier_melee ()
295 monsters_setframe(soldier_anim_shoot);
296 self.attack_finished_single = time + 0.8;
297 monster_melee(self.enemy, autocvar_g_monster_soldier_melee_damage, 0.3, DEATH_MONSTER_MARINE_SLAP, TRUE);
302 Monster_CheckDropCvars ("soldier");
304 self.think = Monster_Fade;
305 self.nextthink = time + 5;
306 monsters_setframe((random() > 0.5) ? soldier_anim_die1 : soldier_anim_die2);
308 monster_hook_death(); // for post-death mods
311 void soldier_spawn ()
314 self.health = autocvar_g_monster_soldier_health * self.scale;
316 self.damageforcescale = 0.003;
317 self.classname = "monster_soldier";
318 self.checkattack = GenericCheckAttack;
319 self.attack_melee = soldier_melee;
320 self.attack_ranged = soldier_attack;
321 self.nextthink = time + random() * 0.5 + 0.1;
322 self.think = soldier_think;
323 self.currentammo = 3;
324 self.sprite_height = 45;
325 self.items = (IT_SHELLS | IT_ROCKETS | IT_NAILS);
327 monsters_setframe(soldier_anim_draw);
329 monster_setupsounds("soldier");
331 setmodel(self, SOLDIER_MODEL);
333 RandomSelection_Init();
334 RandomSelection_Add(world, WEP_LASER, string_null, autocvar_g_monster_soldier_weapon_laser_chance, 1);
335 RandomSelection_Add(world, WEP_SHOTGUN, string_null, autocvar_g_monster_soldier_weapon_shotgun_chance, 1);
336 RandomSelection_Add(world, WEP_UZI, string_null, autocvar_g_monster_soldier_weapon_machinegun_chance, 1);
337 RandomSelection_Add(world, WEP_ROCKET_LAUNCHER, string_null, autocvar_g_monster_soldier_weapon_rocketlauncher_chance, 1);
339 self.weaponentity = spawn();
340 self.weaponentity.movetype = MOVETYPE_NOCLIP;
341 self.weaponentity.team = self.team;
342 self.weaponentity.solid = SOLID_NOT;
343 self.weaponentity.owner = self.weaponentity.realowner = self;
344 setmodel(self.weaponentity, "models/weapons/v_seeker.md3");
345 setattachment(self.weaponentity, self, "bip01 r hand");
347 self.armorvalue = bound(0.5, random(), 1);
348 self.weapon = RandomSelection_chosen_float;
350 monster_hook_spawn(); // for post-spawn mods
353 void spawnfunc_monster_soldier ()
355 if not(autocvar_g_monster_soldier) { remove(self); return; }
357 self.monster_spawnfunc = spawnfunc_monster_soldier;
359 if(Monster_CheckAppearFlags(self))
362 precache_model("models/weapons/v_seeker.md3");
363 precache_model(SOLDIER_MODEL);
365 if not (monster_initialize(
366 "Marine", MONSTER_MARINE,
367 SOLDIER_MIN, SOLDIER_MAX,
369 soldier_die, soldier_spawn))
376 // compatibility with old spawns
377 void spawnfunc_monster_army () { spawnfunc_monster_soldier(); }