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