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_stand 0
20 #define soldier_anim_death1 1
21 #define soldier_anim_death2 2
22 #define soldier_anim_reload 3
23 #define soldier_anim_pain1 4
24 #define soldier_anim_pain2 5
25 #define soldier_anim_pain3 6
26 #define soldier_anim_run 7
27 #define soldier_anim_shoot 8
28 #define soldier_anim_prowl 9
32 self.think = soldier_think;
33 self.nextthink = time + 0.1;
36 self.nextthink = self.delay;
38 if(time < self.attack_finished_single)
39 monster_move(0, 0, 0, soldier_anim_shoot, soldier_anim_shoot, soldier_anim_shoot);
41 monster_move(autocvar_g_monster_soldier_speed_run, autocvar_g_monster_soldier_speed_walk, 50, soldier_anim_run, soldier_anim_prowl, soldier_anim_stand);
44 void soldier_reload ()
46 self.frame = soldier_anim_reload;
47 self.attack_finished_single = time + 2;
48 self.currentammo = autocvar_g_monster_soldier_ammo;
49 sound (self, CH_SHOTS, "weapons/reload.wav", VOL_BASE, ATTN_LARGE);
52 float SoldierCheckAttack ()
54 local vector spot1 = '0 0 0', spot2 = '0 0 0';
55 local entity targ = self.enemy;
56 local float chance = 0;
58 if (self.health <= 0 || targ.health < 1 || targ == world)
61 if (vlen(targ.origin - self.origin) > 2000) // long traces are slow
64 // see if any entities are in the way of the shot
65 spot1 = self.origin + self.view_ofs;
66 spot2 = targ.origin + targ.view_ofs;
68 traceline (spot1, spot2, FALSE, self);
70 if (trace_ent != targ)
71 return FALSE; // don't have a clear shot
75 return FALSE; // sight line crossed contents
77 if(self.monster_delayedattack && self.delay != -1)
82 self.monster_delayedattack();
86 if (time < self.attack_finished_single)
89 if (enemy_range() >= 2000)
92 if (enemy_range() <= 120)
94 else if (enemy_range() <= 500)
95 chance = 0.6; // was 0.4
96 else if (enemy_range() <= 1000)
97 chance = 0.3; // was 0.05
102 if (chance > random())
105 if(self.currentammo <= 0 && enemy_range() <= 120)
111 if(self.currentammo <= 0)
117 if (self.attack_ranged())
123 void soldier_laser ()
125 self.frame = soldier_anim_shoot;
126 self.attack_finished_single = time + 0.8;
130 float soldier_missile_laser ()
132 // FIXME: check if it would hit
138 void soldier_uzi_fire ()
140 self.currentammo -= 1;
141 if(self.currentammo <= 0)
144 self.grunt_cycles += 1;
146 if(self.grunt_cycles > autocvar_g_monster_soldier_attack_uzi_bullets)
148 self.monster_delayedattack = func_null;
152 W_UZI_Attack(DEATH_MONSTER_SOLDIER_NAIL);
153 self.delay = time + 0.1;
154 self.monster_delayedattack = soldier_uzi_fire;
159 if(self.currentammo <= 0)
162 self.frame = soldier_anim_shoot;
163 self.attack_finished_single = time + 0.8;
164 self.delay = time + 0.1;
165 self.monster_delayedattack = soldier_uzi_fire;
168 float soldier_missile_uzi ()
170 self.grunt_cycles = 0;
171 // FIXME: check if it would hit
176 void soldier_shotgun ()
178 self.currentammo -= 1;
179 if(self.currentammo <= 0)
182 self.frame = soldier_anim_shoot;
183 self.attack_finished_single = time + 0.8;
187 float soldier_missile_shotgun ()
189 // FIXME: check if it would hit
190 self.grunt_cycles = 0;
197 self.currentammo -= 1;
198 if(self.currentammo <= 0)
201 self.frame = soldier_anim_shoot;
202 self.attack_finished_single = time + 0.8;
206 float soldier_missile_rl ()
208 // FIXME: check if it would hit
215 self.frame = soldier_anim_shoot;
216 self.attack_finished_single = time + 0.8;
217 monster_melee(self.enemy, autocvar_g_monster_soldier_melee_damage, 70, DEATH_MONSTER_SOLDIER_NAIL);
222 Monster_CheckDropCvars ("soldier");
224 self.solid = SOLID_NOT;
225 self.takedamage = DAMAGE_NO;
226 self.event_damage = func_null;
228 self.movetype = MOVETYPE_TOSS;
229 self.think = Monster_Fade;
230 self.nextthink = time + 2.1;
231 self.pain_finished = self.nextthink;
233 if (self.attack_ranged == soldier_missile_uzi)
234 W_ThrowNewWeapon(self, WEP_UZI, 0, self.origin, self.velocity);
235 else if (self.attack_ranged == soldier_missile_shotgun)
236 W_ThrowNewWeapon(self, WEP_SHOTGUN, 0, self.origin, self.velocity);
237 else if (self.attack_ranged == soldier_missile_rl)
238 W_ThrowNewWeapon(self, WEP_ROCKET_LAUNCHER, 0, self.origin, self.velocity);
240 W_ThrowNewWeapon(self, WEP_LASER, 0, self.origin, self.velocity);
243 self.frame = soldier_anim_death1;
245 self.frame = soldier_anim_death2;
247 monster_hook_death(); // for post-death mods
250 void soldier_spawn ()
253 self.health = autocvar_g_monster_soldier_health * self.scale;
255 self.damageforcescale = 0.003;
256 self.classname = "monster_soldier";
257 self.checkattack = SoldierCheckAttack;
258 self.attack_melee = soldier_bash;
259 self.nextthink = time + random() * 0.5 + 0.1;
260 self.think = soldier_think;
261 self.sprite_height = 30 * self.scale;
262 self.items = (IT_SHELLS | IT_ROCKETS | IT_NAILS);
264 RandomSelection_Init();
265 RandomSelection_Add(world, WEP_LASER, string_null, autocvar_g_monster_soldier_weapon_laser_chance, 1);
266 RandomSelection_Add(world, WEP_SHOTGUN, string_null, autocvar_g_monster_soldier_weapon_shotgun_chance, 1);
267 RandomSelection_Add(world, WEP_UZI, string_null, autocvar_g_monster_soldier_weapon_machinegun_chance, 1);
268 RandomSelection_Add(world, WEP_ROCKET_LAUNCHER, string_null, autocvar_g_monster_soldier_weapon_rocketlauncher_chance, 1);
270 if (RandomSelection_chosen_float == WEP_ROCKET_LAUNCHER)
272 self.weapon = WEP_ROCKET_LAUNCHER;
273 self.currentammo = self.ammo_rockets;
274 self.armorvalue = 10;
275 self.attack_ranged = soldier_missile_rl;
277 else if (RandomSelection_chosen_float == WEP_UZI)
279 self.weapon = WEP_UZI;
280 self.currentammo = self.ammo_nails;
281 self.armorvalue = 100;
282 self.attack_ranged = soldier_missile_uzi;
284 else if (RandomSelection_chosen_float == WEP_SHOTGUN)
286 self.weapon = WEP_SHOTGUN;
287 self.currentammo = self.ammo_shells;
288 self.armorvalue = 25;
289 self.attack_ranged = soldier_missile_shotgun;
293 self.weapon = WEP_LASER;
294 self.armorvalue = 60;
295 self.currentammo = self.ammo_none;
296 self.attack_ranged = soldier_missile_laser;
299 monster_hook_spawn(); // for post-spawn mods
302 void spawnfunc_monster_soldier ()
304 if not(autocvar_g_monster_soldier)
310 self.monster_spawnfunc = spawnfunc_monster_soldier;
312 if(self.spawnflags & MONSTERFLAG_APPEAR)
314 self.think = func_null;
316 self.use = Monster_Appear;
322 if not (monster_initialize(
324 "models/monsters/soldier.mdl",
325 SOLDIER_MIN, SOLDIER_MAX,
327 soldier_die, soldier_spawn))
333 precache_sound ("weapons/shotgun_fire.wav");
334 precache_sound ("weapons/uzi_fire.wav");
335 precache_sound ("weapons/laser_fire.wav");
336 precache_sound ("weapons/reload.wav");
339 // compatibility with old spawns
340 void spawnfunc_monster_army () { spawnfunc_monster_soldier(); }