]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/vehicles/unit/spiderbot.qc
de3cdbb7afa1b0b6931f236b70d9dc7a538c20c8
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / vehicles / unit / spiderbot.qc
1 #ifndef VEHICLE_SPIDERBOT
2 #define VEHICLE_SPIDERBOT
3 int v_spiderbot(int);
4 REGISTER_VEHICLE_SIMPLE(
5 /* VEH_##id   */ SPIDERBOT,
6 /* function   */ v_spiderbot,
7 /* spawnflags */ VHF_DMGSHAKE,
8 /* mins,maxs  */ '-75 -75 10', '75 75 125',
9 /* model          */ "models/vehicles/spiderbot.dpm",
10 /* head_model */ "models/vehicles/spiderbot_top.dpm",
11 /* hud_model  */ "models/vehicles/spiderbot_cockpit.dpm",
12 /* tags           */ "tag_head", "tag_hud", "",
13 /* netname        */ "spiderbot",
14 /* fullname   */ _("Spiderbot")
15 );
16 #endif
17
18 #ifdef IMPLEMENTATION
19
20 const int SBRM_FIRST = 1;
21 const int SBRM_VOLLY = 1;
22 const int SBRM_GUIDE = 2;
23 const int SBRM_ARTILLERY = 3;
24 const int SBRM_LAST = 3;
25
26 #ifdef SVQC
27 bool autocvar_g_vehicle_spiderbot;
28
29 float autocvar_g_vehicle_spiderbot_respawntime;
30
31 float autocvar_g_vehicle_spiderbot_speed_stop;
32 float autocvar_g_vehicle_spiderbot_speed_strafe;
33 float autocvar_g_vehicle_spiderbot_speed_walk;
34 float autocvar_g_vehicle_spiderbot_speed_run = 700;
35 float autocvar_g_vehicle_spiderbot_turnspeed;
36 float autocvar_g_vehicle_spiderbot_turnspeed_strafe;
37 float autocvar_g_vehicle_spiderbot_movement_inertia;
38
39 float autocvar_g_vehicle_spiderbot_springlength;
40 float autocvar_g_vehicle_spiderbot_springup;
41 float autocvar_g_vehicle_spiderbot_springblend;
42 float autocvar_g_vehicle_spiderbot_tiltlimit;
43
44 float autocvar_g_vehicle_spiderbot_head_pitchlimit_down;
45 float autocvar_g_vehicle_spiderbot_head_pitchlimit_up;
46 float autocvar_g_vehicle_spiderbot_head_turnlimit;
47 float autocvar_g_vehicle_spiderbot_head_turnspeed;
48
49 int autocvar_g_vehicle_spiderbot_health;
50 float autocvar_g_vehicle_spiderbot_health_regen;
51 float autocvar_g_vehicle_spiderbot_health_regen_pause;
52
53 int autocvar_g_vehicle_spiderbot_shield;
54 float autocvar_g_vehicle_spiderbot_shield_regen;
55 float autocvar_g_vehicle_spiderbot_shield_regen_pause;
56
57 float autocvar_g_vehicle_spiderbot_minigun_damage;
58 float autocvar_g_vehicle_spiderbot_minigun_refire;
59 float autocvar_g_vehicle_spiderbot_minigun_spread;
60 int autocvar_g_vehicle_spiderbot_minigun_ammo_cost;
61 int autocvar_g_vehicle_spiderbot_minigun_ammo_max;
62 int autocvar_g_vehicle_spiderbot_minigun_ammo_regen;
63 float autocvar_g_vehicle_spiderbot_minigun_ammo_regen_pause;
64 float autocvar_g_vehicle_spiderbot_minigun_force;
65 float autocvar_g_vehicle_spiderbot_minigun_solidpenetration;
66
67 float autocvar_g_vehicle_spiderbot_rocket_damage;
68 float autocvar_g_vehicle_spiderbot_rocket_force;
69 float autocvar_g_vehicle_spiderbot_rocket_radius;
70 float autocvar_g_vehicle_spiderbot_rocket_speed;
71 float autocvar_g_vehicle_spiderbot_rocket_spread;
72 float autocvar_g_vehicle_spiderbot_rocket_refire;
73 float autocvar_g_vehicle_spiderbot_rocket_refire2;
74 float autocvar_g_vehicle_spiderbot_rocket_reload;
75 float autocvar_g_vehicle_spiderbot_rocket_health;
76 float autocvar_g_vehicle_spiderbot_rocket_noise;
77 float autocvar_g_vehicle_spiderbot_rocket_turnrate;
78 float autocvar_g_vehicle_spiderbot_rocket_lifetime;
79
80 vector autocvar_g_vehicle_spiderbot_bouncepain;
81
82 void spiderbot_rocket_artillery()
83 {
84         self.nextthink = time;
85         UpdateCSQCProjectile(self);
86 }
87
88 void spiderbot_rocket_unguided()
89 {
90         vector newdir, olddir;
91
92         self.nextthink  = time;
93
94         olddir = normalize(self.velocity);
95         newdir = normalize(self.pos1 - self.origin) + randomvec() * autocvar_g_vehicle_spiderbot_rocket_noise;
96         self.velocity = normalize(olddir + newdir * autocvar_g_vehicle_spiderbot_rocket_turnrate) * autocvar_g_vehicle_spiderbot_rocket_speed;
97
98         UpdateCSQCProjectile(self);
99
100         if (self.owner.deadflag != DEAD_NO || self.cnt < time || vlen(self.pos1 - self.origin) < 16)
101                 self.use();
102 }
103
104 void spiderbot_rocket_guided()
105 {
106         vector newdir, olddir;
107
108         self.nextthink  = time;
109
110         if(!self.realowner.vehicle)
111                 self.think = spiderbot_rocket_unguided;
112
113         crosshair_trace(self.realowner);
114         olddir = normalize(self.velocity);
115         newdir = normalize(trace_endpos - self.origin) + randomvec() * autocvar_g_vehicle_spiderbot_rocket_noise;
116         self.velocity = normalize(olddir + newdir * autocvar_g_vehicle_spiderbot_rocket_turnrate) * autocvar_g_vehicle_spiderbot_rocket_speed;
117
118         UpdateCSQCProjectile(self);
119
120         if (self.owner.deadflag != DEAD_NO || self.cnt < time)
121                 self.use();
122 }
123
124 void spiderbot_guide_release()
125 {
126         entity rkt;
127         rkt = findchainentity(realowner, self.owner);
128         if(!rkt)
129                 return;
130
131         crosshair_trace(self.owner);
132         while(rkt)
133         {
134                 if(rkt.think == spiderbot_rocket_guided)
135                 {
136                         rkt.pos1 = trace_endpos;
137                         rkt.think = spiderbot_rocket_unguided;
138                 }
139                 rkt = rkt.chain;
140         }
141 }
142
143 float spiberbot_calcartillery_flighttime;
144 vector spiberbot_calcartillery(vector org, vector tgt, float ht)
145 {
146         float grav, sdist, zdist, vs, vz, jumpheight;
147         vector sdir;
148
149         grav  = autocvar_sv_gravity;
150         zdist = tgt_z - org_z;
151         sdist = vlen(tgt - org - zdist * '0 0 1');
152         sdir  = normalize(tgt - org - zdist * '0 0 1');
153
154         // how high do we need to go?
155         jumpheight = fabs(ht);
156         if(zdist > 0)
157                 jumpheight = jumpheight + zdist;
158
159         // push so high...
160         vz = sqrt(2 * grav * jumpheight); // NOTE: sqrt(positive)!
161
162         // we start with downwards velocity only if it's a downjump and the jump apex should be outside the jump!
163         if(ht < 0)
164                 if(zdist < 0)
165                         vz = -vz;
166
167         vector solution;
168         solution = solve_quadratic(0.5 * grav, -vz, zdist); // equation "z(ti) = zdist"
169         // ALWAYS solvable because jumpheight >= zdist
170         if(!solution_z)
171                 solution_y = solution_x; // just in case it is not solvable due to roundoff errors, assume two equal solutions at their center (this is mainly for the usual case with ht == 0)
172         if(zdist == 0)
173                 solution_x = solution_y; // solution_x is 0 in this case, so don't use it, but rather use solution_y (which will be sqrt(0.5 * jumpheight / grav), actually)
174
175         if(zdist < 0)
176         {
177                 // down-jump
178                 if(ht < 0)
179                 {
180                         // almost straight line type
181                         // jump apex is before the jump
182                         // we must take the larger one
183                         spiberbot_calcartillery_flighttime = solution_y;
184                 }
185                 else
186                 {
187                         // regular jump
188                         // jump apex is during the jump
189                         // we must take the larger one too
190                         spiberbot_calcartillery_flighttime = solution_y;
191                 }
192         }
193         else
194         {
195                 // up-jump
196                 if(ht < 0)
197                 {
198                         // almost straight line type
199                         // jump apex is after the jump
200                         // we must take the smaller one
201                         spiberbot_calcartillery_flighttime = solution_x;
202                 }
203                 else
204                 {
205                         // regular jump
206                         // jump apex is during the jump
207                         // we must take the larger one
208                         spiberbot_calcartillery_flighttime = solution_y;
209                 }
210         }
211         vs = sdist / spiberbot_calcartillery_flighttime;
212
213         // finally calculate the velocity
214         return sdir * vs + '0 0 1' * vz;
215 }
216
217 void spiderbot_rocket_do()
218 {
219         vector v;
220         entity rocket = world;
221
222         if (self.wait != -10)
223         {
224                 if (self.owner.BUTTON_ATCK2 && self.vehicle_weapon2mode == SBRM_GUIDE)
225                 {
226                         if (self.wait == 1)
227                         if (self.tur_head.frame == 9 || self.tur_head.frame == 1)
228                         {
229                                 if(self.gun2.cnt < time && self.tur_head.frame == 9)
230                                         self.tur_head.frame = 1;
231
232                                 return;
233                         }
234                         self.wait = 1;
235                 }
236                 else
237                 {
238                         if(self.wait)
239                                 spiderbot_guide_release();
240
241                         self.wait = 0;
242                 }
243         }
244
245         if(self.gun2.cnt > time)
246                 return;
247
248         if (self.tur_head.frame >= 9)
249         {
250                 self.tur_head.frame = 1;
251                 self.wait = 0;
252         }
253
254         if(self.wait != -10)
255         if(!self.owner.BUTTON_ATCK2)
256                 return;
257                 
258         if(forbidWeaponUse(self.owner))
259                 return;
260
261         v = gettaginfo(self.tur_head,gettagindex(self.tur_head,"tag_fire"));
262
263         switch(self.vehicle_weapon2mode)
264         {
265                 case SBRM_VOLLY:
266                         rocket = vehicles_projectile("spiderbot_rocket_launch", "weapons/rocket_fire.wav",
267                                                                    v, normalize(randomvec() * autocvar_g_vehicle_spiderbot_rocket_spread + v_forward) * autocvar_g_vehicle_spiderbot_rocket_speed,
268                                                                    autocvar_g_vehicle_spiderbot_rocket_damage, autocvar_g_vehicle_spiderbot_rocket_radius, autocvar_g_vehicle_spiderbot_rocket_force, 1,
269                                                                    DEATH_VH_SPID_ROCKET, PROJECTILE_SPIDERROCKET, autocvar_g_vehicle_spiderbot_rocket_health, false, true, self.owner);
270                         crosshair_trace(self.owner);
271                         float _dist = (random() * autocvar_g_vehicle_spiderbot_rocket_radius) + vlen(v - trace_endpos);
272                         _dist -= (random() * autocvar_g_vehicle_spiderbot_rocket_radius) ;
273                         rocket.nextthink  = time + (_dist / autocvar_g_vehicle_spiderbot_rocket_speed);
274                         rocket.think     = vehicles_projectile_explode;
275
276                         if(self.owner.BUTTON_ATCK2 && self.tur_head.frame == 1)
277                                 self.wait = -10;
278                         break;
279                 case SBRM_GUIDE:
280                         rocket = vehicles_projectile("spiderbot_rocket_launch", "weapons/rocket_fire.wav",
281                                                                    v, normalize(v_forward) * autocvar_g_vehicle_spiderbot_rocket_speed,
282                                                                    autocvar_g_vehicle_spiderbot_rocket_damage, autocvar_g_vehicle_spiderbot_rocket_radius, autocvar_g_vehicle_spiderbot_rocket_force, 1,
283                                                                    DEATH_VH_SPID_ROCKET, PROJECTILE_SPIDERROCKET, autocvar_g_vehicle_spiderbot_rocket_health, false, false, self.owner);
284                         crosshair_trace(self.owner);
285                         rocket.pos1        = trace_endpos;
286                         rocket.nextthink  = time;
287                         rocket.think      = spiderbot_rocket_guided;
288
289
290                 break;
291                 case SBRM_ARTILLERY:
292                         rocket = vehicles_projectile("spiderbot_rocket_launch", "weapons/rocket_fire.wav",
293                                                                    v, normalize(v_forward) * autocvar_g_vehicle_spiderbot_rocket_speed,
294                                                                    autocvar_g_vehicle_spiderbot_rocket_damage, autocvar_g_vehicle_spiderbot_rocket_radius, autocvar_g_vehicle_spiderbot_rocket_force, 1,
295                                                                    DEATH_VH_SPID_ROCKET, PROJECTILE_SPIDERROCKET, autocvar_g_vehicle_spiderbot_rocket_health, false, true, self.owner);
296
297                         crosshair_trace(self.owner);
298
299                         rocket.pos1        = trace_endpos + randomvec() * (0.75 * autocvar_g_vehicle_spiderbot_rocket_radius);
300                         rocket.pos1_z      = trace_endpos_z;
301
302                         traceline(v, v + '0 0 1' * MAX_SHOT_DISTANCE, MOVE_WORLDONLY, self);
303                         float h1 = 0.75 * vlen(v - trace_endpos);
304
305                         //v = trace_endpos;
306                         traceline(v , rocket.pos1 + '0 0 1' * MAX_SHOT_DISTANCE, MOVE_WORLDONLY, self);
307                         float h2 = 0.75 * vlen(rocket.pos1 - v);
308
309                         rocket.velocity  = spiberbot_calcartillery(v, rocket.pos1, ((h1 < h2) ? h1 : h2));
310                         rocket.movetype  = MOVETYPE_TOSS;
311                         rocket.gravity   = 1;
312                         //rocket.think   = spiderbot_rocket_artillery;
313                 break;
314         }
315         rocket.classname  = "spiderbot_rocket";
316
317         rocket.cnt = time + autocvar_g_vehicle_spiderbot_rocket_lifetime;
318
319         self.tur_head.frame += 1;
320         if (self.tur_head.frame == 9)
321                 self.attack_finished_single = autocvar_g_vehicle_spiderbot_rocket_reload;
322         else
323                 self.attack_finished_single = ((self.vehicle_weapon2mode ==  SBRM_VOLLY) ? autocvar_g_vehicle_spiderbot_rocket_refire2 : autocvar_g_vehicle_spiderbot_rocket_refire);
324
325         self.gun2.cnt = time + self.attack_finished_single;
326 }
327
328 .float jump_delay;
329 float spiderbot_frame()
330 {
331         vector ad, vf;
332         entity player, spider;
333         float ftmp;
334
335         if(intermission_running)
336         {
337                 self.vehicle.velocity = '0 0 0';
338                 self.vehicle.avelocity = '0 0 0';
339                 return 1;
340         }
341
342         player = self;
343         spider = self.vehicle;
344         self   = spider;
345
346         vehicles_painframe();
347
348         player.BUTTON_ZOOM        = 0;
349         player.BUTTON_CROUCH    = 0;
350         player.switchweapon      = 0;
351         player.vehicle_weapon2mode = spider.vehicle_weapon2mode;
352
353
354 #if 1 // 0 to enable per-gun impact aux crosshairs
355         // Avarage gun impact point's -> aux cross
356         ad = gettaginfo(spider.tur_head, gettagindex(spider.tur_head, "tag_hardpoint01"));
357         vf = v_forward;
358         ad += gettaginfo(spider.tur_head, gettagindex(spider.tur_head, "tag_hardpoint02"));
359         vf += v_forward;
360         ad = ad * 0.5;
361         v_forward = vf * 0.5;
362         traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, spider);
363         UpdateAuxiliaryXhair(player, trace_endpos, ('1 0 0' * player.vehicle_reload1) + ('0 1 0' * (1 - player.vehicle_reload1)), 0);
364 #else
365         ad = gettaginfo(spider.gun1, gettagindex(spider.gun1, "barrels"));
366         traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, spider);
367         UpdateAuxiliaryXhair(player, trace_endpos, ('1 0 0' * player.vehicle_reload1) + ('0 1 0' * (1 - player.vehicle_reload1)), 0);
368         vf = ad;
369         ad = gettaginfo(spider.gun2, gettagindex(spider.gun2, "barrels"));
370         traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, spider);
371         UpdateAuxiliaryXhair(player, trace_endpos, ('1 0 0' * player.vehicle_reload1) + ('0 1 0' * (1 - player.vehicle_reload1)), 1);
372         ad = 0.5 * (ad + vf);
373 #endif
374
375         crosshair_trace(player);
376         ad = vectoangles(normalize(trace_endpos - ad));
377         ad = AnglesTransform_ToAngles(AnglesTransform_LeftDivide(AnglesTransform_FromAngles(spider.angles), AnglesTransform_FromAngles(ad))) - spider.tur_head.angles;
378         ad = AnglesTransform_Normalize(ad, true);
379         //UpdateAuxiliaryXhair(player, trace_endpos, ('1 0 0' * player.vehicle_reload2) + ('0 1 0' * (1 - player.vehicle_reload2)), 2);
380
381         // Rotate head
382         ftmp = autocvar_g_vehicle_spiderbot_head_turnspeed * sys_frametime;
383         ad_y = bound(-ftmp, ad_y, ftmp);
384         spider.tur_head.angles_y = bound(autocvar_g_vehicle_spiderbot_head_turnlimit * -1, spider.tur_head.angles_y + ad_y, autocvar_g_vehicle_spiderbot_head_turnlimit);
385
386         // Pitch head
387         ad_x = bound(ftmp * -1, ad_x, ftmp);
388         spider.tur_head.angles_x = bound(autocvar_g_vehicle_spiderbot_head_pitchlimit_down, spider.tur_head.angles_x + ad_x, autocvar_g_vehicle_spiderbot_head_pitchlimit_up);
389
390
391         //fixedmakevectors(spider.angles);
392         makevectors(spider.angles + '-2 0 0' * spider.angles_x);
393
394         movelib_groundalign4point(autocvar_g_vehicle_spiderbot_springlength, autocvar_g_vehicle_spiderbot_springup, autocvar_g_vehicle_spiderbot_springblend, autocvar_g_vehicle_spiderbot_tiltlimit);
395
396         if(spider.flags & FL_ONGROUND)
397                 spider.jump_delay = time; // reset now so movement can begin
398
399         //if(spider.flags & FL_ONGROUND)
400         {
401                 if(spider.flags & FL_ONGROUND)
402                 if(spider.frame == 4 && self.tur_head.wait != 0)
403                 {
404                         sound (self, CH_TRIGGER_SINGLE, "vehicles/spiderbot_land.wav", VOL_VEHICLEENGINE, ATTEN_NORM);
405                         spider.frame = 5;
406                 }
407
408                 if(!player.BUTTON_JUMP)
409                         spider.BUTTON_JUMP = 0;
410
411                 if((spider.flags & FL_ONGROUND) && player.BUTTON_JUMP && !spider.BUTTON_JUMP && self.tur_head.wait < time)
412                 {
413                         sound (self, CH_TRIGGER_SINGLE, "vehicles/spiderbot_jump.wav", VOL_VEHICLEENGINE, ATTEN_NORM);
414                         //dprint("spiderbot_jump:", ftos(soundlength("vehicles/spiderbot_jump.wav")), "\n");
415                         self.delay = 0;
416
417                         self.tur_head.wait = time + 2;
418                         spider.jump_delay = time + 2;
419                         spider.BUTTON_JUMP = 1; // set spider's jump
420                         //player.BUTTON_JUMP = 0;
421
422                         vector movefix = '0 0 0';
423                         if(player.movement_x > 0) movefix_x = 1;
424                         if(player.movement_x < 0) movefix_x = -1;
425                         if(player.movement_y > 0) movefix_y = 1;
426                         if(player.movement_y < 0) movefix_y = -1;
427
428                         vector rt = movefix_y * v_right;
429                         vector sd = movefix_x * v_forward;
430                         if(movefix_y == 0 && movefix_x == 0)
431                                 sd = v_forward; // always do forward
432
433                         spider.flags &= ~FL_ONGROUND;
434
435                         spider.velocity = sd * 700 + rt * 600 + v_up * 600;
436                         spider.frame = 4;
437                 }
438                 else if(time >= spider.jump_delay)
439                 {
440                         if(vlen(player.movement) == 0)
441                         {
442                                 if(spider.flags & FL_ONGROUND)
443                                 {
444                                         if(self.sound_nexttime < time || self.delay != 3)
445                                         {
446                                                 self.delay = 3;
447                                                 self.sound_nexttime = time + 6.486500; //soundlength("vehicles/spiderbot_idle.wav");
448                                                 //dprint("spiderbot_idle:", ftos(soundlength("vehicles/spiderbot_idle.wav")), "\n");
449                                                 sound (self, CH_TRIGGER_SINGLE, "vehicles/spiderbot_idle.wav", VOL_VEHICLEENGINE, ATTEN_NORM);
450                                         }
451                                         movelib_beak_simple(autocvar_g_vehicle_spiderbot_speed_stop);
452                                         spider.frame = 5;
453                                 }
454                         }
455                         else
456                         {
457                                 // Turn Body
458                                 if(player.movement_x == 0 && player.movement_y != 0)
459                                         ftmp = autocvar_g_vehicle_spiderbot_turnspeed_strafe * sys_frametime;
460                                 else
461                                         ftmp = autocvar_g_vehicle_spiderbot_turnspeed * sys_frametime;
462
463                                 ftmp = bound(-ftmp, spider.tur_head.angles_y, ftmp);
464                                 spider.angles_y = anglemods(spider.angles_y + ftmp);
465                                 spider.tur_head.angles_y -= ftmp;
466
467                                 if(player.movement_x != 0)
468                                 {
469                                         if(player.movement_x > 0)
470                                         {
471                                                 player.movement_x = 1;
472                                                 if(spider.flags & FL_ONGROUND)
473                                                         spider.frame = 0;
474                                         }
475                                         else if(player.movement_x < 0)
476                                         {
477                                                 player.movement_x = -1;
478                                                 if(spider.flags & FL_ONGROUND)
479                                                         spider.frame = 1;
480                                         }
481                                         player.movement_y = 0;
482                                         float oldvelz = spider.velocity_z;
483                                         movelib_move_simple(normalize(v_forward * player.movement_x),((player.BUTTON_JUMP) ? autocvar_g_vehicle_spiderbot_speed_run : autocvar_g_vehicle_spiderbot_speed_walk),autocvar_g_vehicle_spiderbot_movement_inertia);
484                                         spider.velocity_z = oldvelz;
485                                         float g = ((autocvar_sv_gameplayfix_gravityunaffectedbyticrate) ? 0.5 : 1);
486                                         if(spider.velocity_z <= 20) // not while jumping
487                                                 spider.velocity_z -= g * sys_frametime * autocvar_sv_gravity;
488                                         if(spider.flags & FL_ONGROUND)
489                                         if(self.sound_nexttime < time || self.delay != 1)
490                                         {
491                                                 self.delay = 1;
492                                                 self.sound_nexttime = time + 6.486500; //soundlength("vehicles/spiderbot_walk.wav");
493                                                 sound (self, CH_TRIGGER_SINGLE, "vehicles/spiderbot_walk.wav", VOL_VEHICLEENGINE, ATTEN_NORM);
494                                                 //dprint("spiderbot_walk:", ftos(soundlength("vehicles/spiderbot_walk.wav")), "\n");
495                                         }
496                                 }
497                                 else if(player.movement_y != 0)
498                                 {
499                                         if(player.movement_y < 0)
500                                         {
501                                                 player.movement_y = -1;
502                                                 if(spider.flags & FL_ONGROUND)
503                                                         spider.frame = 2;
504                                         }
505                                         else if(player.movement_y > 0)
506                                         {
507                                                 player.movement_y = 1;
508                                                 if(spider.flags & FL_ONGROUND)
509                                                         spider.frame = 3;
510                                         }
511
512                                         float oldvelz = spider.velocity_z;
513                                         movelib_move_simple(normalize(v_right * player.movement_y),autocvar_g_vehicle_spiderbot_speed_strafe,autocvar_g_vehicle_spiderbot_movement_inertia);
514                                         spider.velocity_z = oldvelz;
515                                         float g = ((autocvar_sv_gameplayfix_gravityunaffectedbyticrate) ? 0.5 : 1);
516                                         if(spider.velocity_z <= 20) // not while jumping
517                                                 spider.velocity_z -= g * sys_frametime * autocvar_sv_gravity;
518                                         if(spider.flags & FL_ONGROUND)
519                                         if(self.sound_nexttime < time || self.delay != 2)
520                                         {
521                                                 self.delay = 2;
522                                                 self.sound_nexttime = time + 6.486500; //soundlength("vehicles/spiderbot_strafe.wav");
523                                                 sound (self, CH_TRIGGER_SINGLE, "vehicles/spiderbot_strafe.wav", VOL_VEHICLEENGINE, ATTEN_NORM);
524                                                 //dprint("spiderbot_strafe:", ftos(soundlength("vehicles/spiderbot_strafe.wav")), "\n");
525                                         }
526                                 }
527                         }
528                 }
529         }
530
531         self.angles_x = bound(-autocvar_g_vehicle_spiderbot_tiltlimit, self.angles_x, autocvar_g_vehicle_spiderbot_tiltlimit);
532         self.angles_z = bound(-autocvar_g_vehicle_spiderbot_tiltlimit, self.angles_z, autocvar_g_vehicle_spiderbot_tiltlimit);
533
534         if(!forbidWeaponUse(player))
535         if(player.BUTTON_ATCK)
536         {
537                 spider.cnt = time;
538                 if(spider.vehicle_ammo1 >= autocvar_g_vehicle_spiderbot_minigun_ammo_cost && spider.tur_head.attack_finished_single <= time)
539                 {
540                         entity gun;
541                         vector v;
542                         spider.misc_bulletcounter += 1;
543
544                         self = player;
545
546                         gun = (spider.misc_bulletcounter % 2) ? spider.gun1 : spider.gun2;
547
548                         v = gettaginfo(gun, gettagindex(gun, "barrels"));
549                         v_forward = normalize(v_forward);
550                         v += v_forward * 50;
551
552                         fireBullet(v, v_forward, autocvar_g_vehicle_spiderbot_minigun_spread, autocvar_g_vehicle_spiderbot_minigun_solidpenetration,
553                                 autocvar_g_vehicle_spiderbot_minigun_damage, autocvar_g_vehicle_spiderbot_minigun_force, DEATH_VH_SPID_MINIGUN, 0);
554
555                         sound (gun, CH_WEAPON_A, "weapons/uzi_fire.wav", VOL_BASE, ATTEN_NORM);
556                         //trailparticles(self, particleeffectnum("spiderbot_minigun_trail"), v, trace_endpos);
557                         pointparticles(particleeffectnum("spiderbot_minigun_muzzleflash"), v, v_forward * 2500, 1);
558
559                         self = spider;
560
561                         spider.vehicle_ammo1 -= autocvar_g_vehicle_spiderbot_minigun_ammo_cost;
562                         spider.tur_head.attack_finished_single = time + autocvar_g_vehicle_spiderbot_minigun_refire;
563                         player.vehicle_ammo1 = (spider.vehicle_ammo1 / autocvar_g_vehicle_spiderbot_minigun_ammo_max) * 100;
564                         spider.gun1.angles_z += 45;
565                         spider.gun2.angles_z -= 45;
566                         if(spider.gun1.angles_z >= 360)
567                         {
568                                 spider.gun1.angles_z = 0;
569                                 spider.gun2.angles_z = 0;
570                         }
571                 }
572         }
573         else
574                 vehicles_regen(spider.cnt, vehicle_ammo1, autocvar_g_vehicle_spiderbot_minigun_ammo_max,
575                                                                                    autocvar_g_vehicle_spiderbot_minigun_ammo_regen_pause,
576                                                                                    autocvar_g_vehicle_spiderbot_minigun_ammo_regen, frametime, false);
577
578
579         spiderbot_rocket_do();
580
581         if(self.vehicle_flags  & VHF_SHIELDREGEN)
582                 vehicles_regen(spider.dmg_time, vehicle_shield, autocvar_g_vehicle_spiderbot_shield, autocvar_g_vehicle_spiderbot_shield_regen_pause, autocvar_g_vehicle_spiderbot_shield_regen, frametime, true);
583
584         if(self.vehicle_flags  & VHF_HEALTHREGEN)
585                 vehicles_regen(spider.dmg_time, vehicle_health, autocvar_g_vehicle_spiderbot_health, autocvar_g_vehicle_spiderbot_health_regen_pause, autocvar_g_vehicle_spiderbot_health_regen, frametime, false);
586
587         player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0;
588         //player.vehicle_ammo2 = spider.tur_head.frame;
589         player.vehicle_ammo2 = (9 - spider.tur_head.frame) / 8 * 100; // Percentage, like ammo1
590
591         if(spider.gun2.cnt <= time)
592                 player.vehicle_reload2 = 100;
593         else
594                 player.vehicle_reload2 = 100 - ((spider.gun2.cnt - time) / spider.attack_finished_single) * 100;
595
596         setorigin(player, spider.origin + '0 0 1' * spider.maxs_z);
597         player.velocity = spider.velocity;
598
599         VEHICLE_UPDATE_PLAYER(player, health, spiderbot);
600
601         if(self.vehicle_flags & VHF_HASSHIELD)
602                 VEHICLE_UPDATE_PLAYER(player, shield, spiderbot);
603
604         self = player;
605         return 1;
606 }
607
608 void spiderbot_exit(float eject)
609 {
610         entity e;
611         vector spot;
612
613         e = findchain(classname,"spiderbot_rocket");
614         while(e)
615         {
616                 if(e.owner == self.owner)
617                 {
618                         e.realowner = self.owner;
619                         e.owner = world;
620                 }
621                 e = e.chain;
622         }
623
624         self.think = vehicles_think;
625         self.nextthink = time;
626         self.frame = 5;
627         self.movetype = MOVETYPE_WALK;
628
629         if(!self.owner)
630                 return;
631
632         makevectors(self.angles);
633         if(eject)
634         {
635                 spot = self.origin + v_forward * 100 + '0 0 64';
636                 spot = vehicles_findgoodexit(spot);
637                 setorigin(self.owner , spot);
638                 self.owner.velocity = (v_up + v_forward * 0.25) * 750;
639                 self.owner.oldvelocity = self.owner.velocity;
640         }
641         else
642         {
643                 if(vlen(self.velocity) > autocvar_g_vehicle_spiderbot_speed_strafe)
644                 {
645                         self.owner.velocity = normalize(self.velocity) * vlen(self.velocity);
646                         self.owner.velocity_z += 200;
647                         spot = self.origin + v_forward * 128 + '0 0 64';
648                         spot = vehicles_findgoodexit(spot);
649                 }
650                 else
651                 {
652                         self.owner.velocity = self.velocity * 0.5;
653                         self.owner.velocity_z += 10;
654                         spot = self.origin + v_forward * 256 + '0 0 64';
655                         spot = vehicles_findgoodexit(spot);
656                 }
657                 self.owner.oldvelocity = self.owner.velocity;
658                 setorigin(self.owner , spot);
659         }
660
661         antilag_clear(self.owner);
662         self.owner = world;
663 }
664
665 void spiderbot_headfade()
666 {
667         self.think = spiderbot_headfade;
668         self.nextthink = self.fade_time;
669         self.alpha = 1 - (time - self.fade_time) * self.fade_rate;
670
671         if(self.cnt < time || self.alpha < 0.1)
672         {
673                 if(self.alpha > 0.1)
674                 {
675                         sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
676                         Send_Effect("explosion_big", self.origin + '0 0 100', '0 0 0', 1);
677                 }
678                 remove(self);
679         }
680 }
681
682 void spiderbot_blowup()
683 {
684         if(self.cnt > time)
685         {
686                 if(random() < 0.1)
687                 {
688                         sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
689                         Send_Effect("explosion_small", randomvec() * 80 + (self.origin + '0 0 100'), '0 0 0', 1);
690                 }
691                 self.nextthink = time + 0.1;
692                 return;
693         }
694
695         entity h, g1, g2, b;
696         b = spawn();
697         h = spawn();
698         g1 = spawn();
699         g2 = spawn();
700
701         setmodel(b, "models/vehicles/spiderbot.dpm");
702         setmodel(h, "models/vehicles/spiderbot_top.dpm");
703         setmodel(g1, "models/vehicles/spiderbot_barrels.dpm");
704         setmodel(g2, "models/vehicles/spiderbot_barrels.dpm");
705
706         setorigin(b, self.origin);
707         b.frame = 11;
708         b.angles = self.angles;
709         setsize(b, self.mins, self.maxs);
710
711         setorigin(h, gettaginfo(self, gettagindex(self, "tag_head")));
712         h.movetype = MOVETYPE_BOUNCE;
713         h.solid = SOLID_BBOX;
714         h.velocity = v_up * (500 + random() * 500) + randomvec() * 128;
715         h.modelflags = MF_ROCKET;
716         h.effects = EF_FLAME | EF_LOWPRECISION;
717         h.avelocity = randomvec() * 360;
718
719         h.alpha = 1;
720         h.cnt = time + (3.5 * random());
721         h.fade_rate = 1 / min(self.respawntime, 10);
722         h.fade_time = time;
723         h.think = spiderbot_headfade;
724         h.nextthink = time;
725
726         setorigin(g1, gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_hardpoint01")));
727         g1.movetype = MOVETYPE_TOSS;
728         g1.solid = SOLID_CORPSE;
729         g1.velocity = v_forward * 700 + (randomvec() * 32);
730         g1.avelocity = randomvec() * 180;
731
732         setorigin(g2, gettaginfo(self.tur_head, gettagindex(self.tur_head, "tag_hardpoint02")));
733         g2.movetype = MOVETYPE_TOSS;
734         g2.solid = SOLID_CORPSE;
735         g2.velocity = v_forward * 700 + (randomvec() * 32);
736         g2.avelocity = randomvec() * 180;
737
738         h.colormod = b.colormod = g1.colormod = g2.colormod = '-2 -2 -2';
739
740         SUB_SetFade(b,  time + 5, min(self.respawntime, 1));
741         //SUB_SetFade(h,  time, min(self.respawntime, 10));
742         SUB_SetFade(g1, time, min(self.respawntime, 10));
743         SUB_SetFade(g2, time, min(self.respawntime, 10));
744
745         RadiusDamage (self, self.enemy, 250, 15, 250, world, world, 250, DEATH_VH_SPID_DEATH, world);
746
747         self.alpha = self.tur_head.alpha = self.gun1.alpha = self.gun2.alpha = -1;
748         self.movetype = MOVETYPE_NONE;
749         self.deadflag = DEAD_DEAD;
750         self.solid = SOLID_NOT;
751         self.tur_head.effects &= ~EF_FLAME;
752         self.vehicle_hudmodel.viewmodelforclient = self;
753 }
754
755 bool spiderbot_impulse(int _imp)
756 {
757         switch(_imp)
758         {
759                 case 1:
760                 case 230:
761                         self.vehicle.vehicle_weapon2mode = SBRM_VOLLY;
762                         CSQCVehicleSetup(self, 0);
763                         return true;
764                 case 2:
765                 case 231:
766                         self.vehicle.vehicle_weapon2mode = SBRM_GUIDE;
767                         CSQCVehicleSetup(self, 0);
768                         return true;
769                 case 3:
770                 case 232:
771                 case 251:
772                         self.vehicle.vehicle_weapon2mode = SBRM_ARTILLERY;
773                         CSQCVehicleSetup(self, 0);
774                         return true;
775
776                 case 10:
777                 case 15:
778                 case 18:
779                         self.vehicle.vehicle_weapon2mode += 1;
780                         if(self.vehicle.vehicle_weapon2mode > SBRM_LAST)
781                                 self.vehicle.vehicle_weapon2mode = SBRM_FIRST;
782
783                         //centerprint(self, strcat("Rocket mode is ", ftos(self.vehicle.vehicle_weapon2mode)));
784                         CSQCVehicleSetup(self, 0);
785                         return true;
786                 case 11:
787                 case 12:
788                 case 16:
789                 case 19:
790                         self.vehicle.vehicle_weapon2mode -= 1;
791                         if(self.vehicle.vehicle_weapon2mode < SBRM_FIRST)
792                                 self.vehicle.vehicle_weapon2mode = SBRM_LAST;
793
794                         //centerprint(self, strcat("Rocket mode is ", ftos(self.vehicle.vehicle_weapon2mode)));
795                         CSQCVehicleSetup(self, 0);
796                         return true;
797
798                 /*
799                 case 17: // toss gun, could be used to exit?
800                         break;
801                 case 20: // Manual minigun reload?
802                         break;
803                 */
804         }
805         return false;
806 }
807
808 void spawnfunc_vehicle_spiderbot()
809 {
810         if(!autocvar_g_vehicle_spiderbot) { remove(self); return; }
811         if(!vehicle_initialize(VEH_SPIDERBOT.vehicleid, false)) { remove(self); return; }
812 }
813
814 float v_spiderbot(float req)
815 {
816         switch(req)
817         {
818                 case VR_IMPACT:
819                 {
820                         if(autocvar_g_vehicle_spiderbot_bouncepain)
821                                 vehicles_impact(autocvar_g_vehicle_spiderbot_bouncepain_x, autocvar_g_vehicle_spiderbot_bouncepain_y, autocvar_g_vehicle_spiderbot_bouncepain_z);
822                 
823                         return true;
824                 }
825                 case VR_ENTER:
826                 {
827                         self.vehicle_weapon2mode = SBRM_GUIDE;
828                         self.movetype = MOVETYPE_WALK;
829                         CSQCVehicleSetup(self.owner, 0);
830                         self.owner.vehicle_health = (self.vehicle_health / autocvar_g_vehicle_spiderbot_health) * 100;
831                         self.owner.vehicle_shield = (self.vehicle_shield / autocvar_g_vehicle_spiderbot_shield) * 100;
832
833                         if(self.owner.flagcarried)
834                         {
835                                 setattachment(self.owner.flagcarried, self.tur_head, "");
836                                 setorigin(self.owner.flagcarried, '-20 0 120');
837                         }
838                 
839                         return true;
840                 }
841                 case VR_THINK:
842                 {
843                         if(self.flags & FL_ONGROUND)
844                                 movelib_beak_simple(autocvar_g_vehicle_spiderbot_speed_stop);
845                         
846                         return true;
847                 }
848                 case VR_DEATH:
849                 {
850                         self.health                             = 0;
851                         self.event_damage               = func_null;
852                         self.takedamage                 = DAMAGE_NO;
853                         self.touch                              = func_null;
854                         self.cnt                                = 3.4 + time + random() * 2;
855                         self.think                              = spiderbot_blowup;
856                         self.nextthink                  = time;
857                         self.deadflag                   = DEAD_DYING;
858                         self.frame                              = 5;
859                         self.tur_head.effects  |= EF_FLAME;
860                         self.colormod                   = self.tur_head.colormod = '-1 -1 -1';
861                         self.frame                              = 10;
862                         self.movetype                   = MOVETYPE_TOSS;
863                         
864                         CSQCModel_UnlinkEntity(); // networking the death scene would be a nightmare
865
866                         return true;
867                 }
868                 case VR_SPAWN:
869                 {
870                         if(!self.gun1)
871                         {
872                                 self.vehicles_impulse = spiderbot_impulse;
873                                 self.gun1 = spawn();
874                                 self.gun2 = spawn();
875                                 setmodel(self.gun1, "models/vehicles/spiderbot_barrels.dpm");
876                                 setmodel(self.gun2, "models/vehicles/spiderbot_barrels.dpm");
877                                 setattachment(self.gun1, self.tur_head, "tag_hardpoint01");
878                                 setattachment(self.gun2, self.tur_head, "tag_hardpoint02");
879                                 self.gravity = 2;
880                                 self.mass = 5000;
881                         }
882
883                         self.frame = 5;
884                         self.tur_head.frame = 1;
885                         self.movetype = MOVETYPE_WALK;
886                         self.solid = SOLID_SLIDEBOX;
887                         self.alpha = self.tur_head.alpha = self.gun1.alpha = self.gun2.alpha = 1;
888                         self.tur_head.angles = '0 0 0';
889                         self.vehicle_exit = spiderbot_exit;
890
891                         setorigin(self, self.pos1 + '0 0 128');
892                         self.angles = self.pos2;
893                         self.damageforcescale = 0.03;
894                         self.vehicle_health = autocvar_g_vehicle_spiderbot_health;
895                         self.vehicle_shield = autocvar_g_vehicle_spiderbot_shield;
896                         
897                         self.PlayerPhysplug = spiderbot_frame;
898
899                         return true;
900                 }
901                 case VR_SETUP:
902                 {
903                         if(autocvar_g_vehicle_spiderbot_shield)
904                                 self.vehicle_flags |= VHF_HASSHIELD;
905
906                         if(autocvar_g_vehicle_spiderbot_shield_regen)
907                                 self.vehicle_flags |= VHF_SHIELDREGEN;
908
909                         if(autocvar_g_vehicle_spiderbot_health_regen)
910                                 self.vehicle_flags |= VHF_HEALTHREGEN;
911
912                         self.respawntime = autocvar_g_vehicle_spiderbot_respawntime;
913                         self.vehicle_health = autocvar_g_vehicle_spiderbot_health;
914                         self.vehicle_shield = autocvar_g_vehicle_spiderbot_shield;
915                         self.max_health = self.vehicle_health;
916                         self.pushable = true; // spiderbot can use jumppads
917
918                         return true;
919                 }
920                 case VR_PRECACHE:
921                 {
922                         precache_model ("models/vhshield.md3");
923                         precache_model ("models/vehicles/spiderbot.dpm");
924                         precache_model ("models/vehicles/spiderbot_top.dpm");
925                         precache_model ("models/vehicles/spiderbot_barrels.dpm");
926                         precache_model ("models/vehicles/spiderbot_cockpit.dpm");
927                         precache_model ( "models/uziflash.md3");
928
929                         precache_sound ("weapons/uzi_fire.wav" );
930                         precache_sound ("weapons/rocket_impact.wav");
931
932                         precache_sound ("vehicles/spiderbot_die.wav");
933                         precache_sound ("vehicles/spiderbot_idle.wav");
934                         precache_sound ("vehicles/spiderbot_jump.wav");
935                         precache_sound ("vehicles/spiderbot_strafe.wav");
936                         precache_sound ("vehicles/spiderbot_walk.wav");
937                         precache_sound ("vehicles/spiderbot_land.wav");
938                         return true;
939                 }
940         }
941
942         return true;
943 }
944
945 #endif // SVQC
946 #ifdef CSQC
947 float autocvar_cl_vehicle_spiderbot_cross_alpha = 0.6;
948 float autocvar_cl_vehicle_spiderbot_cross_size = 1;
949
950 float v_spiderbot(float req)
951 {
952         switch(req)
953         {
954                 case VR_HUD:
955                 {
956                         string crosshair;
957
958                         switch(weapon2mode)
959                         {
960                                 case SBRM_VOLLY:     crosshair = vCROSS_BURST; break;
961                                 case SBRM_GUIDE:     crosshair = vCROSS_GUIDE; break;
962                                 case SBRM_ARTILLERY: crosshair = vCROSS_RAIN;  break;
963                                 default:             crosshair = vCROSS_BURST;
964                         }
965
966                         Vehicles_drawHUD("vehicle_spider", "vehicle_spider_weapon1", "vehicle_spider_weapon2",
967                                                          "vehicle_icon_ammo1", autocvar_hud_progressbar_vehicles_ammo1_color,
968                                                          "vehicle_icon_ammo2", autocvar_hud_progressbar_vehicles_ammo2_color,
969                                                          crosshair);
970                         return true;
971                 }
972                 case VR_SETUP:
973                 {
974                         AuxiliaryXhair[0].axh_image = vCROSS_HINT; // Minigun1
975                         AuxiliaryXhair[1].axh_image = vCROSS_HINT; // Minigun2
976                 
977                         return true;
978                 }
979                 case VR_PRECACHE:
980                 {
981                         return true;
982                 }
983         }
984
985         return true;
986 }
987
988 #endif // CSQC
989 #endif // REGISTER_VEHICLE