]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/vehicles/vehicle/racer_weapon.qc
Fix FL_WEAPON flag overlapping FL_JUMPRELEASED. This unintentional change was introdu...
[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 this, 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)
18         {
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));
23             w_shotorg = vshotorg;
24             w_shotdir = v_forward;
25
26             // Fix z-aim (for chase mode)
27             crosshair_trace(player);
28             w_shotdir.z = normalize(trace_endpos - vshotorg).z * 0.5;
29         }
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);
39     }
40     if (fire & 2)
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);
45     }
46 }
47
48 METHOD(RacerAttack, wr_checkammo1, bool(RacerAttack thiswep, entity actor, .entity weaponentity))
49 {
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;
54 }
55
56 void racer_rocket_tracker(entity this);
57 void racer_rocket_groundhugger(entity this);
58
59 void racer_fire_rocket(entity this, entity player, vector org, vector dir, entity trg)
60 {
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);
65
66     rocket.lip                    = autocvar_g_vehicle_racer_rocket_accel * sys_frametime;
67     rocket.wait                  = autocvar_g_vehicle_racer_rocket_turnrate;
68     rocket.nextthink            = time;
69     rocket.enemy                        = trg;
70     rocket.cnt                    = time + 15;
71
72     if(trg)
73         setthink(rocket, racer_rocket_tracker);
74     else
75         setthink(rocket, racer_rocket_groundhugger);
76 }
77
78 void racer_rocket_tracker(entity this)
79 {
80     vector olddir, newdir;
81     float oldvel, newvel;
82
83     this.nextthink  = time;
84
85     if (IS_DEAD(this.owner) || this.cnt < time)
86     {
87         this.use(this, NULL, NULL);
88         return;
89     }
90
91     if(!this.realowner.vehicle)
92     {
93         UpdateCSQCProjectile(this);
94         return;
95     }
96
97     olddir = normalize(this.velocity);
98     oldvel = vlen(this.velocity);
99     newvel = oldvel + this.lip;
100     makevectors(vectoangles(olddir));
101
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;
104
105     traceline(this.origin, this.origin + v_forward * 64 - '0 0 32', MOVE_NORMAL, this);
106     newdir = normalize(predicted_origin - this.origin);
107
108     //vector
109     float height_diff = predicted_origin_z - this.origin_z;
110
111     if(vdist(newdir - v_forward, >, autocvar_g_vehicle_racer_rocket_locked_maxangle))
112     {
113         //bprint("Target lost!\n");
114         //dprint("OF:", ftos(vlen(newdir - v_forward)), "\n");
115         setthink(this, racer_rocket_groundhugger);
116         return;
117     }
118
119     if(trace_fraction != 1.0 && trace_ent != this.enemy)
120         newdir_z += 16 * sys_frametime;
121
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 ;
125
126     UpdateCSQCProjectile(this);
127     return;
128 }
129
130 void racer_rocket_groundhugger(entity this)
131 {
132     vector olddir, newdir;
133     float oldvel, newvel;
134
135     this.nextthink  = time;
136
137     if(IS_DEAD(this.owner) || this.cnt < time)
138     {
139         this.use(this, NULL, NULL);
140         return;
141     }
142
143     if(!this.realowner.vehicle)
144     {
145         UpdateCSQCProjectile(this);
146         return;
147     }
148
149     olddir = normalize(this.velocity);
150     oldvel = vlen(this.velocity);
151     newvel = oldvel + this.lip;
152
153     tracebox(this.origin, this.mins, this.maxs, this.origin + olddir * 64, MOVE_WORLDONLY,this);
154     if(trace_fraction <= 0.5)
155     {
156         // Hitting somethign soon, just speed ahead
157         this.velocity = olddir * newvel;
158         UpdateCSQCProjectile(this);
159         return;
160     }
161
162     traceline(trace_endpos, trace_endpos - '0 0 64', MOVE_NORMAL, this);
163     if(trace_fraction != 1.0)
164     {
165         newdir = normalize(trace_endpos + '0 0 64' - this.origin) * autocvar_g_vehicle_racer_rocket_turnrate;
166         this.velocity = normalize(olddir + newdir) * newvel;
167     }
168     else
169     {
170         this.velocity = olddir * newvel;
171         this.velocity_z -= 1600 * sys_frametime; // 2x grav looks better for this one
172     }
173
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;
177
178     UpdateCSQCProjectile(this);
179     return;
180 }
181
182 #endif