diff --git a/src/gs-entbase/server.src b/src/gs-entbase/server.src index c6989441..1757466e 100644 --- a/src/gs-entbase/server.src +++ b/src/gs-entbase/server.src @@ -11,6 +11,7 @@ server/basetrigger.cpp server/basemonster.cpp server/basenpc.cpp server/basephysics.cpp +server/basevehicle.cpp server/button_target.cpp server/ambient_generic.cpp server/cycler.cpp @@ -44,6 +45,8 @@ server/func_wall_toggle.cpp server/func_conveyor.cpp server/func_rotating.cpp server/func_pendulum.cpp +server/func_vehicle.cpp +server/func_vehiclecontrols.cpp server/light.cpp server/stubs.cpp server/infodecal.cpp diff --git a/src/gs-entbase/server/basevehicle.cpp b/src/gs-entbase/server/basevehicle.cpp new file mode 100644 index 00000000..4ea67636 --- /dev/null +++ b/src/gs-entbase/server/basevehicle.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +class CBaseVehicle:CBaseEntity +{ + entity m_eDriver; + + void(void) CBaseVehicle; + vector m_vecPlayerPos; + + virtual void(void) PlayerAlign; + virtual void(base_player) PlayerEnter; + virtual void(base_player) PlayerLeave; +}; + +void +CBaseVehicle::PlayerAlign(void) +{ + vector vecPlayerPos; + + if (!m_eDriver) + return; + + makevectors(angles); + vecPlayerPos = origin + v_forward * m_vecPlayerPos[0]; + vecPlayerPos += v_right * m_vecPlayerPos[1]; + vecPlayerPos += v_up * m_vecPlayerPos[2]; + + setorigin(m_eDriver, vecPlayerPos); +} + +void +CBaseVehicle::PlayerEnter(base_player pl) +{ + vector offs; + + /* cache the position */ + offs = pl.origin - origin; + + makevectors(angles); + m_vecPlayerPos[0] = dotproduct(offs, v_forward); + m_vecPlayerPos[1] = dotproduct(offs, v_right); + m_vecPlayerPos[2] = dotproduct(offs, v_up); + pl.movetype = MOVETYPE_NOCLIP; + m_eDriver = (entity)pl; +} + +void +CBaseVehicle::PlayerLeave(base_player pl) +{ + pl.movetype = MOVETYPE_WALK; + m_eDriver = __NULL__; +} + +void +CBaseVehicle::CBaseVehicle(void) +{ + CBaseEntity::CBaseEntity(); +} diff --git a/src/gs-entbase/server/func_vehicle.cpp b/src/gs-entbase/server/func_vehicle.cpp new file mode 100644 index 00000000..56c598cc --- /dev/null +++ b/src/gs-entbase/server/func_vehicle.cpp @@ -0,0 +1,566 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*QUAKED func_vehicle (0 .5 .8) ? +"targetname" Name +"target" Name of the first path_track/corner (See Notes) +"acceleration" Acceleration multiplier +"speed" Top-speed of the vehicle in q-units per second +"height" Wheel-height in units +"width" Width of the vehicle, used to calculate the wheels +"length" Length of the vehicle, used to calculate the wheels +"bouncefactor" Multiplier for the bouncyness of the vehicle +"skidspeed" At which speed the vehicle starts skidding +"traction" Multiplier for the traction affecting the vehicle +"breakfactor" Multiplier for the breaking mechanics +"steerfactor" Multiplier for the steering speed +"straightenfactor" Multiplier for affecting the straightening mechanism +"gravitydir" Normalized vector setting the direction of gravity + +Unimplemented: +"sounds" A sound-set to use +"volume" Volume at which said sounds play at (from 0-10) +"dmg" Damage inflicted upon entities when blocked + +Primitive brush-based vehicle entity. + +The vehicle's position is set via the 'target' key, which sets the first +path_track/corner. The vehicle is then teleported to the 'target' but +it stays at the same vertical position as originally placed. + +The angle is calculated by aiming the 'target' path_track/corner entity +to its own 'target' entity. So yes, you need two of these path_track/corner +entities. +*/ + +enumflags +{ + FUNCVEH_NOPITCH, + FUNCVEH_NOUSER, + FUNCVEH_UNUSED, + FUNCVEH_PASSABLE, + FUNCVEH_FWDRIVE, + FUNCVEH_RWDRIVE +}; + +class func_vehicle_wheel +{ + void() func_vehicle_wheel; + + virtual void(void) Move; + virtual void(vector) Bounce; + virtual void(float, float m_flTurn) Accel; + virtual void(float) Physics; +}; + +class func_vehicle:CBaseVehicle +{ + /* map-entity fields */ + float m_flBounceFactor; + float m_flAcceleration; + float m_flSkidSpeed; + float m_flTraction; + float m_flBreakFactor; + float m_flSteerFactor; + float m_flStraightenFactor; + vector m_vecGravityDir; + + func_vehicle_wheel m_wlFL; + func_vehicle_wheel m_wlFR; + func_vehicle_wheel m_wlBL; + func_vehicle_wheel m_wlBR; + vector m_vecControlMins; + vector m_vecControlMaxs; + float m_flHeight; + float m_flWidth; + float m_flLength; + float m_flTurn; + + void(void) func_vehicle; + virtual void(void) customphysics; + virtual void(void) Respawn; + virtual void(void) Realign; + virtual void(void) PlayerUse; +}; + +void +func_vehicle_wheel::Bounce(vector normal) +{ + func_vehicle vehParent; + vehParent = (func_vehicle)owner; + velocity -= (velocity * normal) * normal * vehParent.m_flBounceFactor; +} + +void +func_vehicle_wheel::Move(void) +{ + vector vecDest; + vector vecSavedNormal; + float flStepped; + float flMovetime; + int i; + + /* have a few attempts */ + for (i = 3, flMovetime = frametime; flMovetime > 0 && i; i--) { + vecDest = origin + (velocity * flMovetime); + tracebox(origin, mins, maxs, vecDest, MOVE_NOMONSTERS, this); + + if (trace_startsolid) { + continue; + } + + origin = trace_endpos; + + if (trace_fraction < 1) { + vecSavedNormal = trace_plane_normal; + flMovetime -= flMovetime * trace_fraction; + + if (flMovetime) { + float roof_fraction; + vector roof_plane_normal; + + /* step up if we can */ + trace_endpos = origin; + trace_endpos[2] += 4; + + tracebox(origin, mins, maxs, trace_endpos, MOVE_NOMONSTERS, this); + flStepped = trace_endpos[2] - origin[2]; + + roof_fraction = trace_fraction; + roof_plane_normal = trace_plane_normal; + + vecDest = trace_endpos + velocity * flMovetime; + + /* only horizontally */ + vecDest[2] = trace_endpos[2]; + + /* move forwards */ + tracebox(trace_endpos, mins, maxs, vecDest, MOVE_NOMONSTERS, this); + + /* if we got anywhere, make this raised-step move count */ + if (trace_fraction != 0) { + float fwfrac; + vector fwplane; + + fwfrac = trace_fraction; + fwplane = trace_plane_normal; + + /* move down */ + vecDest = trace_endpos; + vecDest[2] -= flStepped + 1; + tracebox(trace_endpos, mins, maxs, vecDest, MOVE_NOMONSTERS, this); + + if (trace_fraction < 1 && trace_plane_normal[2] > 0.7f) { + flMovetime -= flMovetime * fwfrac; + + if (roof_fraction < 1) { + Bounce(roof_plane_normal); + } + + /* FIXME: do we need velocity < 0? */ + if (trace_fraction < 1 && self.velocity[2] < 0) { + Bounce(trace_plane_normal); + } else if (fwfrac < 1) { + Bounce(fwplane); + } + + origin = trace_endpos; + continue; + } + } + } + + /* stepping failed, assume crash? */ + if (trace_ent == world) { + if (vlen(velocity) > 300) { + float impact; + impact = -dotproduct(trace_plane_normal, velocity); + int iImpactDamage = impact / 100; + } + } + + Bounce(vecSavedNormal); + /* Physics_DoTouch(this, trace_ent); */ + } else { + break; + } + } +} + +void +func_vehicle_wheel::Accel(float flMoveTime, float m_flTurn) +{ + func_vehicle vehParent; + entity eDriver; + float flTraction; + vector vecAngle; + + vehParent = (func_vehicle)owner; + eDriver = vehParent.m_eDriver; + vecAngle = vehParent.angles; + + makevectors(vecAngle); + + if (m_flTurn) { + /* rotates v_forward */ + rotatevectorsbyangle([ 0, m_flTurn * 50, 0]); + } + + tracebox(origin, mins, maxs, origin - v_up, MOVE_NOMONSTERS, owner); + + /* allow a range, for 1qu's worth of spare tyre pressure. or something */ + flTraction = 1 - trace_fraction; + + /* air friction, doubles up for general rolling friction, ish */ + velocity *= 1 - flMoveTime * 0.1; + + if (flTraction) { + if (eDriver) { + velocity -= v_forward * bound(-1, vehParent.m_eDriver.movement[0] / 400, 1) * vehParent.m_flAcceleration * flMoveTime * flTraction; + } + + /* nuke sideways velocity. if a wheel is off the ground this probably + means that it'll be pushed further. players should try not to roll + too much. */ + /* FIXME: push opposite wheel up slightly to model chassis momentum + not slowing as much as the wheel itself (zomg: race conditions!) */ + velocity -= (velocity * v_right) * v_right * vehParent.m_flTraction * flMoveTime * flTraction; + + if (eDriver.button2 || eDriver.movement[2] > 0) { + vector t; + + /* empty cars are assumed to have their brakes on. + nuke forward velocity. if a wheel is off the ground this probably + means that it'll be pushed further. players should try not to + roll too much. + + Note: really we ought to be applying some axel friction even + when not breaking, but we'll just depend on air friction for + that. */ + velocity -= (velocity * v_forward) * v_forward * vehParent.m_flBreakFactor * flMoveTime * flTraction; + + /* if break is on, nuke the final part of the velocity, so we can + become truely motionless.*/ + t = velocity - velocity * dotproduct(velocity, vehParent.m_vecGravityDir); + if (vlen(t) < 15) { + velocity -= t; + } + + /* don't bother with gravity if we're already on the ground and + breaking. this avoids weird slides. */ + if (!trace_fraction && trace_plane_normal * vehParent.m_vecGravityDir < -0.7f) { + return; + } + } + } + + /* apply gravity */ + velocity += vehParent.m_vecGravityDir * flMoveTime * serverkeyfloat("phy_gravity") * trace_fraction; +} + +void +func_vehicle_wheel::Physics(float turnrate) +{ + vector owner_pos; + + /* try to correct the wheel's position, in case it got stuck */ + owner_pos = owner.origin + (owner.mins + owner.maxs) * 0.5f; + tracebox(owner_pos, mins, maxs, origin, MOVE_NOMONSTERS, owner); + setorigin(this, trace_endpos); + + Accel(frametime * 0.5f, turnrate); + Move(); + Accel(frametime * 0.5f, turnrate); +} + +void +func_vehicle_wheel::func_vehicle_wheel(void) +{ + hitcontentsmaski = CONTENTBIT_SOLID | CONTENTBIT_BODY; + mins = '-8 -8 -8'; + maxs = '8 8 8'; +} + +void +func_vehicle::customphysics(void) +{ + /* eject the dead */ + if (m_eDriver && m_eDriver.health <= 0) { + PlayerLeave((base_player)m_eDriver); + } + + if (m_eDriver) { + float y; + + y = m_eDriver.movement[1]; + y = bound(-200, y, 200) / 200; + y *= m_flSteerFactor; + + if (y) { + if (y < 0 && m_flTurn < 0) { + m_flTurn = 0.0f; + } else if (y > 0 && m_flTurn > 0) { + m_flTurn = 0.0f; + } else { + m_flTurn = bound(-1, m_flTurn - y * frametime, 1); + } + } else { + /* straighten wheels forward over time */ + if (m_flTurn < 0) { + m_flTurn = min(0, m_flTurn + frametime * m_flStraightenFactor); + } else if (m_flTurn > 0) { + m_flTurn = max(0, m_flTurn - frametime * m_flStraightenFactor); + } + } + m_eDriver.flags |= FL_FROZEN; + } + + angles[0] = Math_FixDelta(angles[0]); + angles[1] = Math_FixDelta(angles[1]); + angles[2] = Math_FixDelta(angles[2]); + makevectors(angles); + + setorigin(m_wlFR, origin + v_right * m_flWidth + v_forward * m_flLength); + setorigin(m_wlFL, origin - v_right * m_flWidth + v_forward * m_flLength); + setorigin(m_wlBR, origin + v_right * m_flWidth - v_forward * m_flLength); + setorigin(m_wlBL, origin - v_right * m_flWidth - v_forward * m_flLength); + + if (spawnflags & FUNCVEH_FWDRIVE) { + m_wlFL.Physics(0); + m_wlFR.Physics(0); + m_wlBL.Physics(m_flTurn); + m_wlBR.Physics(m_flTurn); + } else if (spawnflags & FUNCVEH_RWDRIVE) { + m_wlFL.Physics(-m_flTurn); + m_wlFR.Physics(-m_flTurn); + m_wlBL.Physics(0); + m_wlBR.Physics(0); + } else { + m_wlFL.Physics(-m_flTurn); + m_wlFR.Physics(-m_flTurn); + m_wlBL.Physics(m_flTurn); + m_wlBR.Physics(m_flTurn); + } + + velocity = m_wlFL.velocity; + velocity += m_wlFR.velocity; + velocity += m_wlBL.velocity; + velocity += m_wlBR.velocity; + velocity *= 0.25f; + + v_right = (m_wlFR.origin - m_wlFL.origin); + v_right += (m_wlBR.origin - m_wlBL.origin); + v_forward = (m_wlFL.origin + m_wlFR.origin); + v_forward -= (m_wlBL.origin + m_wlBR.origin); + v_up = -crossproduct(v_forward, v_right); + angles = vectoangles(v_forward, v_up); + + /* figure out the new chassis position */ + vector new_origin; + new_origin = m_wlFL.origin; + new_origin += m_wlFR.origin; + new_origin += m_wlBL.origin; + new_origin += m_wlBR.origin; + new_origin *= 0.25f; + setorigin(this, new_origin); + PlayerAlign(); + + /* support for think/nextthink */ + if (think && nextthink > 0.0f) { + if (nextthink < time) { + nextthink = 0.0f; + think(); + } + } +} + +void +func_vehicle::PlayerUse(void) +{ + vector matrix; + vector offs; + offs = eActivator.origin - origin; + + makevectors(angles); + matrix[0] = dotproduct(offs, v_forward); + matrix[1] = -dotproduct(offs, v_right); + matrix[2] = dotproduct(offs, v_up); + + if not (matrix[0] >= m_vecControlMins[0] && matrix[0] <= m_vecControlMaxs[0]) + return; + if not (matrix[1] >= m_vecControlMins[1] && matrix[1] <= m_vecControlMaxs[1]) + return; + if not (matrix[2] >= m_vecControlMins[2] && matrix[2] <= m_vecControlMaxs[2]) + return; + + if (m_eDriver == eActivator) { + PlayerLeave((base_player)eActivator); + } else if (!m_eDriver) { + PlayerEnter((base_player)eActivator); + } +} + +void +func_vehicle::Realign(void) +{ + entity t; + entity f; + CBaseEntity first, second; + string strFirst, strSecond; + + first = second = t = f = __NULL__; + + for (f = world; (f = find(f, CBaseEntity::m_strTarget, m_strTargetName));) { + /* we found the right entity */ + if (f.classname == "func_vehiclecontrols") { + t = f; + } + } + + if (t) { + vector offs; + offs = t.origin - origin; + m_vecControlMins = t.mins + offs; + m_vecControlMaxs = t.maxs + offs; + } + + /* we rotate and position ourselves after the first path_track/corner */ + strFirst = m_strTarget; + for (f = world; (f = find(f, CBaseEntity::m_strTargetName, strFirst));) { + /* we found the right entity */ + if (f.classname == "path_track" || f.classname == "path_corner") { + first = (CBaseEntity)f; + } + } + + /* now get the second one... */ + strSecond = first.m_strTarget; + for (f = world; (f = find(f, CBaseEntity::m_strTargetName, strSecond));) { + /* we found the right entity */ + if (f.classname == "path_track" || f.classname == "path_corner") { + second = (CBaseEntity)f; + } + } + + if (t) { + vector end_pos; + first = (CBaseEntity)first; + second = (CBaseEntity)second; + angles = vectoangles(first.origin - second.origin); + + end_pos = first.origin; + end_pos[2] = m_oldOrigin[2]; + setorigin(this, end_pos); + } +} + +void +func_vehicle::Respawn(void) +{ + movetype = MOVETYPE_PUSH; + solid = SOLID_BSP; + SetModel(m_oldModel); + SetOrigin(m_oldOrigin); + owner = m_eDriver = __NULL__; + think = Realign; + nextthink = time + 0.1f; + + m_wlFL.velocity = + m_wlFR.velocity = + m_wlBL.velocity = + m_wlBR.velocity = + velocity = [0,0,0]; +} + +void +func_vehicle::func_vehicle(void) +{ + m_flBounceFactor = 1.0f; + m_flAcceleration = 200.0f; + m_flSkidSpeed = 256.0f; + m_flTraction = 1.0f; + m_flBreakFactor = 2.0f; + m_flSteerFactor = 1.0f; + m_flStraightenFactor = 1.0f; + m_vecGravityDir = [0,0,-1]; + + for (int i = 1; i < (tokenize(__fullspawndata) - 1); i += 2) { + switch (argv(i)) { + case "acceleration": + // TODO + break; + case "speed": + m_flAcceleration = stof(argv(i+1)); + break; + case "height": + m_flHeight = stof(argv(i+1)); + break; + case "width": + m_flWidth = stof(argv(i+1)) / 2; + break; + case "length": + m_flLength = stof(argv(i+1)) / 2; + break; + case "bouncefactor": + m_flBounceFactor = stof(argv(i+1)); + break; + case "skidspeed": + m_flSkidSpeed = stof(argv(i+1)); + break; + case "traction": + m_flTraction = stof(argv(i+1)); + break; + case "breakfactor": + m_flBreakFactor = stof(argv(i+1)); + break; + case "steerfactor": + m_flSteerFactor = stof(argv(i+1)); + break; + case "straightenfactor": + m_flStraightenFactor = stof(argv(i+1)); + break; + case "gravitydir": + m_vecGravityDir = stov(argv(i+1)); + break; + case "sounds": + // TODO + break; + case "volume": + // TODO + break; + case "dmg": + // TODO + break; + default: + break; + } + } + + m_wlFL = spawn(func_vehicle_wheel); + m_wlFR = spawn(func_vehicle_wheel); + m_wlBL = spawn(func_vehicle_wheel); + m_wlBR = spawn(func_vehicle_wheel); + m_wlFL.owner = m_wlFR.owner = m_wlBL.owner = m_wlBR.owner = this; + + if (m_flHeight) { + m_wlFL.mins[2] = m_flHeight * -1; + m_wlFR.mins[2] = m_flHeight * -1; + m_wlBL.mins[2] = m_flHeight * -1; + m_wlBR.mins[2] = m_flHeight * -1; + } + + CBaseVehicle::CBaseVehicle(); +} diff --git a/src/gs-entbase/server/func_vehiclecontrols.cpp b/src/gs-entbase/server/func_vehiclecontrols.cpp new file mode 100644 index 00000000..58ad4e3a --- /dev/null +++ b/src/gs-entbase/server/func_vehiclecontrols.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*QUAKED func_vehiclecontrols (0 .5 .8) ? +"targetname" Name +"target" Name of the func_vehicle to control + +Brush that marks the usable region of a func_vehicle, in order +to gain control. +*/ + +class func_vehiclecontrols:CBaseTrigger +{ + void(void) func_vehiclecontrols; +}; + +void +func_vehiclecontrols::func_vehiclecontrols(void) +{ + precache_model(model); + SetModel(model); + SetOrigin(origin); + SetRenderMode(RM_TRIGGER); + SetSolid(SOLID_NOT); + CBaseTrigger::CBaseTrigger(); +}