]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/w_laser.qc
Adapt the laser and its no-ammo usage too
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / w_laser.qc
1 #ifdef REGISTER_WEAPON
2 REGISTER_WEAPON(LASER, w_laser, 0, 1, WEP_FLAG_NORMAL | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH, 0, "laser", "laser", _("Laser"))
3 #else
4 #ifdef SVQC
5 void(float imp) W_SwitchWeapon;
6
7 void W_Laser_SetAmmoCounter()
8 {
9         // set clip_load to the weapon we have switched to, if the gun uses reloading
10         if(!autocvar_g_balance_laser_reload_ammo)
11                 self.clip_load = 0; // also keeps crosshair ammo from displaying
12         else
13         {
14                 self.clip_load = self.weapon_load[WEP_LASER];
15                 self.clip_size = autocvar_g_balance_laser_reload_ammo; // for the crosshair ammo display
16         }
17 }
18
19 void W_Laser_Reload()
20 {
21         self.(self.reload_ammo_player) = 0;
22         self.reload_ammo_min = 0;
23         self.reload_ammo_amount = autocvar_g_balance_laser_reload_ammo;
24         self.reload_time = autocvar_g_balance_laser_reload_time;
25         self.reload_sound = "weapons/reload.wav";
26
27         W_Reload();
28 }
29
30 void W_Laser_Touch (void)
31 {
32         PROJECTILE_TOUCH;
33
34         self.event_damage = SUB_Null;
35         if (self.dmg)
36                 RadiusDamage (self, self.owner, autocvar_g_balance_laser_secondary_damage, autocvar_g_balance_laser_secondary_edgedamage, autocvar_g_balance_laser_secondary_radius, world, autocvar_g_balance_laser_secondary_force, self.projectiledeathtype, other);
37         else
38                 RadiusDamage (self, self.owner, autocvar_g_balance_laser_primary_damage, autocvar_g_balance_laser_primary_edgedamage, autocvar_g_balance_laser_primary_radius, world, autocvar_g_balance_laser_primary_force, self.projectiledeathtype, other);
39
40         remove (self);
41 }
42
43 void W_Laser_Think()
44 {
45         self.movetype = MOVETYPE_FLY;
46         self.think = SUB_Remove;
47         if (self.dmg)
48                 self.nextthink = time + autocvar_g_balance_laser_secondary_lifetime;
49         else
50                 self.nextthink = time + autocvar_g_balance_laser_primary_lifetime;
51         CSQCProjectile(self, TRUE, PROJECTILE_LASER, TRUE);
52 }
53
54 void W_Laser_Attack (float issecondary)
55 {
56         local entity missile;
57         vector s_forward;
58         float a;
59         float nodamage;
60
61         if(issecondary == 2) // minstanex shot
62                 nodamage = g_minstagib;
63         else
64                 nodamage = FALSE;
65
66         a = autocvar_g_balance_laser_primary_shotangle;
67         s_forward = v_forward * cos(a * DEG2RAD) + v_up * sin(a * DEG2RAD);
68
69         if(nodamage)
70                 W_SetupShot_Dir (self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", CHAN_WEAPON2, 0);
71         else if(issecondary == 1)
72                 W_SetupShot_Dir (self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", CHAN_WEAPON2, autocvar_g_balance_laser_secondary_damage);
73         else
74                 W_SetupShot_Dir (self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", CHAN_WEAPON2, autocvar_g_balance_laser_primary_damage);
75         pointparticles(particleeffectnum("laser_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
76
77         missile = spawn ();
78         missile.owner = self;
79         missile.classname = "laserbolt";
80         missile.dmg = 0;
81         if(!nodamage)
82         {
83                 missile.bot_dodge = TRUE;
84                 missile.bot_dodgerating = autocvar_g_balance_laser_primary_damage;
85         }
86
87         PROJECTILE_MAKETRIGGER(missile);
88         missile.projectiledeathtype = WEP_LASER;
89
90         setorigin (missile, w_shotorg);
91         setsize(missile, '0 0 0', '0 0 0');
92
93         W_SETUPPROJECTILEVELOCITY(missile, g_balance_laser_primary);
94         missile.angles = vectoangles (missile.velocity);
95         //missile.glow_color = 250; // 244, 250
96         //missile.glow_size = 120;
97         missile.touch = W_Laser_Touch;
98
99         missile.flags = FL_PROJECTILE;
100
101         missile.think = W_Laser_Think;
102         missile.nextthink = time + autocvar_g_balance_laser_primary_delay;
103
104         other = missile; MUTATOR_CALLHOOK(EditProjectile);
105
106         if(time >= missile.nextthink)
107         {
108                 entity oldself;
109                 oldself = self;
110                 self = missile;
111                 self.think();
112                 self = oldself;
113         }
114 }
115
116 .vector hook_start, hook_end;
117 float gauntletbeam_send(entity to, float sf)
118 {
119         WriteByte(MSG_ENTITY, ENT_CLIENT_GAUNTLET);
120         sf = sf & 0x7F;
121         if(sound_allowed(MSG_BROADCAST, self.owner))
122                 sf |= 0x80;
123         WriteByte(MSG_ENTITY, sf);
124         if(sf & 1)
125         {
126                 WriteByte(MSG_ENTITY, num_for_edict(self.owner));
127         }
128         if(sf & 2)
129         {
130                 WriteCoord(MSG_ENTITY, self.hook_start_x);
131                 WriteCoord(MSG_ENTITY, self.hook_start_y);
132                 WriteCoord(MSG_ENTITY, self.hook_start_z);
133         }
134         if(sf & 4)
135         {
136                 WriteCoord(MSG_ENTITY, self.hook_end_x);
137                 WriteCoord(MSG_ENTITY, self.hook_end_y);
138                 WriteCoord(MSG_ENTITY, self.hook_end_z);
139         }
140         return TRUE;
141 }
142 .entity gauntletbeam;
143 .float prevgauntletfire;
144 void gauntletbeam_think()
145 {
146         float damage, myforce, myradius;
147         damage = autocvar_g_balance_laser_secondary_damage;
148         myforce = autocvar_g_balance_laser_secondary_force;
149         myradius = autocvar_g_balance_laser_secondary_radius;
150
151         self.owner.prevgauntletfire = time;
152         if (self.owner.weaponentity.state != WS_INUSE || self != self.owner.gauntletbeam || self.owner.deadflag != DEAD_NO || !self.owner.BUTTON_ATCK2)
153         {
154                 remove(self);
155                 return;
156         }
157
158         self.nextthink = time;
159
160         makevectors(self.owner.v_angle);
161
162         float dt;
163         dt = frametime;
164
165         W_SetupShot_Range(self.owner, TRUE, 0, "", 0, damage * dt, myradius);
166         WarpZone_traceline_antilag(self.owner, w_shotorg, w_shotend, MOVE_NORMAL, self.owner, ANTILAG_LATENCY(self.owner));
167
168         // apply the damage
169         if(trace_ent)
170         {
171                 vector force;
172                 force = w_shotdir * myforce;
173                 if(accuracy_isgooddamage(self.owner, trace_ent))
174                         accuracy_add(self.owner, WEP_LASER, 0, damage * dt);
175                 Damage (trace_ent, self.owner, self.owner, damage * dt, WEP_LASER | HITTYPE_SECONDARY, trace_endpos, force * dt);
176         }
177
178         // draw effect
179         if(w_shotorg != self.hook_start)
180         {
181                 self.SendFlags |= 2;
182                 self.hook_start = w_shotorg;
183         }
184         if(w_shotend != self.hook_end)
185         {
186                 self.SendFlags |= 4;
187                 self.hook_end = w_shotend;
188         }
189 }
190
191 // experimental gauntlet
192 void W_Laser_Attack2 ()
193 {
194         // only play fire sound if 0.5 sec has passed since player let go the fire button
195         if(time - self.prevgauntletfire > 0.5)
196         {
197                 sound (self, CHAN_WEAPON, "weapons/gauntlet_fire.wav", VOL_BASE, ATTN_NORM);
198         }
199
200         entity beam, oldself;
201
202         self.gauntletbeam = beam = spawn();
203         beam.solid = SOLID_NOT;
204         beam.think = gauntletbeam_think;
205         beam.owner = self;
206         beam.movetype = MOVETYPE_NONE;
207         beam.shot_spread = 0;
208         beam.bot_dodge = TRUE;
209         beam.bot_dodgerating = autocvar_g_balance_laser_primary_damage;
210         Net_LinkEntity(beam, FALSE, 0, gauntletbeam_send);
211
212         oldself = self;
213         self = beam;
214         self.think();
215         self = oldself;
216 }
217
218 void LaserInit()
219 {
220         weapon_action(WEP_LASER, WR_PRECACHE);
221         gauntlet_shotorigin[0] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_LASER), FALSE, FALSE, 1);
222         gauntlet_shotorigin[1] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_LASER), FALSE, FALSE, 2);
223         gauntlet_shotorigin[2] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_LASER), FALSE, FALSE, 3);
224         gauntlet_shotorigin[3] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_LASER), FALSE, FALSE, 4);
225 }
226
227 void spawnfunc_weapon_laser (void)
228 {
229         weapon_defaultspawnfunc(WEP_LASER);
230 }
231
232 float w_laser(float req)
233 {
234         local float r1;
235         local float r2;
236         if (req == WR_AIM)
237         {
238                 if(autocvar_g_balance_laser_secondary)
239                 {
240                         r1 = autocvar_g_balance_laser_primary_damage;
241                         r2 = autocvar_g_balance_laser_secondary_damage;
242                         if (random() * (r2 + r1) > r1)
243                                 self.BUTTON_ATCK2 = bot_aim(autocvar_g_balance_laser_secondary_speed, 0, autocvar_g_balance_laser_secondary_lifetime, FALSE);
244                         else
245                                 self.BUTTON_ATCK = bot_aim(autocvar_g_balance_laser_primary_speed, 0, autocvar_g_balance_laser_primary_lifetime, FALSE);
246                 }
247                 else
248                         self.BUTTON_ATCK = bot_aim(autocvar_g_balance_laser_primary_speed, 0, autocvar_g_balance_laser_primary_lifetime, FALSE);
249         }
250         else if (req == WR_THINK)
251         {
252                 if(autocvar_g_balance_laser_reload_ammo && self.clip_load < 1) // forced reload
253                         W_Laser_Reload();
254                 else if (self.BUTTON_ATCK)
255                 {
256                         if (weapon_prepareattack(0, autocvar_g_balance_laser_primary_refire))
257                         {
258                                 // if this weapon is reloadable, decrease its load
259                                 if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
260                                 {
261                                         if(autocvar_g_balance_laser_reload_ammo)
262                                         {
263                                                 self.clip_load -= 1;
264                                                 self.weapon_load[WEP_LASER] = self.clip_load;
265                                         }
266                                 }
267
268                                 W_Laser_Attack(0);
269                                 weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_laser_primary_animtime, w_ready);
270                         }
271                 }
272                 else if (self.BUTTON_ATCK2)
273                 {
274                         if(autocvar_g_balance_laser_secondary)
275                         {
276                                 // if this weapon is reloadable, decrease its load
277                                 if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
278                                 {
279                                         if(autocvar_g_balance_laser_reload_ammo)
280                                         {
281                                                 self.clip_load -= 1;
282                                                 self.weapon_load[WEP_LASER] = self.clip_load;
283                                         }
284                                 }
285
286                                 if (weapon_prepareattack(0, 0))
287                                 {
288                                         W_Laser_Attack2();
289                                         weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_laser_secondary_animtime, w_ready);
290                                 }
291                         }
292                         else
293                         {
294                                 if(self.switchweapon == WEP_LASER) // don't do this if already switching
295                                         W_SwitchWeapon (self.cnt);
296                         }
297                 }
298         }
299         else if (req == WR_PRECACHE)
300         {
301                 precache_model ("models/weapons/g_laser.md3");
302                 precache_model ("models/weapons/v_laser.md3");
303                 precache_model ("models/weapons/h_laser.iqm");
304                 precache_sound ("weapons/lasergun_fire.wav");
305                 precache_sound ("weapons/gauntlet_fire.wav");
306                 precache_sound ("weapons/reload.wav");
307         }
308         else if (req == WR_SETUP)
309         {
310                 weapon_setup(WEP_LASER);
311                 W_Laser_SetAmmoCounter();
312         }
313         else if (req == WR_CHECKAMMO1)
314         {
315                 return TRUE;
316         }
317         else if (req == WR_CHECKAMMO2)
318         {
319                 return TRUE;
320         }
321         else if (req == WR_RESETPLAYER)
322         {
323                 // all weapons must be fully loaded when we spawn
324                 self.weapon_load[WEP_LASER] = autocvar_g_balance_laser_reload_ammo;
325         }
326         else if (req == WR_RELOAD)
327         {
328                 W_Laser_Reload();
329         }
330         return TRUE;
331 };
332 #endif
333 #ifdef CSQC
334 float w_laser(float req)
335 {
336         if(req == WR_IMPACTEFFECT)
337         {
338                 vector org2;
339                 org2 = w_org + w_backoff * 6;
340                 pointparticles(particleeffectnum("laser_impact"), org2, w_backoff * 1000, 1);
341                 if(!w_issilent)
342                         sound(self, CHAN_PROJECTILE, "weapons/laserimpact.wav", VOL_BASE, ATTN_NORM);
343         }
344         else if(req == WR_PRECACHE)
345         {
346                 precache_sound("weapons/laserimpact.wav");
347         }
348         else if (req == WR_SUICIDEMESSAGE)
349                 w_deathtypestring = _("%s lasered themself to hell");
350         else if (req == WR_KILLMESSAGE)
351         {
352                 if(w_deathtype & HITTYPE_SECONDARY)
353                         w_deathtypestring = _("%s was cut in half by %s's gauntlet"); // unchecked: SPLASH
354                 else
355                         w_deathtypestring = _("%s was lasered to death by %s"); // unchecked: SPLASH
356         }
357         return TRUE;
358 }
359 #endif
360 #endif