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