]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/mapobjects/misc/laser.qc
Add Read/WriteAngleVector macros to simplify the networking of angles
[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                         WriteAngleVector2D(MSG_ENTITY, this.mangle);
165                 }
166         }
167         if(sendflags & SF_LASER_UPDATE_ACTIVE)
168                 WriteByte(MSG_ENTITY, this.active);
169         return true;
170 }
171
172 /*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
173 Any object touching the beam will be hurt
174 Keys:
175 "target"
176  spawnfunc_target_position where the laser ends
177 "mdl"
178  name of beam end effect to use
179 "beam_color"
180  color of the beam (default: red)
181 "dmg"
182  damage per second (-1 for a laser that kills immediately)
183 */
184
185 void laser_setactive(entity this, int act)
186 {
187         int old_status = this.active;
188         if(act == ACTIVE_TOGGLE)
189         {
190                 if(this.active == ACTIVE_ACTIVE)
191                 {
192                         this.active = ACTIVE_NOT;
193                 }
194                 else
195                 {
196                         this.active = ACTIVE_ACTIVE;
197                 }
198         }
199         else
200         {
201                 this.active = act;
202         }
203
204         if (this.active != old_status)
205         {
206                 this.SendFlags |= SF_LASER_UPDATE_ACTIVE;
207                 misc_laser_aim(this);
208         }
209 }
210
211 void laser_use(entity this, entity actor, entity trigger)
212 {
213         this.setactive(this, ACTIVE_TOGGLE);
214 }
215
216 spawnfunc(misc_laser)
217 {
218         if(this.mdl)
219         {
220                 if(this.mdl == "none")
221                         this.cnt = -1;
222                 else
223                 {
224                         this.cnt = _particleeffectnum(this.mdl);
225                         if(this.cnt < 0 && this.dmg)
226                 this.cnt = particleeffectnum(EFFECT_LASER_DEADLY);
227                 }
228         }
229         else if(!this.cnt)
230         {
231                 if(this.dmg)
232                         this.cnt = particleeffectnum(EFFECT_LASER_DEADLY);
233                 else
234                         this.cnt = -1;
235         }
236         if(this.cnt < 0)
237                 this.cnt = -1;
238
239         if(!this.beam_color && this.colormod)
240         {
241                 LOG_WARN("misc_laser uses legacy field 'colormod', please use 'beam_color' instead");
242                 this.beam_color = this.colormod;
243         }
244
245         if(this.beam_color == '0 0 0')
246         {
247                 if(!this.alpha)
248                         this.beam_color = '1 0 0';
249         }
250
251         if(this.message == "")
252         {
253                 this.message = "saw the light";
254         }
255         if (this.message2 == "")
256         {
257                 this.message2 = "was pushed into a laser by";
258         }
259         if(!this.scale)
260         {
261                 this.scale = 1;
262         }
263         if(!this.modelscale)
264         {
265                 this.modelscale = 1;
266         }
267         else if(this.modelscale < 0)
268         {
269                 this.modelscale = 0;
270         }
271         setthink(this, misc_laser_think);
272         this.nextthink = time;
273         InitializeEntity(this, misc_laser_init, INITPRIO_FINDTARGET);
274
275         this.mangle = this.angles;
276
277         Net_LinkEntity(this, false, 0, laser_SendEntity);
278
279         this.setactive = laser_setactive;
280
281         if(this.targetname && this.targetname != "")
282         {
283                 // backwards compatibility
284                 this.use = laser_use;
285         }
286
287         this.reset = generic_netlinked_reset;
288         this.reset(this);
289 }
290 #elif defined(CSQC)
291
292 void Draw_Laser(entity this)
293 {
294         if(this.active == ACTIVE_NOT)
295                 return;
296         InterpolateOrigin_Do(this);
297         if(this.count & SF_LASER_FINITE)
298         {
299                 if(this.count & SF_LASER_NOTRACE)
300                 {
301                         trace_endpos = this.velocity;
302                         trace_dphitq3surfaceflags = 0;
303                 }
304                 else
305                         traceline(this.origin, this.velocity, 0, this);
306         }
307         else
308         {
309                 if(this.count & SF_LASER_NOTRACE)
310                 {
311                         makevectors(this.angles);
312                         trace_endpos = this.origin + v_forward * LASER_BEAM_MAXWORLDSIZE;
313                         trace_dphitq3surfaceflags = Q3SURFACEFLAG_SKY;
314                 }
315                 else
316                 {
317                         makevectors(this.angles);
318                         traceline(this.origin, this.origin + v_forward * LASER_BEAM_MAXLENGTH, 0, this);
319                         if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
320                                 trace_endpos = this.origin + v_forward * LASER_BEAM_MAXWORLDSIZE;
321                 }
322         }
323         if(this.scale != 0)
324         {
325                 if(this.alpha)
326                 {
327                         Draw_CylindricLine(this.origin, trace_endpos, this.scale, "particles/laserbeam", 0, time * 3, this.beam_color, this.alpha, DRAWFLAG_NORMAL, view_origin);
328                 }
329                 else
330                 {
331                         Draw_CylindricLine(this.origin, trace_endpos, this.scale, "particles/laserbeam", 0, time * 3, this.beam_color, 0.5, DRAWFLAG_ADDITIVE, view_origin);
332                 }
333         }
334         if (!(trace_dphitq3surfaceflags & (Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT)))
335         {
336                 if(this.cnt >= 0)
337                         __pointparticles(this.cnt, trace_endpos, trace_plane_normal, drawframetime * 1000);
338                 if(this.beam_color != '0 0 0' && this.modelscale != 0)
339                         adddynamiclight(trace_endpos + trace_plane_normal * 1, this.modelscale, this.beam_color * 5);
340         }
341 }
342
343 NET_HANDLE(ENT_CLIENT_LASER, bool isnew)
344 {
345         InterpolateOrigin_Undo(this);
346
347         // 30 bytes, or 13 bytes for just moving
348         int sendflags = ReadByte();
349         this.count = (sendflags & 0xF0);
350
351         if(this.count & SF_LASER_FINITE)
352                 this.iflags = IFLAG_VELOCITY | IFLAG_ORIGIN;
353         else
354                 this.iflags = IFLAG_ANGLES | IFLAG_ORIGIN;
355
356         if(sendflags & SF_LASER_UPDATE_ORIGIN)
357         {
358                 this.origin = ReadVector();
359                 setorigin(this, this.origin);
360         }
361         if(sendflags & SF_LASER_UPDATE_EFFECT)
362         {
363                 this.beam_color.x = ReadByte() / 255.0;
364                 this.beam_color.y = ReadByte() / 255.0;
365                 this.beam_color.z = ReadByte() / 255.0;
366                 if(sendflags & SF_LASER_ALPHA)
367                         this.alpha = ReadByte() / 255.0;
368                 else
369                         this.alpha = 0;
370                 this.scale = 2; // NOTE: why 2?
371                 this.modelscale = 50; // NOTE: why 50?
372                 if(sendflags & SF_LASER_SCALE)
373                 {
374                         this.scale *= ReadByte() / 16.0; // beam radius
375                         this.modelscale *= ReadByte() / 16.0; // dlight radius
376                 }
377                 if((sendflags & SF_LASER_FINITE) || !(sendflags & SF_LASER_NOTRACE))
378                         this.cnt = ReadShort(); // effect number
379                 else
380                         this.cnt = 0;
381         }
382         if(sendflags & SF_LASER_UPDATE_TARGET)
383         {
384                 if(sendflags & SF_LASER_FINITE)
385                 {
386                         this.velocity = ReadVector();
387                 }
388                 else
389                 {
390                         this.angles = ReadAngleVector2D();
391                 }
392         }
393         if(sendflags & SF_LASER_UPDATE_ACTIVE)
394                 this.active = ReadByte();
395
396         return = true;
397
398         InterpolateOrigin_Note(this);
399         this.draw = Draw_Laser;
400         if (isnew) IL_PUSH(g_drawables, this);
401 }
402 #endif