]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/monsters/monster/soldier.qc
ac5f316127e95de61d0a697c66e18f51d57213a0
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / monsters / monster / soldier.qc
1 #ifndef MENUQC
2 // size
3 const vector SOLDIER_MIN = '-16 -16 -30';
4 const vector SOLDIER_MAX = '16 16 32';
5
6 // model
7 string SOLDIER_MODEL = "models/monsters/soldier.zym";
8
9 #endif
10
11 #ifdef SVQC
12 // cvars
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
31 // animations
32 const float soldier_anim_die1                   = 0;
33 const float soldier_anim_die2                   = 1;
34 const float soldier_anim_draw                   = 2;
35 const float soldier_anim_duck                   = 3;
36 const float soldier_anim_duckwalk               = 4;
37 const float soldier_anim_duckjump               = 5;
38 const float soldier_anim_duckidle               = 6;
39 const float soldier_anim_idle                   = 7;
40 const float soldier_anim_jump                   = 8;
41 const float soldier_anim_pain1                  = 9;
42 const float soldier_anim_pain2                  = 10;
43 const float soldier_anim_shoot                  = 11;
44 const float soldier_anim_taunt                  = 12;
45 const float soldier_anim_run                    = 13;
46 const float soldier_anim_runbackwards   = 14;
47 const float soldier_anim_strafeleft     = 15;
48 const float soldier_anim_straferight    = 16;
49 const float soldier_anim_dead1                  = 17;
50 const float soldier_anim_dead2                  = 18;
51 const float soldier_anim_forwardright   = 19;
52 const float soldier_anim_forwardleft    = 20;
53 const float soldier_anim_backright              = 21;
54 const float soldier_anim_backleft               = 22;
55
56 void soldier_think ()
57 {
58         self.think = soldier_think;
59         self.nextthink = time + self.ticrate;
60         
61         if(self.delay != -1)
62                 self.nextthink = self.delay;
63         
64         if(time < self.attack_finished_single)
65                 monster_move(0, 0, 0, soldier_anim_shoot, soldier_anim_shoot, soldier_anim_shoot);
66         else
67                 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 }
69
70 void soldier_reload ()
71 {
72         self.monster_delayedattack = func_null; // out of ammo, don't keep attacking
73         self.delay = -1;
74         monsters_setframe(soldier_anim_draw);
75         self.attack_finished_single = time + 2;
76         self.currentammo = autocvar_g_monster_soldier_ammo;
77         sound (self, CH_SHOTS, "weapons/reload.wav", VOL_BASE, ATTN_LARGE);
78 }
79
80 .float grunt_cycles;
81 void soldier_uzi_fire ()
82 {
83         self.currentammo -= 1;
84         if(self.currentammo <= 0)
85         {
86                 soldier_reload();
87                 return;
88         }
89                 
90         self.grunt_cycles += 1;
91         
92         if(self.grunt_cycles > autocvar_g_monster_soldier_attack_uzi_bullets)
93         {
94                 self.monster_delayedattack = func_null;
95                 self.delay = -1;
96                 return;
97         }
98         
99         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);
100         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);
101         endFireBallisticBullet();
102         
103         self.delay = time + 0.1;
104         self.monster_delayedattack = soldier_uzi_fire;
105 }
106
107 void soldier_shotgun_fire()
108 {
109         float sc;
110         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);
111         for (sc = 0;sc < autocvar_g_monster_soldier_attack_shotgun_bullets;sc = sc + 1)
112                 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);
113         endFireBallisticBullet();
114 }
115
116 float soldier_attack()
117 {
118         monsters_setframe(soldier_anim_shoot);
119         makevectors(self.angles);
120         
121         if(self.currentammo <= 0)
122         {
123                 soldier_reload();
124                 return FALSE;
125         }
126         
127         self.grunt_cycles = 0;
128         
129         switch(self.weapon)
130         {
131                 case WEP_ROCKET_LAUNCHER:
132                 {
133                         self.currentammo -= 1;
134                         self.attack_finished_single = time + 0.8;
135                         W_Rocket_Attack();
136                         return TRUE;
137                 }
138                 case WEP_SHOTGUN:
139                 {
140                         self.currentammo -= 1;
141                         self.attack_finished_single = time + 0.8;
142                         soldier_shotgun_fire();
143                         return TRUE;
144                 }
145                 case WEP_UZI:
146                 {
147                         self.attack_finished_single = time + 0.8;
148                         self.delay = time + 0.1;
149                         self.monster_delayedattack = soldier_uzi_fire;
150                         return TRUE;
151                 }
152                 case WEP_LASER:
153                 {
154                         self.attack_finished_single = time + 0.8;
155                         W_Laser_Attack(0);
156                         return TRUE;
157                 }
158                 default:
159                         return FALSE; // no weapon?
160         }
161 }
162
163 void soldier_melee ()
164 {
165         monsters_setframe(soldier_anim_shoot);
166         self.attack_finished_single = time + 0.8;
167         monster_melee(self.enemy, autocvar_g_monster_soldier_melee_damage, 0.3, DEATH_MONSTER_MARINE_SLAP, TRUE);
168 }
169
170 void soldier_die()
171 {
172         Monster_CheckDropCvars ("soldier");
173         
174         self.think = Monster_Fade;
175         self.nextthink = time + 5;
176         monsters_setframe((random() > 0.5) ? soldier_anim_die1 : soldier_anim_die2);
177                 
178         monster_hook_death(); // for post-death mods
179 }
180
181 void soldier_spawn ()
182 {
183         if not(self.health)
184                 self.health = autocvar_g_monster_soldier_health * self.scale;
185
186         self.damageforcescale   = 0.003;
187         self.classname                  = "monster_soldier";
188         self.checkattack                = GenericCheckAttack;
189         self.attack_melee               = soldier_melee;
190         self.attack_ranged              = soldier_attack;
191         self.nextthink                  = time + random() * 0.5 + 0.1;
192         self.think                              = soldier_think;
193         self.currentammo                = 3;
194         self.sprite_height              = 45;
195         self.items                              = (IT_SHELLS | IT_ROCKETS | IT_NAILS);
196         
197         monsters_setframe(soldier_anim_draw);
198         
199         monster_setupsounds("soldier");
200         
201         setmodel(self, SOLDIER_MODEL);
202         
203         RandomSelection_Init();
204         RandomSelection_Add(world, WEP_LASER, string_null, autocvar_g_monster_soldier_weapon_laser_chance, 1);
205         RandomSelection_Add(world, WEP_SHOTGUN, string_null, autocvar_g_monster_soldier_weapon_shotgun_chance, 1);
206         RandomSelection_Add(world, WEP_UZI, string_null, autocvar_g_monster_soldier_weapon_machinegun_chance, 1);
207         RandomSelection_Add(world, WEP_ROCKET_LAUNCHER, string_null, autocvar_g_monster_soldier_weapon_rocketlauncher_chance, 1);
208         
209         self.weaponentity = spawn();
210         self.weaponentity.movetype = MOVETYPE_NOCLIP;
211         self.weaponentity.team = self.team;
212         self.weaponentity.solid = SOLID_NOT;
213         self.weaponentity.owner = self.weaponentity.realowner = self;
214         setmodel(self.weaponentity, "models/weapons/v_seeker.md3");
215         setattachment(self.weaponentity, self, "bip01 r hand");
216         
217         self.armorvalue = bound(0.5, random(), 1);
218         self.weapon = RandomSelection_chosen_float;
219
220         monster_hook_spawn(); // for post-spawn mods
221 }
222
223 void spawnfunc_monster_soldier ()
224 {       
225         if not(autocvar_g_monster_soldier) { remove(self); return; }
226         
227         self.monster_spawnfunc = spawnfunc_monster_soldier;
228         
229         if(Monster_CheckAppearFlags(self))
230                 return;
231                 
232         precache_model("models/weapons/v_seeker.md3");
233         precache_model(SOLDIER_MODEL);
234         
235         if not (monster_initialize(
236                          "Marine", MONSTER_MARINE,
237                          SOLDIER_MIN, SOLDIER_MAX,
238                          FALSE,
239                          soldier_die, soldier_spawn))
240         {
241                 remove(self);
242                 return;
243         }
244 }
245
246 // compatibility with old spawns
247 void spawnfunc_monster_army () { spawnfunc_monster_soldier(); }
248
249 #endif // SVQC