]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/w_laser.qc
Working on new laser primary attack -- now has no projectile and works with its own...
[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 void() W_LastWeapon;
7
8 void W_Laser_Touch (void)
9 {
10         PROJECTILE_TOUCH;
11
12         self.event_damage = SUB_Null;
13         if (self.dmg)
14                 RadiusDamage (self, self.realowner, 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);
15         else
16                 RadiusDamage (self, self.realowner, 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);
17
18         remove (self);
19 }
20
21 void W_Laser_Think()
22 {
23         self.movetype = MOVETYPE_FLY;
24         self.think = SUB_Remove;
25         if (self.dmg)
26                 self.nextthink = time + autocvar_g_balance_laser_secondary_lifetime;
27         else
28                 self.nextthink = time + autocvar_g_balance_laser_primary_lifetime;
29         CSQCProjectile(self, TRUE, PROJECTILE_LASER, TRUE);
30 }
31
32 void W_Laser_Shockwave (void)
33 {
34         // declarations
35         float final_damage, final_spread;
36         entity head, next;
37         vector nearest, attack_endpos, angle_to_head, angle_to_attack, final_force;
38         
39         // set up the shot direction
40         vector wanted_shot_direction = (v_forward * cos(autocvar_g_balance_laser_primary_shotangle * DEG2RAD) + v_up * sin(autocvar_g_balance_laser_primary_shotangle * DEG2RAD));
41         W_SetupShot_Dir(self, wanted_shot_direction, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, autocvar_g_balance_laser_primary_damage);
42         vector targpos = (w_shotorg + (w_shotdir * autocvar_g_balance_laser_primary_jumpradius));
43
44         // trace to see if this is a self jump
45         WarpZone_TraceLine(w_shotorg, targpos, FALSE, self);
46         //te_lightning2(world, targpos, w_shotorg);
47
48         if(trace_fraction < 1) // Yes, it is a close range jump
49         {
50                 RadiusDamageForSource(self, trace_endpos, '0 0 0', self, autocvar_g_balance_laser_primary_damage, autocvar_g_balance_laser_primary_edgedamage, autocvar_g_balance_laser_primary_jumpradius, world, TRUE, autocvar_g_balance_laser_primary_force, WEP_LASER, world);
51                 pointparticles(particleeffectnum("laser_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
52         }
53         else // No, it's a mid range attack
54         {
55                 // find out what i'm pointing at
56                 targpos = (w_shotorg + (w_shotdir * autocvar_g_balance_laser_primary_radius));
57                 WarpZone_TraceLine(w_shotorg, targpos, MOVE_WORLDONLY, self);
58                 
59                 te_lightning2(world, trace_endpos, w_shotorg);
60                 
61                 attack_endpos = trace_endpos;
62                 //total_attack_range = vlen(w_shotorg - trace_endpos);
63                 
64                 // now figure out if I hit anything...
65                 head = WarpZone_FindRadius(attack_endpos, vlen(w_shotorg - trace_endpos) + MAX_DAMAGEEXTRARADIUS, FALSE);
66                 while(head)
67                 {
68                         next = head.chain;
69                         
70                         if((head != self) && (head.takedamage))
71                         {
72                                 // is it in range of the attack?
73                                 nearest = WarpZoneLib_NearestPointOnBox(head.origin + head.mins, head.origin + head.maxs, w_shotorg);
74                                 if(vlen(w_shotorg - nearest) <= autocvar_g_balance_laser_primary_radius)
75                                 {
76                                         // is it within the limit of the spread?
77                                         nearest = head.WarpZone_findradius_nearest;
78                                         angle_to_head = normalize(nearest - w_shotorg);
79                                         angle_to_attack = w_shotdir;
80                                         final_spread = vlen(angle_to_head - angle_to_attack);
81                                         if(final_spread <= autocvar_g_balance_laser_primary_spread)
82                                         {
83                                                 // is it visible to the weapon?
84                                                 //WarpZone_TraceLine(w_shotorg, nearest, MOVE_WORLDONLY, self);
85                                                 //if(trace_fraction == 1)
86                                                 //{
87                                                         // finally lets do some damage bitches!
88                                                         if(autocvar_g_balance_laser_primary_spread)
89                                                                 final_damage = (final_spread / autocvar_g_balance_laser_primary_spread);
90                                                         else
91                                                                 final_damage = 1;
92                                                                 
93                                                         final_force = (normalize(nearest - w_shotorg) * autocvar_g_balance_laser_primary_force);
94                                                         final_damage = (autocvar_g_balance_laser_primary_damage * final_damage + autocvar_g_balance_laser_primary_edgedamage * (1 - final_damage));
95                                                         
96                                                         Damage(head, self, self, final_damage, WEP_LASER, w_shotorg, final_force);
97                                                         
98                                                         print(strcat(vtos(angle_to_head), " - ", vtos(angle_to_attack), ": ", ftos(vlen(angle_to_head - angle_to_attack)), ".\n"));
99                                                         te_lightning2(world, nearest, w_shotorg);
100                                                 //}
101                                         }
102                                 }
103                         }
104                         
105                         head = next;
106                 }
107                 pointparticles(particleeffectnum("laser_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
108         }
109 }
110
111 void W_Laser_Attack (float issecondary)
112 {
113         entity missile;
114         vector s_forward;
115         float a;
116         float nodamage;
117
118         if(issecondary == 2) // minstanex shot
119                 nodamage = g_minstagib;
120         else
121                 nodamage = FALSE;
122
123         a = autocvar_g_balance_laser_primary_shotangle;
124         s_forward = v_forward * cos(a * DEG2RAD) + v_up * sin(a * DEG2RAD);
125
126         if(nodamage)
127                 W_SetupShot_Dir (self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, 0);
128         else if(issecondary == 1)
129                 W_SetupShot_Dir (self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, autocvar_g_balance_laser_secondary_damage);
130         else
131                 W_SetupShot_Dir (self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, autocvar_g_balance_laser_primary_damage);
132         pointparticles(particleeffectnum("laser_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
133
134         missile = spawn ();
135         missile.owner = missile.realowner = self;
136         missile.classname = "laserbolt";
137         missile.dmg = 0;
138         if(!nodamage)
139         {
140                 missile.bot_dodge = TRUE;
141                 missile.bot_dodgerating = autocvar_g_balance_laser_primary_damage;
142         }
143
144         PROJECTILE_MAKETRIGGER(missile);
145         missile.projectiledeathtype = WEP_LASER;
146
147         setorigin (missile, w_shotorg);
148         setsize(missile, '0 0 0', '0 0 0');
149
150         W_SETUPPROJECTILEVELOCITY(missile, g_balance_laser_primary);
151         missile.angles = vectoangles (missile.velocity);
152         //missile.glow_color = 250; // 244, 250
153         //missile.glow_size = 120;
154         missile.touch = W_Laser_Touch;
155
156         missile.flags = FL_PROJECTILE;
157
158         missile.think = W_Laser_Think;
159         missile.nextthink = time + autocvar_g_balance_laser_primary_delay;
160
161         other = missile; MUTATOR_CALLHOOK(EditProjectile);
162
163         if(time >= missile.nextthink)
164         {
165                 entity oldself;
166                 oldself = self;
167                 self = missile;
168                 self.think();
169                 self = oldself;
170         }
171 }
172
173 .vector hook_start, hook_end;
174 float gauntletbeam_send(entity to, float sf)
175 {
176         WriteByte(MSG_ENTITY, ENT_CLIENT_GAUNTLET);
177         sf = sf & 0x7F;
178         if(sound_allowed(MSG_BROADCAST, self.realowner))
179                 sf |= 0x80;
180         WriteByte(MSG_ENTITY, sf);
181         if(sf & 1)
182         {
183                 WriteByte(MSG_ENTITY, num_for_edict(self.realowner));
184         }
185         if(sf & 2)
186         {
187                 WriteCoord(MSG_ENTITY, self.hook_start_x);
188                 WriteCoord(MSG_ENTITY, self.hook_start_y);
189                 WriteCoord(MSG_ENTITY, self.hook_start_z);
190         }
191         if(sf & 4)
192         {
193                 WriteCoord(MSG_ENTITY, self.hook_end_x);
194                 WriteCoord(MSG_ENTITY, self.hook_end_y);
195                 WriteCoord(MSG_ENTITY, self.hook_end_z);
196         }
197         return TRUE;
198 }
199 .entity gauntletbeam;
200 .float prevgauntletfire;
201 entity lgbeam_owner_ent;
202 void gauntletbeam_think()
203 {
204         float damage, myforce, myradius;
205         damage = autocvar_g_balance_laser_secondary_damage;
206         myforce = autocvar_g_balance_laser_secondary_force;
207         myradius = autocvar_g_balance_laser_secondary_radius;
208
209         self.realowner.prevgauntletfire = time;
210         if (self.realowner.weaponentity.state != WS_INUSE || self != self.realowner.gauntletbeam || self.realowner.deadflag != DEAD_NO || !self.realowner.BUTTON_ATCK2)
211         {
212                 remove(self);
213                 return;
214         }
215
216         self.nextthink = time;
217
218         makevectors(self.realowner.v_angle);
219
220         float dt;
221         dt = frametime;
222
223         W_SetupShot_Range(self.realowner, TRUE, 0, "", 0, damage * dt, myradius);
224         if(!lgbeam_owner_ent)
225         {
226                 lgbeam_owner_ent = spawn();
227                 lgbeam_owner_ent.classname = "lgbeam_owner_ent";
228         }
229         WarpZone_traceline_antilag(lgbeam_owner_ent, w_shotorg, w_shotend, MOVE_NORMAL, lgbeam_owner_ent, ANTILAG_LATENCY(self.owner));
230
231         // apply the damage
232         if(trace_ent)
233         {
234                 vector force;
235                 force = w_shotdir * myforce;
236                 if(accuracy_isgooddamage(self.owner, trace_ent))
237                         accuracy_add(self.owner, WEP_LASER, 0, damage * dt);
238                 Damage (trace_ent, self.owner, self.owner, damage * dt, WEP_LASER | HITTYPE_SECONDARY, trace_endpos, force * dt);
239         }
240
241         // draw effect
242         if(w_shotorg != self.hook_start)
243         {
244                 self.SendFlags |= 2;
245                 self.hook_start = w_shotorg;
246         }
247         if(w_shotend != self.hook_end)
248         {
249                 self.SendFlags |= 4;
250                 self.hook_end = w_shotend;
251         }
252 }
253
254 // experimental gauntlet
255 void W_Laser_Attack2 ()
256 {
257         // only play fire sound if 0.5 sec has passed since player let go the fire button
258         if(time - self.prevgauntletfire > 0.5)
259         {
260                 sound (self, CH_WEAPON_A, "weapons/gauntlet_fire.wav", VOL_BASE, ATTN_NORM);
261         }
262
263         entity beam, oldself;
264
265         self.gauntletbeam = beam = spawn();
266         beam.solid = SOLID_NOT;
267         beam.think = gauntletbeam_think;
268         beam.owner = self;
269         beam.movetype = MOVETYPE_NONE;
270         beam.shot_spread = 0;
271         beam.bot_dodge = TRUE;
272         beam.bot_dodgerating = autocvar_g_balance_laser_primary_damage;
273         Net_LinkEntity(beam, FALSE, 0, gauntletbeam_send);
274
275         oldself = self;
276         self = beam;
277         self.think();
278         self = oldself;
279 }
280
281 void LaserInit()
282 {
283         weapon_action(WEP_LASER, WR_PRECACHE);
284         gauntlet_shotorigin[0] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_LASER), FALSE, FALSE, 1);
285         gauntlet_shotorigin[1] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_LASER), FALSE, FALSE, 2);
286         gauntlet_shotorigin[2] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_LASER), FALSE, FALSE, 3);
287         gauntlet_shotorigin[3] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_LASER), FALSE, FALSE, 4);
288 }
289
290 void spawnfunc_weapon_laser (void)
291 {
292         weapon_defaultspawnfunc(WEP_LASER);
293 }
294
295 float w_laser(float req)
296 {
297         float r1;
298         float r2;
299         if (req == WR_AIM)
300         {
301                 if(autocvar_g_balance_laser_secondary)
302                 {
303                         r1 = autocvar_g_balance_laser_primary_damage;
304                         r2 = autocvar_g_balance_laser_secondary_damage;
305                         if (random() * (r2 + r1) > r1)
306                                 self.BUTTON_ATCK2 = bot_aim(autocvar_g_balance_laser_secondary_speed, 0, autocvar_g_balance_laser_secondary_lifetime, FALSE);
307                         else
308                                 self.BUTTON_ATCK = bot_aim(autocvar_g_balance_laser_primary_speed, 0, autocvar_g_balance_laser_primary_lifetime, FALSE);
309                 }
310                 else
311                         self.BUTTON_ATCK = bot_aim(autocvar_g_balance_laser_primary_speed, 0, autocvar_g_balance_laser_primary_lifetime, FALSE);
312         }
313         else if (req == WR_THINK)
314         {
315                 if(autocvar_g_balance_laser_reload_ammo && self.clip_load < 1) // forced reload
316                         weapon_action(self.weapon, WR_RELOAD);
317                 else if (self.BUTTON_ATCK)
318                 {
319                         if (weapon_prepareattack(0, autocvar_g_balance_laser_primary_refire))
320                         {
321                                 W_DecreaseAmmo(ammo_none, 1, TRUE);
322
323                                 W_Laser_Shockwave();
324                                 weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_laser_primary_animtime, w_ready);
325                         }
326                 }
327                 else if (self.BUTTON_ATCK2)
328                 {
329                         if(autocvar_g_balance_laser_secondary)
330                         {
331                                 W_DecreaseAmmo(ammo_none, 1, TRUE);
332
333                                 if (weapon_prepareattack(0, 0))
334                                 {
335                                         W_Laser_Attack2();
336                                         weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_laser_secondary_animtime, w_ready);
337                                 }
338                         }
339                         else
340                         {
341                                 if(self.switchweapon == WEP_LASER) // don't do this if already switching
342                                         W_LastWeapon();
343                         }
344                 }
345         }
346         else if (req == WR_PRECACHE)
347         {
348                 precache_model ("models/weapons/g_laser.md3");
349                 precache_model ("models/weapons/v_laser.md3");
350                 precache_model ("models/weapons/h_laser.iqm");
351                 precache_sound ("weapons/lasergun_fire.wav");
352                 precache_sound ("weapons/gauntlet_fire.wav");
353                 //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else
354         }
355         else if (req == WR_SETUP)
356         {
357                 weapon_setup(WEP_LASER);
358                 self.current_ammo = ammo_none;
359         }
360         else if (req == WR_CHECKAMMO1)
361         {
362                 return TRUE;
363         }
364         else if (req == WR_CHECKAMMO2)
365         {
366                 return TRUE;
367         }
368         else if (req == WR_RELOAD)
369         {
370                 W_Reload(0, autocvar_g_balance_laser_reload_ammo, autocvar_g_balance_laser_reload_time, "weapons/reload.wav");
371         }
372         return TRUE;
373 }
374 #endif
375 #ifdef CSQC
376 float w_laser(float req)
377 {
378         if(req == WR_IMPACTEFFECT)
379         {
380                 vector org2;
381                 org2 = w_org + w_backoff * 6;
382                 pointparticles(particleeffectnum("laser_impact"), org2, w_backoff * 1000, 1);
383                 if(!w_issilent)
384                         sound(self, CH_SHOTS, "weapons/laserimpact.wav", VOL_BASE, ATTN_NORM);
385         }
386         else if(req == WR_PRECACHE)
387         {
388                 precache_sound("weapons/laserimpact.wav");
389         }
390         else if (req == WR_SUICIDEMESSAGE)
391                 w_deathtypestring = _("%s lasered themself to hell");
392         else if (req == WR_KILLMESSAGE)
393         {
394                 if(w_deathtype & HITTYPE_SECONDARY)
395                         w_deathtypestring = _("%s was cut in half by %s's gauntlet"); // unchecked: SPLASH
396                 else
397                         w_deathtypestring = _("%s was lasered to death by %s"); // unchecked: SPLASH
398         }
399         return TRUE;
400 }
401 #endif
402 #endif