02015eb8daba23c239bee5794c9734b206b2decc
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / vehicles / vehicle / racer_weapon.qc
1 #include "racer_weapon.qh"
2
3 #ifdef SVQC
4
5 void racer_fire_rocket(entity player, vector org, vector dir, entity trg);
6 METHOD(RacerAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
7 {
8     bool isPlayer = IS_PLAYER(actor);
9     entity player = isPlayer ? actor : actor.owner;
10     entity veh = player.vehicle;
11     if (fire & 1)
12     if (weapon_prepareattack(thiswep, player, weaponentity, false, autocvar_g_vehicle_racer_cannon_refire)) {
13         if (veh) {
14             veh.vehicle_energy -= autocvar_g_vehicle_racer_cannon_cost;
15             veh.wait = time;
16         }
17         if (isPlayer) W_SetupShot_Dir(player, weaponentity, v_forward, false, 0, SND_Null, CH_WEAPON_B, 0);
18         vector org = w_shotorg;
19         vector dir = w_shotdir;
20         entity bolt = vehicles_projectile(veh, EFFECT_RACER_MUZZLEFLASH.eent_eff_name, SND_LASERGUN_FIRE,
21                                org, normalize(v_forward + randomvec() * autocvar_g_vehicle_racer_cannon_spread) * autocvar_g_vehicle_racer_cannon_speed,
22                                autocvar_g_vehicle_racer_cannon_damage, autocvar_g_vehicle_racer_cannon_radius, autocvar_g_vehicle_racer_cannon_force,  0,
23                                DEATH_VH_WAKI_GUN.m_id, PROJECTILE_WAKICANNON, 0, true, true, player);
24         bolt.velocity = normalize(dir) * autocvar_g_vehicle_racer_cannon_speed;
25         weapon_thinkf(player, weaponentity, WFRAME_FIRE1, 0, w_ready);
26     }
27     if (fire & 2)
28     if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, 0.2)) {
29         if (isPlayer) W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_Null, CH_WEAPON_B, 0);
30         racer_fire_rocket(player, w_shotorg, w_shotdir, NULL);
31         weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, 0, w_ready);
32     }
33 }
34
35 METHOD(RacerAttack, wr_checkammo1, bool(RacerAttack thiswep, entity actor, .entity weaponentity))
36 {
37     bool isPlayer = IS_PLAYER(actor);
38     entity player = isPlayer ? actor : actor.owner;
39     entity veh = player.vehicle;
40     return isPlayer || veh.vehicle_energy >= autocvar_g_vehicle_racer_cannon_cost;
41 }
42
43 void racer_rocket_tracker(entity this);
44 void racer_rocket_groundhugger(entity this);
45
46 void racer_fire_rocket(entity player, vector org, vector dir, entity trg)
47 {
48     entity rocket = vehicles_projectile(player.vehicle, EFFECT_RACER_ROCKETLAUNCH.eent_eff_name, SND_ROCKET_FIRE,
49                            org, dir * autocvar_g_vehicle_racer_rocket_speed,
50                            autocvar_g_vehicle_racer_rocket_damage, autocvar_g_vehicle_racer_rocket_radius, autocvar_g_vehicle_racer_rocket_force, 3,
51                            DEATH_VH_WAKI_ROCKET.m_id, PROJECTILE_WAKIROCKET, 20, false, false, player);
52
53     rocket.lip                    = autocvar_g_vehicle_racer_rocket_accel * sys_frametime;
54     rocket.wait                  = autocvar_g_vehicle_racer_rocket_turnrate;
55     rocket.nextthink            = time;
56     rocket.enemy                        = trg;
57     rocket.cnt                    = time + 15;
58
59     if(trg)
60         setthink(rocket, racer_rocket_tracker);
61     else
62         setthink(rocket, racer_rocket_groundhugger);
63 }
64
65 void racer_rocket_tracker(entity this)
66 {
67     vector olddir, newdir;
68     float oldvel, newvel;
69
70     this.nextthink  = time;
71
72     if (IS_DEAD(this.owner) || this.cnt < time)
73     {
74         this.use(this, NULL, NULL);
75         return;
76     }
77
78     if(!this.realowner.vehicle)
79     {
80         UpdateCSQCProjectile(this);
81         return;
82     }
83
84     olddir = normalize(this.velocity);
85     oldvel = vlen(this.velocity);
86     newvel = oldvel + this.lip;
87     makevectors(vectoangles(olddir));
88
89     float time_to_impact = min(vlen(this.enemy.origin - this.origin) / vlen(this.velocity), 1);
90     vector predicted_origin = this.enemy.origin + this.enemy.velocity * time_to_impact;
91
92     traceline(this.origin, this.origin + v_forward * 64 - '0 0 32', MOVE_NORMAL, this);
93     newdir = normalize(predicted_origin - this.origin);
94
95     //vector
96     float height_diff = predicted_origin_z - this.origin_z;
97
98     if(vdist(newdir - v_forward, >, autocvar_g_vehicle_racer_rocket_locked_maxangle))
99     {
100         //bprint("Target lost!\n");
101         //dprint("OF:", ftos(vlen(newdir - v_forward)), "\n");
102         setthink(this, racer_rocket_groundhugger);
103         return;
104     }
105
106     if(trace_fraction != 1.0 && trace_ent != this.enemy)
107         newdir_z += 16 * sys_frametime;
108
109     this.velocity = normalize(olddir + newdir * autocvar_g_vehicle_racer_rocket_turnrate) * newvel;
110     this.velocity_z -= 800 * sys_frametime;
111     this.velocity_z += max(height_diff, autocvar_g_vehicle_racer_rocket_climbspeed) * sys_frametime ;
112
113     UpdateCSQCProjectile(this);
114     return;
115 }
116
117 void racer_rocket_groundhugger(entity this)
118 {
119     vector olddir, newdir;
120     float oldvel, newvel;
121
122     this.nextthink  = time;
123
124     if(IS_DEAD(this.owner) || this.cnt < time)
125     {
126         this.use(this, NULL, NULL);
127         return;
128     }
129
130     if(!this.realowner.vehicle)
131     {
132         UpdateCSQCProjectile(this);
133         return;
134     }
135
136     olddir = normalize(this.velocity);
137     oldvel = vlen(this.velocity);
138     newvel = oldvel + this.lip;
139
140     tracebox(this.origin, this.mins, this.maxs, this.origin + olddir * 64, MOVE_WORLDONLY,this);
141     if(trace_fraction <= 0.5)
142     {
143         // Hitting somethign soon, just speed ahead
144         this.velocity = olddir * newvel;
145         UpdateCSQCProjectile(this);
146         return;
147     }
148
149     traceline(trace_endpos, trace_endpos - '0 0 64', MOVE_NORMAL, this);
150     if(trace_fraction != 1.0)
151     {
152         newdir = normalize(trace_endpos + '0 0 64' - this.origin) * autocvar_g_vehicle_racer_rocket_turnrate;
153         this.velocity = normalize(olddir + newdir) * newvel;
154     }
155     else
156     {
157         this.velocity = olddir * newvel;
158         this.velocity_z -= 1600 * sys_frametime; // 2x grav looks better for this one
159     }
160
161     int cont = pointcontents(this.origin - '0 0 32');
162     if(cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME)
163         this.velocity_z += 200;
164
165     UpdateCSQCProjectile(this);
166     return;
167 }
168
169 #endif