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