2 const vector SOLDIER_MIN = '-16 -16 -30';
3 const vector SOLDIER_MAX = '16 16 32';
6 float autocvar_g_monster_soldier;
7 float autocvar_g_monster_soldier_health;
8 float autocvar_g_monster_soldier_melee_damage;
9 float autocvar_g_monster_soldier_speed_walk;
10 float autocvar_g_monster_soldier_speed_run;
11 float autocvar_g_monster_soldier_ammo;
12 float autocvar_g_monster_soldier_weapon_laser_chance;
13 float autocvar_g_monster_soldier_weapon_shotgun_chance;
14 float autocvar_g_monster_soldier_weapon_machinegun_chance;
15 float autocvar_g_monster_soldier_weapon_rocketlauncher_chance;
16 float autocvar_g_monster_soldier_attack_uzi_bullets;
19 #define soldier_anim_die1 0
20 #define soldier_anim_die2 1
21 #define soldier_anim_draw 2
22 #define soldier_anim_duck 3
23 #define soldier_anim_duckwalk 4
24 #define soldier_anim_duckjump 5
25 #define soldier_anim_duckidle 6
26 #define soldier_anim_idle 7
27 #define soldier_anim_jump 8
28 #define soldier_anim_pain1 9
29 #define soldier_anim_pain2 10
30 #define soldier_anim_shoot 11
31 #define soldier_anim_taunt 12
32 #define soldier_anim_run 13
33 #define soldier_anim_runbackwards 14
34 #define soldier_anim_strafeleft 15
35 #define soldier_anim_straferight 16
36 #define soldier_anim_dead1 17
37 #define soldier_anim_dead2 18
38 #define soldier_anim_forwardright 19
39 #define soldier_anim_forwardleft 20
40 #define soldier_anim_backright 21
41 #define soldier_anim_backleft 22
43 //#define soldier_anim_stand 0
44 //#define soldier_anim_death1 1
45 //#define soldier_anim_death2 2
46 //#define soldier_anim_reload 3
47 //#define soldier_anim_pain1 4
48 //#define soldier_anim_pain2 5
49 //#define soldier_anim_pain3 6
50 //#define soldier_anim_run 7
51 //#define soldier_anim_shoot 8
52 //#define soldier_anim_prowl 9
56 self.think = soldier_think;
57 self.nextthink = time + 0.1;
60 self.nextthink = self.delay;
62 if(time < self.attack_finished_single)
63 monster_move(0, 0, 0, soldier_anim_shoot, soldier_anim_shoot, soldier_anim_shoot);
65 monster_move(autocvar_g_monster_soldier_speed_run, autocvar_g_monster_soldier_speed_walk, 50, soldier_anim_run, soldier_anim_run, soldier_anim_idle);
68 void soldier_reload ()
70 self.frame = soldier_anim_draw;
71 self.attack_finished_single = time + 2;
72 self.currentammo = autocvar_g_monster_soldier_ammo;
73 sound (self, CH_SHOTS, "weapons/reload.wav", VOL_BASE, ATTN_LARGE);
76 float SoldierCheckAttack ()
78 local vector spot1 = '0 0 0', spot2 = '0 0 0';
79 local entity targ = self.enemy;
80 local float chance = 0;
82 if (self.health <= 0 || targ.health < 1 || targ == world)
85 if (vlen(targ.origin - self.origin) > 2000) // long traces are slow
88 // see if any entities are in the way of the shot
89 spot1 = self.origin + self.view_ofs;
90 spot2 = targ.origin + targ.view_ofs;
92 traceline (spot1, spot2, FALSE, self);
94 if (trace_ent != targ)
95 return FALSE; // don't have a clear shot
99 return FALSE; // sight line crossed contents
101 if(self.monster_delayedattack && self.delay != -1)
103 if(time < self.delay)
106 self.monster_delayedattack();
110 if (time < self.attack_finished_single)
113 if (enemy_range() >= 2000)
116 if (enemy_range() <= 120)
118 else if (enemy_range() <= 500)
119 chance = 0.6; // was 0.4
120 else if (enemy_range() <= 1000)
121 chance = 0.3; // was 0.05
126 if (chance > random())
129 if(self.currentammo <= 0 && enemy_range() <= 120)
131 monster_sound(self.msound_attack_melee, 0, FALSE); // no delay for attack sounds
136 if(self.currentammo <= 0)
142 if (self.attack_ranged())
148 void soldier_laser ()
150 self.frame = soldier_anim_shoot;
151 self.attack_finished_single = time + 0.8;
155 float soldier_missile_laser ()
157 // FIXME: check if it would hit
163 void soldier_uzi_fire ()
165 self.currentammo -= 1;
166 if(self.currentammo <= 0)
169 self.grunt_cycles += 1;
171 if(self.grunt_cycles > autocvar_g_monster_soldier_attack_uzi_bullets)
173 self.monster_delayedattack = func_null;
177 W_UZI_Attack(DEATH_MONSTER_SOLDIER_NAIL);
178 self.delay = time + 0.1;
179 self.monster_delayedattack = soldier_uzi_fire;
184 if(self.currentammo <= 0)
187 self.frame = soldier_anim_shoot;
188 self.attack_finished_single = time + 0.8;
189 self.delay = time + 0.1;
190 self.monster_delayedattack = soldier_uzi_fire;
193 float soldier_missile_uzi ()
195 self.grunt_cycles = 0;
196 // FIXME: check if it would hit
201 void soldier_shotgun ()
203 self.currentammo -= 1;
204 if(self.currentammo <= 0)
207 self.frame = soldier_anim_shoot;
208 self.attack_finished_single = time + 0.8;
212 float soldier_missile_shotgun ()
214 // FIXME: check if it would hit
215 self.grunt_cycles = 0;
222 self.currentammo -= 1;
223 if(self.currentammo <= 0)
226 self.frame = soldier_anim_shoot;
227 self.attack_finished_single = time + 0.8;
231 float soldier_missile_rl ()
233 // FIXME: check if it would hit
240 self.frame = soldier_anim_shoot;
241 self.attack_finished_single = time + 0.8;
242 monster_melee(self.enemy, autocvar_g_monster_soldier_melee_damage, 70, DEATH_MONSTER_SOLDIER_NAIL);
247 Monster_CheckDropCvars ("soldier");
249 remove(self.weaponentity);
251 self.solid = SOLID_NOT;
252 self.takedamage = DAMAGE_NO;
253 self.event_damage = func_null;
255 self.movetype = MOVETYPE_TOSS;
256 self.think = Monster_Fade;
257 self.nextthink = time + 2.1;
258 self.weaponentity = world;
260 if (self.attack_ranged == soldier_missile_uzi)
261 W_ThrowNewWeapon(self, WEP_UZI, 0, self.origin, self.velocity);
262 else if (self.attack_ranged == soldier_missile_shotgun)
263 W_ThrowNewWeapon(self, WEP_SHOTGUN, 0, self.origin, self.velocity);
264 else if (self.attack_ranged == soldier_missile_rl)
265 W_ThrowNewWeapon(self, WEP_ROCKET_LAUNCHER, 0, self.origin, self.velocity);
267 W_ThrowNewWeapon(self, WEP_LASER, 0, self.origin, self.velocity);
270 self.frame = soldier_anim_die1;
272 self.frame = soldier_anim_die2;
274 monster_hook_death(); // for post-death mods
277 void soldier_spawn ()
280 self.health = autocvar_g_monster_soldier_health * self.scale;
282 self.damageforcescale = 0.003;
283 self.classname = "monster_soldier";
284 self.checkattack = SoldierCheckAttack;
285 self.attack_melee = soldier_bash;
286 self.frame = soldier_anim_draw;
287 self.nextthink = time + random() * 0.5 + 0.1;
288 self.think = soldier_think;
289 self.sprite_height = 45 * self.scale;
290 self.items = (IT_SHELLS | IT_ROCKETS | IT_NAILS);
292 RandomSelection_Init();
293 RandomSelection_Add(world, WEP_LASER, string_null, autocvar_g_monster_soldier_weapon_laser_chance, 1);
294 RandomSelection_Add(world, WEP_SHOTGUN, string_null, autocvar_g_monster_soldier_weapon_shotgun_chance, 1);
295 RandomSelection_Add(world, WEP_UZI, string_null, autocvar_g_monster_soldier_weapon_machinegun_chance, 1);
296 RandomSelection_Add(world, WEP_ROCKET_LAUNCHER, string_null, autocvar_g_monster_soldier_weapon_rocketlauncher_chance, 1);
298 self.weaponentity = spawn();
299 self.weaponentity.movetype = MOVETYPE_NOCLIP;
300 self.weaponentity.team = self.team;
301 self.weaponentity.solid = SOLID_NOT;
302 self.weaponentity.owner = self.weaponentity.realowner = self;
303 setmodel(self.weaponentity, "models/weapons/v_seeker.md3");
304 setattachment(self.weaponentity, self, "bip01 r hand");
306 if (RandomSelection_chosen_float == WEP_ROCKET_LAUNCHER)
308 self.weapon = WEP_ROCKET_LAUNCHER;
309 self.currentammo = self.ammo_rockets;
310 self.armorvalue = 10;
311 self.attack_ranged = soldier_missile_rl;
313 else if (RandomSelection_chosen_float == WEP_UZI)
315 self.weapon = WEP_UZI;
316 self.currentammo = self.ammo_nails;
317 self.armorvalue = 100;
318 self.attack_ranged = soldier_missile_uzi;
320 else if (RandomSelection_chosen_float == WEP_SHOTGUN)
322 self.weapon = WEP_SHOTGUN;
323 self.currentammo = self.ammo_shells;
324 self.armorvalue = 25;
325 self.attack_ranged = soldier_missile_shotgun;
329 self.weapon = WEP_LASER;
330 self.armorvalue = 60;
331 self.currentammo = self.ammo_none;
332 self.attack_ranged = soldier_missile_laser;
335 monster_hook_spawn(); // for post-spawn mods
338 void spawnfunc_monster_soldier ()
340 if not(autocvar_g_monster_soldier) { remove(self); return; }
342 self.monster_spawnfunc = spawnfunc_monster_soldier;
344 if(self.spawnflags & MONSTERFLAG_APPEAR)
346 self.think = func_null;
348 self.use = Monster_Appear;
352 if not (monster_initialize(
354 "models/monsters/soldier.zym",
355 SOLDIER_MIN, SOLDIER_MAX,
357 soldier_die, soldier_spawn))
363 precache_sound ("weapons/shotgun_fire.wav");
364 precache_sound ("weapons/uzi_fire.wav");
365 precache_sound ("weapons/laser_fire.wav");
366 precache_sound ("weapons/reload.wav");
369 // compatibility with old spawns
370 void spawnfunc_monster_army () { spawnfunc_monster_soldier(); }