Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
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
9REGISTER_NET_LINKED(ENT_CLIENT_LASER)
10
11#ifdef SVQC
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;
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;
33 }
34 }
35 }
36 else
37 {
38 if(this.angles != this.mangle)
39 {
40 this.mangle = this.angles;
42 }
43 }
44 if(this.origin != this.oldorigin)
45 {
47 this.oldorigin = this.origin;
48 }
49}
50
52{
53 if(this.target != "")
54 this.enemy = find(NULL, targetname, this.target);
55}
56
57.entity pusher;
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
123bool 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
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)
168 return true;
169}
170
171/*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
172Any object touching the beam will be hurt
173Keys:
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
184void 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 {
206 misc_laser_aim(this);
207 }
208}
209
210void laser_use(entity this, entity actor, entity trigger)
211{
212 this.setactive(this, ACTIVE_TOGGLE);
213}
214
215spawnfunc(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 }
271 this.nextthink = time;
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
291void Draw_Laser(entity this)
292{
293 if(this.active == ACTIVE_NOT)
294 return;
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
342NET_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
float dmg
Definition breakable.qc:12
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
float cnt
Definition powerups.qc:24
string message
Definition powerups.qc:19
vector colormod
Definition powerups.qc:21
float count
Definition powerups.qc:22
float alpha
Definition items.qc:13
IntrusiveList g_drawables
Definition main.qh:91
vector view_origin
Definition main.qh:109
float drawframetime
Definition main.qh:108
int team
Definition main.qh:188
int spawnflags
Definition ammo.qh:15
string mdl
Definition item.qh:89
void Draw_CylindricLine(vector from, vector to, float thickness, string texture, float aspect, float shift, vector rgb, float theAlpha, float drawflag, vector vieworg)
Definition draw.qh:11
const int INITPRIO_FINDTARGET
Definition constants.qh:96
float Q3SURFACEFLAG_SKY
const float DRAWFLAG_NORMAL
entity trace_ent
const float DRAWFLAG_ADDITIVE
float frametime
const float MOVE_NORMAL
vector velocity
float time
vector trace_endpos
float nextthink
float trace_dphitq3surfaceflags
vector v_forward
vector origin
vector oldorigin
float Q3SURFACEFLAG_NOIMPACT
vector trace_plane_normal
#define use
void Damage(entity targ, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition damage.qc:503
#define DMG_NOWEP
Definition damage.qh:104
const int ACTIVE_TOGGLE
Definition defs.qh:40
const int ACTIVE_NOT
Definition defs.qh:36
int active
Definition defs.qh:34
const int ACTIVE_ACTIVE
Definition defs.qh:37
#define particleeffectnum(e)
Definition effect.qh:3
ent angles
Definition ent_cs.qc:121
void InterpolateOrigin_Undo(entity this)
snap origin to iorigin2 (actual origin)
void InterpolateOrigin_Note(entity this)
void InterpolateOrigin_Do(entity this)
set origin based on iorigin1 (old pos), iorigin2 (desired pos), and time
const int IFLAG_VELOCITY
const int IFLAG_ANGLES
const int IFLAG_ORIGIN
int iflags
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
void SUB_UseTargets(entity this, entity actor, entity trigger)
Definition triggers.qc:344
void misc_laser_aim(entity this)
Definition laser.qc:12
void misc_laser_think(entity this)
Definition laser.qc:58
void laser_setactive(entity this, int act)
Definition laser.qc:184
entity pusher
Definition laser.qc:57
bool laser_SendEntity(entity this, entity to, float sendflags)
Definition laser.qc:123
void laser_use(entity this, entity actor, entity trigger)
Definition laser.qc:210
void misc_laser_init(entity this)
Definition laser.qc:51
const int LASER_NOTRACE
Definition laser.qh:17
const int SF_LASER_NOTRACE
Definition laser.qh:25
const int SF_LASER_FINITE
Definition laser.qh:28
const float LASER_BEAM_MAXLENGTH
Definition laser.qh:32
vector beam_color
Definition laser.qh:30
const float LASER_BEAM_MAXWORLDSIZE
Definition laser.qh:34
const int SF_LASER_UPDATE_EFFECT
Definition laser.qh:23
const int SF_LASER_UPDATE_ACTIVE
Definition laser.qh:22
const int SF_LASER_UPDATE_TARGET
Definition laser.qh:21
const int LASER_FINITE
Definition laser.qh:16
const int SF_LASER_ALPHA
Definition laser.qh:27
const int SF_LASER_UPDATE_ORIGIN
Definition laser.qh:20
const int LASER_INVERT_TEAM
Definition laser.qh:18
const int SF_LASER_SCALE
Definition laser.qh:26
int SendFlags
Definition net.qh:118
#define NET_HANDLE(id, param)
Definition net.qh:15
const int MSG_ENTITY
Definition net.qh:115
#define ReadVector()
Definition net.qh:367
#define WriteHeader(to, id)
Definition net.qh:221
#define REGISTER_NET_LINKED(id)
Definition net.qh:55
#define ReadAngleVector2D()
Definition net.qh:370
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:123
int ReadByte()
#define LOG_WARN(...)
Definition log.qh:61
float bound(float min, float value, float max)
entity find(entity start,.string field, string match)
void WriteShort(float data, float dest, float desto)
vector vectoangles(vector v)
vector normalize(vector v)
void WriteByte(float data, float dest, float desto)
float modelscale
Definition models.qh:3
#define NULL
Definition post.qh:14
#define makevectors
Definition post.qh:21
#define adddynamiclight
Definition post.qh:29
float scale
Definition projectile.qc:14
#define setthink(e, f)
vector
Definition self.qh:92
#define spawnfunc(id)
Definition spawnfunc.qh:96
vector mangle
Definition subs.qh:51
entity enemy
Definition sv_ctf.qh:153
void generic_netlinked_reset(entity this)
Definition triggers.qc:76
string message2
Definition triggers.qh:19
string targetname
Definition triggers.qh:56
string target
Definition triggers.qh:55
void InitializeEntity(entity e, void(entity this) func, int order)
Definition world.qc:2209