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