#include "racer_weapon.qh" #ifdef SVQC void racer_fire_rocket(entity this, entity player, vector org, vector dir, entity trg); SOUND(RacerAttack_LASER_FIRE, W_Sound("lasergun_fire")); SOUND(RacerAttack_ROCKET_FIRE, W_Sound("rocket_fire")); METHOD(RacerAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { bool isPlayer = IS_PLAYER(actor); entity player = isPlayer ? actor : actor.owner; entity veh = player.vehicle; if (fire & 1) if (weapon_prepareattack(thiswep, player, weaponentity, false, autocvar_g_vehicle_racer_cannon_refire)) { if (veh) { veh.vehicle_energy -= autocvar_g_vehicle_racer_cannon_cost; veh.wait = time; } if (!isPlayer) { vector vshotorg = gettaginfo(veh, gettagindex(veh, (veh.cnt ? "tag_fire1" : "tag_fire2"))); veh.cnt = !veh.cnt; w_shotorg = vshotorg; w_shotdir = v_forward; // Fix z-aim (for chase mode) crosshair_trace(player); w_shotdir.z = normalize(trace_endpos - vshotorg).z * 0.5; } if (isPlayer) W_SetupShot_Dir(player, weaponentity, v_forward, false, 0, SND_Null, CH_WEAPON_B, 0, DEATH_VH_WAKI_GUN.m_id); vector org = w_shotorg; vector dir = w_shotdir; entity bolt = vehicles_projectile((veh ? veh : player), EFFECT_RACER_MUZZLEFLASH, SND_RacerAttack_LASER_FIRE, org, normalize(v_forward + randomvec() * autocvar_g_vehicle_racer_cannon_spread) * autocvar_g_vehicle_racer_cannon_speed, autocvar_g_vehicle_racer_cannon_damage, autocvar_g_vehicle_racer_cannon_radius, autocvar_g_vehicle_racer_cannon_force, 0, DEATH_VH_WAKI_GUN.m_id, PROJECTILE_WAKICANNON, 0, true, true, player); bolt.velocity = normalize(dir) * autocvar_g_vehicle_racer_cannon_speed; weapon_thinkf(player, weaponentity, WFRAME_FIRE1, 0, w_ready); } if (fire & 2) if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, 0.2)) { if (isPlayer) W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_Null, CH_WEAPON_B, 0, DEATH_VH_WAKI_ROCKET.m_id); racer_fire_rocket((veh ? veh : player), player, w_shotorg, w_shotdir, NULL); weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, 0, w_ready); } } METHOD(RacerAttack, wr_checkammo1, bool(RacerAttack thiswep, entity actor, .entity weaponentity)) { bool isPlayer = IS_PLAYER(actor); entity player = isPlayer ? actor : actor.owner; entity veh = player.vehicle; return isPlayer || veh.vehicle_energy >= autocvar_g_vehicle_racer_cannon_cost; } void racer_rocket_tracker(entity this); void racer_rocket_groundhugger(entity this); void racer_fire_rocket(entity this, entity player, vector org, vector dir, entity targ) { entity rocket = vehicles_projectile(this, EFFECT_RACER_ROCKETLAUNCH, SND_RacerAttack_ROCKET_FIRE, org, dir * autocvar_g_vehicle_racer_rocket_speed, autocvar_g_vehicle_racer_rocket_damage, autocvar_g_vehicle_racer_rocket_radius, autocvar_g_vehicle_racer_rocket_force, 3, DEATH_VH_WAKI_ROCKET.m_id, PROJECTILE_WAKIROCKET, 20, false, false, player); rocket.lip = autocvar_g_vehicle_racer_rocket_accel * sys_frametime; rocket.wait = autocvar_g_vehicle_racer_rocket_turnrate; rocket.nextthink = time; rocket.enemy = targ; rocket.cnt = time + 15; if (targ) setthink(rocket, racer_rocket_tracker); else setthink(rocket, racer_rocket_groundhugger); } void racer_rocket_tracker(entity this) { this.nextthink = time; if (IS_DEAD(this.owner) || this.cnt < time) { this.use(this, NULL, NULL); return; } if (!this.realowner.vehicle) { UpdateCSQCProjectile(this); return; } vector olddir = normalize(this.velocity); float oldvel = vlen(this.velocity); float newvel = oldvel + this.lip; makevectors(vectoangles(olddir)); float time_to_impact = min(vlen(this.enemy.origin - this.origin) / vlen(this.velocity), 1); vector predicted_origin = this.enemy.origin + this.enemy.velocity * time_to_impact; traceline(this.origin, this.origin + v_forward * 64 - '0 0 32', MOVE_NORMAL, this); vector newdir = normalize(predicted_origin - this.origin); //vector float height_diff = predicted_origin.z - this.origin.z; if (vdist(newdir - v_forward, >, autocvar_g_vehicle_racer_rocket_locked_maxangle)) { //bprint("Target lost!\n"); //dprint("OF:", ftos(vlen(newdir - v_forward)), "\n"); setthink(this, racer_rocket_groundhugger); return; } if (trace_fraction != 1.0 && trace_ent != this.enemy) newdir.z += 16 * sys_frametime; this.velocity = normalize(olddir + newdir * autocvar_g_vehicle_racer_rocket_turnrate) * newvel; this.velocity.z -= 800 * sys_frametime; this.velocity.z += max(height_diff, autocvar_g_vehicle_racer_rocket_climbspeed) * sys_frametime; UpdateCSQCProjectile(this); return; } void racer_rocket_groundhugger(entity this) { this.nextthink = time; if (IS_DEAD(this.owner) || this.cnt < time) { this.use(this, NULL, NULL); return; } if (!this.realowner.vehicle) { UpdateCSQCProjectile(this); return; } vector olddir = normalize(this.velocity); float oldvel = vlen(this.velocity); float newvel = oldvel + this.lip; tracebox(this.origin, this.mins, this.maxs, this.origin + olddir * 64, MOVE_WORLDONLY, this); if (trace_fraction <= 0.5) { // Hitting somethign soon, just speed ahead this.velocity = olddir * newvel; UpdateCSQCProjectile(this); return; } traceline(trace_endpos, trace_endpos - '0 0 64', MOVE_NORMAL, this); if (trace_fraction != 1.0) { vector newdir = normalize(trace_endpos + '0 0 64' - this.origin) * autocvar_g_vehicle_racer_rocket_turnrate; this.velocity = normalize(olddir + newdir) * newvel; } else { this.velocity = olddir * newvel; this.velocity.z -= 1600 * sys_frametime; // 2x grav looks better for this one } int cont = pointcontents(this.origin - '0 0 32'); if (cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME) this.velocity.z += 200; UpdateCSQCProjectile(this); return; } #endif // SVQC