1 #include "racer_weapon.qh"
5 void racer_fire_rocket(entity this, entity player, vector org, vector dir, entity trg);
6 METHOD(RacerAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
8 bool isPlayer = IS_PLAYER(actor);
9 entity player = isPlayer ? actor : actor.owner;
10 entity veh = player.vehicle;
12 if (weapon_prepareattack(thiswep, player, weaponentity, false, autocvar_g_vehicle_racer_cannon_refire)) {
14 veh.vehicle_energy -= autocvar_g_vehicle_racer_cannon_cost;
19 string tagname = (veh.cnt)
20 ? (veh.cnt = 0, "tag_fire1")
21 : (veh.cnt = 1, "tag_fire2");
22 vector vshotorg = gettaginfo(veh, gettagindex(veh, tagname));
24 w_shotdir = v_forward;
26 // Fix z-aim (for chase mode)
27 crosshair_trace(player);
28 w_shotdir.z = normalize(trace_endpos - vshotorg).z * 0.5;
30 if (isPlayer) W_SetupShot_Dir(player, weaponentity, v_forward, false, 0, SND_Null, CH_WEAPON_B, 0, DEATH_VH_WAKI_GUN.m_id);
31 vector org = w_shotorg;
32 vector dir = w_shotdir;
33 entity bolt = vehicles_projectile(((veh) ? veh : player), EFFECT_RACER_MUZZLEFLASH, SND_LASERGUN_FIRE,
34 org, normalize(v_forward + randomvec() * autocvar_g_vehicle_racer_cannon_spread) * autocvar_g_vehicle_racer_cannon_speed,
35 autocvar_g_vehicle_racer_cannon_damage, autocvar_g_vehicle_racer_cannon_radius, autocvar_g_vehicle_racer_cannon_force, 0,
36 DEATH_VH_WAKI_GUN.m_id, PROJECTILE_WAKICANNON, 0, true, true, player);
37 bolt.velocity = normalize(dir) * autocvar_g_vehicle_racer_cannon_speed;
38 weapon_thinkf(player, weaponentity, WFRAME_FIRE1, 0, w_ready);
41 if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, 0.2)) {
42 if (isPlayer) W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_Null, CH_WEAPON_B, 0, DEATH_VH_WAKI_ROCKET.m_id);
43 racer_fire_rocket(((veh) ? veh : player), player, w_shotorg, w_shotdir, NULL);
44 weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, 0, w_ready);
48 METHOD(RacerAttack, wr_checkammo1, bool(RacerAttack thiswep, entity actor, .entity weaponentity))
50 bool isPlayer = IS_PLAYER(actor);
51 entity player = isPlayer ? actor : actor.owner;
52 entity veh = player.vehicle;
53 return isPlayer || veh.vehicle_energy >= autocvar_g_vehicle_racer_cannon_cost;
56 void racer_rocket_tracker(entity this);
57 void racer_rocket_groundhugger(entity this);
59 void racer_fire_rocket(entity this, entity player, vector org, vector dir, entity trg)
61 entity rocket = vehicles_projectile(this, EFFECT_RACER_ROCKETLAUNCH, SND_ROCKET_FIRE,
62 org, dir * autocvar_g_vehicle_racer_rocket_speed,
63 autocvar_g_vehicle_racer_rocket_damage, autocvar_g_vehicle_racer_rocket_radius, autocvar_g_vehicle_racer_rocket_force, 3,
64 DEATH_VH_WAKI_ROCKET.m_id, PROJECTILE_WAKIROCKET, 20, false, false, player);
66 rocket.lip = autocvar_g_vehicle_racer_rocket_accel * sys_frametime;
67 rocket.wait = autocvar_g_vehicle_racer_rocket_turnrate;
68 rocket.nextthink = time;
70 rocket.cnt = time + 15;
73 setthink(rocket, racer_rocket_tracker);
75 setthink(rocket, racer_rocket_groundhugger);
78 void racer_rocket_tracker(entity this)
80 vector olddir, newdir;
83 this.nextthink = time;
85 if (IS_DEAD(this.owner) || this.cnt < time)
87 this.use(this, NULL, NULL);
91 if(!this.realowner.vehicle)
93 UpdateCSQCProjectile(this);
97 olddir = normalize(this.velocity);
98 oldvel = vlen(this.velocity);
99 newvel = oldvel + this.lip;
100 makevectors(vectoangles(olddir));
102 float time_to_impact = min(vlen(this.enemy.origin - this.origin) / vlen(this.velocity), 1);
103 vector predicted_origin = this.enemy.origin + this.enemy.velocity * time_to_impact;
105 traceline(this.origin, this.origin + v_forward * 64 - '0 0 32', MOVE_NORMAL, this);
106 newdir = normalize(predicted_origin - this.origin);
109 float height_diff = predicted_origin_z - this.origin_z;
111 if(vdist(newdir - v_forward, >, autocvar_g_vehicle_racer_rocket_locked_maxangle))
113 //bprint("Target lost!\n");
114 //dprint("OF:", ftos(vlen(newdir - v_forward)), "\n");
115 setthink(this, racer_rocket_groundhugger);
119 if(trace_fraction != 1.0 && trace_ent != this.enemy)
120 newdir_z += 16 * sys_frametime;
122 this.velocity = normalize(olddir + newdir * autocvar_g_vehicle_racer_rocket_turnrate) * newvel;
123 this.velocity_z -= 800 * sys_frametime;
124 this.velocity_z += max(height_diff, autocvar_g_vehicle_racer_rocket_climbspeed) * sys_frametime ;
126 UpdateCSQCProjectile(this);
130 void racer_rocket_groundhugger(entity this)
132 vector olddir, newdir;
133 float oldvel, newvel;
135 this.nextthink = time;
137 if(IS_DEAD(this.owner) || this.cnt < time)
139 this.use(this, NULL, NULL);
143 if(!this.realowner.vehicle)
145 UpdateCSQCProjectile(this);
149 olddir = normalize(this.velocity);
150 oldvel = vlen(this.velocity);
151 newvel = oldvel + this.lip;
153 tracebox(this.origin, this.mins, this.maxs, this.origin + olddir * 64, MOVE_WORLDONLY,this);
154 if(trace_fraction <= 0.5)
156 // Hitting somethign soon, just speed ahead
157 this.velocity = olddir * newvel;
158 UpdateCSQCProjectile(this);
162 traceline(trace_endpos, trace_endpos - '0 0 64', MOVE_NORMAL, this);
163 if(trace_fraction != 1.0)
165 newdir = normalize(trace_endpos + '0 0 64' - this.origin) * autocvar_g_vehicle_racer_rocket_turnrate;
166 this.velocity = normalize(olddir + newdir) * newvel;
170 this.velocity = olddir * newvel;
171 this.velocity_z -= 1600 * sys_frametime; // 2x grav looks better for this one
174 int cont = pointcontents(this.origin - '0 0 32');
175 if(cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME)
176 this.velocity_z += 200;
178 UpdateCSQCProjectile(this);