+++ /dev/null
-#if defined(CSQC)
- #include "../dpdefs/csprogsdefs.qh"
- #include "../common/buffs.qh"
- #include "../csqcmodellib/interpolate.qh"
- #include "main.qh"
- #include "../csqcmodellib/cl_model.qh"
-#elif defined(MENUQC)
-#elif defined(SVQC)
-#endif
-
-
-// a laser goes from origin in direction angles
-// it has color 'colormod'
-// and stops when something is in the way
-.int cnt; // end effect
-.vector colormod;
-.int state; // on-off
-.int count; // flags for the laser
-.vector velocity;
-.float alpha;
-.float scale; // scaling factor of the thickness
-.float modelscale; // scaling factor of the dlight
-
-void Draw_Laser()
-{
- if(!self.state)
- return;
- InterpolateOrigin_Do();
- if(self.count & 0x80)
- {
- if(self.count & 0x10)
- {
- trace_endpos = self.velocity;
- trace_dphitq3surfaceflags = 0;
- }
- else
- traceline(self.origin, self.velocity, 0, self);
- }
- else
- {
- if(self.count & 0x10)
- {
- makevectors(self.angles);
- trace_endpos = self.origin + v_forward * 1048576;
- trace_dphitq3surfaceflags = Q3SURFACEFLAG_SKY;
- }
- else
- {
- makevectors(self.angles);
- traceline(self.origin, self.origin + v_forward * 32768, 0, self);
- if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
- trace_endpos = self.origin + v_forward * 1048576;
- }
- }
- if(self.scale != 0)
- {
- if(self.alpha)
- {
- Draw_CylindricLine(self.origin, trace_endpos, self.scale, "particles/laserbeam", 0, time * 3, self.colormod, self.alpha, DRAWFLAG_NORMAL, view_origin);
- }
- else
- {
- Draw_CylindricLine(self.origin, trace_endpos, self.scale, "particles/laserbeam", 0, time * 3, self.colormod, 0.5, DRAWFLAG_ADDITIVE, view_origin);
- }
- }
- if (!(trace_dphitq3surfaceflags & (Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT)))
- {
- if(self.cnt >= 0)
- pointparticles(self.cnt, trace_endpos, trace_plane_normal, drawframetime * 1000);
- if(self.colormod != '0 0 0' && self.modelscale != 0)
- adddynamiclight(trace_endpos + trace_plane_normal * 1, self.modelscale, self.colormod * 5);
- }
-}
-
-void Ent_Laser()
-{
- InterpolateOrigin_Undo();
-
- // 30 bytes, or 13 bytes for just moving
- int f = ReadByte();
- self.count = (f & 0xF0);
-
- if(self.count & 0x80)
- self.iflags = IFLAG_VELOCITY | IFLAG_ORIGIN;
- else
- self.iflags = IFLAG_ANGLES | IFLAG_ORIGIN;
-
- if(f & 1)
- {
- self.origin_x = ReadCoord();
- self.origin_y = ReadCoord();
- self.origin_z = ReadCoord();
- setorigin(self, self.origin);
- }
- if(f & 8)
- {
- self.colormod_x = ReadByte() / 255.0;
- self.colormod_y = ReadByte() / 255.0;
- self.colormod_z = ReadByte() / 255.0;
- if(f & 0x40)
- self.alpha = ReadByte() / 255.0;
- else
- self.alpha = 0;
- self.scale = 2;
- self.modelscale = 50;
- if(f & 0x20)
- {
- self.scale *= ReadByte() / 16.0; // beam radius
- self.modelscale *= ReadByte() / 16.0; // dlight radius
- }
- if((f & 0x80) || !(f & 0x10))
- self.cnt = ReadShort() - 1; // effect number
- else
- self.cnt = 0;
- }
- if(f & 2)
- {
- if(f & 0x80)
- {
- self.velocity_x = ReadCoord();
- self.velocity_y = ReadCoord();
- self.velocity_z = ReadCoord();
- }
- else
- {
- self.angles_x = ReadAngle();
- self.angles_y = ReadAngle();
- }
- }
- if(f & 4)
- self.state = ReadByte();
- InterpolateOrigin_Note();
- self.draw = Draw_Laser;
-}
#include "particles.qh"
#include "scoreboard.qh"
#include "shownames.qh"
-#include "target_music.qh"
#include "tturrets.qh"
#include "tuba.qh"
#include "wall.qh"
case ENT_CLIENT_PLAT: ent_plat(); break;
case ENT_CLIENT_PLAT_TRIGGER: ent_plat_trigger(); break;
case ENT_CLIENT_SWAMP: ent_swamp(); break;
+ case ENT_CLIENT_CORNER: ent_corner(); break;
+ case ENT_CLIENT_KEYLOCK: ent_keylock(); break;
default:
//error(strcat(_("unknown entity type in CSQC_Ent_Update: %d\n"), self.enttype));
float _Movetype_CheckWater(entity ent) // SV_CheckWater
{
vector point = ent.move_origin;
- point.z += (ent.mins.z + 1);
+ point_z += (ent.mins_z + 1);
int nativecontents = pointcontents(point);
{
ent.move_watertype = nativecontents;
ent.move_waterlevel = 1;
- point.y = (ent.origin.y + ((ent.mins.z + ent.maxs.y) * 0.5));
+ point_y = (ent.origin_y + ((ent.mins_z + ent.maxs_y) * 0.5));
if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
{
ent.move_waterlevel = 2;
- point.y = ent.origin.y + ent.view_ofs.y;
+ point_y = ent.origin_y + ent.view_ofs_y;
if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK)
ent.move_waterlevel = 3;
}
if(self.move_flags & FL_ITEM)
{
- mi.x -= 15;
- mi.y -= 15;
- mi.z -= 1;
- ma.x += 15;
- ma.y += 15;
- ma.z += 1;
+ mi_x -= 15;
+ mi_y -= 15;
+ mi_z -= 1;
+ ma_x += 15;
+ ma_y += 15;
+ ma_z += 1;
}
else
{
- mi.x -= 1;
- mi.y -= 1;
- mi.z -= 1;
- ma.x += 1;
- ma.y += 1;
- ma.z += 1;
+ mi_x -= 1;
+ mi_y -= 1;
+ mi_z -= 1;
+ ma_x += 1;
+ ma_y += 1;
+ ma_z += 1;
}
self.absmin = mi;
{
vel = vel - ((vel * norm) * norm) * f;
- if(vel.x > -0.1 && vel.x < 0.1) vel.x = 0;
- if(vel.y > -0.1 && vel.y < 0.1) vel.y = 0;
- if(vel.z > -0.1 && vel.z < 0.1) vel.z = 0;
+ if(vel_x > -0.1 && vel_x < 0.1) vel_x = 0;
+ if(vel_y > -0.1 && vel_y < 0.1) vel_y = 0;
+ if(vel_z > -0.1 && vel_z < 0.1) vel_z = 0;
return vel;
}
return trace_fraction;
}
-const float MAX_CLIP_PLANES = 5;
+
+.float ltime;
+.void() blocked;
+void _Movetype_VectorAngles_FLU(vector myangles)
+{
+ float angle, sr, sp, sy, cr, cp, cy;
+
+ angle = myangles_y * (M_PI*2 / 360);
+ sy = sin(angle);
+ cy = cos(angle);
+ angle = myangles_x * (M_PI*2 / 360);
+ sp = sin(angle);
+ cp = cos(angle);
+ if(v_forward)
+ {
+ v_forward_x = cp*cy;
+ v_forward_y = cp*sy;
+ v_forward_z = -sp;
+ }
+ if(v_right || v_up)
+ {
+ if(myangles_z)
+ {
+ angle = myangles_z * (M_PI*2 / 360);
+ sr = sin(angle);
+ cr = cos(angle);
+ if(v_right)
+ {
+ v_right_x = sr*sp*cy+cr*-sy;
+ v_right_y = sr*sp*sy+cr*cy;
+ v_right_z = sr*cp;
+ }
+ if(v_up)
+ {
+ v_up_x = cr*sp*cy+-sr*-sy;
+ v_up_y = cr*sp*sy+-sr*cy;
+ v_up_z = cr*cp;
+ }
+ }
+ else
+ {
+ if(v_right)
+ {
+ v_right_x = -sy;
+ v_right_y = cy;
+ v_right_z = 0;
+ }
+ if(v_up)
+ {
+ v_up_x = sp*cy;
+ v_up_y = sp*sy;
+ v_up_z = cp;
+ }
+ }
+ }
+}
+
+void _Movetype_PushMove(float dt) // SV_PushMove
+{
+ float pushltime;
+ //int pusherowner;
+ //int i;
+ //int num_moved;
+ //int numcheckentities;
+ int savesolid;
+ bool rotated;
+ vector move1, moveangle;
+ vector a;
+ vector pushorig, pushang;
+ vector org;
+ vector move;
+ entity oldself;
+
+ if(!vlen(self.move_velocity) && !vlen(self.move_avelocity))
+ {
+ self.ltime += dt;
+ return;
+ }
+
+ switch(self.solid)
+ {
+ // LordHavoc: valid pusher types
+ case SOLID_BSP:
+ case SOLID_BBOX:
+ case SOLID_SLIDEBOX:
+ case SOLID_CORPSE: // LordHavoc: this would be weird...
+ break;
+ // LordHavoc: no collisions
+ case SOLID_NOT:
+ case SOLID_TRIGGER:
+
+ self.move_origin = self.move_origin + self.move_velocity * dt;
+ self.move_angles_x -= 360.0 * floor(self.move_angles_x * (1.0 / 360.0));
+ self.move_angles_y -= 360.0 * floor(self.move_angles_y * (1.0 / 360.0));
+ self.move_angles_z -= 360.0 * floor(self.move_angles_z * (1.0 / 360.0));
+ self.ltime += dt;
+ _Movetype_LinkEdict(true);
+ return;
+ default:
+ printf("_Movetype_PushMove: entity %e with classname %s, unrecognized solid type %d\n", num_for_edict(self), self.classname, self.solid);
+ return;
+ }
+ if(!self.modelindex || self.model == "null")
+ {
+ printf("_Movetype_PushMove: entity %e with classname %s, unusable modelindex %f\n", num_for_edict(self), self.classname, self.modelindex);
+ return;
+ }
+ //pusherowner = self.owner;
+
+ rotated = (self.move_angles * self.move_avelocity) > 0;
+
+ move1 = self.move_velocity * dt;
+ moveangle = self.move_avelocity * dt;
+
+ a = -moveangle;
+
+ // sets v_forward, v_right and v_up
+ _Movetype_VectorAngles_FLU(a);
+
+ pushorig = self.move_origin;
+ pushang = self.move_angles;
+ pushltime = self.ltime;
+
+// move the pusher to its final position
+
+ self.move_origin = self.move_origin + self.move_velocity * dt;
+ self.move_angles = self.move_angles + self.move_avelocity * dt;
+ self.ltime += dt;
+ _Movetype_LinkEdict(true);
+
+ //pushermodel = SV_GetModelFromEdict(pusher);
+ //Matrix4x4_CreateFromQuakeEntity(&pusherfinalmatrix, PRVM_serveredictvector(pusher, origin)[0], PRVM_serveredictvector(pusher, origin)[1], PRVM_serveredictvector(pusher, origin)[2],
+ //PRVM_serveredictvector(pusher, angles)[0], PRVM_serveredictvector(pusher, angles)[1], PRVM_serveredictvector(pusher, angles)[2], 1);
+ //Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
+
+ savesolid = self.solid;
+
+// see if any solid entities are inside the final position
+ //num_moved = 0;
+
+ entity e;
+ if(self.move_movetype != MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
+ for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
+ {
+ if(e.owner == self)
+ continue;
+
+ if(self.owner == e)
+ continue;
+
+ //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(PRVM_serveredictstring(check, classname)));
+
+ // tell any MOVETYPE_STEP entity that it may need to check for water transitions
+ //check->priv.server->waterposition_forceupdate = true;
+
+ //int checkcontents = pointcontents(e.move_origin);
+
+ // if the entity is standing on the pusher, it will definitely be moved
+ // if the entity is not standing on the pusher, but is in the pusher's
+ // final position, move it
+ if(!(e.move_flags & FL_ONGROUND) || e.move_groundentity != self)
+ {
+ //vector pushermins = self.mins;
+ //vector pushermaxs = self.maxs;
+ //vector checkorigin = e.origin;
+ //vector checkmins = e.mins;
+ //vector checkmaxs = e.maxs;
+ //Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pushermins, pushermaxs, SUPERCONTENTS_BODY, &pusherfinalmatrix,
+ //&pusherfinalimatrix, checkorigin, checkmins, checkmaxs, checkorigin, checkcontents, collision_extendmovelength.value);
+ tracebox(e.move_origin, e.mins, e.maxs, self.move_origin, MOVE_NOMONSTERS, e);
+ if(!trace_startsolid)
+ {
+ //Con_Printf("- not in solid\n");
+ continue;
+ }
+ }
+ vector pivot = e.mins + 0.5 * (e.maxs - e.mins);
+ //VectorClear(pivot);
+
+ if(rotated)
+ {
+ vector org2;
+ org = e.move_origin - self.move_origin;
+ org += pivot;
+ org2_x = (org * v_forward);
+ org2_y = (org * v_right);
+ org2_z = (org * v_up);
+ move = org2 - org;
+ move += move1;
+ }
+ else
+ move = move1;
+
+ //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
+
+ //VectorCopy (PRVM_serveredictvector(check, origin), check->priv.server->moved_from);
+ //VectorCopy (PRVM_serveredictvector(check, angles), check->priv.server->moved_fromangles);
+ //moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
+
+ // physics objects need better collisions than this code can do
+ /*if(e.move_movetype == MOVETYPE_PHYSICS)
+ {
+ e.move_origin += move;
+ oldself = self;
+ self = e;
+ _Movetype_LinkEdict(false);
+ _Movetype_LinkEdict_TouchAreaGrid();
+ self = oldself;
+ continue;
+ }*/
+
+ // try moving the contacted entity
+ self.solid = SOLID_NOT;
+ oldself = self;
+ self = e;
+ if(!_Movetype_PushEntity(move, true))
+ {
+ // entity "check" got teleported
+ self.angles_y += trace_fraction * moveangle_y;
+ oldself.solid = savesolid;
+ continue; // pushed enough
+ }
+ self = oldself;
+ // FIXME: turn players specially
+ e.angles_y += trace_fraction * moveangle_y;
+ self.solid = savesolid;
+ //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
+
+ // this trace.fraction < 1 check causes items to fall off of pushers
+ // if they pass under or through a wall
+ // the groundentity check causes items to fall off of ledges
+ if(e.move_movetype != MOVETYPE_WALK && (trace_fraction < 1 || e.move_groundentity != self))
+ e.move_flags &= ~FL_ONGROUND;
+
+ // if it is still inside the pusher, block
+ //vector pushermins = self.mins;
+ //vector pushermaxs = self.maxs;
+ //vector checkorigin = e.move_origin;
+ //vector checkmins = e.mins;
+ //vector checkmaxs = e.maxs;
+ //Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pushermins, pushermaxs, SUPERCONTENTS_BODY,
+ //&pusherfinalmatrix, &pusherfinalimatrix, checkorigin, checkmins, checkmaxs, checkorigin, checkcontents, collision_extendmovelength.value);
+ tracebox(e.move_origin, e.mins, e.maxs, self.move_origin, MOVE_NOMONSTERS, e);
+ if(trace_startsolid)
+ {
+ /*vector move2;
+ if(SV_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
+ {
+ // hack to invoke all necessary movement triggers
+ move2 = '0 0 0';
+ if(!_Movetype_PushEntity(move2, true))
+ {
+ // entity "check" got teleported
+ continue;
+ }
+ // we could fix it
+ continue;
+ }*/
+
+ // still inside pusher, so it's really blocked
+
+ // fail the move
+ if(e.mins_x == e.maxs_x)
+ continue;
+ if(e.solid == SOLID_NOT || e.solid == SOLID_TRIGGER)
+ {
+ // corpse
+ e.mins_x = e.mins_y = 0;
+ e.maxs = e.mins;
+ continue;
+ }
+
+ self.move_origin = pushorig;
+ self.move_angles = pushang;
+ self.ltime = pushltime;
+ _Movetype_LinkEdict(true);
+
+ // move back any entities we already moved
+ /*for (i = 0;i < num_moved;i++)
+ {
+ prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
+ VectorCopy (ed->priv.server->moved_from, PRVM_serveredictvector(ed, origin));
+ VectorCopy (ed->priv.server->moved_fromangles, PRVM_serveredictvector(ed, angles));
+ SV_LinkEdict(ed);
+ }*/
+
+ // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
+ if(self.blocked)
+ {
+ self.move_time = time;
+ other = e;
+ self.blocked();
+ }
+ break;
+ }
+ }
+ self.move_angles_x -= 360.0 * floor(self.move_angles_x * (1.0 / 360.0));
+ self.move_angles_y -= 360.0 * floor(self.move_angles_y * (1.0 / 360.0));
+ self.move_angles_z -= 360.0 * floor(self.move_angles_z * (1.0 / 360.0));
+}
+
+void _Movetype_Physics_Pusher(float dt) // SV_Physics_Pusher
+{
+ float oldltime, movetime;
+
+ oldltime = self.ltime;
+
+ if (self.nextthink < self.ltime + dt)
+ {
+ movetime = self.nextthink - self.ltime;
+ if (movetime < 0)
+ movetime = 0;
+ }
+ else
+ movetime = dt;
+
+ if (movetime)
+ // advances PRVM_serveredictfloat(ent, ltime) if not blocked
+ _Movetype_PushMove(movetime);
+
+ if (self.nextthink > oldltime && self.nextthink <= self.ltime)
+ {
+ self.nextthink = 0;
+ //time = dt;
+ self.move_time = time;
+ other = world;
+ if(self.think)
+ self.think();
+ }
+}
+
void _Movetype_Physics_Toss(float dt) // SV_Physics_Toss
{
if(self.move_flags & FL_ONGROUND)
{
- if(self.move_velocity.z >= 1/32)
+ if(self.move_velocity_z >= 1/32)
self.move_flags &= ~FL_ONGROUND;
else if(!self.move_groundentity)
return;
self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 1 + bouncefac);
d = trace_plane_normal * self.move_velocity;
- if(trace_plane_normal.z > 0.7 && d < bouncestop && d > -bouncestop)
+ if(trace_plane_normal_z > 0.7 && d < bouncestop && d > -bouncestop)
{
self.move_flags |= FL_ONGROUND;
self.move_groundentity = trace_ent;
else
{
self.move_velocity = _Movetype_ClipVelocity(self.move_velocity, trace_plane_normal, 1.0);
- if(trace_plane_normal.z > 0.7)
+ if(trace_plane_normal_z > 0.7)
{
self.move_flags |= FL_ONGROUND;
self.move_groundentity = trace_ent;
{
case MOVETYPE_PUSH:
case MOVETYPE_FAKEPUSH:
- error("SV_Physics_Pusher not implemented");
+ _Movetype_Physics_Pusher(movedt);
break;
case MOVETYPE_NONE:
break;
void Movetype_Physics_MatchServer(bool sloppy);
void Movetype_Physics_NoMatchServer();
+float _Movetype_UnstickEntity();
+
+const int MAX_CLIP_PLANES = 5;
+
const int MOVETYPE_NONE = 0;
const int MOVETYPE_ANGLENOCLIP = 1;
const int MOVETYPE_ANGLECLIP = 2;
#include "particles.qh"
-void Draw_PointParticles()
-{
- float n, i, fail;
- vector p;
- vector sz;
- vector o;
- o = self.origin;
- sz = self.maxs - self.mins;
- n = BGMScript(self);
- if(self.absolute == 2)
- {
- if(n >= 0)
- n = self.just_toggled ? self.impulse : 0;
- else
- n = self.impulse * drawframetime;
- }
- else
- {
- n *= self.impulse * drawframetime;
- if(self.just_toggled)
- if(n < 1)
- n = 1;
- }
- if(n == 0)
- return;
- fail = 0;
- for(i = random(); i <= n && fail <= 64*n; ++i)
- {
- p = o + self.mins;
- p.x += random() * sz.x;
- p.y += random() * sz.y;
- p.z += random() * sz.z;
- if(WarpZoneLib_BoxTouchesBrush(p, p, self, world))
- {
- if(self.movedir != '0 0 0')
- {
- traceline(p, p + normalize(self.movedir) * 4096, 0, world);
- p = trace_endpos;
- pointparticles(self.cnt, p, trace_plane_normal * vlen(self.movedir) + self.velocity + randomvec() * self.waterlevel, self.count);
- }
- else
- {
- pointparticles(self.cnt, p, self.velocity + randomvec() * self.waterlevel, self.count);
- }
- if(self.noise != "")
- {
- setorigin(self, p);
- sound(self, CH_AMBIENT, self.noise, VOL_BASE * self.volume, self.atten);
- }
- self.just_toggled = 0;
- }
- else if(self.absolute)
- {
- ++fail;
- --i;
- }
- }
- setorigin(self, o);
-}
-
-void Ent_PointParticles_Remove()
-{
- if(self.noise)
- strunzone(self.noise);
- self.noise = string_null;
- if(self.bgmscript)
- strunzone(self.bgmscript);
- self.bgmscript = string_null;
-}
-
-void Ent_PointParticles()
-{
- float i;
- vector v;
- int f = ReadByte();
- if(f & 2)
- {
- i = ReadCoord(); // density (<0: point, >0: volume)
- if(i && !self.impulse && self.cnt) // self.cnt check is so it only happens if the ent already existed
- self.just_toggled = 1;
- self.impulse = i;
- }
- if(f & 4)
- {
- self.origin_x = ReadCoord();
- self.origin_y = ReadCoord();
- self.origin_z = ReadCoord();
- }
- if(f & 1)
- {
- self.modelindex = ReadShort();
- if(f & 0x80)
- {
- if(self.modelindex)
- {
- self.mins_x = ReadCoord();
- self.mins_y = ReadCoord();
- self.mins_z = ReadCoord();
- self.maxs_x = ReadCoord();
- self.maxs_y = ReadCoord();
- self.maxs_z = ReadCoord();
- }
- else
- {
- self.mins = '0 0 0';
- self.maxs_x = ReadCoord();
- self.maxs_y = ReadCoord();
- self.maxs_z = ReadCoord();
- }
- }
- else
- {
- self.mins = self.maxs = '0 0 0';
- }
-
- self.cnt = ReadShort(); // effect number
-
- if(f & 0x20)
- {
- self.velocity = decompressShortVector(ReadShort());
- self.movedir = decompressShortVector(ReadShort());
- }
- else
- {
- self.velocity = self.movedir = '0 0 0';
- }
- if(f & 0x40)
- {
- self.waterlevel = ReadShort() / 16.0;
- self.count = ReadByte() / 16.0;
- }
- else
- {
- self.waterlevel = 0;
- self.count = 1;
- }
- if(self.noise)
- strunzone(self.noise);
- if(self.bgmscript)
- strunzone(self.bgmscript);
- self.noise = strzone(ReadString());
- if(self.noise != "")
- {
- self.atten = ReadByte() / 64.0;
- self.volume = ReadByte() / 255.0;
- }
- self.bgmscript = strzone(ReadString());
- if(self.bgmscript != "")
- {
- self.bgmscriptattack = ReadByte() / 64.0;
- self.bgmscriptdecay = ReadByte() / 64.0;
- self.bgmscriptsustain = ReadByte() / 255.0;
- self.bgmscriptrelease = ReadByte() / 64.0;
- }
- BGMScript_InitEntity(self);
- }
-
- if(f & 2)
- {
- self.absolute = (self.impulse >= 0);
- if(!self.absolute)
- {
- v = self.maxs - self.mins;
- self.impulse *= -v.x * v.y * v.z / 262144; // relative: particles per 64^3 cube
- }
- }
-
- if(f & 0x10)
- self.absolute = 2;
-
- setorigin(self, self.origin);
- setsize(self, self.mins, self.maxs);
- self.solid = SOLID_NOT;
- self.draw = Draw_PointParticles;
- self.entremove = Ent_PointParticles_Remove;
-}
-
-void Draw_Rain()
-{
- te_particlerain(self.origin + self.mins, self.origin + self.maxs, self.velocity, floor(self.count * drawframetime + random()), self.glow_color);
-}
-
-void Draw_Snow()
-{
- te_particlesnow(self.origin + self.mins, self.origin + self.maxs, self.velocity, floor(self.count * drawframetime + random()), self.glow_color);
-}
-
-void Ent_RainOrSnow()
-{
- self.impulse = ReadByte(); // Rain, Snow, or Whatever
- self.origin_x = ReadCoord();
- self.origin_y = ReadCoord();
- self.origin_z = ReadCoord();
- self.maxs_x = ReadCoord();
- self.maxs_y = ReadCoord();
- self.maxs_z = ReadCoord();
- self.velocity = decompressShortVector(ReadShort());
- self.count = ReadShort() * 10;
- self.glow_color = ReadByte(); // color
-
- self.mins = -0.5 * self.maxs;
- self.maxs = 0.5 * self.maxs;
- self.origin = self.origin - self.mins;
-
- setorigin(self, self.origin);
- setsize(self, self.mins, self.maxs);
- self.solid = SOLID_NOT;
- if(self.impulse)
- self.draw = Draw_Rain;
- else
- self.draw = Draw_Snow;
-}
-
void Net_ReadVortexBeamParticle()
{
vector shotorg, endpos;
#ifndef PARTICLES_H
#define PARTICLES_H
-
.int dphitcontentsmask;
.int cnt; // effect number
.float absolute; // 1 = count per second is absolute, 2 = only spawn at toggle
.vector movedir; // trace direction
-void Draw_PointParticles();
-
-void Ent_PointParticles_Remove();
-
-void Ent_PointParticles();
-
.float glow_color; // palette index
-void Draw_Rain();
-
-void Draw_Snow();
-
-void Ent_RainOrSnow();
-
void Net_ReadVortexBeamParticle();
#endif
hook.qc
hud_config.qc
hud.qc
-laser.qc
main.qc
mapvoting.qc
miscfunctions.qc
scoreboard.qc
shownames.qc
sortlist.qc
-target_music.qc
teamradar.qc
tturrets.qc
tuba.qc
+++ /dev/null
-#include "target_music.qh"
-
-void TargetMusic_Advance()
-{
- // run AFTER all the thinks!
- entity best, e;
- float vol, vol0;
- best = music_default;
- if(music_target && time < music_target.lifetime)
- best = music_target;
- if(music_trigger)
- best = music_trigger;
- for(e = world; (e = findfloat(e, enttype, ENT_CLIENT_TRIGGER_MUSIC)); ) if(e.noise)
- {
- vol0 = e.lastvol;
- if(getsoundtime(e, CH_BGM_SINGLE) < 0)
- {
- vol0 = -1;
- }
- if(e == best)
- {
- // increase volume
- if(e.fade_time > 0)
- e.state = bound(0, e.state + frametime / e.fade_time, 1);
- else
- e.state = 1;
- }
- else
- {
- // decrease volume
- if(e.fade_rate > 0)
- e.state = bound(0, e.state - frametime / e.fade_rate, 1);
- else
- e.state = 0;
- }
- vol = e.state * e.volume * autocvar_bgmvolume;
- if(vol != vol0)
- {
- if(vol0 < 0)
- sound(e, CH_BGM_SINGLE, e.noise, vol, ATTEN_NONE); // restart
- else
- sound(e, CH_BGM_SINGLE, "", vol, ATTEN_NONE);
- e.lastvol = vol;
- }
- }
- music_trigger = world;
-
- if(best)
- bgmtime = getsoundtime(best, CH_BGM_SINGLE);
- else
- bgmtime = gettime(GETTIME_CDTRACK);
-}
-
-void Net_TargetMusic()
-{
- int id = ReadShort();
- float vol = ReadByte() / 255.0;
- float fai = ReadByte() / 16.0;
- float fao = ReadByte() / 16.0;
- float tim = ReadByte();
- string noi = ReadString();
-
- entity e;
- for(e = world; (e = findfloat(e, enttype, ENT_CLIENT_TRIGGER_MUSIC)); )
- {
- if(e.count == id)
- break;
- }
- if(!e)
- {
- e = spawn();
- e.enttype = ENT_CLIENT_TRIGGER_MUSIC;
- e.count = id;
- }
- if(e.noise != noi)
- {
- if(e.noise)
- strunzone(e.noise);
- e.noise = strzone(noi);
- precache_sound(e.noise);
- sound(e, CH_BGM_SINGLE, e.noise, 0, ATTEN_NONE);
- if(getsoundtime(e, CH_BGM_SINGLE) < 0)
- {
- dprintf("Cannot initialize sound %s\n", e.noise);
- strunzone(e.noise);
- e.noise = string_null;
- }
- }
- e.volume = vol;
- e.fade_time = fai;
- e.fade_rate = fao;
- if(vol > 0)
- {
- if(tim == 0)
- {
- music_default = e;
- if(!music_disabled)
- {
- e.state = 2;
- cvar_settemp("music_playlist_index", "-1"); // don't use playlists
- localcmd("cd stop\n"); // just in case
- music_disabled = 1;
- }
- }
- else
- {
- music_target = e;
- e.lifetime = time + tim;
- }
- }
-}
-
-void Ent_TriggerMusic_Think()
-{
- if(WarpZoneLib_BoxTouchesBrush(view_origin, view_origin, self, world))
- {
- music_trigger = self;
- }
- self.nextthink = time;
-}
-
-void Ent_TriggerMusic_Remove()
-{
- if(self.noise)
- strunzone(self.noise);
- self.noise = string_null;
-}
-
-void Ent_ReadTriggerMusic()
-{
- int f = ReadByte();
- if(f & 4)
- {
- self.origin_x = ReadCoord();
- self.origin_y = ReadCoord();
- self.origin_z = ReadCoord();
- }
- if(f & 1)
- {
- self.modelindex = ReadShort();
- if(self.modelindex)
- {
- self.mins_x = ReadCoord();
- self.mins_y = ReadCoord();
- self.mins_z = ReadCoord();
- self.maxs_x = ReadCoord();
- self.maxs_y = ReadCoord();
- self.maxs_z = ReadCoord();
- }
- else
- {
- self.mins = '0 0 0';
- self.maxs_x = ReadCoord();
- self.maxs_y = ReadCoord();
- self.maxs_z = ReadCoord();
- }
-
- self.volume = ReadByte() / 255.0;
- self.fade_time = ReadByte() / 16.0;
- self.fade_rate = ReadByte() / 16.0;
- string s = self.noise;
- if(self.noise)
- strunzone(self.noise);
- self.noise = strzone(ReadString());
- if(self.noise != s)
- {
- precache_sound(self.noise);
- sound(self, CH_BGM_SINGLE, self.noise, 0, ATTEN_NONE);
- if(getsoundtime(self, CH_BGM_SINGLE) < 0)
- {
- dprintf("Cannot initialize sound %s\n", self.noise);
- strunzone(self.noise);
- self.noise = string_null;
- }
- }
- }
-
- setorigin(self, self.origin);
- setsize(self, self.mins, self.maxs);
- self.cnt = 1;
- self.think = Ent_TriggerMusic_Think;
- self.nextthink = time;
-}
+++ /dev/null
-#ifndef TARGET_MUSIC_H
-#define TARGET_MUSIC_H
-
-float music_disabled;
-entity music_default;
-entity music_target;
-entity music_trigger;
-// FIXME also control bgmvolume here, to not require a target_music for the default track.
-
-.int state;
-.float lastvol;
-
-void TargetMusic_Advance();
-
-void Net_TargetMusic();
-
-void Ent_TriggerMusic_Think();
-
-void Ent_TriggerMusic_Remove();
-
-void Ent_ReadTriggerMusic();
-#endif
const int ENT_CLIENT_PLAT = 67;
const int ENT_CLIENT_PLAT_TRIGGER = 68;
const int ENT_CLIENT_SWAMP = 69;
+const int ENT_CLIENT_CORNER = 70;
+const int ENT_CLIENT_KEYLOCK = 71;
const int ENT_CLIENT_HEALING_ORB = 80;
#ifdef SVQC
+#include "../server/miscfunctions.qh"
+
void Physics_AddStats()
{
// g_movementspeed hack
tracebox(neworigin, PL_CROUCH_MIN, PL_CROUCH_MAX, neworigin, MOVE_NORMAL, self);
if (!trace_startsolid)
{
- self.origin = neworigin;
+ setorigin(self, neworigin);
return;// true;
}
}
pmove_waterjumptime = 0;
}
+#ifdef CSQC
+.float t_fraction;
+.vector t_plane_normal;
+.float t_startsolid;
+.vector t_endpos;
+entity t_tracebox(vector thevec1, vector tmin, vector tmax, vector thevec2, float type, entity forentity)
+{
+ entity e = spawn();
+ tracebox(thevec1, tmin, tmax, thevec2, type, forentity);
+ e.t_fraction = trace_fraction;
+ e.t_plane_normal = trace_plane_normal;
+ e.t_startsolid = trace_startsolid;
+ e.t_endpos = trace_endpos;
+
+ return e;
+}
+#endif
+
+void PM_ClientMovement_Move()
+{
+#ifdef CSQC
+
+ int bump;
+ float t;
+ float f;
+ vector neworigin = '0 0 0';
+ vector currentorigin2 = '0 0 0';
+ vector neworigin2 = '0 0 0';
+ vector primalvelocity;
+ entity trace = world, trace2 = world, trace3 = world;
+ entity oldtrace = world;
+
+ PM_ClientMovement_UpdateStatus();
+ primalvelocity = self.velocity;
+ for (bump = 0, t = PHYS_INPUT_TIMELENGTH; bump < 8 && dotproduct(self.velocity, self.velocity) > 0; bump++)
+ {
+ neworigin = self.velocity * t;
+ trace = t_tracebox(self.origin, self.mins, self.maxs, neworigin, MOVE_NORMAL, self);
+ if(trace.t_fraction < 1 && trace.t_plane_normal_z == 0)
+ {
+ // may be a step or wall, try stepping up
+ // first move forward at a higher level
+ currentorigin2 = self.origin;
+ currentorigin2_z += PHYS_STEPHEIGHT;
+ neworigin2 = neworigin;
+ neworigin_z = self.origin_z + PHYS_STEPHEIGHT;
+
+ trace2 = t_tracebox(currentorigin2, self.mins, self.maxs, neworigin2, MOVE_NORMAL, self);
+ if(!trace2.t_startsolid)
+ {
+ // then move down from there
+ currentorigin2 = trace2.t_endpos;
+ neworigin2 = trace2.t_endpos;
+ neworigin_z = self.origin_z;
+
+ trace3 = t_tracebox(currentorigin2, self.mins, self.maxs, neworigin2, MOVE_NORMAL, self);
+ // accept the new trace if it made some progress
+ if(fabs(trace3.t_endpos_x - trace.t_endpos_x) >= 0.03125 || fabs(trace3.t_endpos_y - trace.t_endpos_y) >= 0.03125)
+ {
+ oldtrace = trace;
+ trace = trace2;
+ trace.t_endpos = trace3.t_endpos;
+ }
+ else if(oldtrace) // TODO: check if we even need this
+ {
+ trace = oldtrace;
+ }
+ }
+ }
+
+ // check if it moved at all
+ if(trace.t_fraction >= 0.001)
+ setorigin(self, trace.t_endpos);
+
+ // check if it moved all the way
+ if(trace.t_fraction == 1)
+ break;
+
+ // this is only really needed for nogravityonground combined with gravityunaffectedbyticrate
+ // <LordHavoc> I'm pretty sure I commented it out solely because it seemed redundant
+ // this got commented out in a change that supposedly makes the code match QW better
+ // so if this is broken, maybe put it in an if(cls.protocol != PROTOCOL_QUAKEWORLD) block
+ if(trace.t_plane_normal_z > 0.7)
+ SET_ONGROUND(self);
+
+ t -= t * trace.t_fraction;
+
+ f = dotproduct(self.velocity, trace.t_plane_normal);
+ self.velocity = self.velocity + trace.t_plane_normal * -f;
+ }
+ if(pmove_waterjumptime > 0)
+ self.velocity = primalvelocity;
+
+ if(trace && !wasfreed(trace)) { remove(trace); }
+ if(trace2 && !wasfreed(trace2)) { remove(trace2); }
+ if(trace3 && !wasfreed(trace3)) { remove(trace3); }
+#endif
+}
+
+#if 0
void PM_ClientMovement_Move()
{
#ifdef CSQC
// check if it moved at all
if (trace_fraction >= 0.001)
- self.origin = trace_endpos;
+ setorigin(self, trace_endpos);
// check if it moved all the way
if (trace_fraction == 1)
self.velocity = primalvelocity;
#endif
}
+#endif
void CPM_PM_Aircontrol(vector wishdir, float wishspeed)
{
if (dot > 0) // we can't change direction while slowing down
{
- k *= pow(dot, PHYS_AIRCONTROL_POWER)*PHYS_INPUT_TIMELENGTH;
+ k *= pow(dot, PHYS_AIRCONTROL_POWER) * PHYS_INPUT_TIMELENGTH;
xyspeed = max(0, xyspeed - PHYS_AIRCONTROL_PENALTY * sqrt(max(0, 1 - dot*dot)) * k/32);
k *= PHYS_AIRCONTROL;
self.velocity = normalize(self.velocity * xyspeed + wishdir * k);
}
}
+void PM_check_hitground()
+{
+#ifdef SVQC
+ if (IS_ONGROUND(self))
+ if (IS_PLAYER(self)) // no fall sounds for observers thank you very much
+ if (self.wasFlying)
+ {
+ self.wasFlying = 0;
+ if (self.waterlevel < WATERLEVEL_SWIMMING)
+ if (time >= self.ladder_time)
+ if (!self.hook)
+ {
+ self.nextstep = time + 0.3 + random() * 0.1;
+ trace_dphitq3surfaceflags = 0;
+ tracebox(self.origin, self.mins, self.maxs, self.origin - '0 0 1', MOVE_NOMONSTERS, self);
+ if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOSTEPS))
+ {
+ if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS)
+ GlobalSound(globalsound_metalfall, CH_PLAYER, VOICETYPE_PLAYERSOUND);
+ else
+ GlobalSound(globalsound_fall, CH_PLAYER, VOICETYPE_PLAYERSOUND);
+ }
+ }
+ }
+#endif
+}
+
void PM_check_blocked(void)
{
#ifdef SVQC
#ifdef SVQC
float speedaward_lastsent;
float speedaward_lastupdate;
-string GetMapname(void);
#endif
void PM_check_race(void)
{
#ifdef SVQC
- if not(g_cts || g_race)
+ if(!(g_cts || g_race))
return;
if (vlen(self.velocity - self.velocity_z * '0 0 1') > speedaward_speed)
{
// try moving up and forward to go up a step
// back to start pos
- self.origin = start_origin;
+ setorigin(self, start_origin);
self.velocity = start_velocity;
// move up
{
//Con_Printf("wall\n");
// stepping up didn't make any progress, revert to original move
- self.origin = originalmove_origin;
+ setorigin(self, originalmove_origin);
self.velocity = originalmove_velocity;
self.flags = originalmove_flags;
self.groundentity = originalmove_groundentity;
// if the push down didn't end up on good ground, use the move without
// the step up. This happens near wall / slope combinations, and can
// cause the player to hop up higher on a slope too steep to climb
- self.origin = originalmove_origin;
+ setorigin(self, originalmove_origin);
self.velocity = originalmove_velocity;
self.flags = originalmove_flags;
self.groundentity = originalmove_groundentity;
}
// used for calculating airshots
-float PM_is_flying()
+bool IsFlying(entity a)
{
- if (IS_ONGROUND(self))
- return 0;
- if (self.waterlevel >= WATERLEVEL_SWIMMING)
- return 0;
- traceline(self.origin, self.origin - '0 0 48', MOVE_NORMAL, self);
- return trace_fraction >= 1;
+ if(IS_ONGROUND(a))
+ return false;
+ if(a.waterlevel >= WATERLEVEL_SWIMMING)
+ return false;
+ traceline(a.origin, a.origin - '0 0 48', MOVE_NORMAL, a);
+ if(trace_fraction < 1)
+ return false;
+ return true;
}
void PM_Main()
}
#endif
-#ifdef SVQC
- // if dead, behave differently
- // in CSQC, physics don't handle dead player
- if (self.deadflag)
+ if(PHYS_DEAD(self))
goto end;
-#endif
#ifdef SVQC
if (!self.fixangle && !g_bugrigs)
self.angles = '0 1 0' * PHYS_INPUT_ANGLES(self).y;
#endif
-#ifdef SVQC
- if (IS_ONGROUND(self))
- if (IS_PLAYER(self)) // no fall sounds for observers thank you very much
- if (self.wasFlying)
- {
- self.wasFlying = 0;
- if (self.waterlevel < WATERLEVEL_SWIMMING)
- if (time >= self.ladder_time)
- if (!self.hook)
- {
- self.nextstep = time + 0.3 + random() * 0.1;
- trace_dphitq3surfaceflags = 0;
- tracebox(self.origin, self.mins, self.maxs, self.origin - '0 0 1', MOVE_NOMONSTERS, self);
- if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOSTEPS))
- {
- if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS)
- GlobalSound(globalsound_metalfall, CH_PLAYER, VOICETYPE_PLAYERSOUND);
- else
- GlobalSound(globalsound_fall, CH_PLAYER, VOICETYPE_PLAYERSOUND);
- }
- }
- }
-#endif
+ PM_check_hitground();
- if (PM_is_flying())
+ if(IsFlying(self))
self.wasFlying = 1;
-#ifdef SVQC
if (IS_PLAYER(self))
-#endif
CheckPlayerJump();
if (self.flags & /* FL_WATERJUMP */ 2048)
else if (ITEMS(self) & IT_USING_JETPACK)
PM_jetpack(maxspeed_mod);
+ else if (IS_ONGROUND(self))
+ PM_walk(buttons_prev, maxspeed_mod);
+
else
- {
- if (IS_ONGROUND(self))
- PM_walk(buttons_prev, maxspeed_mod);
- else
- PM_air(buttons_prev, maxspeed_mod);
- }
+ PM_air(buttons_prev, maxspeed_mod);
#ifdef SVQC
if (!IS_OBSERVER(self))
self.lastground = time;
// conveyors: then break velocity again
- if (self.conveyor.state)
+ if(self.conveyor.state)
self.velocity += self.conveyor.movedir;
-#ifdef SVQC
- self.lastflags = self.flags;
-#elif defined(CSQC)
- self.lastflags = self.pmove_flags;
-#endif
+ self.lastflags = FLAGS(self);
self.lastclassname = self.classname;
}
#endif
{
PM_Main();
+
+#ifdef CSQC
+ pmove_org = self.origin;
+ pmove_vel = self.velocity;
+#endif
}
.float() PlayerPhysplug;
float AdjustAirAccelQW(float accelqw, float factor);
+bool IsFlying(entity a);
+
#ifdef CSQC
float PM_multijump_checkjump();
#define ITEMS(s) getstati(STAT_ITEMS, 0, 24)
+ #define FLAGS(s) (s).pmove_flags
+
#define PHYS_AMMO_FUEL(s) getstati(STAT_FUEL)
#define PHYS_FROZEN(s) getstati(STAT_FROZEN)
#define ITEMS(s) s.items
+ #define FLAGS(s) (s).flags
+
#define PHYS_AMMO_FUEL(s) s.ammo_fuel
#define PHYS_FROZEN(s) s.frozen
self.SendFlags |= 2;
}
-float conveyor_send(entity to, float sf)
+bool conveyor_send(entity to, int sf)
{
WriteByte(MSG_ENTITY, ENT_CLIENT_CONVEYOR);
WriteByte(MSG_ENTITY, sf);
#include "door.qh"
#include "ladder.qh"
#include "plat.qh"
+#include "rainsnow.qh"
+#include "pointparticles.qh"
#endif
#elif defined(CSQC)
void plat_draw()
{
- float dt = time - self.move_time;
- self.move_time = time;
- if(dt <= 0) { return; }
-
- setorigin(self, self.origin + self.velocity * dt);
+ Movetype_Physics_NoMatchServer();
}
void ent_plat()
+#ifdef CSQC
+ #include "../../../client/particles.qh"
+#endif
+
#ifdef SVQC
// NOTE: also contains func_sparks
spawnfunc_func_pointparticles();
}
+#elif defined(CSQC)
+
+void Draw_PointParticles()
+{
+ float n, i, fail;
+ vector p;
+ vector sz;
+ vector o;
+ o = self.origin;
+ sz = self.maxs - self.mins;
+ n = BGMScript(self);
+ if(self.absolute == 2)
+ {
+ if(n >= 0)
+ n = self.just_toggled ? self.impulse : 0;
+ else
+ n = self.impulse * drawframetime;
+ }
+ else
+ {
+ n *= self.impulse * drawframetime;
+ if(self.just_toggled)
+ if(n < 1)
+ n = 1;
+ }
+ if(n == 0)
+ return;
+ fail = 0;
+ for(i = random(); i <= n && fail <= 64*n; ++i)
+ {
+ p = o + self.mins;
+ p.x += random() * sz.x;
+ p.y += random() * sz.y;
+ p.z += random() * sz.z;
+ if(WarpZoneLib_BoxTouchesBrush(p, p, self, world))
+ {
+ if(self.movedir != '0 0 0')
+ {
+ traceline(p, p + normalize(self.movedir) * 4096, 0, world);
+ p = trace_endpos;
+ pointparticles(self.cnt, p, trace_plane_normal * vlen(self.movedir) + self.velocity + randomvec() * self.waterlevel, self.count);
+ }
+ else
+ {
+ pointparticles(self.cnt, p, self.velocity + randomvec() * self.waterlevel, self.count);
+ }
+ if(self.noise != "")
+ {
+ setorigin(self, p);
+ sound(self, CH_AMBIENT, self.noise, VOL_BASE * self.volume, self.atten);
+ }
+ self.just_toggled = 0;
+ }
+ else if(self.absolute)
+ {
+ ++fail;
+ --i;
+ }
+ }
+ setorigin(self, o);
+}
+
+void Ent_PointParticles_Remove()
+{
+ if(self.noise)
+ strunzone(self.noise);
+ self.noise = string_null;
+ if(self.bgmscript)
+ strunzone(self.bgmscript);
+ self.bgmscript = string_null;
+}
+
+void Ent_PointParticles()
+{
+ float i;
+ vector v;
+ int f = ReadByte();
+ if(f & 2)
+ {
+ i = ReadCoord(); // density (<0: point, >0: volume)
+ if(i && !self.impulse && self.cnt) // self.cnt check is so it only happens if the ent already existed
+ self.just_toggled = 1;
+ self.impulse = i;
+ }
+ if(f & 4)
+ {
+ self.origin_x = ReadCoord();
+ self.origin_y = ReadCoord();
+ self.origin_z = ReadCoord();
+ }
+ if(f & 1)
+ {
+ self.modelindex = ReadShort();
+ if(f & 0x80)
+ {
+ if(self.modelindex)
+ {
+ self.mins_x = ReadCoord();
+ self.mins_y = ReadCoord();
+ self.mins_z = ReadCoord();
+ self.maxs_x = ReadCoord();
+ self.maxs_y = ReadCoord();
+ self.maxs_z = ReadCoord();
+ }
+ else
+ {
+ self.mins = '0 0 0';
+ self.maxs_x = ReadCoord();
+ self.maxs_y = ReadCoord();
+ self.maxs_z = ReadCoord();
+ }
+ }
+ else
+ {
+ self.mins = self.maxs = '0 0 0';
+ }
+
+ self.cnt = ReadShort(); // effect number
+
+ if(f & 0x20)
+ {
+ self.velocity = decompressShortVector(ReadShort());
+ self.movedir = decompressShortVector(ReadShort());
+ }
+ else
+ {
+ self.velocity = self.movedir = '0 0 0';
+ }
+ if(f & 0x40)
+ {
+ self.waterlevel = ReadShort() / 16.0;
+ self.count = ReadByte() / 16.0;
+ }
+ else
+ {
+ self.waterlevel = 0;
+ self.count = 1;
+ }
+ if(self.noise)
+ strunzone(self.noise);
+ if(self.bgmscript)
+ strunzone(self.bgmscript);
+ self.noise = strzone(ReadString());
+ if(self.noise != "")
+ {
+ self.atten = ReadByte() / 64.0;
+ self.volume = ReadByte() / 255.0;
+ }
+ self.bgmscript = strzone(ReadString());
+ if(self.bgmscript != "")
+ {
+ self.bgmscriptattack = ReadByte() / 64.0;
+ self.bgmscriptdecay = ReadByte() / 64.0;
+ self.bgmscriptsustain = ReadByte() / 255.0;
+ self.bgmscriptrelease = ReadByte() / 64.0;
+ }
+ BGMScript_InitEntity(self);
+ }
+
+ if(f & 2)
+ {
+ self.absolute = (self.impulse >= 0);
+ if(!self.absolute)
+ {
+ v = self.maxs - self.mins;
+ self.impulse *= -v.x * v.y * v.z / 262144; // relative: particles per 64^3 cube
+ }
+ }
+
+ if(f & 0x10)
+ self.absolute = 2;
+
+ setorigin(self, self.origin);
+ setsize(self, self.mins, self.maxs);
+ self.solid = SOLID_NOT;
+ self.draw = Draw_PointParticles;
+ self.entremove = Ent_PointParticles_Remove;
+}
#endif
--- /dev/null
+#ifdef CSQC
+
+void Ent_PointParticles();
+
+#endif
\ No newline at end of file
Net_LinkEntity(self, false, 0, rainsnow_SendEntity);
}
+#elif defined(CSQC)
+void Draw_Rain()
+{
+ te_particlerain(self.origin + self.mins, self.origin + self.maxs, self.velocity, floor(self.count * drawframetime + random()), self.glow_color);
+}
+
+void Draw_Snow()
+{
+ te_particlesnow(self.origin + self.mins, self.origin + self.maxs, self.velocity, floor(self.count * drawframetime + random()), self.glow_color);
+}
+
+void Ent_RainOrSnow()
+{
+ self.impulse = ReadByte(); // Rain, Snow, or Whatever
+ self.origin_x = ReadCoord();
+ self.origin_y = ReadCoord();
+ self.origin_z = ReadCoord();
+ self.maxs_x = ReadCoord();
+ self.maxs_y = ReadCoord();
+ self.maxs_z = ReadCoord();
+ self.velocity = decompressShortVector(ReadShort());
+ self.count = ReadShort() * 10;
+ self.glow_color = ReadByte(); // color
+
+ self.mins = -0.5 * self.maxs;
+ self.maxs = 0.5 * self.maxs;
+ self.origin = self.origin - self.mins;
+
+ setorigin(self, self.origin);
+ setsize(self, self.mins, self.maxs);
+ self.solid = SOLID_NOT;
+ if(self.impulse)
+ self.draw = Draw_Rain;
+ else
+ self.draw = Draw_Snow;
+}
#endif
--- /dev/null
+#ifdef CSQC
+void Ent_RainOrSnow();
+#endif
void spawnfunc_func_stardust()
{
self.effects = EF_STARDUST;
+
+ CSQCMODEL_AUTOINIT();
}
#endif
#ifdef SVQC
+bool corner_send(entity to, int sf)
+{
+ WriteByte(MSG_ENTITY, ENT_CLIENT_CORNER);
+
+ WriteString(MSG_ENTITY, self.platmovetype);
+
+ WriteCoord(MSG_ENTITY, self.origin_x);
+ WriteCoord(MSG_ENTITY, self.origin_y);
+ WriteCoord(MSG_ENTITY, self.origin_z);
+
+ WriteString(MSG_ENTITY, self.target);
+ WriteString(MSG_ENTITY, self.target2);
+ WriteString(MSG_ENTITY, self.target3);
+ WriteString(MSG_ENTITY, self.target4);
+ WriteString(MSG_ENTITY, self.targetname);
+
+ return true;
+}
+
+void corner_link()
+{
+ Net_LinkEntity(self, false, 0, corner_send);
+}
+
void spawnfunc_path_corner()
{
+ corner_link();
+
// setup values for overriding train movement
// if a second value does not exist, both start and end speeds are the single value specified
- if(!set_platmovetype(self, self.platmovetype))
- return;
+ set_platmovetype(self, self.platmovetype);
+}
+#elif defined(CSQC)
+
+void corner_remove()
+{
+ if(self.target) { strunzone(self.target); }
+ self.target = string_null;
+
+ if(self.target2) { strunzone(self.target2); }
+ self.target2 = string_null;
+
+ if(self.target3) { strunzone(self.target3); }
+ self.target3 = string_null;
+
+ if(self.target4) { strunzone(self.target4); }
+ self.target4 = string_null;
+
+ if(self.targetname) { strunzone(self.targetname); }
+ self.targetname = string_null;
+
+ if(self.platmovetype) { strunzone(self.platmovetype); }
+ self.targetname = string_null;
+}
+
+void ent_corner()
+{
+ self.platmovetype = strzone(ReadString());
+
+ self.origin_x = ReadCoord();
+ self.origin_y = ReadCoord();
+ self.origin_z = ReadCoord();
+
+ self.target = strzone(ReadString());
+ self.target2 = strzone(ReadString());
+ self.target3 = strzone(ReadString());
+ self.target4 = strzone(ReadString());
+ self.targetname = strzone(ReadString());
+
+ self.classname = "path_corner";
+ self.drawmask = MASK_NORMAL;
+ self.entremove = corner_remove;
+
+ set_platmovetype(self, self.platmovetype);
}
+
#endif
--- /dev/null
+#ifdef CSQC
+void ent_corner();
+#endif
+// the way this entity works makes it no use to CSQC, as it removes itself instantly
+
#ifdef SVQC
void follow_init()
{
#ifndef TRIGGERS_MISC_INCLUDE_H
#define TRIGGERS_MISC_INCLUDE_H
-// nothing yet
+#include "corner.qh"
+#include "laser.qh"
#endif
+#if defined(CSQC)
+ #include "../../../dpdefs/csprogsdefs.qh"
+ #include "../../buffs.qh"
+ #include "../../../csqcmodellib/interpolate.qh"
+ #include "../../../client/main.qh"
+ #include "../../../csqcmodellib/cl_model.qh"
+#elif defined(MENUQC)
+#elif defined(SVQC)
+#endif
+
#ifdef SVQC
.float modelscale;
void misc_laser_aim()
else
self.state = 1;
}
+#elif defined(CSQC)
+
+// a laser goes from origin in direction angles
+// it has color 'colormod'
+// and stops when something is in the way
+.int cnt; // end effect
+.vector colormod;
+.int state; // on-off
+.int count; // flags for the laser
+.vector velocity;
+.float alpha;
+.float scale; // scaling factor of the thickness
+.float modelscale; // scaling factor of the dlight
+
+void Draw_Laser()
+{
+ if(!self.state)
+ return;
+ InterpolateOrigin_Do();
+ if(self.count & 0x80)
+ {
+ if(self.count & 0x10)
+ {
+ trace_endpos = self.velocity;
+ trace_dphitq3surfaceflags = 0;
+ }
+ else
+ traceline(self.origin, self.velocity, 0, self);
+ }
+ else
+ {
+ if(self.count & 0x10)
+ {
+ makevectors(self.angles);
+ trace_endpos = self.origin + v_forward * 1048576;
+ trace_dphitq3surfaceflags = Q3SURFACEFLAG_SKY;
+ }
+ else
+ {
+ makevectors(self.angles);
+ traceline(self.origin, self.origin + v_forward * 32768, 0, self);
+ if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
+ trace_endpos = self.origin + v_forward * 1048576;
+ }
+ }
+ if(self.scale != 0)
+ {
+ if(self.alpha)
+ {
+ Draw_CylindricLine(self.origin, trace_endpos, self.scale, "particles/laserbeam", 0, time * 3, self.colormod, self.alpha, DRAWFLAG_NORMAL, view_origin);
+ }
+ else
+ {
+ Draw_CylindricLine(self.origin, trace_endpos, self.scale, "particles/laserbeam", 0, time * 3, self.colormod, 0.5, DRAWFLAG_ADDITIVE, view_origin);
+ }
+ }
+ if (!(trace_dphitq3surfaceflags & (Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT)))
+ {
+ if(self.cnt >= 0)
+ pointparticles(self.cnt, trace_endpos, trace_plane_normal, drawframetime * 1000);
+ if(self.colormod != '0 0 0' && self.modelscale != 0)
+ adddynamiclight(trace_endpos + trace_plane_normal * 1, self.modelscale, self.colormod * 5);
+ }
+}
+
+void Ent_Laser()
+{
+ InterpolateOrigin_Undo();
+
+ // 30 bytes, or 13 bytes for just moving
+ int f = ReadByte();
+ self.count = (f & 0xF0);
+
+ if(self.count & 0x80)
+ self.iflags = IFLAG_VELOCITY | IFLAG_ORIGIN;
+ else
+ self.iflags = IFLAG_ANGLES | IFLAG_ORIGIN;
+
+ if(f & 1)
+ {
+ self.origin_x = ReadCoord();
+ self.origin_y = ReadCoord();
+ self.origin_z = ReadCoord();
+ setorigin(self, self.origin);
+ }
+ if(f & 8)
+ {
+ self.colormod_x = ReadByte() / 255.0;
+ self.colormod_y = ReadByte() / 255.0;
+ self.colormod_z = ReadByte() / 255.0;
+ if(f & 0x40)
+ self.alpha = ReadByte() / 255.0;
+ else
+ self.alpha = 0;
+ self.scale = 2;
+ self.modelscale = 50;
+ if(f & 0x20)
+ {
+ self.scale *= ReadByte() / 16.0; // beam radius
+ self.modelscale *= ReadByte() / 16.0; // dlight radius
+ }
+ if((f & 0x80) || !(f & 0x10))
+ self.cnt = ReadShort() - 1; // effect number
+ else
+ self.cnt = 0;
+ }
+ if(f & 2)
+ {
+ if(f & 0x80)
+ {
+ self.velocity_x = ReadCoord();
+ self.velocity_y = ReadCoord();
+ self.velocity_z = ReadCoord();
+ }
+ else
+ {
+ self.angles_x = ReadAngle();
+ self.angles_y = ReadAngle();
+ }
+ }
+ if(f & 4)
+ self.state = ReadByte();
+ InterpolateOrigin_Note();
+ self.draw = Draw_Laser;
+}
#endif
--- /dev/null
+#ifdef CSQC
+void Ent_Laser();
+#endif
#endif
}
-#ifdef SVQC
.float platmovetype_start_default, platmovetype_end_default;
-float set_platmovetype(entity e, string s)
+bool set_platmovetype(entity e, string s)
{
// sets platmovetype_start and platmovetype_end based on a string consisting of two values
- float n;
- n = tokenize_console(s);
+ int n = tokenize_console(s);
if(n > 0)
e.platmovetype_start = stof(argv(0));
else
return true;
}
-#endif
#ifndef TRIGGERS_TARGET_INCLUDE_H
#define TRIGGERS_TARGET_INCLUDE_H
-// nothing yet
+#include "music.qh"
#endif
Net_LinkEntity(self, false, 0, trigger_music_SendEntity);
}
+#elif defined(CSQC)
+
+void TargetMusic_Advance()
+{
+ // run AFTER all the thinks!
+ entity best, e;
+ float vol, vol0;
+ best = music_default;
+ if(music_target && time < music_target.lifetime)
+ best = music_target;
+ if(music_trigger)
+ best = music_trigger;
+ for(e = world; (e = findfloat(e, enttype, ENT_CLIENT_TRIGGER_MUSIC)); ) if(e.noise)
+ {
+ vol0 = e.lastvol;
+ if(getsoundtime(e, CH_BGM_SINGLE) < 0)
+ {
+ vol0 = -1;
+ }
+ if(e == best)
+ {
+ // increase volume
+ if(e.fade_time > 0)
+ e.state = bound(0, e.state + frametime / e.fade_time, 1);
+ else
+ e.state = 1;
+ }
+ else
+ {
+ // decrease volume
+ if(e.fade_rate > 0)
+ e.state = bound(0, e.state - frametime / e.fade_rate, 1);
+ else
+ e.state = 0;
+ }
+ vol = e.state * e.volume * autocvar_bgmvolume;
+ if(vol != vol0)
+ {
+ if(vol0 < 0)
+ sound(e, CH_BGM_SINGLE, e.noise, vol, ATTEN_NONE); // restart
+ else
+ sound(e, CH_BGM_SINGLE, "", vol, ATTEN_NONE);
+ e.lastvol = vol;
+ }
+ }
+ music_trigger = world;
+
+ if(best)
+ bgmtime = getsoundtime(best, CH_BGM_SINGLE);
+ else
+ bgmtime = gettime(GETTIME_CDTRACK);
+}
+
+void Net_TargetMusic()
+{
+ int id = ReadShort();
+ float vol = ReadByte() / 255.0;
+ float fai = ReadByte() / 16.0;
+ float fao = ReadByte() / 16.0;
+ float tim = ReadByte();
+ string noi = ReadString();
+
+ entity e;
+ for(e = world; (e = findfloat(e, enttype, ENT_CLIENT_TRIGGER_MUSIC)); )
+ {
+ if(e.count == id)
+ break;
+ }
+ if(!e)
+ {
+ e = spawn();
+ e.enttype = ENT_CLIENT_TRIGGER_MUSIC;
+ e.count = id;
+ }
+ if(e.noise != noi)
+ {
+ if(e.noise)
+ strunzone(e.noise);
+ e.noise = strzone(noi);
+ precache_sound(e.noise);
+ sound(e, CH_BGM_SINGLE, e.noise, 0, ATTEN_NONE);
+ if(getsoundtime(e, CH_BGM_SINGLE) < 0)
+ {
+ dprintf("Cannot initialize sound %s\n", e.noise);
+ strunzone(e.noise);
+ e.noise = string_null;
+ }
+ }
+ e.volume = vol;
+ e.fade_time = fai;
+ e.fade_rate = fao;
+ if(vol > 0)
+ {
+ if(tim == 0)
+ {
+ music_default = e;
+ if(!music_disabled)
+ {
+ e.state = 2;
+ cvar_settemp("music_playlist_index", "-1"); // don't use playlists
+ localcmd("cd stop\n"); // just in case
+ music_disabled = 1;
+ }
+ }
+ else
+ {
+ music_target = e;
+ e.lifetime = time + tim;
+ }
+ }
+}
+
+void Ent_TriggerMusic_Think()
+{
+ if(WarpZoneLib_BoxTouchesBrush(view_origin, view_origin, self, world))
+ {
+ music_trigger = self;
+ }
+ self.nextthink = time;
+}
+
+void Ent_TriggerMusic_Remove()
+{
+ if(self.noise)
+ strunzone(self.noise);
+ self.noise = string_null;
+}
+
+void Ent_ReadTriggerMusic()
+{
+ int f = ReadByte();
+ if(f & 4)
+ {
+ self.origin_x = ReadCoord();
+ self.origin_y = ReadCoord();
+ self.origin_z = ReadCoord();
+ }
+ if(f & 1)
+ {
+ self.modelindex = ReadShort();
+ if(self.modelindex)
+ {
+ self.mins_x = ReadCoord();
+ self.mins_y = ReadCoord();
+ self.mins_z = ReadCoord();
+ self.maxs_x = ReadCoord();
+ self.maxs_y = ReadCoord();
+ self.maxs_z = ReadCoord();
+ }
+ else
+ {
+ self.mins = '0 0 0';
+ self.maxs_x = ReadCoord();
+ self.maxs_y = ReadCoord();
+ self.maxs_z = ReadCoord();
+ }
+
+ self.volume = ReadByte() / 255.0;
+ self.fade_time = ReadByte() / 16.0;
+ self.fade_rate = ReadByte() / 16.0;
+ string s = self.noise;
+ if(self.noise)
+ strunzone(self.noise);
+ self.noise = strzone(ReadString());
+ if(self.noise != s)
+ {
+ precache_sound(self.noise);
+ sound(self, CH_BGM_SINGLE, self.noise, 0, ATTEN_NONE);
+ if(getsoundtime(self, CH_BGM_SINGLE) < 0)
+ {
+ dprintf("Cannot initialize sound %s\n", self.noise);
+ strunzone(self.noise);
+ self.noise = string_null;
+ }
+ }
+ }
+
+ setorigin(self, self.origin);
+ setsize(self, self.mins, self.maxs);
+ self.cnt = 1;
+ self.think = Ent_TriggerMusic_Think;
+ self.nextthink = time;
+}
+
#endif
.float lifetime;
+#ifdef CSQC
+float music_disabled;
+entity music_default;
+entity music_target;
+entity music_trigger;
+// FIXME also control bgmvolume here, to not require a target_music for the default track.
+
+.int state;
+.float lastvol;
+
+void TargetMusic_Advance();
+
+void Net_TargetMusic();
+
+void Ent_TriggerMusic_Think();
+
+void Ent_TriggerMusic_Remove();
+
+void Ent_ReadTriggerMusic();
+#endif
+
#endif
#include "hurt.qc"
#include "impulse.qc"
#include "jumppads.qc"
+#include "keylock.qc"
#include "magicear.qc"
#include "monoflop.qc"
#include "multi.qc"
#include "jumppads.qh"
#include "secret.qh"
#include "swamp.qh"
+#include "keylock.qh"
#endif
--- /dev/null
+/**
+ * trigger given targets
+ */
+void trigger_keylock_trigger(string s)
+{
+ entity stemp = self;
+ entity otemp = other;
+ entity atemp = activator;
+
+ entity t;
+ for(t = world; (t = find(t, targetname, s)); )
+ if(t.use)
+ {
+ self = t;
+ other = stemp;
+ activator = atemp;
+ self.use();
+ }
+
+ self = stemp;
+ other = otemp;
+ activator = atemp;
+}
+
+/**
+ * kill killtarget of trigger keylock.
+ */
+void trigger_keylock_kill(string s)
+{
+ entity t;
+ for(t = world; (t = find(t, targetname, s)); )
+ remove(t);
+}
+
+void trigger_keylock_touch()
+{
+ bool key_used = false;
+ bool started_delay = false;
+
+ // only player may trigger the lock
+ if(!IS_PLAYER(other))
+ return;
+
+ // check silver key
+ if(self.itemkeys)
+ key_used = item_keys_usekey(self, other);
+
+ activator = other;
+
+ if(self.itemkeys)
+ {
+#ifdef SVQC
+ // at least one of the keys is missing
+ if(key_used)
+ {
+ // one or more keys were given, but others are still missing!
+ play2(other, self.noise1);
+ Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_ALSONEED, item_keys_keylist(self.itemkeys));
+ other.key_door_messagetime = time + 2;
+ }
+ else if(other.key_door_messagetime <= time)
+ {
+ // no keys were given
+ play2(other, self.noise2);
+ Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(self.itemkeys));
+ other.key_door_messagetime = time + 2;
+ }
+#endif
+
+ // trigger target2
+ if(self.delay <= time || started_delay == true)
+ if(self.target2)
+ {
+ trigger_keylock_trigger(self.target2);
+ started_delay = true;
+ self.delay = time + self.wait;
+ }
+ }
+ else
+ {
+#ifdef SVQC
+ // all keys were given!
+ play2(other, self.noise);
+ centerprint(other, self.message);
+#endif
+
+ if(self.target)
+ trigger_keylock_trigger(self.target);
+
+ if(self.killtarget)
+ trigger_keylock_kill(self.killtarget);
+
+ remove(self);
+ }
+
+}
+
+#ifdef SVQC
+bool trigger_keylock_send(entity to, int sf)
+{
+ WriteByte(MSG_ENTITY, ENT_CLIENT_KEYLOCK);
+
+ WriteString(MSG_ENTITY, self.target);
+ WriteString(MSG_ENTITY, self.target2);
+ WriteString(MSG_ENTITY, self.target3);
+ WriteString(MSG_ENTITY, self.target4);
+ WriteString(MSG_ENTITY, self.killtarget);
+ WriteString(MSG_ENTITY, self.targetname);
+
+ WriteInt24_t(MSG_ENTITY, self.itemkeys);
+ WriteByte(MSG_ENTITY, self.warpzone_isboxy);
+ WriteByte(MSG_ENTITY, self.height);
+ WriteByte(MSG_ENTITY, self.scale);
+
+ WriteCoord(MSG_ENTITY, self.origin_x);
+ WriteCoord(MSG_ENTITY, self.origin_y);
+ WriteCoord(MSG_ENTITY, self.origin_z);
+
+ WriteCoord(MSG_ENTITY, self.mins_x);
+ WriteCoord(MSG_ENTITY, self.mins_y);
+ WriteCoord(MSG_ENTITY, self.mins_z);
+ WriteCoord(MSG_ENTITY, self.maxs_x);
+ WriteCoord(MSG_ENTITY, self.maxs_y);
+ WriteCoord(MSG_ENTITY, self.maxs_z);
+
+ WriteCoord(MSG_ENTITY, self.movedir_x);
+ WriteCoord(MSG_ENTITY, self.movedir_y);
+ WriteCoord(MSG_ENTITY, self.movedir_z);
+
+ return true;
+}
+
+void trigger_keylock_link()
+{
+ // uncomment to network keylocks
+ //Net_LinkEntity(self, false, 0, trigger_keylock_send);
+}
+
+/*QUAKED trigger_keylock (.0 .5 .8) ?
+Keylock trigger. Must target other entities.
+This trigger will trigger target entities when all required keys are provided.
+-------- KEYS --------
+itemkeys: A bit field with key IDs that are needed to open this lock.
+sounds: 1 to play misc/secret.wav, 2 to play misc/talk.wav, 3 to play misc/trigger1.wav (3 is default)
+target: trigger all entities with this targetname when triggered and all keys have been given to it, then remove this trigger
+target2: trigger all entities with this targetname when triggered without giving it all the required keys.
+killtarget: remove all entities with this targetname when triggered with all the needed keys.
+message: print this message to the player who activated the trigger when all needed keys have been given.
+message2: print this message to the player who activated the trigger when not all of the needed keys have been given.
+noise: sound to play when lock gets unlocked (default: see sounds)
+noise1: sound to play when only some of the needed key were used but not all (default: misc/decreasevalue.wav)
+noise2: sound to play when a key is missing (default: misc/talk.wav)
+wait: prevent triggering again for this amount of time (default: 5) - applies to target2, target3, target4.
+---------NOTES----------
+If spawned without any key specified in itemkeys, this trigger will display an error and remove itself.
+message2 and noise2 will be resent to the player every 2 seconds while he is in the trigger zone.
+*/
+void spawnfunc_trigger_keylock(void)
+{
+ if(!self.itemkeys) { remove(self); return; }
+
+ // set unlocked message
+ if(self.message == "")
+ self.message = "Unlocked!";
+
+ // set default unlock noise
+ if(self.noise == "")
+ {
+ if(self.sounds == 1)
+ self.noise = "misc/secret.wav";
+ else if(self.sounds == 2)
+ self.noise = "misc/talk.wav";
+ else //if (self.sounds == 3) {
+ self.noise = "misc/trigger1.wav";
+ }
+
+ // set default use key sound
+ if(self.noise1 == "")
+ self.noise1 = "misc/decreasevalue.wav";
+
+ // set closed sourd
+ if(self.noise2 == "")
+ self.noise2 = "misc/talk.wav";
+
+ // delay between triggering message2 and trigger2
+ if(!self.wait) { self.wait = 5; }
+
+ // precache sounds
+ precache_sound(self.noise);
+ precache_sound(self.noise1);
+ precache_sound(self.noise2);
+
+ EXACTTRIGGER_INIT;
+
+ self.touch = trigger_keylock_touch;
+
+ trigger_keylock_link();
+}
+#elif defined(CSQC)
+void keylock_remove()
+{
+ if(self.target) { strunzone(self.target); }
+ self.target = string_null;
+
+ if(self.target2) { strunzone(self.target2); }
+ self.target2 = string_null;
+
+ if(self.target3) { strunzone(self.target3); }
+ self.target3 = string_null;
+
+ if(self.target4) { strunzone(self.target4); }
+ self.target4 = string_null;
+
+ if(self.killtarget) { strunzone(self.killtarget); }
+ self.killtarget = string_null;
+
+ if(self.targetname) { strunzone(self.targetname); }
+ self.targetname = string_null;
+}
+
+void ent_keylock()
+{
+ self.target = strzone(ReadString());
+ self.target2 = strzone(ReadString());
+ self.target3 = strzone(ReadString());
+ self.target4 = strzone(ReadString());
+ self.killtarget = strzone(ReadString());
+ self.targetname = strzone(ReadString());
+
+ self.itemkeys = ReadInt24_t();
+ self.warpzone_isboxy = ReadByte();
+ self.height = ReadByte();
+ self.scale = ReadByte();
+
+ self.origin_x = ReadCoord();
+ self.origin_y = ReadCoord();
+ self.origin_z = ReadCoord();
+
+ self.mins_x = ReadCoord();
+ self.mins_y = ReadCoord();
+ self.mins_z = ReadCoord();
+ self.maxs_x = ReadCoord();
+ self.maxs_y = ReadCoord();
+ self.maxs_z = ReadCoord();
+
+ self.movedir_x = ReadCoord();
+ self.movedir_y = ReadCoord();
+ self.movedir_z = ReadCoord();
+
+ self.classname = "trigger_keylock";
+ self.drawmask = MASK_NORMAL;
+ self.draw = trigger_draw_generic;
+ self.trigger_touch = trigger_keylock_touch;
+ self.entremove = keylock_remove;
+}
+#endif
--- /dev/null
+#ifdef CSQC
+void ent_keylock();
+bool item_keys_usekey(entity l, entity p)
+{
+ float valid = l.itemkeys & p.itemkeys;
+
+ if (!valid) {
+ // other has none of the needed keys
+ return false;
+ } else if (l.itemkeys == valid) {
+ // ALL needed keys were given
+ l.itemkeys = 0;
+ return true;
+ } else {
+ // only some of the needed keys were given
+ l.itemkeys &= ~valid;
+ return true;
+ }
+}
+#endif
strunzone(title);
}
-string GetMapname();
void CampaignPostInit()
{
// now some sanity checks
Net_LinkEntity(e, false, 0.2, Damage_DamageInfo_SendEntity);
}
-float IsFlying(entity a)
-{
- if(a.flags & FL_ONGROUND)
- return 0;
- if(a.waterlevel >= WATERLEVEL_SWIMMING)
- return 0;
- traceline(a.origin, a.origin - '0 0 48', MOVE_NORMAL, a);
- if(trace_fraction < 1)
- return 0;
- return 1;
-}
-
void UpdateFrags(entity player, float f)
{
PlayerTeamScore_AddScore(player, f);
- should keys have a trigger?
*/
-bool item_keys_usekey(entity l, entity p) {
+bool item_keys_usekey(entity l, entity p)
+{
float valid = l.itemkeys & p.itemkeys;
if (!valid) {
self.itemkeys = ITEM_KEY_BIT(0);
spawnfunc_item_key();
};
-
-
-/*
-================================
-trigger_keylock
-================================
-*/
-
-/**
- * trigger givent targets
- */
-void trigger_keylock_trigger(string s) {
- entity stemp = self;
- entity otemp = other;
- entity atemp = activator;
-
- entity t;
- for(t = world; (t = find(t, targetname, s)); )
- if (t.use) {
- self = t;
- other = stemp;
- activator = atemp;
- self.use();
- }
-
- self = stemp;
- other = otemp;
- activator = atemp;
-};
-
-/**
- * kill killtarget of trigger keylock.
- */
-void trigger_keylock_kill(string s) {
- entity t;
- for(t = world; (t = find(t, targetname, s)); )
- remove(t);
-};
-
-void trigger_keylock_touch(void) {
- bool key_used = false;
- bool started_delay = false;
-
- // only player may trigger the lock
- if (!IS_PLAYER(other))
- return;
-
-
- // check silver key
- if (self.itemkeys)
- key_used = item_keys_usekey(self, other);
-
- activator = other;
-
- if (self.itemkeys) {
- // at least one of the keys is missing
- if (key_used) {
- // one or more keys were given, but others are still missing!
- play2(other, self.noise1);
- Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_ALSONEED, item_keys_keylist(self.itemkeys));
- other.key_door_messagetime = time + 2;
- } else if (other.key_door_messagetime <= time) {
- // no keys were given
- play2(other, self.noise2);
- Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(self.itemkeys));
- other.key_door_messagetime = time + 2;
- }
-
- // trigger target2
- if (self.delay <= time || started_delay == true)
- if (self.target2) {
- trigger_keylock_trigger(self.target2);
- started_delay = true;
- self.delay = time + self.wait;
- }
- } else {
- // all keys were given!
- play2(other, self.noise);
- centerprint(other, self.message);
-
- if (self.target)
- trigger_keylock_trigger(self.target);
-
- if (self.killtarget)
- trigger_keylock_kill(self.killtarget);
-
- remove(self);
- }
-
-};
-
-/*QUAKED trigger_keylock (.0 .5 .8) ?
-Keylock trigger. Must target other entities.
-This trigger will trigger target entities when all required keys are provided.
--------- KEYS --------
-itemkeys: A bit field with key IDs that are needed to open this lock.
-sounds: 1 to play misc/secret.wav, 2 to play misc/talk.wav, 3 to play misc/trigger1.wav (3 is default)
-target: trigger all entities with this targetname when triggered and all keys have been given to it, then remove this trigger
-target2: trigger all entities with this targetname when triggered without giving it all the required keys.
-killtarget: remove all entities with this targetname when triggered with all the needed keys.
-message: print this message to the player who activated the trigger when all needed keys have been given.
-message2: print this message to the player who activated the trigger when not all of the needed keys have been given.
-noise: sound to play when lock gets unlocked (default: see sounds)
-noise1: sound to play when only some of the needed key were used but not all (default: misc/decreasevalue.wav)
-noise2: sound to play when a key is missing (default: misc/talk.wav)
-wait: prevent triggering again for this amount of time (default: 5) - applies to target2, target3, target4.
----------NOTES----------
-If spawned without any key specified in itemkeys, this trigger will display an error and remove itself.
-message2 and noise2 will be resent to the player every 2 seconds while he is in the trigger zone.
-*/
-void spawnfunc_trigger_keylock(void) {
- if (!self.itemkeys) {
- remove(self);
- return;
- }
-
- // set unlocked message
- if (self.message == "")
- self.message = "Unlocked!";
-
- // set default unlock noise
- if (self.noise == "") {
- if (self.sounds == 1)
- self.noise = "misc/secret.wav";
- else if (self.sounds == 2)
- self.noise = "misc/talk.wav";
- else //if (self.sounds == 3) {
- self.noise = "misc/trigger1.wav";
- }
-
- // set default use key sound
- if (self.noise1 == "")
- self.noise1 = "misc/decreasevalue.wav";
-
- // set closed sourd
- if (self.noise2 == "")
- self.noise2 = "misc/talk.wav";
-
- // delay between triggering message2 and trigger2
- if (!self.wait)
- self.wait = 5;
-
- // precache sounds
- precache_sound(self.noise);
- precache_sound(self.noise1);
- precache_sound(self.noise2);
-
- EXACTTRIGGER_INIT;
-
- self.touch = trigger_keylock_touch;
-}