1 #ifndef VEHICLE_RACER_WEAPON_H
2 #define VEHICLE_RACER_WEAPON_H
4 #include <common/weapons/all.qh>
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"));
12 REGISTER_WEAPON(RACER, NEW(RacerAttack));
14 // TODO: move into implementation
16 float autocvar_g_vehicle_racer_rocket_refire;
17 void racer_fire_rocket(entity player, vector org, vector dir, entity trg);
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;
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;
41 float autocvar_g_vehicle_racer_rocket_climbspeed;
42 float autocvar_g_vehicle_racer_rocket_locked_maxangle;
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))
47 bool isPlayer = IS_PLAYER(actor);
48 entity player = isPlayer ? actor : actor.owner;
49 entity veh = player.vehicle;
51 if (weapon_prepareattack(thiswep, player, weaponentity, false, autocvar_g_vehicle_racer_cannon_refire)) {
53 veh.vehicle_energy -= autocvar_g_vehicle_racer_cannon_cost;
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);
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);
74 METHOD(RacerAttack, wr_checkammo1, bool(RacerAttack thiswep))
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;
83 void racer_rocket_tracker();
84 void racer_rocket_groundhugger();
86 void racer_fire_rocket(entity player, vector org, vector dir, entity trg)
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);
93 rocket.lip = autocvar_g_vehicle_racer_rocket_accel * sys_frametime;
94 rocket.wait = autocvar_g_vehicle_racer_rocket_turnrate;
95 rocket.nextthink = time;
97 rocket.cnt = time + 15;
100 rocket.think = racer_rocket_tracker;
102 rocket.think = racer_rocket_groundhugger;
105 void racer_rocket_tracker()
107 vector olddir, newdir;
108 float oldvel, newvel;
110 self.nextthink = time;
112 if (IS_DEAD(self.owner) || self.cnt < time)
118 if(!self.realowner.vehicle)
120 UpdateCSQCProjectile(self);
124 olddir = normalize(self.velocity);
125 oldvel = vlen(self.velocity);
126 newvel = oldvel + self.lip;
127 makevectors(vectoangles(olddir));
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;
132 traceline(self.origin, self.origin + v_forward * 64 - '0 0 32', MOVE_NORMAL, self);
133 newdir = normalize(predicted_origin - self.origin);
136 float height_diff = predicted_origin_z - self.origin_z;
138 if(vdist(newdir - v_forward, >, autocvar_g_vehicle_racer_rocket_locked_maxangle))
140 //bprint("Target lost!\n");
141 //dprint("OF:", ftos(vlen(newdir - v_forward)), "\n");
142 self.think = racer_rocket_groundhugger;
146 if(trace_fraction != 1.0 && trace_ent != self.enemy)
147 newdir_z += 16 * sys_frametime;
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 ;
153 UpdateCSQCProjectile(self);
157 void racer_rocket_groundhugger()
159 vector olddir, newdir;
160 float oldvel, newvel;
162 self.nextthink = time;
164 if(IS_DEAD(self.owner) || self.cnt < time)
170 if(!self.realowner.vehicle)
172 UpdateCSQCProjectile(self);
176 olddir = normalize(self.velocity);
177 oldvel = vlen(self.velocity);
178 newvel = oldvel + self.lip;
180 tracebox(self.origin, self.mins, self.maxs, self.origin + olddir * 64, MOVE_WORLDONLY,self);
181 if(trace_fraction <= 0.5)
183 // Hitting somethign soon, just speed ahead
184 self.velocity = olddir * newvel;
185 UpdateCSQCProjectile(self);
189 traceline(trace_endpos, trace_endpos - '0 0 64', MOVE_NORMAL, self);
190 if(trace_fraction != 1.0)
192 newdir = normalize(trace_endpos + '0 0 64' - self.origin) * autocvar_g_vehicle_racer_rocket_turnrate;
193 self.velocity = normalize(olddir + newdir) * newvel;
197 self.velocity = olddir * newvel;
198 self.velocity_z -= 1600 * sys_frametime; // 2x grav looks better for this one
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;
205 UpdateCSQCProjectile(self);