Xonotic
laser.qc
Go to the documentation of this file.
1 #include "laser.qh"
2 #if defined(CSQC)
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);
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;
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);
313  }
314  else
315  {
316  makevectors(this.angles);
317  traceline(this.origin, this.origin + v_forward * LASER_BEAM_MAXLENGTH, 0, this);
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  }
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)
339  }
340 }
341 
342 NET_HANDLE(ENT_CLIENT_LASER, bool isnew)
343 {
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)
352  else
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 
398  this.draw = Draw_Laser;
399  if (isnew) IL_PUSH(g_drawables, this);
400 }
401 #endif
void InterpolateOrigin_Note(entity this)
Definition: interpolate.qc:37
const float LASER_BEAM_MAXLENGTH
Definition: laser.qh:32
#define LOG_WARN(...)
Definition: log.qh:66
const int SF_LASER_UPDATE_EFFECT
Definition: laser.qh:23
float alpha
Definition: items.qc:14
const int LASER_NOTRACE
Definition: laser.qh:17
#define REGISTER_NET_LINKED(id)
Definition: net.qh:67
int iflags
Definition: interpolate.qh:26
const float LASER_BEAM_MAXWORLDSIZE
Definition: laser.qh:34
void InterpolateOrigin_Undo(entity this)
snap origin to iorigin2 (actual origin)
Definition: interpolate.qc:159
#define adddynamiclight
Definition: post.qh:29
vector colormod
Definition: powerups.qc:21
float trace_dphitq3surfaceflags
const int LASER_FINITE
Definition: laser.qh:16
const int ACTIVE_TOGGLE
Definition: defs.qh:40
const int SF_LASER_UPDATE_TARGET
Definition: laser.qh:21
int team
Definition: main.qh:157
vector oldorigin
Definition: csprogsdefs.qc:102
entity() spawn
vector beam_color
Definition: laser.qh:30
const float MOVE_NORMAL
Definition: csprogsdefs.qc:252
const int SF_LASER_UPDATE_ORIGIN
Definition: laser.qh:20
vector view_origin
Definition: main.qh:93
const int SF_LASER_SCALE
Definition: laser.qh:26
void SUB_UseTargets(entity this, entity actor, entity trigger)
Definition: triggers.qc:366
#define NET_HANDLE(id, param)
Definition: net.qh:12
float dmg
Definition: platforms.qh:6
entity to
Definition: self.qh:96
spawnfunc(info_player_attacker)
Definition: sv_assault.qc:283
origin
Definition: ent_cs.qc:114
const int SF_LASER_ALPHA
Definition: laser.qh:27
float spawnflags
Definition: progsdefs.qc:191
#define DMG_NOWEP
Definition: damage.qh:126
entity pusher
Definition: teleporters.qh:13
const int SF_LASER_UPDATE_ACTIVE
Definition: laser.qh:22
const int SF_LASER_NOTRACE
Definition: laser.qh:25
entity trace_ent
Definition: csprogsdefs.qc:40
const int IFLAG_VELOCITY
Definition: interpolate.qh:27
const float DRAWFLAG_ADDITIVE
Definition: csprogsdefs.qc:318
entity enemy
Definition: sv_ctf.qh:143
float Q3SURFACEFLAG_SKY
float cnt
Definition: powerups.qc:24
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
const int ACTIVE_ACTIVE
Definition: defs.qh:37
string message
Definition: powerups.qc:19
#define NULL
Definition: post.qh:17
float frametime
Definition: csprogsdefs.qc:17
const float DRAWFLAG_NORMAL
Definition: csprogsdefs.qc:317
vector trace_endpos
Definition: csprogsdefs.qc:37
const int SF_LASER_FINITE
Definition: laser.qh:28
void Damage(entity targ, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition: damage.qc:583
float Q3SURFACEFLAG_NOIMPACT
float nextthink
Definition: csprogsdefs.qc:121
float scale
Definition: projectile.qc:14
vector(float skel, float bonenum) _skel_get_boneabs_hidden
const int IFLAG_ORIGIN
Definition: interpolate.qh:36
void InitializeEntity(entity e, void(entity this) func, int order)
Definition: world.qc:2146
float drawframetime
Definition: main.qh:92
float count
Definition: powerups.qc:22
string targetname
Definition: progsdefs.qc:194
int active
Definition: defs.qh:34
vector mangle
Definition: subs.qh:51
entity int sendflags
Definition: self.qh:96
setorigin(ent, v)
#define setthink(e, f)
vector trace_plane_normal
Definition: csprogsdefs.qc:38
vector angles
Definition: csprogsdefs.qc:104
#define use
Definition: csprogsdefs.qh:50
string target
Definition: progsdefs.qc:193
const int ACTIVE_NOT
Definition: defs.qh:36
const int LASER_INVERT_TEAM
Definition: laser.qh:18
float time
Definition: csprogsdefs.qc:16
vector velocity
Definition: csprogsdefs.qc:103
float modelscale
Definition: models.qh:3
#define makevectors
Definition: post.qh:21
void InterpolateOrigin_Do(entity this)
set origin based on iorigin1 (old pos), iorigin2 (desired pos), and time
Definition: interpolate.qc:129
IntrusiveList g_drawables
Definition: main.qh:77
const int IFLAG_ANGLES
Definition: interpolate.qh:28
vector v_forward
Definition: csprogsdefs.qc:31
#define particleeffectnum(e)
Definition: effect.qh:3