21f4313c6337a716d37b8a09caa7b3a1c4ca704a
[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 | WEP_FLAG_MUTATORBLOCKED);
8 /* impulse   */ ATTRIB(RacerAttack, impulse, int, 3);
9 /* refname   */ ATTRIB(RacerAttack, netname, string, "racercannon");
10 /* wepname   */ ATTRIB(RacerAttack, m_name, 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(entity player, 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(entity player, vector org, vector dir, entity trg);
45 METHOD(RacerAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
46 {
47     bool isPlayer = IS_PLAYER(actor);
48     entity player = isPlayer ? actor : actor.owner;
49     entity veh = player.vehicle;
50     if (fire & 1)
51     if (weapon_prepareattack(thiswep, player, weaponentity, false, autocvar_g_vehicle_racer_cannon_refire)) {
52         if (veh) {
53             veh.vehicle_energy -= autocvar_g_vehicle_racer_cannon_cost;
54             veh.wait = time;
55         }
56         if (isPlayer) W_SetupShot_Dir(player, v_forward, false, 0, SND(Null), CH_WEAPON_B, 0);
57         vector org = w_shotorg;
58         vector dir = w_shotdir;
59         entity bolt = vehicles_projectile(EFFECT_RACER_MUZZLEFLASH.eent_eff_name, SND(LASERGUN_FIRE),
60                                org, normalize(v_forward + randomvec() * autocvar_g_vehicle_racer_cannon_spread) * autocvar_g_vehicle_racer_cannon_speed,
61                                autocvar_g_vehicle_racer_cannon_damage, autocvar_g_vehicle_racer_cannon_radius, autocvar_g_vehicle_racer_cannon_force,  0,
62                                DEATH_VH_WAKI_GUN.m_id, PROJECTILE_WAKICANNON, 0, true, true, player);
63         bolt.velocity = normalize(dir) * autocvar_g_vehicle_racer_cannon_speed;
64         weapon_thinkf(player, weaponentity, WFRAME_FIRE1, 0, w_ready);
65     }
66     if (fire & 2)
67     if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, 0.2)) {
68         if (isPlayer) W_SetupShot_Dir(actor, v_forward, false, 0, SND(Null), CH_WEAPON_B, 0);
69         racer_fire_rocket(player, w_shotorg, w_shotdir, NULL);
70         weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, 0, w_ready);
71     }
72 }
73
74 METHOD(RacerAttack, wr_checkammo1, bool(RacerAttack thiswep))
75 {
76     SELFPARAM();
77     bool isPlayer = IS_PLAYER(self);
78     entity player = isPlayer ? self : self.owner;
79     entity veh = player.vehicle;
80     return isPlayer || veh.vehicle_energy >= autocvar_g_vehicle_racer_cannon_cost;
81 }
82
83 void racer_rocket_tracker();
84 void racer_rocket_groundhugger();
85
86 void racer_fire_rocket(entity player, vector org, vector dir, entity trg)
87 {SELFPARAM();
88     entity rocket = vehicles_projectile(EFFECT_RACER_ROCKETLAUNCH.eent_eff_name, SND(ROCKET_FIRE),
89                            org, dir * autocvar_g_vehicle_racer_rocket_speed,
90                            autocvar_g_vehicle_racer_rocket_damage, autocvar_g_vehicle_racer_rocket_radius, autocvar_g_vehicle_racer_rocket_force, 3,
91                            DEATH_VH_WAKI_ROCKET.m_id, PROJECTILE_WAKIROCKET, 20, false, false, player);
92
93     rocket.lip                    = autocvar_g_vehicle_racer_rocket_accel * sys_frametime;
94     rocket.wait                  = autocvar_g_vehicle_racer_rocket_turnrate;
95     rocket.nextthink            = time;
96     rocket.enemy                        = trg;
97     rocket.cnt                    = time + 15;
98
99     if(trg)
100         rocket.think                    = racer_rocket_tracker;
101     else
102         rocket.think                    = racer_rocket_groundhugger;
103 }
104
105 void racer_rocket_tracker()
106 {SELFPARAM();
107     vector olddir, newdir;
108     float oldvel, newvel;
109
110     self.nextthink  = time;
111
112     if (self.owner.deadflag != DEAD_NO || self.cnt < time)
113     {
114         self.use();
115         return;
116     }
117
118     if(!self.realowner.vehicle)
119     {
120         UpdateCSQCProjectile(self);
121         return;
122     }
123
124     olddir = normalize(self.velocity);
125     oldvel = vlen(self.velocity);
126     newvel = oldvel + self.lip;
127     makevectors(vectoangles(olddir));
128
129     float time_to_impact = min(vlen(self.enemy.origin - self.origin) / vlen(self.velocity), 1);
130     vector predicted_origin = self.enemy.origin + self.enemy.velocity * time_to_impact;
131
132     traceline(self.origin, self.origin + v_forward * 64 - '0 0 32', MOVE_NORMAL, self);
133     newdir = normalize(predicted_origin - self.origin);
134
135     //vector
136     float height_diff = predicted_origin_z - self.origin_z;
137
138     if(vlen(newdir - v_forward) > autocvar_g_vehicle_racer_rocket_locked_maxangle)
139     {
140         //bprint("Target lost!\n");
141         //dprint("OF:", ftos(vlen(newdir - v_forward)), "\n");
142         self.think = racer_rocket_groundhugger;
143         return;
144     }
145
146     if(trace_fraction != 1.0 && trace_ent != self.enemy)
147         newdir_z += 16 * sys_frametime;
148
149     self.velocity = normalize(olddir + newdir * autocvar_g_vehicle_racer_rocket_turnrate) * newvel;
150     self.velocity_z -= 800 * sys_frametime;
151     self.velocity_z += max(height_diff, autocvar_g_vehicle_racer_rocket_climbspeed) * sys_frametime ;
152
153     UpdateCSQCProjectile(self);
154     return;
155 }
156
157 void racer_rocket_groundhugger()
158 {SELFPARAM();
159     vector olddir, newdir;
160     float oldvel, newvel;
161
162     self.nextthink  = time;
163
164     if(self.owner.deadflag != DEAD_NO || self.cnt < time)
165     {
166         self.use();
167         return;
168     }
169
170     if(!self.realowner.vehicle)
171     {
172         UpdateCSQCProjectile(self);
173         return;
174     }
175
176     olddir = normalize(self.velocity);
177     oldvel = vlen(self.velocity);
178     newvel = oldvel + self.lip;
179
180     tracebox(self.origin, self.mins, self.maxs, self.origin + olddir * 64, MOVE_WORLDONLY,self);
181     if(trace_fraction <= 0.5)
182     {
183         // Hitting somethign soon, just speed ahead
184         self.velocity = olddir * newvel;
185         UpdateCSQCProjectile(self);
186         return;
187     }
188
189     traceline(trace_endpos, trace_endpos - '0 0 64', MOVE_NORMAL, self);
190     if(trace_fraction != 1.0)
191     {
192         newdir = normalize(trace_endpos + '0 0 64' - self.origin) * autocvar_g_vehicle_racer_rocket_turnrate;
193         self.velocity = normalize(olddir + newdir) * newvel;
194     }
195     else
196     {
197         self.velocity = olddir * newvel;
198         self.velocity_z -= 1600 * sys_frametime; // 2x grav looks better for this one
199     }
200
201     int cont = pointcontents(self.origin - '0 0 32');
202     if(cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME)
203         self.velocity_z += 200;
204
205     UpdateCSQCProjectile(self);
206     return;
207 }
208
209 #endif
210
211 #endif