]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/mapobjects/misc/laser.qc
Merge branch 'master' into terencehill/glowmod_color_fix
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / mapobjects / misc / laser.qc
1 #include "laser.qh"
2 #if defined(CSQC)
3         #include <lib/csqcmodel/interpolate.qh>
4         #include <lib/csqcmodel/cl_model.qh>
5 #elif defined(MENUQC)
6 #elif defined(SVQC)
7 #endif
8
9 REGISTER_NET_LINKED(ENT_CLIENT_LASER)
10
11 #ifdef SVQC
12 void misc_laser_aim(entity this)
13 {
14         vector a;
15         if(this.enemy)
16         {
17                 if(this.spawnflags & LASER_FINITE)
18                 {
19                         if(this.enemy.origin != this.mangle)
20                         {
21                                 this.mangle = this.enemy.origin;
22                                 this.SendFlags |= SF_LASER_UPDATE_TARGET;
23                         }
24                 }
25                 else
26                 {
27                         a = vectoangles(this.enemy.origin - this.origin);
28                         a_x = -a_x;
29                         if(a != this.mangle)
30                         {
31                                 this.mangle = a;
32                                 this.SendFlags |= SF_LASER_UPDATE_TARGET;
33                         }
34                 }
35         }
36         else
37         {
38                 if(this.angles != this.mangle)
39                 {
40                         this.mangle = this.angles;
41                         this.SendFlags |= SF_LASER_UPDATE_TARGET;
42                 }
43         }
44         if(this.origin != this.oldorigin)
45         {
46                 this.SendFlags |= SF_LASER_UPDATE_ORIGIN;
47                 this.oldorigin = this.origin;
48         }
49 }
50
51 void misc_laser_init(entity this)
52 {
53         if(this.target != "")
54                 this.enemy = find(NULL, targetname, this.target);
55 }
56
57 .entity pusher;
58 void misc_laser_think(entity this)
59 {
60         vector o;
61         entity hitent;
62         vector hitloc;
63
64         this.nextthink = time;
65
66         if(this.active == ACTIVE_NOT)
67                 return;
68
69         misc_laser_aim(this);
70
71         if(this.enemy)
72         {
73                 o = this.enemy.origin;
74                 if (!(this.spawnflags & LASER_FINITE))
75                         o = this.origin + normalize(o - this.origin) * LASER_BEAM_MAXLENGTH;
76         }
77         else
78         {
79                 makevectors(this.mangle);
80                 o = this.origin + v_forward * LASER_BEAM_MAXLENGTH;
81         }
82
83         if(this.dmg || this.enemy.target != "")
84         {
85                 traceline(this.origin, o, MOVE_NORMAL, this);
86         }
87         hitent = trace_ent;
88         hitloc = trace_endpos;
89
90         if(this.enemy.target != "") // DETECTOR laser
91         {
92                 if(trace_ent.iscreature)
93                 {
94                         this.pusher = hitent;
95                         if(!this.count)
96                         {
97                                 this.count = 1;
98
99                                 SUB_UseTargets(this.enemy, this.enemy.pusher, NULL);
100                         }
101                 }
102                 else
103                 {
104                         if(this.count)
105                         {
106                                 this.count = 0;
107
108                                 SUB_UseTargets(this.enemy, this.enemy.pusher, NULL);
109                         }
110                 }
111         }
112
113         if(this.dmg)
114         {
115                 if(this.team)
116                         if(((this.spawnflags & LASER_INVERT_TEAM) == 0) == (this.team != hitent.team))
117                                 return;
118                 if(hitent.takedamage)
119                         Damage(hitent, this, this, ((this.dmg < 0) ? 100000 : (this.dmg * frametime)), DEATH_HURTTRIGGER.m_id, DMG_NOWEP, hitloc, '0 0 0');
120         }
121 }
122
123 bool laser_SendEntity(entity this, entity to, float sendflags)
124 {
125         WriteHeader(MSG_ENTITY, ENT_CLIENT_LASER);
126         sendflags = sendflags & 0x0F; // use that bit to indicate finite length laser
127         if(this.spawnflags & LASER_FINITE)
128                 sendflags |= SF_LASER_FINITE;
129         if(this.alpha)
130                 sendflags |= SF_LASER_ALPHA;
131         if(this.scale != 1 || this.modelscale != 1)
132                 sendflags |= SF_LASER_SCALE;
133         if(this.spawnflags & LASER_NOTRACE)
134                 sendflags |= SF_LASER_NOTRACE;
135         WriteByte(MSG_ENTITY, sendflags);
136         if(sendflags & SF_LASER_UPDATE_ORIGIN)
137         {
138                 WriteVector(MSG_ENTITY, this.origin);
139         }
140         if(sendflags & SF_LASER_UPDATE_EFFECT)
141         {
142                 WriteByte(MSG_ENTITY, this.beam_color.x * 255.0);
143                 WriteByte(MSG_ENTITY, this.beam_color.y * 255.0);
144                 WriteByte(MSG_ENTITY, this.beam_color.z * 255.0);
145                 if(sendflags & SF_LASER_ALPHA)
146                         WriteByte(MSG_ENTITY, this.alpha * 255.0);
147                 if(sendflags & SF_LASER_SCALE)
148                 {
149                         WriteByte(MSG_ENTITY, bound(0, this.scale * 16.0, 255));
150                         WriteByte(MSG_ENTITY, bound(0, this.modelscale * 16.0, 255));
151                 }
152                 if((sendflags & SF_LASER_FINITE) || !(sendflags & SF_LASER_NOTRACE)) // effect doesn't need sending if the laser is infinite and has collision testing turned off
153                         WriteShort(MSG_ENTITY, this.cnt);
154         }
155         if(sendflags & SF_LASER_UPDATE_TARGET)
156         {
157                 if(sendflags & SF_LASER_FINITE)
158                 {
159                         WriteVector(MSG_ENTITY, this.enemy.origin);
160                 }
161                 else
162                 {
163                         WriteAngleVector2D(MSG_ENTITY, this.mangle);
164                 }
165         }
166         if(sendflags & SF_LASER_UPDATE_ACTIVE)
167                 WriteByte(MSG_ENTITY, this.active);
168         return true;
169 }
170
171 /*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
172 Any object touching the beam will be hurt
173 Keys:
174 "target"
175  spawnfunc_target_position where the laser ends
176 "mdl"
177  name of beam end effect to use
178 "beam_color"
179  color of the beam (default: red)
180 "dmg"
181  damage per second (-1 for a laser that kills immediately)
182 */
183
184 void laser_setactive(entity this, int act)
185 {
186         int old_status = this.active;
187         if(act == ACTIVE_TOGGLE)
188         {
189                 if(this.active == ACTIVE_ACTIVE)
190                 {
191                         this.active = ACTIVE_NOT;
192                 }
193                 else
194                 {
195                         this.active = ACTIVE_ACTIVE;
196                 }
197         }
198         else
199         {
200                 this.active = act;
201         }
202
203         if (this.active != old_status)
204         {
205                 this.SendFlags |= SF_LASER_UPDATE_ACTIVE;
206                 misc_laser_aim(this);
207         }
208 }
209
210 void laser_use(entity this, entity actor, entity trigger)
211 {
212         this.setactive(this, ACTIVE_TOGGLE);
213 }
214
215 spawnfunc(misc_laser)
216 {
217         if(this.mdl)
218         {
219                 if(this.mdl == "none")
220                         this.cnt = -1;
221                 else
222                 {
223                         this.cnt = _particleeffectnum(this.mdl);
224                         if(this.cnt < 0 && this.dmg)
225                 this.cnt = particleeffectnum(EFFECT_LASER_DEADLY);
226                 }
227         }
228         else if(!this.cnt)
229         {
230                 if(this.dmg)
231                         this.cnt = particleeffectnum(EFFECT_LASER_DEADLY);
232                 else
233                         this.cnt = -1;
234         }
235         if(this.cnt < 0)
236                 this.cnt = -1;
237
238         if(!this.beam_color && this.colormod)
239         {
240                 LOG_WARN("misc_laser uses legacy field 'colormod', please use 'beam_color' instead");
241                 this.beam_color = this.colormod;
242         }
243
244         if(this.beam_color == '0 0 0')
245         {
246                 if(!this.alpha)
247                         this.beam_color = '1 0 0';
248         }
249
250         if(this.message == "")
251         {
252                 this.message = "saw the light";
253         }
254         if (this.message2 == "")
255         {
256                 this.message2 = "was pushed into a laser by";
257         }
258         if(!this.scale)
259         {
260                 this.scale = 1;
261         }
262         if(!this.modelscale)
263         {
264                 this.modelscale = 1;
265         }
266         else if(this.modelscale < 0)
267         {
268                 this.modelscale = 0;
269         }
270         setthink(this, misc_laser_think);
271         this.nextthink = time;
272         InitializeEntity(this, misc_laser_init, INITPRIO_FINDTARGET);
273
274         this.mangle = this.angles;
275
276         Net_LinkEntity(this, false, 0, laser_SendEntity);
277
278         this.setactive = laser_setactive;
279
280         if(this.targetname && this.targetname != "")
281         {
282                 // backwards compatibility
283                 this.use = laser_use;
284         }
285
286         this.reset = generic_netlinked_reset;
287         this.reset(this);
288 }
289 #elif defined(CSQC)
290
291 void Draw_Laser(entity this)
292 {
293         if(this.active == ACTIVE_NOT)
294                 return;
295         InterpolateOrigin_Do(this);
296         if(this.count & SF_LASER_FINITE)
297         {
298                 if(this.count & SF_LASER_NOTRACE)
299                 {
300                         trace_endpos = this.velocity;
301                         trace_dphitq3surfaceflags = 0;
302                 }
303                 else
304                         traceline(this.origin, this.velocity, 0, this);
305         }
306         else
307         {
308                 if(this.count & SF_LASER_NOTRACE)
309                 {
310                         makevectors(this.angles);
311                         trace_endpos = this.origin + v_forward * LASER_BEAM_MAXWORLDSIZE;
312                         trace_dphitq3surfaceflags = Q3SURFACEFLAG_SKY;
313                 }
314                 else
315                 {
316                         makevectors(this.angles);
317                         traceline(this.origin, this.origin + v_forward * LASER_BEAM_MAXLENGTH, 0, this);
318                         if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
319                                 trace_endpos = this.origin + v_forward * LASER_BEAM_MAXWORLDSIZE;
320                 }
321         }
322         if(this.scale != 0)
323         {
324                 if(this.alpha)
325                 {
326                         Draw_CylindricLine(this.origin, trace_endpos, this.scale, "particles/laserbeam", 0, time * 3, this.beam_color, this.alpha, DRAWFLAG_NORMAL, view_origin);
327                 }
328                 else
329                 {
330                         Draw_CylindricLine(this.origin, trace_endpos, this.scale, "particles/laserbeam", 0, time * 3, this.beam_color, 0.5, DRAWFLAG_ADDITIVE, view_origin);
331                 }
332         }
333         if (!(trace_dphitq3surfaceflags & (Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT)))
334         {
335                 if(this.cnt >= 0)
336                         __pointparticles(this.cnt, trace_endpos, trace_plane_normal, drawframetime * 1000);
337                 if(this.beam_color != '0 0 0' && this.modelscale != 0)
338                         adddynamiclight(trace_endpos + trace_plane_normal * 1, this.modelscale, this.beam_color * 5);
339         }
340 }
341
342 NET_HANDLE(ENT_CLIENT_LASER, bool isnew)
343 {
344         InterpolateOrigin_Undo(this);
345
346         // 30 bytes, or 13 bytes for just moving
347         int sendflags = ReadByte();
348         this.count = (sendflags & 0xF0);
349
350         if(this.count & SF_LASER_FINITE)
351                 this.iflags = IFLAG_VELOCITY | IFLAG_ORIGIN;
352         else
353                 this.iflags = IFLAG_ANGLES | IFLAG_ORIGIN;
354
355         if(sendflags & SF_LASER_UPDATE_ORIGIN)
356         {
357                 this.origin = ReadVector();
358                 setorigin(this, this.origin);
359         }
360         if(sendflags & SF_LASER_UPDATE_EFFECT)
361         {
362                 this.beam_color.x = ReadByte() / 255.0;
363                 this.beam_color.y = ReadByte() / 255.0;
364                 this.beam_color.z = ReadByte() / 255.0;
365                 if(sendflags & SF_LASER_ALPHA)
366                         this.alpha = ReadByte() / 255.0;
367                 else
368                         this.alpha = 0;
369                 this.scale = 2; // NOTE: why 2?
370                 this.modelscale = 50; // NOTE: why 50?
371                 if(sendflags & SF_LASER_SCALE)
372                 {
373                         this.scale *= ReadByte() / 16.0; // beam radius
374                         this.modelscale *= ReadByte() / 16.0; // dlight radius
375                 }
376                 if((sendflags & SF_LASER_FINITE) || !(sendflags & SF_LASER_NOTRACE))
377                         this.cnt = ReadShort(); // effect number
378                 else
379                         this.cnt = 0;
380         }
381         if(sendflags & SF_LASER_UPDATE_TARGET)
382         {
383                 if(sendflags & SF_LASER_FINITE)
384                 {
385                         this.velocity = ReadVector();
386                 }
387                 else
388                 {
389                         this.angles = ReadAngleVector2D();
390                 }
391         }
392         if(sendflags & SF_LASER_UPDATE_ACTIVE)
393                 this.active = ReadByte();
394
395         return = true;
396
397         InterpolateOrigin_Note(this);
398         this.draw = Draw_Laser;
399         if (isnew) IL_PUSH(g_drawables, this);
400 }
401 #endif