3 #include <lib/csqcmodel/interpolate.qh>
4 #include <client/main.qh>
5 #include <lib/csqcmodel/cl_model.qh>
10 REGISTER_NET_LINKED(ENT_CLIENT_LASER)
13 void misc_laser_aim(entity this)
18 if(this.spawnflags & LASER_FINITE)
20 if(this.enemy.origin != this.mangle)
22 this.mangle = this.enemy.origin;
23 this.SendFlags |= SF_LASER_UPDATE_TARGET;
28 a = vectoangles(this.enemy.origin - this.origin);
33 this.SendFlags |= SF_LASER_UPDATE_TARGET;
39 if(this.angles != this.mangle)
41 this.mangle = this.angles;
42 this.SendFlags |= SF_LASER_UPDATE_TARGET;
45 if(this.origin != this.oldorigin)
47 this.SendFlags |= SF_LASER_UPDATE_ORIGIN;
48 this.oldorigin = this.origin;
52 void misc_laser_init(entity this)
55 this.enemy = find(NULL, targetname, this.target);
59 void misc_laser_think(entity this)
65 this.nextthink = time;
67 if(this.active == ACTIVE_NOT)
74 o = this.enemy.origin;
75 if (!(this.spawnflags & LASER_FINITE))
76 o = this.origin + normalize(o - this.origin) * LASER_BEAM_MAXLENGTH;
80 makevectors(this.mangle);
81 o = this.origin + v_forward * LASER_BEAM_MAXLENGTH;
84 if(this.dmg || this.enemy.target != "")
86 traceline(this.origin, o, MOVE_NORMAL, this);
89 hitloc = trace_endpos;
91 if(this.enemy.target != "") // DETECTOR laser
93 if(trace_ent.iscreature)
100 SUB_UseTargets(this.enemy, this.enemy.pusher, NULL);
109 SUB_UseTargets(this.enemy, this.enemy.pusher, NULL);
117 if(((this.spawnflags & LASER_INVERT_TEAM) == 0) == (this.team != hitent.team))
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');
124 bool laser_SendEntity(entity this, entity to, float sendflags)
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;
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)
139 WriteVector(MSG_ENTITY, this.origin);
141 if(sendflags & SF_LASER_UPDATE_EFFECT)
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)
150 WriteByte(MSG_ENTITY, bound(0, this.scale * 16.0, 255));
151 WriteByte(MSG_ENTITY, bound(0, this.modelscale * 16.0, 255));
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);
156 if(sendflags & SF_LASER_UPDATE_TARGET)
158 if(sendflags & SF_LASER_FINITE)
160 WriteVector(MSG_ENTITY, this.enemy.origin);
164 WriteAngleVector2D(MSG_ENTITY, this.mangle);
167 if(sendflags & SF_LASER_UPDATE_ACTIVE)
168 WriteByte(MSG_ENTITY, this.active);
172 /*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
173 Any object touching the beam will be hurt
176 spawnfunc_target_position where the laser ends
178 name of beam end effect to use
180 color of the beam (default: red)
182 damage per second (-1 for a laser that kills immediately)
185 void laser_setactive(entity this, int act)
187 int old_status = this.active;
188 if(act == ACTIVE_TOGGLE)
190 if(this.active == ACTIVE_ACTIVE)
192 this.active = ACTIVE_NOT;
196 this.active = ACTIVE_ACTIVE;
204 if (this.active != old_status)
206 this.SendFlags |= SF_LASER_UPDATE_ACTIVE;
207 misc_laser_aim(this);
211 void laser_use(entity this, entity actor, entity trigger)
213 this.setactive(this, ACTIVE_TOGGLE);
216 spawnfunc(misc_laser)
220 if(this.mdl == "none")
224 this.cnt = _particleeffectnum(this.mdl);
225 if(this.cnt < 0 && this.dmg)
226 this.cnt = particleeffectnum(EFFECT_LASER_DEADLY);
232 this.cnt = particleeffectnum(EFFECT_LASER_DEADLY);
239 if(!this.beam_color && this.colormod)
241 LOG_WARN("misc_laser uses legacy field 'colormod', please use 'beam_color' instead");
242 this.beam_color = this.colormod;
245 if(this.beam_color == '0 0 0')
248 this.beam_color = '1 0 0';
251 if(this.message == "")
253 this.message = "saw the light";
255 if (this.message2 == "")
257 this.message2 = "was pushed into a laser by";
267 else if(this.modelscale < 0)
271 setthink(this, misc_laser_think);
272 this.nextthink = time;
273 InitializeEntity(this, misc_laser_init, INITPRIO_FINDTARGET);
275 this.mangle = this.angles;
277 Net_LinkEntity(this, false, 0, laser_SendEntity);
279 this.setactive = laser_setactive;
281 if(this.targetname && this.targetname != "")
283 // backwards compatibility
284 this.use = laser_use;
287 this.reset = generic_netlinked_reset;
292 void Draw_Laser(entity this)
294 if(this.active == ACTIVE_NOT)
296 InterpolateOrigin_Do(this);
297 if(this.count & SF_LASER_FINITE)
299 if(this.count & SF_LASER_NOTRACE)
301 trace_endpos = this.velocity;
302 trace_dphitq3surfaceflags = 0;
305 traceline(this.origin, this.velocity, 0, this);
309 if(this.count & SF_LASER_NOTRACE)
311 makevectors(this.angles);
312 trace_endpos = this.origin + v_forward * LASER_BEAM_MAXWORLDSIZE;
313 trace_dphitq3surfaceflags = Q3SURFACEFLAG_SKY;
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;
327 Draw_CylindricLine(this.origin, trace_endpos, this.scale, "particles/laserbeam", 0, time * 3, this.beam_color, this.alpha, DRAWFLAG_NORMAL, view_origin);
331 Draw_CylindricLine(this.origin, trace_endpos, this.scale, "particles/laserbeam", 0, time * 3, this.beam_color, 0.5, DRAWFLAG_ADDITIVE, view_origin);
334 if (!(trace_dphitq3surfaceflags & (Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT)))
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);
343 NET_HANDLE(ENT_CLIENT_LASER, bool isnew)
345 InterpolateOrigin_Undo(this);
347 // 30 bytes, or 13 bytes for just moving
348 int sendflags = ReadByte();
349 this.count = (sendflags & 0xF0);
351 if(this.count & SF_LASER_FINITE)
352 this.iflags = IFLAG_VELOCITY | IFLAG_ORIGIN;
354 this.iflags = IFLAG_ANGLES | IFLAG_ORIGIN;
356 if(sendflags & SF_LASER_UPDATE_ORIGIN)
358 this.origin = ReadVector();
359 setorigin(this, this.origin);
361 if(sendflags & SF_LASER_UPDATE_EFFECT)
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;
370 this.scale = 2; // NOTE: why 2?
371 this.modelscale = 50; // NOTE: why 50?
372 if(sendflags & SF_LASER_SCALE)
374 this.scale *= ReadByte() / 16.0; // beam radius
375 this.modelscale *= ReadByte() / 16.0; // dlight radius
377 if((sendflags & SF_LASER_FINITE) || !(sendflags & SF_LASER_NOTRACE))
378 this.cnt = ReadShort(); // effect number
382 if(sendflags & SF_LASER_UPDATE_TARGET)
384 if(sendflags & SF_LASER_FINITE)
386 this.velocity = ReadVector();
390 this.angles = ReadAngleVector2D();
393 if(sendflags & SF_LASER_UPDATE_ACTIVE)
394 this.active = ReadByte();
398 InterpolateOrigin_Note(this);
399 this.draw = Draw_Laser;
400 if (isnew) IL_PUSH(g_drawables, this);