3 #include <lib/csqcmodel/interpolate.qh>
4 #include <lib/csqcmodel/cl_model.qh>
9 REGISTER_NET_LINKED(ENT_CLIENT_LASER)
12 void misc_laser_aim(entity this)
17 if(this.spawnflags & LASER_FINITE)
19 if(this.enemy.origin != this.mangle)
21 this.mangle = this.enemy.origin;
22 this.SendFlags |= SF_LASER_UPDATE_TARGET;
27 a = vectoangles(this.enemy.origin - this.origin);
32 this.SendFlags |= SF_LASER_UPDATE_TARGET;
38 if(this.angles != this.mangle)
40 this.mangle = this.angles;
41 this.SendFlags |= SF_LASER_UPDATE_TARGET;
44 if(this.origin != this.oldorigin)
46 this.SendFlags |= SF_LASER_UPDATE_ORIGIN;
47 this.oldorigin = this.origin;
51 void misc_laser_init(entity this)
54 this.enemy = find(NULL, targetname, this.target);
58 void misc_laser_think(entity this)
64 this.nextthink = time;
66 if(this.active == ACTIVE_NOT)
73 o = this.enemy.origin;
74 if (!(this.spawnflags & LASER_FINITE))
75 o = this.origin + normalize(o - this.origin) * LASER_BEAM_MAXLENGTH;
79 makevectors(this.mangle);
80 o = this.origin + v_forward * LASER_BEAM_MAXLENGTH;
83 if(this.dmg || this.enemy.target != "")
85 traceline(this.origin, o, MOVE_NORMAL, this);
88 hitloc = trace_endpos;
90 if(this.enemy.target != "") // DETECTOR laser
92 if(trace_ent.iscreature)
99 SUB_UseTargets(this.enemy, this.enemy.pusher, NULL);
108 SUB_UseTargets(this.enemy, this.enemy.pusher, NULL);
116 if(((this.spawnflags & LASER_INVERT_TEAM) == 0) == (this.team != hitent.team))
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');
123 bool laser_SendEntity(entity this, entity to, float sendflags)
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;
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)
138 WriteVector(MSG_ENTITY, this.origin);
140 if(sendflags & SF_LASER_UPDATE_EFFECT)
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)
149 WriteByte(MSG_ENTITY, bound(0, this.scale * 16.0, 255));
150 WriteByte(MSG_ENTITY, bound(0, this.modelscale * 16.0, 255));
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);
155 if(sendflags & SF_LASER_UPDATE_TARGET)
157 if(sendflags & SF_LASER_FINITE)
159 WriteVector(MSG_ENTITY, this.enemy.origin);
163 WriteAngleVector2D(MSG_ENTITY, this.mangle);
166 if(sendflags & SF_LASER_UPDATE_ACTIVE)
167 WriteByte(MSG_ENTITY, this.active);
171 /*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
172 Any object touching the beam will be hurt
175 spawnfunc_target_position where the laser ends
177 name of beam end effect to use
179 color of the beam (default: red)
181 damage per second (-1 for a laser that kills immediately)
184 void laser_setactive(entity this, int act)
186 int old_status = this.active;
187 if(act == ACTIVE_TOGGLE)
189 if(this.active == ACTIVE_ACTIVE)
191 this.active = ACTIVE_NOT;
195 this.active = ACTIVE_ACTIVE;
203 if (this.active != old_status)
205 this.SendFlags |= SF_LASER_UPDATE_ACTIVE;
206 misc_laser_aim(this);
210 void laser_use(entity this, entity actor, entity trigger)
212 this.setactive(this, ACTIVE_TOGGLE);
215 spawnfunc(misc_laser)
219 if(this.mdl == "none")
223 this.cnt = _particleeffectnum(this.mdl);
224 if(this.cnt < 0 && this.dmg)
225 this.cnt = particleeffectnum(EFFECT_LASER_DEADLY);
231 this.cnt = particleeffectnum(EFFECT_LASER_DEADLY);
238 if(!this.beam_color && this.colormod)
240 LOG_WARN("misc_laser uses legacy field 'colormod', please use 'beam_color' instead");
241 this.beam_color = this.colormod;
244 if(this.beam_color == '0 0 0')
247 this.beam_color = '1 0 0';
250 if(this.message == "")
252 this.message = "saw the light";
254 if (this.message2 == "")
256 this.message2 = "was pushed into a laser by";
266 else if(this.modelscale < 0)
270 setthink(this, misc_laser_think);
271 this.nextthink = time;
272 InitializeEntity(this, misc_laser_init, INITPRIO_FINDTARGET);
274 this.mangle = this.angles;
276 Net_LinkEntity(this, false, 0, laser_SendEntity);
278 this.setactive = laser_setactive;
280 if(this.targetname && this.targetname != "")
282 // backwards compatibility
283 this.use = laser_use;
286 this.reset = generic_netlinked_reset;
291 void Draw_Laser(entity this)
293 if(this.active == ACTIVE_NOT)
295 InterpolateOrigin_Do(this);
296 if(this.count & SF_LASER_FINITE)
298 if(this.count & SF_LASER_NOTRACE)
300 trace_endpos = this.velocity;
301 trace_dphitq3surfaceflags = 0;
304 traceline(this.origin, this.velocity, 0, this);
308 if(this.count & SF_LASER_NOTRACE)
310 makevectors(this.angles);
311 trace_endpos = this.origin + v_forward * LASER_BEAM_MAXWORLDSIZE;
312 trace_dphitq3surfaceflags = Q3SURFACEFLAG_SKY;
316 makevectors(this.angles);
317 traceline(this.origin, this.origin + v_forward * LASER_BEAM_MAXLENGTH, 0, this);
318 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
319 trace_endpos = this.origin + v_forward * LASER_BEAM_MAXWORLDSIZE;
326 Draw_CylindricLine(this.origin, trace_endpos, this.scale, "particles/laserbeam", 0, time * 3, this.beam_color, this.alpha, DRAWFLAG_NORMAL, view_origin);
330 Draw_CylindricLine(this.origin, trace_endpos, this.scale, "particles/laserbeam", 0, time * 3, this.beam_color, 0.5, DRAWFLAG_ADDITIVE, view_origin);
333 if (!(trace_dphitq3surfaceflags & (Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT)))
336 __pointparticles(this.cnt, trace_endpos, trace_plane_normal, drawframetime * 1000);
337 if(this.beam_color != '0 0 0' && this.modelscale != 0)
338 adddynamiclight(trace_endpos + trace_plane_normal * 1, this.modelscale, this.beam_color * 5);
342 NET_HANDLE(ENT_CLIENT_LASER, bool isnew)
344 InterpolateOrigin_Undo(this);
346 // 30 bytes, or 13 bytes for just moving
347 int sendflags = ReadByte();
348 this.count = (sendflags & 0xF0);
350 if(this.count & SF_LASER_FINITE)
351 this.iflags = IFLAG_VELOCITY | IFLAG_ORIGIN;
353 this.iflags = IFLAG_ANGLES | IFLAG_ORIGIN;
355 if(sendflags & SF_LASER_UPDATE_ORIGIN)
357 this.origin = ReadVector();
358 setorigin(this, this.origin);
360 if(sendflags & SF_LASER_UPDATE_EFFECT)
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;
369 this.scale = 2; // NOTE: why 2?
370 this.modelscale = 50; // NOTE: why 50?
371 if(sendflags & SF_LASER_SCALE)
373 this.scale *= ReadByte() / 16.0; // beam radius
374 this.modelscale *= ReadByte() / 16.0; // dlight radius
376 if((sendflags & SF_LASER_FINITE) || !(sendflags & SF_LASER_NOTRACE))
377 this.cnt = ReadShort(); // effect number
381 if(sendflags & SF_LASER_UPDATE_TARGET)
383 if(sendflags & SF_LASER_FINITE)
385 this.velocity = ReadVector();
389 this.angles = ReadAngleVector2D();
392 if(sendflags & SF_LASER_UPDATE_ACTIVE)
393 this.active = ReadByte();
397 InterpolateOrigin_Note(this);
398 this.draw = Draw_Laser;
399 if (isnew) IL_PUSH(g_drawables, this);