587debabae6ed1bdf1acce132143d1a7833e20f4
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / vehicles / vehicle / raptor.qc
1 #ifndef VEHICLE_RAPTOR
2 #define VEHICLE_RAPTOR
3 #include "raptor.qh"
4
5 CLASS(Raptor, Vehicle)
6 /* spawnflags */ ATTRIB(Raptor, spawnflags, int, VHF_DMGSHAKE | VHF_DMGROLL);
7 /* mins       */ ATTRIB(Raptor, mins, vector, '-80 -80 0');
8 /* maxs       */ ATTRIB(Raptor, maxs, vector, '80 80 70');
9 /* model          */ ATTRIB(Raptor, mdl, string, "models/vehicles/raptor.dpm");
10 /* model          */ ATTRIB(Raptor, model, string, "models/vehicles/raptor.dpm");
11 /* head_model */ ATTRIB(Raptor, head_model, string, "");
12 /* hud_model  */ ATTRIB(Raptor, hud_model, string, "models/vehicles/raptor_cockpit.dpm");
13 /* tags       */ ATTRIB(Raptor, tag_head, string, "");
14 /* tags       */ ATTRIB(Raptor, tag_hud, string, "tag_hud");
15 /* tags       */ ATTRIB(Raptor, tag_hview, string, "tag_camera");
16 /* netname    */ ATTRIB(Raptor, netname, string, "raptor");
17 /* fullname   */ ATTRIB(Raptor, vehicle_name, string, _("Raptor"));
18 /* icon       */ ATTRIB(Raptor, m_icon, string, "vehicle_raptor");
19 ENDCLASS(Raptor)
20
21 REGISTER_VEHICLE(RAPTOR, NEW(Raptor));
22
23 #endif
24
25 #ifdef IMPLEMENTATION
26
27 #ifdef SVQC
28 bool autocvar_g_vehicle_raptor;
29
30 float autocvar_g_vehicle_raptor_respawntime;
31 float autocvar_g_vehicle_raptor_takeofftime;
32
33 float autocvar_g_vehicle_raptor_movestyle;
34 float autocvar_g_vehicle_raptor_turnspeed;
35 float autocvar_g_vehicle_raptor_pitchspeed;
36 float autocvar_g_vehicle_raptor_pitchlimit;
37
38 float autocvar_g_vehicle_raptor_speed_forward;
39 float autocvar_g_vehicle_raptor_speed_strafe;
40 float autocvar_g_vehicle_raptor_speed_up;
41 float autocvar_g_vehicle_raptor_speed_down;
42 float autocvar_g_vehicle_raptor_friction;
43
44 float autocvar_g_vehicle_raptor_bomblets;
45 float autocvar_g_vehicle_raptor_bomblet_alt;
46 float autocvar_g_vehicle_raptor_bomblet_time;
47 float autocvar_g_vehicle_raptor_bomblet_damage;
48 float autocvar_g_vehicle_raptor_bomblet_spread;
49 float autocvar_g_vehicle_raptor_bomblet_edgedamage;
50 float autocvar_g_vehicle_raptor_bomblet_radius;
51 float autocvar_g_vehicle_raptor_bomblet_force;
52 float autocvar_g_vehicle_raptor_bomblet_explode_delay;
53 float autocvar_g_vehicle_raptor_bombs_refire;
54
55 float autocvar_g_vehicle_raptor_flare_refire;
56 float autocvar_g_vehicle_raptor_flare_lifetime;
57 float autocvar_g_vehicle_raptor_flare_chase;
58 float autocvar_g_vehicle_raptor_flare_range;
59
60 float autocvar_g_vehicle_raptor_cannon_turnspeed;
61 float autocvar_g_vehicle_raptor_cannon_turnlimit;
62 float autocvar_g_vehicle_raptor_cannon_pitchlimit_up;
63 float autocvar_g_vehicle_raptor_cannon_pitchlimit_down;
64
65 float autocvar_g_vehicle_raptor_cannon_locktarget;
66 float autocvar_g_vehicle_raptor_cannon_locking_time;
67 float autocvar_g_vehicle_raptor_cannon_locking_releasetime;
68 float autocvar_g_vehicle_raptor_cannon_locked_time;
69 float autocvar_g_vehicle_raptor_cannon_predicttarget;
70
71 float autocvar_g_vehicle_raptor_cannon_cost;
72 float autocvar_g_vehicle_raptor_cannon_damage;
73 float autocvar_g_vehicle_raptor_cannon_radius;
74 float autocvar_g_vehicle_raptor_cannon_refire;
75 float autocvar_g_vehicle_raptor_cannon_speed;
76 float autocvar_g_vehicle_raptor_cannon_spread;
77 float autocvar_g_vehicle_raptor_cannon_force;
78
79 float autocvar_g_vehicle_raptor_energy;
80 float autocvar_g_vehicle_raptor_energy_regen;
81 float autocvar_g_vehicle_raptor_energy_regen_pause;
82
83 float autocvar_g_vehicle_raptor_health;
84 float autocvar_g_vehicle_raptor_health_regen;
85 float autocvar_g_vehicle_raptor_health_regen_pause;
86
87 float autocvar_g_vehicle_raptor_shield;
88 float autocvar_g_vehicle_raptor_shield_regen;
89 float autocvar_g_vehicle_raptor_shield_regen_pause;
90
91 float autocvar_g_vehicle_raptor_bouncefactor;
92 float autocvar_g_vehicle_raptor_bouncestop;
93 vector autocvar_g_vehicle_raptor_bouncepain;
94
95 .entity bomb1;
96 .entity bomb2;
97
98 float raptor_altitude(float amax)
99 {SELFPARAM();
100         tracebox(self.origin, self.mins, self.maxs, self.origin - ('0 0 1' * amax), MOVE_WORLDONLY, self);
101         return vlen(self.origin - trace_endpos);
102 }
103
104 void raptor_bomblet_boom()
105 {SELFPARAM();
106         RadiusDamage (self, self.realowner, autocvar_g_vehicle_raptor_bomblet_damage,
107                                                                         autocvar_g_vehicle_raptor_bomblet_edgedamage,
108                                                                         autocvar_g_vehicle_raptor_bomblet_radius, world, world,
109                                                                         autocvar_g_vehicle_raptor_bomblet_force, DEATH_VH_RAPT_BOMB, world);
110         remove(self);
111 }
112
113 void raptor_bomblet_touch()
114 {SELFPARAM();
115         if(other == self.owner)
116                 return;
117
118         PROJECTILE_TOUCH;
119         self.think = raptor_bomblet_boom;
120         self.nextthink = time + random() * autocvar_g_vehicle_raptor_bomblet_explode_delay;
121 }
122
123 void raptor_bomb_burst()
124 {SELFPARAM();
125         if(self.cnt > time)
126         if(autocvar_g_vehicle_raptor_bomblet_alt)
127         {
128                 self.nextthink = time;
129                 traceline(self.origin, self.origin + (normalize(self.velocity) * autocvar_g_vehicle_raptor_bomblet_alt), MOVE_NORMAL, self);
130                 if((trace_fraction == 1.0) || (vlen(self.origin - self.owner.origin) < autocvar_g_vehicle_raptor_bomblet_radius))
131                 {
132                         UpdateCSQCProjectile(self);
133                         return;
134                 }
135         }
136
137         entity bomblet;
138         float i;
139
140         Damage_DamageInfo(self.origin, 0, 0, 0, '0 0 0', DEATH_VH_RAPT_FRAGMENT, 0, self);
141
142         for(i = 0; i < autocvar_g_vehicle_raptor_bomblets; ++i)
143         {
144                 bomblet = spawn();
145                 setorigin(bomblet, self.origin);
146
147                 bomblet.movetype        = MOVETYPE_TOSS;
148                 bomblet.touch      = raptor_bomblet_touch;
149                 bomblet.think      = raptor_bomblet_boom;
150                 bomblet.nextthink   = time + 5;
151                 bomblet.owner      = self.owner;
152                 bomblet.realowner   = self.realowner;
153                 bomblet.velocity        = normalize(normalize(self.velocity) + (randomvec() * autocvar_g_vehicle_raptor_bomblet_spread)) * vlen(self.velocity);
154
155                 PROJECTILE_MAKETRIGGER(bomblet);
156                 CSQCProjectile(bomblet, true, PROJECTILE_RAPTORBOMBLET, true);
157         }
158
159         remove(self);
160 }
161
162 void raptor_bombdrop()
163 {SELFPARAM();
164         entity bomb_1, bomb_2;
165
166         bomb_1 = spawn();
167         bomb_2 = spawn();
168
169         setorigin(bomb_1, gettaginfo(self, gettagindex(self, "bombmount_left")));
170         setorigin(bomb_2, gettaginfo(self, gettagindex(self, "bombmount_right")));
171
172         bomb_1.movetype  = bomb_2.movetype   = MOVETYPE_BOUNCE;
173         bomb_1.velocity  = bomb_2.velocity   = self.velocity;
174         bomb_1.touch            = bomb_2.touch    = raptor_bomb_burst;
175         bomb_1.think            = bomb_2.think    = raptor_bomb_burst;
176         bomb_1.cnt                = bomb_2.cnt          = time + 10;
177
178         if(autocvar_g_vehicle_raptor_bomblet_alt)
179                 bomb_1.nextthink = bomb_2.nextthink  = time;
180         else
181                 bomb_1.nextthink = bomb_2.nextthink  = time + autocvar_g_vehicle_raptor_bomblet_time;
182
183         bomb_1.owner     = bomb_2.owner   = self;
184         bomb_1.realowner = bomb_2.realowner  = self.owner;
185         bomb_1.solid     = bomb_2.solid   = SOLID_BBOX;
186         bomb_1.gravity   = bomb_2.gravity       = 1;
187
188         PROJECTILE_MAKETRIGGER(bomb_1);
189         PROJECTILE_MAKETRIGGER(bomb_2);
190
191         CSQCProjectile(bomb_1, true, PROJECTILE_RAPTORBOMB, true);
192         CSQCProjectile(bomb_2, true, PROJECTILE_RAPTORBOMB, true);
193 }
194
195
196 void raptor_fire_cannon(entity gun, string tagname)
197 {SELFPARAM();
198         vehicles_projectile(EFFECT_RAPTOR_MUZZLEFLASH.eent_eff_name, SND(LASERGUN_FIRE),
199                                                    gettaginfo(gun, gettagindex(gun, tagname)), normalize(v_forward + randomvec() * autocvar_g_vehicle_raptor_cannon_spread) * autocvar_g_vehicle_raptor_cannon_speed,
200                                                    autocvar_g_vehicle_raptor_cannon_damage, autocvar_g_vehicle_raptor_cannon_radius, autocvar_g_vehicle_raptor_cannon_force,  0,
201                                                    DEATH_VH_RAPT_CANNON, PROJECTILE_RAPTORCANNON, 0, true, true, self.owner);
202 }
203
204 void raptor_land()
205 {SELFPARAM();
206         float hgt;
207
208         hgt = raptor_altitude(512);
209         self.velocity = (self.velocity * 0.9) + ('0 0 -1800' * (hgt / 256) * sys_frametime);
210         self.angles_x *= 0.95;
211         self.angles_z *= 0.95;
212
213         if(hgt < 128)
214         if(hgt > 0)
215                 self.frame = (hgt / 128) * 25;
216
217         self.bomb1.gun1.avelocity_y = 90 + ((self.frame / 25) * 2000);
218         self.bomb1.gun2.avelocity_y = -self.bomb1.gun1.avelocity_y;
219
220         if(hgt < 16)
221         {
222                 self.movetype = MOVETYPE_TOSS;
223                 self.think      = vehicles_think;
224                 self.frame      = 0;
225         }
226
227         self.nextthink  = time;
228
229         CSQCMODEL_AUTOUPDATE(self);
230 }
231
232 void raptor_exit(float eject)
233 {SELFPARAM();
234         vector spot;
235         self.tur_head.exteriormodeltoclient = world;
236
237         if(self.deadflag == DEAD_NO)
238         {
239                 self.think        = raptor_land;
240                 self.nextthink  = time;
241         }
242
243         if(!self.owner)
244                 return;
245
246         makevectors(self.angles);
247         if(eject)
248         {
249                 spot = self.origin + v_forward * 100 + '0 0 64';
250                 spot = vehicles_findgoodexit(spot);
251                 setorigin(self.owner , spot);
252                 self.owner.velocity = (v_up + v_forward * 0.25) * 750;
253                 self.owner.oldvelocity = self.owner.velocity;
254         }
255         else
256         {
257                 if(vlen(self.velocity) > 2 * autocvar_sv_maxairspeed)
258                 {
259                         self.owner.velocity = normalize(self.velocity) * autocvar_sv_maxairspeed * 2;
260                         self.owner.velocity_z += 200;
261                         spot = self.origin + v_forward * 32 + '0 0 64';
262                         spot = vehicles_findgoodexit(spot);
263                 }
264                 else
265                 {
266                         self.owner.velocity = self.velocity * 0.5;
267                         self.owner.velocity_z += 10;
268                         spot = self.origin - v_forward * 200 + '0 0 64';
269                         spot = vehicles_findgoodexit(spot);
270                 }
271                 self.owner.oldvelocity = self.owner.velocity;
272                 setorigin(self.owner , spot);
273         }
274
275         antilag_clear(self.owner);
276         self.owner = world;
277 }
278
279 void raptor_flare_touch()
280 {SELFPARAM();
281         remove(self);
282 }
283
284 void raptor_flare_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
285 {SELFPARAM();
286         self.health -= damage;
287         if(self.health <= 0)
288                 remove(self);
289 }
290
291 void raptor_flare_think()
292 {SELFPARAM();
293         self.nextthink = time + 0.1;
294         entity _missile = findchainentity(enemy, self.owner);
295         while(_missile)
296         {
297                 if(_missile.flags & FL_PROJECTILE)
298                 if(vlen(self.origin - _missile.origin) < autocvar_g_vehicle_raptor_flare_range)
299                 if(random() > autocvar_g_vehicle_raptor_flare_chase)
300                         _missile.enemy = self;
301                 _missile = _missile.chain;
302         }
303
304         if(self.tur_impacttime < time)
305                 remove(self);
306 }
307
308 float raptor_frame()
309 {SELFPARAM();
310         entity player, raptor;
311         float ftmp = 0;
312         vector df;
313
314         if(intermission_running)
315         {
316                 self.vehicle.velocity = '0 0 0';
317                 self.vehicle.avelocity = '0 0 0';
318                 return 1;
319         }
320
321         player = self;
322         raptor = self.vehicle;
323         setself(raptor);
324
325         vehicles_painframe();
326         /*
327         ftmp = vlen(self.velocity);
328         if(ftmp > autocvar_g_vehicle_raptor_speed_forward)
329                 ftmp = 1;
330         else
331                 ftmp = ftmp / autocvar_g_vehicle_raptor_speed_forward;
332         */
333
334         if(self.sound_nexttime < time)
335         {
336                 self.sound_nexttime = time + 7.955812;
337                 //sound (self.tur_head, CH_TRIGGER_SINGLE, SND_VEH_RAPTOR_FLY, 1 - ftmp,   ATTEN_NORM );
338                 sound (self, CH_TRIGGER_SINGLE, SND_VEH_RAPTOR_SPEED, 1, ATTEN_NORM);
339                 self.wait = ftmp;
340         }
341         /*
342         else if(fabs(ftmp - self.wait) > 0.2)
343         {
344                 sound (self.tur_head, CH_TRIGGER_SINGLE, SND_Null, 1 - ftmp,   ATTEN_NORM );
345                 sound (self, CH_TRIGGER_SINGLE, SND_Null, ftmp, ATTEN_NORM);
346                 self.wait = ftmp;
347         }
348         */
349
350         if(raptor.deadflag != DEAD_NO)
351         {
352                 setself(player);
353                 player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0;
354                 return 1;
355         }
356         crosshair_trace(player);
357
358         //if(time - self.lastteleporttime < 1)
359         //{
360                 if(raptor.angles_z > 50 || raptor.angles_z < -50)
361                 {
362                         if(player.BUTTON_JUMP)
363                         {
364                                 player.BUTTON_CROUCH = true;
365                                 player.BUTTON_JUMP = false;
366                         }
367                 }
368         //}
369
370         vector vang;
371         vang = raptor.angles;
372         df = vectoangles(normalize(trace_endpos - self.origin + '0 0 32'));
373         vang_x *= -1;
374         df_x *= -1;
375         if(df_x > 180)  df_x -= 360;
376         if(df_x < -180) df_x += 360;
377         if(df_y > 180)  df_y -= 360;
378         if(df_y < -180) df_y += 360;
379
380         ftmp = shortangle_f(player.v_angle_y - vang_y, vang_y);
381         if(ftmp > 180)  ftmp -= 360; if(ftmp < -180) ftmp += 360;
382         raptor.avelocity_y = bound(-autocvar_g_vehicle_raptor_turnspeed, ftmp + raptor.avelocity_y * 0.9, autocvar_g_vehicle_raptor_turnspeed);
383
384         // Pitch
385         ftmp = 0;
386         if(player.movement_x > 0 && vang_x < autocvar_g_vehicle_raptor_pitchlimit) ftmp = 5;
387         else if(player.movement_x < 0 && vang_x > -autocvar_g_vehicle_raptor_pitchlimit) ftmp = -20;
388
389         df_x = bound(-autocvar_g_vehicle_raptor_pitchlimit, df_x , autocvar_g_vehicle_raptor_pitchlimit);
390         ftmp = vang_x - bound(-autocvar_g_vehicle_raptor_pitchlimit, df_x + ftmp, autocvar_g_vehicle_raptor_pitchlimit);
391         raptor.avelocity_x = bound(-autocvar_g_vehicle_raptor_pitchspeed, ftmp + raptor.avelocity_x * 0.9, autocvar_g_vehicle_raptor_pitchspeed);
392
393         raptor.angles_x = anglemods(raptor.angles_x);
394         raptor.angles_y = anglemods(raptor.angles_y);
395         raptor.angles_z = anglemods(raptor.angles_z);
396
397         if(autocvar_g_vehicle_raptor_movestyle == 1)
398                 makevectors('0 1 0' * raptor.angles_y);
399         else
400                 makevectors(player.v_angle);
401
402         df = raptor.velocity * -autocvar_g_vehicle_raptor_friction;
403
404         if(player.movement_x != 0)
405         {
406                 if(player.movement_x > 0)
407                         df += v_forward  * autocvar_g_vehicle_raptor_speed_forward;
408                 else if(player.movement_x < 0)
409                         df -= v_forward  * autocvar_g_vehicle_raptor_speed_forward;
410         }
411
412         if(player.movement_y != 0)
413         {
414                 if(player.movement_y < 0)
415                         df -= v_right * autocvar_g_vehicle_raptor_speed_strafe;
416                 else if(player.movement_y > 0)
417                         df += v_right * autocvar_g_vehicle_raptor_speed_strafe;
418
419                 raptor.angles_z = bound(-30,raptor.angles_z + (player.movement_y / autocvar_g_vehicle_raptor_speed_strafe),30);
420         }
421         else
422         {
423                 raptor.angles_z *= 0.95;
424                 if(raptor.angles_z >= -1 && raptor.angles_z <= -1)
425                         raptor.angles_z = 0;
426         }
427
428         if(player.BUTTON_CROUCH)
429                 df -=   v_up * autocvar_g_vehicle_raptor_speed_down;
430         else if (player.BUTTON_JUMP)
431                 df +=  v_up * autocvar_g_vehicle_raptor_speed_up;
432
433         raptor.velocity  += df * frametime;
434         player.velocity = player.movement  = raptor.velocity;
435         setorigin(player, raptor.origin + '0 0 32');
436
437         player.vehicle_weapon2mode = raptor.vehicle_weapon2mode;
438
439         vector vf, ad;
440         // Target lock & predict
441         if(autocvar_g_vehicle_raptor_cannon_locktarget == 2)
442         {
443                 if(raptor.gun1.lock_time < time || raptor.gun1.enemy.deadflag)
444                         raptor.gun1.enemy = world;
445
446                 if(trace_ent)
447                 if(trace_ent.movetype)
448                 if(trace_ent.takedamage)
449                 if(!trace_ent.deadflag)
450                 {
451                         if(teamplay)
452                         {
453                                 if(trace_ent.team != player.team)
454                                 {
455                                         raptor.gun1.enemy = trace_ent;
456                                         raptor.gun1.lock_time = time + 5;
457                                 }
458                         }
459                         else
460                         {
461                                 raptor.gun1.enemy = trace_ent;
462                                 raptor.gun1.lock_time = time + 0.5;
463                         }
464                 }
465
466                 if(raptor.gun1.enemy)
467                 {
468                         float distance, impact_time;
469
470                         vf = real_origin(raptor.gun1.enemy);
471                         UpdateAuxiliaryXhair(player, vf, '1 0 0', 1);
472                         vector _vel = raptor.gun1.enemy.velocity;
473                         if(raptor.gun1.enemy.movetype == MOVETYPE_WALK)
474                                 _vel_z *= 0.1;
475
476                         if(autocvar_g_vehicle_raptor_cannon_predicttarget)
477                         {
478                                 ad = vf;
479                                 distance = vlen(ad - player.origin);
480                                 impact_time = distance / autocvar_g_vehicle_raptor_cannon_speed;
481                                 ad = vf + _vel * impact_time;
482                                 trace_endpos = ad;
483                         }
484                         else
485                                 trace_endpos = vf;
486                 }
487         }
488         else if(autocvar_g_vehicle_raptor_cannon_locktarget == 1)
489         {
490
491                 vehicles_locktarget((1 / autocvar_g_vehicle_raptor_cannon_locking_time) * frametime,
492                                                          (1 / autocvar_g_vehicle_raptor_cannon_locking_releasetime) * frametime,
493                                                          autocvar_g_vehicle_raptor_cannon_locked_time);
494
495                 if(self.lock_target != world)
496                 if(autocvar_g_vehicle_raptor_cannon_predicttarget)
497                 if(self.lock_strength == 1)
498                 {
499                         float i, distance, impact_time;
500
501                         vf = real_origin(raptor.lock_target);
502                         ad = vf;
503                         for(i = 0; i < 4; ++i)
504                         {
505                                 distance = vlen(ad - raptor.origin);
506                                 impact_time = distance / autocvar_g_vehicle_raptor_cannon_speed;
507                                 ad = vf + raptor.lock_target.velocity * impact_time;
508                         }
509                         trace_endpos = ad;
510                 }
511
512                 if(self.lock_target)
513                 {
514                         if(raptor.lock_strength == 1)
515                                 UpdateAuxiliaryXhair(player, real_origin(raptor.lock_target), '1 0 0', 1);
516                         else if(self.lock_strength > 0.5)
517                                 UpdateAuxiliaryXhair(player, real_origin(raptor.lock_target), '0 1 0', 1);
518                         else if(self.lock_strength < 0.5)
519                                 UpdateAuxiliaryXhair(player, real_origin(raptor.lock_target), '0 0 1', 1);
520                 }
521         }
522
523
524         vehicle_aimturret(raptor, trace_endpos, raptor.gun1, "fire1",
525                                                   autocvar_g_vehicle_raptor_cannon_pitchlimit_down * -1,  autocvar_g_vehicle_raptor_cannon_pitchlimit_up,
526                                                   autocvar_g_vehicle_raptor_cannon_turnlimit * -1,  autocvar_g_vehicle_raptor_cannon_turnlimit,  autocvar_g_vehicle_raptor_cannon_turnspeed);
527
528         vehicle_aimturret(raptor, trace_endpos, raptor.gun2, "fire1",
529                                                   autocvar_g_vehicle_raptor_cannon_pitchlimit_down * -1,  autocvar_g_vehicle_raptor_cannon_pitchlimit_up,
530                                                   autocvar_g_vehicle_raptor_cannon_turnlimit * -1,  autocvar_g_vehicle_raptor_cannon_turnlimit,  autocvar_g_vehicle_raptor_cannon_turnspeed);
531
532         /*
533         ad = ad * 0.5;
534         v_forward = vf * 0.5;
535         traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, raptor);
536         UpdateAuxiliaryXhair(player, trace_endpos, '0 1 0', 0);
537         */
538
539         if(!forbidWeaponUse(player))
540         if(player.BUTTON_ATCK)
541         if(raptor.attack_finished_single <= time)
542         if(raptor.vehicle_energy > autocvar_g_vehicle_raptor_cannon_cost)
543         {
544                 raptor.misc_bulletcounter += 1;
545                 raptor.attack_finished_single = time + autocvar_g_vehicle_raptor_cannon_refire;
546                 if(raptor.misc_bulletcounter <= 2)
547                         raptor_fire_cannon(self.gun1, "fire1");
548                 else if(raptor.misc_bulletcounter == 3)
549                         raptor_fire_cannon(self.gun2, "fire1");
550                 else
551                 {
552                         raptor.attack_finished_single = time + autocvar_g_vehicle_raptor_cannon_refire * 2;
553                         raptor_fire_cannon(self.gun2, "fire1");
554                         raptor.misc_bulletcounter = 0;
555                 }
556                 raptor.vehicle_energy -= autocvar_g_vehicle_raptor_cannon_cost;
557                 self.cnt = time;
558         }
559
560         if(self.vehicle_flags  & VHF_SHIELDREGEN)
561                 vehicles_regen(raptor.dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, frametime, true);
562
563         if(self.vehicle_flags  & VHF_HEALTHREGEN)
564                 vehicles_regen(raptor.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, frametime, false);
565
566         if(self.vehicle_flags  & VHF_ENERGYREGEN)
567                 vehicles_regen(raptor.cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, frametime, false);
568
569         if(!forbidWeaponUse(player))
570         if(raptor.vehicle_weapon2mode == RSM_BOMB)
571         {
572                 if(time > raptor.lip + autocvar_g_vehicle_raptor_bombs_refire)
573                 if(player.BUTTON_ATCK2)
574                 {
575                         raptor_bombdrop();
576                         raptor.delay = time + autocvar_g_vehicle_raptor_bombs_refire;
577                         raptor.lip   = time;
578                 }
579         }
580         else
581         {
582                 if(time > raptor.lip + autocvar_g_vehicle_raptor_flare_refire)
583                 if(player.BUTTON_ATCK2)
584                 {
585                         float i;
586                         entity _flare;
587
588                         for(i = 0; i < 3; ++i)
589                         {
590                         _flare = spawn();
591                         setmodel(_flare, MDL_VEH_RAPTOR_FLARE);
592                         _flare.effects = EF_LOWPRECISION | EF_FLAME;
593                         _flare.scale = 0.5;
594                         setorigin(_flare, self.origin - '0 0 16');
595                         _flare.movetype = MOVETYPE_TOSS;
596                         _flare.gravity = 0.15;
597                         _flare.velocity = 0.25 * raptor.velocity + (v_forward + randomvec() * 0.25)* -500;
598                         _flare.think = raptor_flare_think;
599                         _flare.nextthink = time;
600                         _flare.owner = raptor;
601                         _flare.solid = SOLID_CORPSE;
602                         _flare.takedamage = DAMAGE_YES;
603                         _flare.event_damage = raptor_flare_damage;
604                         _flare.health = 20;
605                         _flare.tur_impacttime = time + autocvar_g_vehicle_raptor_flare_lifetime;
606                         _flare.touch = raptor_flare_touch;
607                         }
608                         raptor.delay = time + autocvar_g_vehicle_raptor_flare_refire;
609                         raptor.lip   = time;
610                 }
611         }
612
613         raptor.bomb1.alpha = raptor.bomb2.alpha = (time - raptor.lip) / (raptor.delay - raptor.lip);
614         player.vehicle_reload2 = bound(0, raptor.bomb1.alpha * 100, 100);
615         player.vehicle_ammo2 = (player.vehicle_reload2 == 100) ? 100 : 0;
616
617         if(self.bomb1.cnt < time)
618         {
619                 entity _missile = findchainentity(enemy, raptor);
620                 float _incomming = 0;
621                 while(_missile)
622                 {
623                         if(_missile.flags & FL_PROJECTILE)
624                         if(MISSILE_IS_TRACKING(_missile))
625                         if(vlen(self.origin - _missile.origin) < 2 * autocvar_g_vehicle_raptor_flare_range)
626                                 ++_incomming;
627
628                         _missile = _missile.chain;
629                 }
630
631                 if(_incomming)
632                         sound(self, CH_PAIN_SINGLE, SND_VEH_MISSILE_ALARM, VOL_BASE, ATTEN_NONE);
633
634                 self.bomb1.cnt = time + 1;
635         }
636
637
638         VEHICLE_UPDATE_PLAYER(player, health, raptor);
639         VEHICLE_UPDATE_PLAYER(player, energy, raptor);
640         if(self.vehicle_flags & VHF_HASSHIELD)
641                 VEHICLE_UPDATE_PLAYER(player, shield, raptor);
642
643         player.BUTTON_ATCK = player.BUTTON_ATCK2 = player.BUTTON_CROUCH = 0;
644
645         setself(player);
646         return 1;
647 }
648
649 float raptor_takeoff()
650 {SELFPARAM();
651         entity player, raptor;
652
653         player = self;
654         raptor = self.vehicle;
655         setself(raptor);
656
657         self.nextthink = time;
658         CSQCMODEL_AUTOUPDATE(self);
659         self.nextthink = 0; // will this work?
660
661         if(self.sound_nexttime < time)
662         {
663                 self.sound_nexttime = time + 7.955812; //soundlength("vehicles/raptor_fly.wav");
664                 sound (self, CH_TRIGGER_SINGLE, SND_VEH_RAPTOR_SPEED, VOL_VEHICLEENGINE, ATTEN_NORM);
665         }
666
667         // Takeoff sequense
668         if(raptor.frame < 25)
669         {
670                 raptor.frame += 25 / (autocvar_g_vehicle_raptor_takeofftime / sys_frametime);
671                 raptor.velocity_z = min(raptor.velocity_z * 1.5, 256);
672                 self.bomb1.gun1.avelocity_y = 90 + ((raptor.frame / 25) * 25000);
673                 self.bomb1.gun2.avelocity_y = -self.bomb1.gun1.avelocity_y;
674                 player.BUTTON_ATCK = player.BUTTON_ATCK2 = player.BUTTON_CROUCH = 0;
675
676                 setorigin(player, raptor.origin + '0 0 32');
677         }
678         else
679                 player.PlayerPhysplug = raptor_frame;
680
681         if(self.vehicle_flags  & VHF_SHIELDREGEN)
682                 vehicles_regen(raptor.dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, frametime, true);
683
684         if(self.vehicle_flags  & VHF_HEALTHREGEN)
685                 vehicles_regen(raptor.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, frametime, false);
686
687         if(self.vehicle_flags  & VHF_ENERGYREGEN)
688                 vehicles_regen(raptor.cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, frametime, false);
689
690
691         raptor.bomb1.alpha = raptor.bomb2.alpha = (time - raptor.lip) / (raptor.delay - raptor.lip);
692         player.vehicle_reload2 = bound(0, raptor.bomb1.alpha * 100, 100);
693         player.vehicle_ammo2 = (player.vehicle_reload2 == 100) ? 100 : 0;
694
695         VEHICLE_UPDATE_PLAYER(player, health, raptor);
696         VEHICLE_UPDATE_PLAYER(player, energy, raptor);
697         if(self.vehicle_flags & VHF_HASSHIELD)
698                 VEHICLE_UPDATE_PLAYER(player, shield, raptor);
699
700         player.BUTTON_ATCK = player.BUTTON_ATCK2 = player.BUTTON_CROUCH = 0;
701         setself(player);
702         return 1;
703 }
704
705 void raptor_blowup()
706 {SELFPARAM();
707         self.deadflag   = DEAD_DEAD;
708         self.vehicle_exit(VHEF_NORMAL);
709         RadiusDamage (self, self.enemy, 250, 15, 250, world, world, 250, DEATH_VH_RAPT_DEATH, world);
710
711         self.alpha                = -1;
712         self.movetype      = MOVETYPE_NONE;
713         self.effects            = EF_NODRAW;
714         self.colormod      = '0 0 0';
715         self.avelocity    = '0 0 0';
716         self.velocity      = '0 0 0';
717
718         setorigin(self, self.pos1);
719         self.touch = func_null;
720         self.nextthink = 0;
721 }
722
723 void raptor_diethink()
724 {SELFPARAM();
725         if(time >= self.wait)
726                 self.think = raptor_blowup;
727
728         if(random() < 0.05)
729         {
730                 sound (self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
731                 Send_Effect(EFFECT_EXPLOSION_SMALL, randomvec() * 80 + (self.origin + '0 0 100'), '0 0 0', 1);
732         }
733         self.nextthink = time;
734
735         CSQCMODEL_AUTOUPDATE(self);
736 }
737
738 // If we dont do this ever now and then, the raptors rotors
739 // stop working, presumably due to angle overflow. cute.
740 void raptor_rotor_anglefix()
741 {SELFPARAM();
742         self.gun1.angles_y = anglemods(self.gun1.angles_y);
743         self.gun2.angles_y = anglemods(self.gun2.angles_y);
744         self.nextthink = time + 15;
745 }
746
747 float raptor_impulse(float _imp)
748 {SELFPARAM();
749         switch(_imp)
750         {
751                 case 1:
752                 case 230:
753                         self.vehicle.vehicle_weapon2mode = RSM_BOMB;
754                         CSQCVehicleSetup(self, 0);
755                         return true;
756                 case 2:
757                 case 231:
758                         self.vehicle.vehicle_weapon2mode = RSM_FLARE;
759                         CSQCVehicleSetup(self, 0);
760                         return true;
761
762                 case 10:
763                 case 15:
764                 case 18:
765                         self.vehicle.vehicle_weapon2mode += 1;
766                         if(self.vehicle.vehicle_weapon2mode > RSM_LAST)
767                                 self.vehicle.vehicle_weapon2mode = RSM_FIRST;
768
769                         CSQCVehicleSetup(self, 0);
770                         return true;
771                 case 11:
772                 case 12:
773                 case 16:
774                 case 19:
775                         self.vehicle.vehicle_weapon2mode -= 1;
776                         if(self.vehicle.vehicle_weapon2mode < RSM_FIRST)
777                                 self.vehicle.vehicle_weapon2mode = RSM_LAST;
778
779                         CSQCVehicleSetup(self, 0);
780                         return true;
781
782                 /*
783                 case 17: // toss gun, could be used to exit?
784                         break;
785                 case 20: // Manual minigun reload?
786                         break;
787                 */
788         }
789         return false;
790 }
791
792 void spawnfunc_vehicle_raptor()
793 {SELFPARAM();
794         if(!autocvar_g_vehicle_raptor) { remove(self); return; }
795         if(!vehicle_initialize(VEH_RAPTOR, false)) { remove(self); return; }
796 }
797
798                 METHOD(Raptor, vr_impact, bool(Raptor thisveh))
799                 {
800                         if(autocvar_g_vehicle_raptor_bouncepain)
801                                 vehicles_impact(autocvar_g_vehicle_raptor_bouncepain_x, autocvar_g_vehicle_raptor_bouncepain_y, autocvar_g_vehicle_raptor_bouncepain_z);
802
803                         return true;
804                 }
805                 METHOD(Raptor, vr_enter, bool(Raptor thisveh))
806                 {
807                         self.vehicle_weapon2mode = RSM_BOMB;
808                         self.owner.PlayerPhysplug = raptor_takeoff;
809                         self.movetype      = MOVETYPE_BOUNCEMISSILE;
810                         self.solid                = SOLID_SLIDEBOX;
811                         self.owner.vehicle_health = (self.vehicle_health / autocvar_g_vehicle_raptor_health) * 100;
812                         self.owner.vehicle_shield = (self.vehicle_shield / autocvar_g_vehicle_raptor_shield) * 100;
813                         self.velocity_z = 1; // Nudge upwards to takeoff sequense can work.
814                         self.tur_head.exteriormodeltoclient = self.owner;
815
816                         self.delay = time + autocvar_g_vehicle_raptor_bombs_refire;
817                         self.lip   = time;
818
819                         if(self.owner.flagcarried)
820                            setorigin(self.owner.flagcarried, '-20 0 96');
821
822                         CSQCVehicleSetup(self.owner, 0);
823                         return true;
824                 }
825                 METHOD(Raptor, vr_think, bool(Raptor thisveh))
826                 {
827                         return true;
828                 }
829                 METHOD(Raptor, vr_death, bool(Raptor thisveh))
830                 {
831                         self.health                             = 0;
832                         self.event_damage               = func_null;
833                         self.solid                              = SOLID_CORPSE;
834                         self.takedamage                 = DAMAGE_NO;
835                         self.deadflag                   = DEAD_DYING;
836                         self.movetype                   = MOVETYPE_BOUNCE;
837                         self.think                              = raptor_diethink;
838                         self.nextthink                  = time;
839                         self.wait                               = time + 5 + (random() * 5);
840
841                         Send_Effect(EFFECT_EXPLOSION_MEDIUM, findbetterlocation (self.origin, 16), '0 0 0', 1);
842
843                         self.velocity_z += 600;
844
845                         self.avelocity = '0 0.5 1' * (random() * 400);
846                         self.avelocity -= '0 0.5 1' * (random() * 400);
847
848                         self.colormod = '-0.5 -0.5 -0.5';
849                         self.touch = raptor_blowup;
850                         return true;
851                 }
852                 METHOD(Raptor, vr_spawn, bool(Raptor thisveh))
853                 {
854                         if(!self.gun1)
855                         {
856                                 entity spinner;
857                                 vector ofs;
858
859                                 //FIXME: Camera is in a bad place in HUD model.
860                                 //setorigin(self.vehicle_viewport, '25 0 5');
861
862                                 self.vehicles_impulse   = raptor_impulse;
863
864                                 self.frame = 0;
865
866                                 self.bomb1 = spawn();
867                                 self.bomb2 = spawn();
868                                 self.gun1  = spawn();
869                                 self.gun2  = spawn();
870
871                                 setmodel(self.bomb1, MDL_VEH_RAPTOR_CB_FOLDED);
872                                 setmodel(self.bomb2, MDL_VEH_RAPTOR_CB_FOLDED);
873                                 setmodel(self.gun1, MDL_VEH_RAPTOR_GUN);
874                                 setmodel(self.gun2, MDL_VEH_RAPTOR_GUN);
875                                 setmodel(self.tur_head, MDL_VEH_RAPTOR_TAIL);
876
877                                 setattachment(self.bomb1, self, "bombmount_left");
878                                 setattachment(self.bomb2, self, "bombmount_right");
879                                 setattachment(self.tur_head, self,"root");
880
881                                 // FIXMODEL Guns mounts to angled bones
882                                 self.bomb1.angles = self.angles;
883                                 self.angles = '0 0 0';
884                                 // This messes up gun-aim, so work arround it.
885                                 //setattachment(self.gun1, self, "gunmount_left");
886                                 ofs = gettaginfo(self, gettagindex(self, "gunmount_left"));
887                                 ofs -= self.origin;
888                                 setattachment(self.gun1, self, "");
889                                 setorigin(self.gun1, ofs);
890
891                                 //setattachment(self.gun2, self, "gunmount_right");
892                                 ofs = gettaginfo(self, gettagindex(self, "gunmount_right"));
893                                 ofs -= self.origin;
894                                 setattachment(self.gun2, self, "");
895                                 setorigin(self.gun2, ofs);
896
897                                 self.angles = self.bomb1.angles;
898                                 self.bomb1.angles = '0 0 0';
899
900                                 spinner = spawn();
901                                 spinner.owner = self;
902                                 setmodel(spinner, MDL_VEH_RAPTOR_PROP);
903                                 setattachment(spinner, self, "engine_left");
904                                 spinner.movetype = MOVETYPE_NOCLIP;
905                                 spinner.avelocity = '0 90 0';
906                                 self.bomb1.gun1 = spinner;
907
908                                 spinner = spawn();
909                                 spinner.owner = self;
910                                 setmodel(spinner, MDL_VEH_RAPTOR_PROP);
911                                 setattachment(spinner, self, "engine_right");
912                                 spinner.movetype = MOVETYPE_NOCLIP;
913                                 spinner.avelocity = '0 -90 0';
914                                 self.bomb1.gun2 = spinner;
915
916                                 // Sigh.
917                                 self.bomb1.think = raptor_rotor_anglefix;
918                                 self.bomb1.nextthink = time;
919
920                                 self.mass                          = 1 ;
921                         }
922
923                         self.frame                = 0;
924                         self.vehicle_health = autocvar_g_vehicle_raptor_health;
925                         self.vehicle_shield = autocvar_g_vehicle_raptor_shield;
926                         self.movetype      = MOVETYPE_TOSS;
927                         self.solid                = SOLID_SLIDEBOX;
928                         self.vehicle_energy = 1;
929
930                         self.PlayerPhysplug = raptor_frame;
931
932                         self.bomb1.gun1.avelocity_y = 90;
933                         self.bomb1.gun2.avelocity_y = -90;
934
935                         self.delay = time;
936
937                         self.bouncefactor = autocvar_g_vehicle_raptor_bouncefactor;
938                         self.bouncestop = autocvar_g_vehicle_raptor_bouncestop;
939                         self.damageforcescale = 0.25;
940                         self.vehicle_health = autocvar_g_vehicle_raptor_health;
941                         self.vehicle_shield = autocvar_g_vehicle_raptor_shield;
942                         return true;
943                 }
944                 METHOD(Raptor, vr_setup, bool(Raptor thisveh))
945                 {
946                         if(autocvar_g_vehicle_raptor_shield)
947                                 self.vehicle_flags |= VHF_HASSHIELD;
948
949                         if(autocvar_g_vehicle_raptor_shield_regen)
950                                 self.vehicle_flags |= VHF_SHIELDREGEN;
951
952                         if(autocvar_g_vehicle_raptor_health_regen)
953                                 self.vehicle_flags |= VHF_HEALTHREGEN;
954
955                         if(autocvar_g_vehicle_raptor_energy_regen)
956                                 self.vehicle_flags |= VHF_ENERGYREGEN;
957
958                         self.vehicle_exit = raptor_exit;
959                         self.respawntime = autocvar_g_vehicle_raptor_respawntime;
960                         self.vehicle_health = autocvar_g_vehicle_raptor_health;
961                         self.vehicle_shield = autocvar_g_vehicle_raptor_shield;
962                         self.max_health = self.vehicle_health;
963
964                         return true;
965                 }
966                 METHOD(Raptor, vr_precache, bool(Raptor thisveh))
967                 {
968                         return true;
969                 }
970
971 #endif // SVQC
972 #ifdef CSQC
973
974 void RaptorCBShellfragDraw()
975 {SELFPARAM();
976         if(wasfreed(self))
977                 return;
978
979         Movetype_Physics_MatchTicrate(autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy);
980         self.move_avelocity += randomvec() * 15;
981         self.renderflags = 0;
982
983         if(self.cnt < time)
984                 self.alpha = bound(0, self.nextthink - time, 1);
985
986         if(self.alpha < ALPHA_MIN_VISIBLE)
987                 remove(self);
988 }
989
990 void RaptorCBShellfragToss(vector _org, vector _vel, vector _ang)
991 {SELFPARAM();
992         entity sfrag;
993
994         sfrag = spawn();
995         setmodel(sfrag, MDL_VEH_RAPTOR_CB_FRAGMENT);
996         setorigin(sfrag, _org);
997
998         sfrag.move_movetype = MOVETYPE_BOUNCE;
999         sfrag.gravity = 0.15;
1000         sfrag.solid = SOLID_CORPSE;
1001
1002         sfrag.draw = RaptorCBShellfragDraw;
1003
1004         sfrag.move_origin = sfrag.origin = _org;
1005         sfrag.move_velocity = _vel;
1006         sfrag.move_avelocity = prandomvec() * vlen(sfrag.move_velocity);
1007         sfrag.angles = self.move_angles = _ang;
1008
1009         sfrag.move_time = time;
1010         sfrag.damageforcescale = 4;
1011
1012         sfrag.nextthink = time + 3;
1013         sfrag.cnt = time + 2;
1014         sfrag.alpha = 1;
1015         sfrag.drawmask = MASK_NORMAL;
1016 }
1017
1018                 METHOD(Raptor, vr_hud, bool(Raptor thisveh))
1019                 {
1020                         string crosshair;
1021
1022                         switch(weapon2mode)
1023                         {
1024                                 case RSM_FLARE: crosshair = vCROSS_RAIN;  break;
1025                                 case RSM_BOMB:  crosshair = vCROSS_BURST; break;
1026                                 default:        crosshair = vCROSS_BURST;
1027                         }
1028
1029                         Vehicles_drawHUD(VEH_RAPTOR.m_icon, "vehicle_raptor_weapon1", "vehicle_raptor_weapon2",
1030                                                          "vehicle_icon_ammo1", autocvar_hud_progressbar_vehicles_ammo1_color,
1031                                                          "vehicle_icon_ammo2", autocvar_hud_progressbar_vehicles_ammo2_color,
1032                                                          crosshair);
1033                         return true;
1034                 }
1035                 METHOD(Raptor, vr_setup, bool(Raptor thisveh))
1036                 {
1037                         AuxiliaryXhair[1].axh_image = vCROSS_LOCK;
1038                         return true;
1039                 }
1040                 METHOD(Raptor, vr_precache, bool(Raptor thisveh))
1041                 {
1042                         return true;
1043                 }
1044
1045 #endif // CSQC
1046 #endif // REGISTER_VEHICLE