c5059a0049a0ffde8a01e58f43acdcd5e4d6db39
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / vehicles / vehicle / racer_weapon.qc
1 #ifndef VEHICLE_RACER_WEAPON_H
2 #define VEHICLE_RACER_WEAPON_H
3
4 #include "../../weapons/all.qh"
5
6 CLASS(RacerAttack, PortoLaunch)
7 /* flags     */ ATTRIB(RacerAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN);
8 /* impulse   */ ATTRIB(RacerAttack, impulse, int, 3);
9 /* refname   */ ATTRIB(RacerAttack, netname, string, "racercannon");
10 /* wepname   */ ATTRIB(RacerAttack, message, string, _("Racer cannon"));
11 ENDCLASS(RacerAttack)
12 REGISTER_WEAPON(RACER, NEW(RacerAttack));
13
14 // TODO: move into implementation
15 #ifdef SVQC
16 float autocvar_g_vehicle_racer_rocket_refire;
17 void racer_fire_rocket(vector org, vector dir, entity trg);
18 #endif
19
20 #endif
21
22 #ifdef IMPLEMENTATION
23
24 #ifdef SVQC
25
26 float autocvar_g_vehicle_racer_cannon_cost;
27 float autocvar_g_vehicle_racer_cannon_damage;
28 float autocvar_g_vehicle_racer_cannon_radius;
29 float autocvar_g_vehicle_racer_cannon_refire;
30 float autocvar_g_vehicle_racer_cannon_speed;
31 float autocvar_g_vehicle_racer_cannon_spread;
32 float autocvar_g_vehicle_racer_cannon_force;
33
34 float autocvar_g_vehicle_racer_rocket_accel;
35 float autocvar_g_vehicle_racer_rocket_damage;
36 float autocvar_g_vehicle_racer_rocket_radius;
37 float autocvar_g_vehicle_racer_rocket_force;
38 float autocvar_g_vehicle_racer_rocket_speed;
39 float autocvar_g_vehicle_racer_rocket_turnrate;
40
41 float autocvar_g_vehicle_racer_rocket_climbspeed;
42 float autocvar_g_vehicle_racer_rocket_locked_maxangle;
43
44 void racer_fire_rocket(vector org, vector dir, entity trg);
45 METHOD(RacerAttack, wr_think, bool(entity thiswep, bool fire1, bool fire2))
46 {
47     SELFPARAM();
48     bool isPlayer = IS_PLAYER(self);
49     entity player = isPlayer ? self : self.owner;
50     entity veh = player.vehicle;
51     setself(player);
52     if (fire1)
53     if (weapon_prepareattack(false, autocvar_g_vehicle_racer_cannon_refire)) {
54         if (veh) {
55             veh.vehicle_energy -= autocvar_g_vehicle_racer_cannon_cost;
56             veh.wait = time;
57         }
58         if (isPlayer) W_SetupShot_Dir(player, v_forward, false, 0, SND(Null), CH_WEAPON_B, 0);
59         vector org = w_shotorg;
60         vector dir = w_shotdir;
61         entity bolt = vehicles_projectile(EFFECT_RACER_MUZZLEFLASH.eent_eff_name, SND(LASERGUN_FIRE),
62                                org, normalize(v_forward + randomvec() * autocvar_g_vehicle_racer_cannon_spread) * autocvar_g_vehicle_racer_cannon_speed,
63                                autocvar_g_vehicle_racer_cannon_damage, autocvar_g_vehicle_racer_cannon_radius, autocvar_g_vehicle_racer_cannon_force,  0,
64                                DEATH_VH_WAKI_GUN, PROJECTILE_WAKICANNON, 0, true, true, player);
65         bolt.velocity = normalize(dir) * autocvar_g_vehicle_racer_cannon_speed;
66         weapon_thinkf(WFRAME_FIRE1, 0, w_ready);
67     }
68     if (fire2)
69     if (!isPlayer || weapon_prepareattack(false, 0.2)) {
70         if (isPlayer) W_SetupShot_Dir(self, v_forward, false, 0, SND(Null), CH_WEAPON_B, 0);
71         racer_fire_rocket(w_shotorg, w_shotdir, NULL);
72         weapon_thinkf(WFRAME_FIRE2, 0, w_ready);
73     }
74     setself(this);
75     return true;
76 }
77
78 METHOD(RacerAttack, wr_checkammo1, bool(RacerAttack thiswep))
79 {
80     SELFPARAM();
81     bool isPlayer = IS_PLAYER(self);
82     entity player = isPlayer ? self : self.owner;
83     entity veh = player.vehicle;
84     return isPlayer || veh.vehicle_energy >= autocvar_g_vehicle_racer_cannon_cost;
85 }
86
87 void racer_rocket_tracker();
88 void racer_rocket_groundhugger();
89
90 void racer_fire_rocket(vector org, vector dir, entity trg)
91 {SELFPARAM();
92     entity rocket = vehicles_projectile(EFFECT_RACER_ROCKETLAUNCH.eent_eff_name, SND(ROCKET_FIRE),
93                            org, dir * autocvar_g_vehicle_racer_rocket_speed,
94                            autocvar_g_vehicle_racer_rocket_damage, autocvar_g_vehicle_racer_rocket_radius, autocvar_g_vehicle_racer_rocket_force, 3,
95                            DEATH_VH_WAKI_ROCKET, PROJECTILE_WAKIROCKET, 20, false, false, self.owner);
96
97     rocket.lip                    = autocvar_g_vehicle_racer_rocket_accel * sys_frametime;
98     rocket.wait                  = autocvar_g_vehicle_racer_rocket_turnrate;
99     rocket.nextthink            = time;
100     rocket.enemy                        = trg;
101     rocket.cnt                    = time + 15;
102
103     if(trg)
104         rocket.think                    = racer_rocket_tracker;
105     else
106         rocket.think                    = racer_rocket_groundhugger;
107 }
108
109 void racer_rocket_tracker()
110 {SELFPARAM();
111     vector olddir, newdir;
112     float oldvel, newvel;
113
114     self.nextthink  = time;
115
116     if (self.owner.deadflag != DEAD_NO || self.cnt < time)
117     {
118         self.use();
119         return;
120     }
121
122     if(!self.realowner.vehicle)
123     {
124         UpdateCSQCProjectile(self);
125         return;
126     }
127
128     olddir = normalize(self.velocity);
129     oldvel = vlen(self.velocity);
130     newvel = oldvel + self.lip;
131     makevectors(vectoangles(olddir));
132
133     float time_to_impact = min(vlen(self.enemy.origin - self.origin) / vlen(self.velocity), 1);
134     vector predicted_origin = self.enemy.origin + self.enemy.velocity * time_to_impact;
135
136     traceline(self.origin, self.origin + v_forward * 64 - '0 0 32', MOVE_NORMAL, self);
137     newdir = normalize(predicted_origin - self.origin);
138
139     //vector
140     float height_diff = predicted_origin_z - self.origin_z;
141
142     if(vlen(newdir - v_forward) > autocvar_g_vehicle_racer_rocket_locked_maxangle)
143     {
144         //bprint("Target lost!\n");
145         //dprint("OF:", ftos(vlen(newdir - v_forward)), "\n");
146         self.think = racer_rocket_groundhugger;
147         return;
148     }
149
150     if(trace_fraction != 1.0 && trace_ent != self.enemy)
151         newdir_z += 16 * sys_frametime;
152
153     self.velocity = normalize(olddir + newdir * autocvar_g_vehicle_racer_rocket_turnrate) * newvel;
154     self.velocity_z -= 800 * sys_frametime;
155     self.velocity_z += max(height_diff, autocvar_g_vehicle_racer_rocket_climbspeed) * sys_frametime ;
156
157     UpdateCSQCProjectile(self);
158     return;
159 }
160
161 void racer_rocket_groundhugger()
162 {SELFPARAM();
163     vector olddir, newdir;
164     float oldvel, newvel;
165
166     self.nextthink  = time;
167
168     if(self.owner.deadflag != DEAD_NO || self.cnt < time)
169     {
170         self.use();
171         return;
172     }
173
174     if(!self.realowner.vehicle)
175     {
176         UpdateCSQCProjectile(self);
177         return;
178     }
179
180     olddir = normalize(self.velocity);
181     oldvel = vlen(self.velocity);
182     newvel = oldvel + self.lip;
183
184     tracebox(self.origin, self.mins, self.maxs, self.origin + olddir * 64, MOVE_WORLDONLY,self);
185     if(trace_fraction <= 0.5)
186     {
187         // Hitting somethign soon, just speed ahead
188         self.velocity = olddir * newvel;
189         UpdateCSQCProjectile(self);
190         return;
191     }
192
193     traceline(trace_endpos, trace_endpos - '0 0 64', MOVE_NORMAL, self);
194     if(trace_fraction != 1.0)
195     {
196         newdir = normalize(trace_endpos + '0 0 64' - self.origin) * autocvar_g_vehicle_racer_rocket_turnrate;
197         self.velocity = normalize(olddir + newdir) * newvel;
198     }
199     else
200     {
201         self.velocity = olddir * newvel;
202         self.velocity_z -= 1600 * sys_frametime; // 2x grav looks better for this one
203     }
204
205     int cont = pointcontents(self.origin - '0 0 32');
206     if(cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME)
207         self.velocity_z += 200;
208
209     UpdateCSQCProjectile(self);
210     return;
211 }
212
213 #endif
214
215 #endif