Merge remote branch 'origin/fruitiex/fruitbalance' into divVerent/fruitbalance
[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_Touch (void)
8 {
9         PROJECTILE_TOUCH;
10
11         self.event_damage = SUB_Null;
12         if (self.dmg)
13                 RadiusDamage (self, self.owner, cvar("g_balance_laser_secondary_damage"), cvar("g_balance_laser_secondary_edgedamage"), cvar("g_balance_laser_secondary_radius"), world, cvar("g_balance_laser_secondary_force"), self.projectiledeathtype, other);
14         else
15                 RadiusDamage (self, self.owner, cvar("g_balance_laser_primary_damage"), cvar("g_balance_laser_primary_edgedamage"), cvar("g_balance_laser_primary_radius"), world, cvar("g_balance_laser_primary_force"), self.projectiledeathtype, other);
16
17         remove (self);
18 }
19
20 void W_Laser_Think()
21 {
22         self.movetype = MOVETYPE_FLY;
23         self.think = SUB_Remove;
24         if (self.dmg)
25                 self.nextthink = time + cvar("g_balance_laser_secondary_lifetime");
26         else
27                 self.nextthink = time + cvar("g_balance_laser_primary_lifetime");
28         CSQCProjectile(self, TRUE, PROJECTILE_LASER, TRUE);
29 }
30
31 void W_Laser_Attack (float issecondary)
32 {
33         local entity missile;
34         vector s_forward;
35         float a;
36         float nodamage;
37
38         if(issecondary == 2) // minstanex shot
39                 nodamage = g_minstagib;
40         else
41                 nodamage = FALSE;
42
43         if (issecondary == 1)
44                 a = cvar("g_balance_laser_secondary_shotangle");
45         else
46                 a = cvar("g_balance_laser_primary_shotangle");
47         s_forward = v_forward * cos(a * DEG2RAD) + v_up * sin(a * DEG2RAD);
48
49         if(nodamage)
50                 W_SetupShot_Dir (self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", 0);
51         else if(issecondary == 1)
52                 W_SetupShot_Dir (self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", cvar("g_balance_laser_secondary_damage"));
53         else
54                 W_SetupShot_Dir (self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", cvar("g_balance_laser_primary_damage"));
55         pointparticles(particleeffectnum("laser_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
56
57         missile = spawn ();
58         missile.owner = self;
59         missile.classname = "laserbolt";
60         missile.dmg = (issecondary == 1);
61         if(!nodamage)
62         {
63                 missile.bot_dodge = TRUE;
64                 if (issecondary == 1)
65                         missile.bot_dodgerating = cvar("g_balance_laser_secondary_damage");
66                 else
67                         missile.bot_dodgerating = cvar("g_balance_laser_primary_damage");
68         }
69
70         PROJECTILE_MAKETRIGGER(missile);
71         missile.projectiledeathtype = WEP_LASER;
72         if(issecondary == 1)
73                 missile.projectiledeathtype |= HITTYPE_SECONDARY;
74
75         setorigin (missile, w_shotorg);
76         setsize(missile, '0 0 0', '0 0 0');
77
78         if (issecondary == 1)
79                 W_SETUPPROJECTILEVELOCITY(missile, g_balance_laser_secondary);
80         else
81                 W_SETUPPROJECTILEVELOCITY(missile, g_balance_laser_primary);
82         missile.angles = vectoangles (missile.velocity);
83         //missile.glow_color = 250; // 244, 250
84         //missile.glow_size = 120;
85         missile.touch = W_Laser_Touch;
86
87         missile.flags = FL_PROJECTILE;
88
89         missile.think = W_Laser_Think;
90         if (issecondary == 1)
91                 missile.nextthink = time + cvar("g_balance_laser_secondary_delay");
92         else
93                 missile.nextthink = time + cvar("g_balance_laser_primary_delay");
94         if(time >= missile.nextthink)
95         {
96                 entity oldself;
97                 oldself = self;
98                 self = missile;
99                 self.think();
100                 self = oldself;
101         }
102 }
103
104 .vector hook_start, hook_end;
105 float gauntletbeam_send(entity to, float sf)
106 {
107         WriteByte(MSG_ENTITY, ENT_CLIENT_GAUNTLET);
108         sf = sf & 0x7F;
109         if(sound_allowed(MSG_BROADCAST, self.owner))
110                 sf |= 0x80;
111         WriteByte(MSG_ENTITY, sf);
112         if(sf & 1)
113         {
114                 WriteByte(MSG_ENTITY, num_for_edict(self.owner));
115         }
116         if(sf & 2)
117         {
118                 WriteCoord(MSG_ENTITY, self.hook_start_x);
119                 WriteCoord(MSG_ENTITY, self.hook_start_y);
120                 WriteCoord(MSG_ENTITY, self.hook_start_z);
121         }
122         if(sf & 4)
123         {
124                 WriteCoord(MSG_ENTITY, self.hook_end_x);
125                 WriteCoord(MSG_ENTITY, self.hook_end_y);
126                 WriteCoord(MSG_ENTITY, self.hook_end_z);
127         }
128         return TRUE;
129 }
130 .entity gauntletbeam;
131 .float prevgauntletfire;
132 void gauntletbeam_think()
133 {
134         vector endpos;
135         float oldsolid;
136
137         float damage, myforce, myradius;
138         if(self.cnt)
139         {
140                 damage = cvar("g_balance_laser_secondary_damage");
141                 myforce = cvar("g_balance_laser_secondary_force");
142                 myradius = cvar("g_balance_laser_secondary_radius");
143         }
144         else
145         {
146                 damage = cvar("g_balance_laser_primary_damage");
147                 myforce = cvar("g_balance_laser_primary_force");
148                 myradius = cvar("g_balance_laser_primary_radius");
149         }
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 && self.cnt) || (!self.owner.BUTTON_ATCK && !self.cnt))
153         {
154                 remove(self);
155                 return;
156         }
157
158         self.nextthink = time;
159
160         makevectors(self.owner.v_angle);
161         vector angle;
162         angle = v_forward;
163         // get effect origin
164         vector vecs, org;
165         if(self.owner.weaponentity.movedir_x > 0)
166                 vecs = self.owner.weaponentity.movedir;
167         else
168                 vecs = '0 0 0';
169         if(debug_shotorg != '0 0 0')
170                 vecs = debug_shotorg;
171         org = self.owner.origin + self.owner.view_ofs + v_forward * vecs_x + v_right * -vecs_y + v_up * vecs_z;
172         
173         oldsolid = self.owner.dphitcontentsmask;
174         self.owner.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
175
176         WarpZone_traceline_antilag(self.owner, self.owner.origin + self.owner.view_ofs, self.owner.origin + self.owner.view_ofs + angle * myradius, MOVE_NORMAL, self.owner, ANTILAG_LATENCY(self.owner));
177         endpos = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
178         WarpZone_traceline_antilag(self.owner, org, endpos + 4 * normalize(endpos - org), MOVE_NORMAL, self.owner, ANTILAG_LATENCY(self.owner));
179
180         self.owner.dphitcontentsmask = oldsolid;
181
182         // apply the damage
183         if(trace_fraction < 1)
184         {
185                 vector force;
186                 force = angle * myforce;
187                 Damage (trace_ent, self.owner, self.owner, damage * frametime, WEP_ELECTRO, trace_endpos, force * frametime);
188         }
189
190         // draw effect
191         if(org != self.hook_start)
192         {
193                 self.SendFlags |= 2;
194                 self.hook_start = org;
195         }
196         if(endpos != self.hook_end)
197         {
198                 self.SendFlags |= 4;
199                 self.hook_end = endpos;
200         }
201 }
202
203 // experimental gauntlet
204 void W_Laser_Attack2 (float issecondary)
205 {
206         // only play fire sound if 0.5 sec has passed since player let go the fire button
207         if(time - self.prevgauntletfire > 0.5)
208         {
209                 sound (self, CHAN_WEAPON, "weapons/gauntlet_fire.wav", VOL_BASE, ATTN_NORM);
210         }
211
212         entity beam, oldself;
213
214         self.gauntletbeam = beam = spawn();
215         beam.solid = SOLID_NOT;
216         beam.think = gauntletbeam_think;
217         beam.owner = self;
218         beam.movetype = MOVETYPE_NONE;
219         beam.shot_spread = 0;
220         beam.bot_dodge = TRUE;
221         beam.bot_dodgerating = cvar("g_balance_laser_primary_damage");
222         beam.cnt = issecondary;
223         Net_LinkEntity(beam, FALSE, 0, gauntletbeam_send);
224
225         oldself = self;
226         self = beam;
227         self.think();
228         self = oldself;
229 }
230
231 void LaserInit()
232 {
233         weapon_action(WEP_LASER, WR_PRECACHE);
234         gauntlet_shotorigin[0] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_LASER), FALSE, FALSE, 1);
235         gauntlet_shotorigin[1] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_LASER), FALSE, FALSE, 2);
236         gauntlet_shotorigin[2] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_LASER), FALSE, FALSE, 3);
237         gauntlet_shotorigin[3] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_LASER), FALSE, FALSE, 4);
238 }
239
240 void spawnfunc_weapon_laser (void)
241 {
242         weapon_defaultspawnfunc(WEP_LASER);
243 }
244
245 float w_laser(float req)
246 {
247         local float r1;
248         local float r2;
249         if (req == WR_AIM)
250         {
251                 if(cvar("g_balance_laser_secondary"))
252                 {
253                         r1 = cvar("g_balance_laser_primary_damage");
254                         r2 = cvar("g_balance_laser_secondary_damage");
255                         if (random() * (r2 + r1) > r1)
256                                 self.BUTTON_ATCK2 = bot_aim(cvar("g_balance_laser_secondary_speed"), 0, cvar("g_balance_laser_secondary_lifetime"), FALSE);
257                         else
258                                 self.BUTTON_ATCK = bot_aim(cvar("g_balance_laser_primary_speed"), 0, cvar("g_balance_laser_primary_lifetime"), FALSE);
259                 }
260                 else
261                         self.BUTTON_ATCK = bot_aim(cvar("g_balance_laser_primary_speed"), 0, cvar("g_balance_laser_primary_lifetime"), FALSE);
262         }
263         else if (req == WR_THINK)
264         {
265                 if (self.BUTTON_ATCK)
266                 if (weapon_prepareattack(0, (cvar("g_balance_laser_primary_gauntlet") ? 0 : cvar("g_balance_laser_primary_refire"))))
267                 {
268                         if(cvar("g_balance_laser_primary_gauntlet"))
269                                 W_Laser_Attack2(0);
270                         else
271                                 W_Laser_Attack(0);
272                         weapon_thinkf(WFRAME_FIRE1, cvar("g_balance_laser_primary_animtime"), w_ready);
273                 }
274                 if (self.BUTTON_ATCK2)
275                 {
276                         if(cvar("g_balance_laser_secondary"))
277                         {
278                                 if (weapon_prepareattack(0, (cvar("g_balance_laser_secondary_gauntlet") ? 0 : cvar("g_balance_laser_secondary_refire"))))
279                                 {
280                                         if(cvar("g_balance_laser_secondary_gauntlet"))
281                                                 W_Laser_Attack2(1);
282                                         else
283                                                 W_Laser_Attack(1);
284                                         weapon_thinkf(WFRAME_FIRE2, cvar("g_balance_laser_secondary_animtime"), w_ready);
285                                 }
286                         }
287                         else
288                         {
289                                 if(self.switchweapon == WEP_LASER) // don't do this if already switching
290                                         W_SwitchWeapon (self.cnt);
291                         }
292                 }
293         }
294         else if (req == WR_PRECACHE)
295         {
296                 precache_model ("models/weapons/g_laser.md3");
297                 precache_model ("models/weapons/v_laser.md3");
298                 precache_model ("models/weapons/h_laser.iqm");
299                 precache_sound ("weapons/lasergun_fire.wav");
300                 precache_sound ("weapons/gauntlet_fire.wav");
301         }
302         else if (req == WR_SETUP)
303                 weapon_setup(WEP_LASER);
304         else if (req == WR_CHECKAMMO1)
305                 return TRUE;
306         else if (req == WR_CHECKAMMO2)
307                 return TRUE;
308         return TRUE;
309 };
310 #endif
311 #ifdef CSQC
312 float w_laser(float req)
313 {
314         if(req == WR_IMPACTEFFECT)
315         {
316                 vector org2;
317                 org2 = w_org + w_backoff * 6;
318                 pointparticles(particleeffectnum("laser_impact"), org2, w_backoff * 1000, 1);
319                 if(!w_issilent)
320                         sound(self, CHAN_PROJECTILE, "weapons/laserimpact.wav", VOL_BASE, ATTN_NORM);
321         }
322         else if(req == WR_PRECACHE)
323         {
324                 precache_sound("weapons/laserimpact.wav");
325         }
326         else if (req == WR_SUICIDEMESSAGE)
327                 w_deathtypestring = "lasered themself to hell";
328         else if (req == WR_KILLMESSAGE)
329         {
330                 w_deathtypestring = "was lasered to death by"; // unchecked: SPLASH
331         }
332         return TRUE;
333 }
334 #endif
335 #endif