From 0a6815bcec864407078389d25da5afa9a51d3e32 Mon Sep 17 00:00:00 2001 From: Mario Date: Thu, 19 Feb 2015 15:19:19 +1100 Subject: [PATCH] A few trillion lines of untested junk, committing now so it doesn't become quadrillions --- qcsrc/client/laser.qc | 134 ------- qcsrc/client/main.qc | 3 +- qcsrc/client/movetypes.qc | 376 +++++++++++++++++-- qcsrc/client/movetypes.qh | 4 + qcsrc/client/particles.qc | 213 ----------- qcsrc/client/particles.qh | 13 - qcsrc/client/progs.src | 2 - qcsrc/client/target_music.qc | 183 --------- qcsrc/client/target_music.qh | 22 -- qcsrc/common/constants.qh | 2 + qcsrc/common/physics.qc | 218 ++++++++--- qcsrc/common/physics.qh | 6 + qcsrc/common/triggers/func/conveyor.qc | 2 +- qcsrc/common/triggers/func/include.qh | 2 + qcsrc/common/triggers/func/plat.qc | 6 +- qcsrc/common/triggers/func/pointparticles.qc | 182 +++++++++ qcsrc/common/triggers/func/pointparticles.qh | 5 + qcsrc/common/triggers/func/rainsnow.qc | 36 ++ qcsrc/common/triggers/func/rainsnow.qh | 3 + qcsrc/common/triggers/func/stardust.qc | 2 + qcsrc/common/triggers/misc/corner.qc | 73 +++- qcsrc/common/triggers/misc/corner.qh | 3 + qcsrc/common/triggers/misc/follow.qc | 2 + qcsrc/common/triggers/misc/include.qh | 3 +- qcsrc/common/triggers/misc/laser.qc | 135 +++++++ qcsrc/common/triggers/misc/laser.qh | 3 + qcsrc/common/triggers/platforms.qc | 7 +- qcsrc/common/triggers/target/include.qh | 2 +- qcsrc/common/triggers/target/music.qc | 184 +++++++++ qcsrc/common/triggers/target/music.qh | 21 ++ qcsrc/common/triggers/trigger/include.qc | 1 + qcsrc/common/triggers/trigger/include.qh | 1 + qcsrc/common/triggers/trigger/keylock.qc | 256 +++++++++++++ qcsrc/common/triggers/trigger/keylock.qh | 20 + qcsrc/server/campaign.qc | 1 - qcsrc/server/g_damage.qc | 12 - qcsrc/server/item_key.qc | 154 +------- 37 files changed, 1463 insertions(+), 829 deletions(-) delete mode 100644 qcsrc/client/laser.qc delete mode 100644 qcsrc/client/target_music.qc delete mode 100644 qcsrc/client/target_music.qh create mode 100644 qcsrc/common/triggers/func/pointparticles.qh create mode 100644 qcsrc/common/triggers/func/rainsnow.qh create mode 100644 qcsrc/common/triggers/misc/corner.qh create mode 100644 qcsrc/common/triggers/misc/laser.qh create mode 100644 qcsrc/common/triggers/trigger/keylock.qc create mode 100644 qcsrc/common/triggers/trigger/keylock.qh diff --git a/qcsrc/client/laser.qc b/qcsrc/client/laser.qc deleted file mode 100644 index a74c6683a..000000000 --- a/qcsrc/client/laser.qc +++ /dev/null @@ -1,134 +0,0 @@ -#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; -} diff --git a/qcsrc/client/main.qc b/qcsrc/client/main.qc index 3903cb427..afca059b1 100644 --- a/qcsrc/client/main.qc +++ b/qcsrc/client/main.qc @@ -3,7 +3,6 @@ #include "particles.qh" #include "scoreboard.qh" #include "shownames.qh" -#include "target_music.qh" #include "tturrets.qh" #include "tuba.qh" #include "wall.qh" @@ -854,6 +853,8 @@ void CSQC_Ent_Update(float bIsNewEntity) 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)); diff --git a/qcsrc/client/movetypes.qc b/qcsrc/client/movetypes.qc index 92fb64ab7..19a5d41e8 100644 --- a/qcsrc/client/movetypes.qc +++ b/qcsrc/client/movetypes.qc @@ -24,7 +24,7 @@ void _Movetype_CheckVelocity() // SV_CheckVelocity 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); @@ -44,11 +44,11 @@ float _Movetype_CheckWater(entity ent) // SV_CheckWater { 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; } @@ -170,21 +170,21 @@ void _Movetype_LinkEdict(float touch_triggers) // SV_LinkEdict 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; @@ -242,9 +242,9 @@ vector _Movetype_ClipVelocity(vector vel, vector norm, float f) // SV_ClipVeloci { 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; } @@ -284,12 +284,342 @@ float _Movetype_PushEntity(vector push, float failonstartsolid) // SV_PushEntity 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; @@ -367,7 +697,7 @@ void _Movetype_Physics_Toss(float dt) // SV_Physics_Toss 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; @@ -380,7 +710,7 @@ void _Movetype_Physics_Toss(float dt) // SV_Physics_Toss 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; @@ -422,7 +752,7 @@ void _Movetype_Physics_Frame(float movedt) { case MOVETYPE_PUSH: case MOVETYPE_FAKEPUSH: - error("SV_Physics_Pusher not implemented"); + _Movetype_Physics_Pusher(movedt); break; case MOVETYPE_NONE: break; diff --git a/qcsrc/client/movetypes.qh b/qcsrc/client/movetypes.qh index f20977146..b4dea1de1 100644 --- a/qcsrc/client/movetypes.qh +++ b/qcsrc/client/movetypes.qh @@ -23,6 +23,10 @@ void Movetype_Physics_MatchTicrate(float tr, bool sloppy); 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; diff --git a/qcsrc/client/particles.qc b/qcsrc/client/particles.qc index 6d2ddf87d..59b9b75f4 100644 --- a/qcsrc/client/particles.qc +++ b/qcsrc/client/particles.qc @@ -1,218 +1,5 @@ #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; diff --git a/qcsrc/client/particles.qh b/qcsrc/client/particles.qh index 12c999ba2..a70aef86d 100644 --- a/qcsrc/client/particles.qh +++ b/qcsrc/client/particles.qh @@ -1,6 +1,5 @@ #ifndef PARTICLES_H #define PARTICLES_H - .int dphitcontentsmask; .int cnt; // effect number @@ -14,19 +13,7 @@ .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 diff --git a/qcsrc/client/progs.src b/qcsrc/client/progs.src index bd6d82f80..098fbbdd5 100644 --- a/qcsrc/client/progs.src +++ b/qcsrc/client/progs.src @@ -13,7 +13,6 @@ gibs.qc hook.qc hud_config.qc hud.qc -laser.qc main.qc mapvoting.qc miscfunctions.qc @@ -27,7 +26,6 @@ rubble.qc scoreboard.qc shownames.qc sortlist.qc -target_music.qc teamradar.qc tturrets.qc tuba.qc diff --git a/qcsrc/client/target_music.qc b/qcsrc/client/target_music.qc deleted file mode 100644 index 662a673d7..000000000 --- a/qcsrc/client/target_music.qc +++ /dev/null @@ -1,183 +0,0 @@ -#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; -} diff --git a/qcsrc/client/target_music.qh b/qcsrc/client/target_music.qh deleted file mode 100644 index c685214be..000000000 --- a/qcsrc/client/target_music.qh +++ /dev/null @@ -1,22 +0,0 @@ -#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 diff --git a/qcsrc/common/constants.qh b/qcsrc/common/constants.qh index 7f594be06..47d153c67 100644 --- a/qcsrc/common/constants.qh +++ b/qcsrc/common/constants.qh @@ -112,6 +112,8 @@ const int ENT_CLIENT_DOOR_TRIGGER = 66; 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; diff --git a/qcsrc/common/physics.qc b/qcsrc/common/physics.qc index e77184750..730cfc40a 100644 --- a/qcsrc/common/physics.qc +++ b/qcsrc/common/physics.qc @@ -4,6 +4,8 @@ #ifdef SVQC +#include "../server/miscfunctions.qh" + void Physics_AddStats() { // g_movementspeed hack @@ -117,7 +119,7 @@ void PM_ClientMovement_Unstick() tracebox(neworigin, PL_CROUCH_MIN, PL_CROUCH_MAX, neworigin, MOVE_NORMAL, self); if (!trace_startsolid) { - self.origin = neworigin; + setorigin(self, neworigin); return;// true; } } @@ -188,6 +190,106 @@ void PM_ClientMovement_UpdateStatus() 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 + // 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 @@ -238,7 +340,7 @@ void PM_ClientMovement_Move() // 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) @@ -260,6 +362,7 @@ void PM_ClientMovement_Move() self.velocity = primalvelocity; #endif } +#endif void CPM_PM_Aircontrol(vector wishdir, float wishspeed) { @@ -278,7 +381,7 @@ 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); @@ -986,6 +1089,33 @@ void PM_check_frozen(void) } } +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 @@ -999,12 +1129,11 @@ void PM_check_blocked(void) #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) { @@ -1411,7 +1540,7 @@ void SV_WalkMove () // 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 @@ -1446,7 +1575,7 @@ void SV_WalkMove () { //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; @@ -1483,7 +1612,7 @@ void SV_WalkMove () // 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; @@ -1648,14 +1777,16 @@ void PM_air(float buttons_prev, float maxspd_mod) } // 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() @@ -1816,48 +1947,20 @@ 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) @@ -1888,13 +1991,11 @@ void PM_Main() 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)) @@ -1907,14 +2008,10 @@ void PM_Main() 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; } @@ -1926,4 +2023,9 @@ void CSQC_ClientMovement_PlayerMove_Frame(void) #endif { PM_Main(); + +#ifdef CSQC + pmove_org = self.origin; + pmove_vel = self.velocity; +#endif } diff --git a/qcsrc/common/physics.qh b/qcsrc/common/physics.qh index c256a9eb1..c779d116c 100644 --- a/qcsrc/common/physics.qh +++ b/qcsrc/common/physics.qh @@ -22,6 +22,8 @@ .float() PlayerPhysplug; float AdjustAirAccelQW(float accelqw, float factor); +bool IsFlying(entity a); + #ifdef CSQC float PM_multijump_checkjump(); @@ -82,6 +84,8 @@ float AdjustAirAccelQW(float accelqw, float factor); #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) @@ -200,6 +204,8 @@ float AdjustAirAccelQW(float accelqw, float factor); #define ITEMS(s) s.items + #define FLAGS(s) (s).flags + #define PHYS_AMMO_FUEL(s) s.ammo_fuel #define PHYS_FROZEN(s) s.frozen diff --git a/qcsrc/common/triggers/func/conveyor.qc b/qcsrc/common/triggers/func/conveyor.qc index 05f0fa093..6ed01b0c5 100644 --- a/qcsrc/common/triggers/func/conveyor.qc +++ b/qcsrc/common/triggers/func/conveyor.qc @@ -70,7 +70,7 @@ void conveyor_reset() 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); diff --git a/qcsrc/common/triggers/func/include.qh b/qcsrc/common/triggers/func/include.qh index cca6f91df..e49a9671e 100644 --- a/qcsrc/common/triggers/func/include.qh +++ b/qcsrc/common/triggers/func/include.qh @@ -5,5 +5,7 @@ #include "door.qh" #include "ladder.qh" #include "plat.qh" +#include "rainsnow.qh" +#include "pointparticles.qh" #endif diff --git a/qcsrc/common/triggers/func/plat.qc b/qcsrc/common/triggers/func/plat.qc index b55669831..ce4e3e2e4 100644 --- a/qcsrc/common/triggers/func/plat.qc +++ b/qcsrc/common/triggers/func/plat.qc @@ -147,11 +147,7 @@ void spawnfunc_func_plat() #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() diff --git a/qcsrc/common/triggers/func/pointparticles.qc b/qcsrc/common/triggers/func/pointparticles.qc index 896bd796b..773ce6a51 100644 --- a/qcsrc/common/triggers/func/pointparticles.qc +++ b/qcsrc/common/triggers/func/pointparticles.qc @@ -1,3 +1,7 @@ +#ifdef CSQC + #include "../../../client/particles.qh" +#endif + #ifdef SVQC // NOTE: also contains func_sparks @@ -177,4 +181,182 @@ void spawnfunc_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 diff --git a/qcsrc/common/triggers/func/pointparticles.qh b/qcsrc/common/triggers/func/pointparticles.qh new file mode 100644 index 000000000..d446e7225 --- /dev/null +++ b/qcsrc/common/triggers/func/pointparticles.qh @@ -0,0 +1,5 @@ +#ifdef CSQC + +void Ent_PointParticles(); + +#endif \ No newline at end of file diff --git a/qcsrc/common/triggers/func/rainsnow.qc b/qcsrc/common/triggers/func/rainsnow.qc index e86b15682..deb5ee58b 100644 --- a/qcsrc/common/triggers/func/rainsnow.qc +++ b/qcsrc/common/triggers/func/rainsnow.qc @@ -89,4 +89,40 @@ void spawnfunc_func_snow() 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 diff --git a/qcsrc/common/triggers/func/rainsnow.qh b/qcsrc/common/triggers/func/rainsnow.qh new file mode 100644 index 000000000..5d8d92350 --- /dev/null +++ b/qcsrc/common/triggers/func/rainsnow.qh @@ -0,0 +1,3 @@ +#ifdef CSQC +void Ent_RainOrSnow(); +#endif diff --git a/qcsrc/common/triggers/func/stardust.qc b/qcsrc/common/triggers/func/stardust.qc index e8043fea7..30c406f61 100644 --- a/qcsrc/common/triggers/func/stardust.qc +++ b/qcsrc/common/triggers/func/stardust.qc @@ -2,5 +2,7 @@ void spawnfunc_func_stardust() { self.effects = EF_STARDUST; + + CSQCMODEL_AUTOINIT(); } #endif diff --git a/qcsrc/common/triggers/misc/corner.qc b/qcsrc/common/triggers/misc/corner.qc index 82fe6bbaf..5b2d8fcff 100644 --- a/qcsrc/common/triggers/misc/corner.qc +++ b/qcsrc/common/triggers/misc/corner.qc @@ -1,9 +1,78 @@ #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 diff --git a/qcsrc/common/triggers/misc/corner.qh b/qcsrc/common/triggers/misc/corner.qh new file mode 100644 index 000000000..62b3aaed7 --- /dev/null +++ b/qcsrc/common/triggers/misc/corner.qh @@ -0,0 +1,3 @@ +#ifdef CSQC +void ent_corner(); +#endif diff --git a/qcsrc/common/triggers/misc/follow.qc b/qcsrc/common/triggers/misc/follow.qc index aace4531e..bfeb865c1 100644 --- a/qcsrc/common/triggers/misc/follow.qc +++ b/qcsrc/common/triggers/misc/follow.qc @@ -1,3 +1,5 @@ +// the way this entity works makes it no use to CSQC, as it removes itself instantly + #ifdef SVQC void follow_init() { diff --git a/qcsrc/common/triggers/misc/include.qh b/qcsrc/common/triggers/misc/include.qh index 6f43e2a89..71b220521 100644 --- a/qcsrc/common/triggers/misc/include.qh +++ b/qcsrc/common/triggers/misc/include.qh @@ -1,6 +1,7 @@ #ifndef TRIGGERS_MISC_INCLUDE_H #define TRIGGERS_MISC_INCLUDE_H -// nothing yet +#include "corner.qh" +#include "laser.qh" #endif diff --git a/qcsrc/common/triggers/misc/laser.qc b/qcsrc/common/triggers/misc/laser.qc index 2aaf399f5..c67eb3207 100644 --- a/qcsrc/common/triggers/misc/laser.qc +++ b/qcsrc/common/triggers/misc/laser.qc @@ -1,3 +1,13 @@ +#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() @@ -254,4 +264,129 @@ void spawnfunc_misc_laser() 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 diff --git a/qcsrc/common/triggers/misc/laser.qh b/qcsrc/common/triggers/misc/laser.qh new file mode 100644 index 000000000..a77c4c58e --- /dev/null +++ b/qcsrc/common/triggers/misc/laser.qh @@ -0,0 +1,3 @@ +#ifdef CSQC +void Ent_Laser(); +#endif diff --git a/qcsrc/common/triggers/platforms.qc b/qcsrc/common/triggers/platforms.qc index 0176fe9b0..c5f50c46e 100644 --- a/qcsrc/common/triggers/platforms.qc +++ b/qcsrc/common/triggers/platforms.qc @@ -236,14 +236,12 @@ void plat_reset() #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 @@ -266,4 +264,3 @@ float set_platmovetype(entity e, string s) return true; } -#endif diff --git a/qcsrc/common/triggers/target/include.qh b/qcsrc/common/triggers/target/include.qh index cadbf3746..4e44b9762 100644 --- a/qcsrc/common/triggers/target/include.qh +++ b/qcsrc/common/triggers/target/include.qh @@ -1,6 +1,6 @@ #ifndef TRIGGERS_TARGET_INCLUDE_H #define TRIGGERS_TARGET_INCLUDE_H -// nothing yet +#include "music.qh" #endif diff --git a/qcsrc/common/triggers/target/music.qc b/qcsrc/common/triggers/target/music.qc index fb38dad0b..136288bdc 100644 --- a/qcsrc/common/triggers/target/music.qc +++ b/qcsrc/common/triggers/target/music.qc @@ -143,4 +143,188 @@ void spawnfunc_trigger_music() 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 diff --git a/qcsrc/common/triggers/target/music.qh b/qcsrc/common/triggers/target/music.qh index 400e4b8e5..8d014c819 100644 --- a/qcsrc/common/triggers/target/music.qh +++ b/qcsrc/common/triggers/target/music.qh @@ -3,4 +3,25 @@ .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 diff --git a/qcsrc/common/triggers/trigger/include.qc b/qcsrc/common/triggers/trigger/include.qc index a71de8990..2a92f7349 100644 --- a/qcsrc/common/triggers/trigger/include.qc +++ b/qcsrc/common/triggers/trigger/include.qc @@ -10,6 +10,7 @@ #include "hurt.qc" #include "impulse.qc" #include "jumppads.qc" +#include "keylock.qc" #include "magicear.qc" #include "monoflop.qc" #include "multi.qc" diff --git a/qcsrc/common/triggers/trigger/include.qh b/qcsrc/common/triggers/trigger/include.qh index 6f4825b13..5c862b950 100644 --- a/qcsrc/common/triggers/trigger/include.qh +++ b/qcsrc/common/triggers/trigger/include.qh @@ -5,5 +5,6 @@ #include "jumppads.qh" #include "secret.qh" #include "swamp.qh" +#include "keylock.qh" #endif diff --git a/qcsrc/common/triggers/trigger/keylock.qc b/qcsrc/common/triggers/trigger/keylock.qc new file mode 100644 index 000000000..729b83a08 --- /dev/null +++ b/qcsrc/common/triggers/trigger/keylock.qc @@ -0,0 +1,256 @@ +/** + * 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 diff --git a/qcsrc/common/triggers/trigger/keylock.qh b/qcsrc/common/triggers/trigger/keylock.qh new file mode 100644 index 000000000..b21145d42 --- /dev/null +++ b/qcsrc/common/triggers/trigger/keylock.qh @@ -0,0 +1,20 @@ +#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 diff --git a/qcsrc/server/campaign.qc b/qcsrc/server/campaign.qc index 2c091d56e..650855989 100644 --- a/qcsrc/server/campaign.qc +++ b/qcsrc/server/campaign.qc @@ -121,7 +121,6 @@ void CampaignPreInit() strunzone(title); } -string GetMapname(); void CampaignPostInit() { // now some sanity checks diff --git a/qcsrc/server/g_damage.qc b/qcsrc/server/g_damage.qc index d170cf4c0..cb62b61aa 100644 --- a/qcsrc/server/g_damage.qc +++ b/qcsrc/server/g_damage.qc @@ -66,18 +66,6 @@ void Damage_DamageInfo(vector org, float coredamage, float edgedamage, float rad 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); diff --git a/qcsrc/server/item_key.qc b/qcsrc/server/item_key.qc index 85159e49c..a130b826c 100644 --- a/qcsrc/server/item_key.qc +++ b/qcsrc/server/item_key.qc @@ -21,7 +21,8 @@ TODO: - 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) { @@ -282,154 +283,3 @@ void spawnfunc_item_key2(void) { 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; -} -- 2.39.2