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