/* * Copyright (c) 2016-2022 Vera Visions LLC. * * 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) ? FUNCVEH_NOPITCH FUNCVEH_NOUSER x FUNCVEH_PASSABLE FUNCVEH_FWDRIVE FUNCVEH_RWDRIVE Primitive brush-based vehicle entity. -------- KEYS -------- "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 -------- SPAWNFLAGS -------- FUNCVEH_NOPITCH : Don't adjust the pitch angle of the vehicle, only point forward. FUNCVEH_NOUSER : Don't allow pressing "use" key/button to control it. FUNCVEH_PASSABLE : Don't do collision testing against this entity. FUNCVEH_FWDRIVE : Front wheel drive mode. FUNCVEH_RWDRIVE : Rear wheel drive mode. -------- NOTES -------- 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. -------- TRIVIA -------- This entity was introduced in Counter-Strike (2000). */ enumflags { FUNCVEH_NOPITCH, FUNCVEH_NOUSER, FUNCVEH_UNUSED, FUNCVEH_PASSABLE, FUNCVEH_FWDRIVE, FUNCVEH_RWDRIVE }; enumflags { FNCVEHNET_DRIVER, FNCVEHNET_MODELINDEX, FNCVEHNET_ORIGIN, FNCVEHNET_ANGLES, FNCVEHNET_VELOCITY, FNCVEHNET_TURNING, FNCVEHNET_SOLIDMOVETYPE, FNCVEHNET_FLAGS }; class func_vehicle_wheel { void() func_vehicle_wheel; #ifdef CLIENT vector origin_net; vector velocity_net; vector angles_net; virtual void(void) PredictPreFrame; virtual void(void) PredictPostFrame; #endif virtual void(float) Move; virtual void(vector) Bounce; virtual void(float, float m_flTurn) Accel; virtual void(float, float) Physics; }; class func_vehicle:NSVehicle { /* 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; float m_flUseTime; float m_flTimeLength; vector m_vecSeatOffest; 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; PREDICTED_FLOAT(m_flTurn) void(void) func_vehicle; virtual void(void) Spawned; virtual void(void) Physics; virtual void(void) RunVehiclePhysics; virtual void(void) PlayerInput; virtual void(void) OnRemoveEntity; #ifdef CLIENT virtual void(void) PredictPreFrame; virtual void(void) PredictPostFrame; virtual void(float, float) ReceiveEntity; virtual void(void) UpdateView; #else virtual void(float) Save; virtual void(string, string) Restore; virtual void(string, string) SpawnKey; virtual void(void) Respawn; virtual void(void) Realign; virtual void(void) OnPlayerUse; virtual void(void) EvaluateEntity; virtual float(entity, float) SendEntity; #endif }; void func_vehicle_wheel::func_vehicle_wheel(void) { mins = [-8,-8,-8]; maxs = [8,8,8]; hitcontentsmaski = CONTENTBIT_BODY | CONTENTBITS_POINTSOLID | CONTENTBIT_VEHICLECLIP; } #ifdef CLIENT void func_vehicle_wheel::PredictPreFrame(void) { SAVE_STATE(angles); SAVE_STATE(origin); SAVE_STATE(velocity); } void func_vehicle_wheel::PredictPostFrame(void) { ROLL_BACK(angles); ROLL_BACK(origin); ROLL_BACK(velocity); } #endif 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(float flTimeLength) { vector vecDest; vector vecSavedNormal; float flStepped; float flMovetime; int i; /* have a few attempts */ for (i = 3, flMovetime = flTimeLength; 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) { 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_vecMoveValues[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 || (vehParent.m_iMoveButtons & INPUT_BUTTON2 || vehParent.m_vecMoveValues[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, float flTimeLength) { 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(flTimeLength / 2, turnrate); Move(flTimeLength); Accel(flTimeLength / 2, turnrate); } 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]; m_iVehicleFlags |= VHF_FROZEN; hitcontentsmaski = CONTENTBIT_BODY | CONTENTBITS_POINTSOLID | CONTENTBIT_VEHICLECLIP; m_flWidth = 40; m_flLength = 85; m_flHeight = 32; if (!m_wlFL) m_wlFL = spawn(func_vehicle_wheel); if (!m_wlFR) m_wlFR = spawn(func_vehicle_wheel); if (!m_wlBL) m_wlBL = spawn(func_vehicle_wheel); if (!m_wlBR) m_wlBR = spawn(func_vehicle_wheel); m_wlFL.owner = m_wlFR.owner = m_wlBL.owner = m_wlBR.owner = this; customphysics = Physics; } #ifdef CLIENT void func_vehicle::UpdateView(void) { if (GetDriver() != __NULL__) { PlayerAlign(); } } void func_vehicle::PredictPreFrame(void) { SAVE_STATE(modelindex); SAVE_STATE(origin); SAVE_STATE(angles); SAVE_STATE(velocity); SAVE_STATE(m_flTurn); SAVE_STATE(flags); SAVE_STATE(driver_entnum); m_wlFL.PredictPreFrame(); m_wlFR.PredictPreFrame(); m_wlBL.PredictPreFrame(); m_wlBR.PredictPreFrame(); } void func_vehicle::PredictPostFrame(void) { ROLL_BACK(modelindex); ROLL_BACK(angles); ROLL_BACK(origin); ROLL_BACK(velocity); ROLL_BACK(m_flTurn); ROLL_BACK(flags); ROLL_BACK(driver_entnum); m_wlFL.PredictPostFrame(); m_wlFR.PredictPostFrame(); m_wlBL.PredictPostFrame(); m_wlBR.PredictPostFrame(); } #else void func_vehicle::Save(float handle) { super::Save(handle); SaveFloat(handle, "m_flBounceFactor", m_flBounceFactor); SaveFloat(handle, "m_flAcceleration", m_flAcceleration); SaveFloat(handle, "m_flSkidSpeed", m_flSkidSpeed); SaveFloat(handle, "m_flTraction", m_flTraction); SaveFloat(handle, "m_flBreakFactor", m_flBreakFactor); SaveFloat(handle, "m_flSteerFactor", m_flSteerFactor); SaveFloat(handle, "m_flStraightenFactor", m_flStraightenFactor); SaveVector(handle, "m_vecGravityDir", m_vecGravityDir); SaveEntity(handle, "m_wlFL", m_wlFL); SaveEntity(handle, "m_wlFR", m_wlFR); SaveEntity(handle, "m_wlBL", m_wlBL); SaveEntity(handle, "m_wlBR", m_wlBR); SaveVector(handle, "m_vecControlMins", m_vecControlMins); SaveVector(handle, "m_vecControlMaxs", m_vecControlMaxs); SaveFloat(handle, "m_flHeight", m_flHeight); SaveFloat(handle, "m_flWidth", m_flWidth); SaveFloat(handle, "m_flLength", m_flLength); SaveFloat(handle, "m_flTurn", m_flTurn); } void func_vehicle::Restore(string strKey, string strValue) { switch (strKey) { case "m_flBounceFactor": m_flBounceFactor = ReadFloat(strValue); break; case "m_flAcceleration": m_flAcceleration = ReadFloat(strValue); break; case "m_flSkidSpeed": m_flSkidSpeed = ReadFloat(strValue); break; case "m_flTraction": m_flTraction = ReadFloat(strValue); break; case "m_flBreakFactor": m_flBreakFactor = ReadFloat(strValue); break; case "m_flSteerFactor": m_flSteerFactor = ReadFloat(strValue); break; case "m_flStraightenFactor": m_flStraightenFactor = ReadFloat(strValue); break; case "m_vecGravityDir": m_vecGravityDir = ReadVector(strValue); break; case "m_wlFL": m_wlFL = (func_vehicle_wheel)ReadEntity(strValue); break; case "m_wlFR": m_wlFR = (func_vehicle_wheel)ReadEntity(strValue); break; case "m_wlBL": m_wlBL = (func_vehicle_wheel)ReadEntity(strValue); break; case "m_wlBR": m_wlBR = (func_vehicle_wheel)ReadEntity(strValue); break; case "m_vecControlMins": m_vecControlMins = ReadVector(strValue); break; case "m_vecControlMaxs": m_vecControlMaxs = ReadVector(strValue); break; case "m_flHeight": m_flHeight = ReadFloat(strValue); break; case "m_flWidth": m_flWidth = ReadFloat(strValue); break; case "m_flLength": m_flLength = ReadFloat(strValue); break; case "m_flTurn": m_flTurn = ReadFloat(strValue); break; default: super::Restore(strKey, strValue); } } void func_vehicle::SpawnKey(string strKey, string strValue) { switch (strKey) { case "acceleration": // TODO break; case "speed": m_flAcceleration = stof(strValue); break; case "height": m_flHeight = stof(strValue); break; case "width": m_flWidth = stof(strValue) / 2; break; case "length": m_flLength = stof(strValue) / 2; break; case "bouncefactor": m_flBounceFactor = stof(strValue); break; case "skidspeed": m_flSkidSpeed = stof(strValue); break; case "traction": m_flTraction = stof(strValue); break; case "breakfactor": m_flBreakFactor = stof(strValue); break; case "steerfactor": m_flSteerFactor = stof(strValue); break; case "straightenfactor": m_flStraightenFactor = stof(strValue); break; case "gravitydir": m_vecGravityDir = stov(strValue); break; case "sounds": // TODO break; case "volume": // TODO break; case "dmg": // TODO break; default: super::SpawnKey(strKey, strValue); } } void func_vehicle::Respawn(void) { SetMovetype(MOVETYPE_PUSH); SetSolid(SOLID_BSP); SetModel(GetSpawnModel()); SetOrigin(GetSpawnOrigin()); SetAngles([0,0,0]); ScheduleThink(Realign, 0.0f); m_wlFL.velocity = m_wlFR.velocity = m_wlBL.velocity = m_wlBR.velocity = [0.0f, 0.0f, 0.0f]; ClearVelocity(); PlayerUse = OnPlayerUse; if (m_eDriver) PlayerLeave((NSClientPlayer)m_eDriver); } void func_vehicle::OnPlayerUse(void) { vector matrix; vector offs; offs = eActivator.origin - origin; if (m_flUseTime > time) return; 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((NSClientPlayer)eActivator); } else if (!m_eDriver) { PlayerEnter((NSClientPlayer)eActivator); } m_flUseTime = time + 2.0f; } void func_vehicle::Realign(void) { entity t; entity f; NSEntity first, second; string strFirst, strSecond; first = second = __NULL__; t = f = __NULL__; for (f = world; (f = find(f, ::target, targetname));) { /* 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 = target; for (f = world; (f = find(f, ::targetname, strFirst));) { /* we found the right entity */ if (f.classname == "path_track" || f.classname == "path_corner") { first = (NSEntity)f; } } /* now get the second one... */ strSecond = first.target; for (f = world; (f = find(f, ::targetname, strSecond));) { /* we found the right entity */ if (f.classname == "path_track" || f.classname == "path_corner") { second = (NSEntity)f; } } if (first && second) { vector end_pos; first = (NSEntity)first; second = (NSEntity)second; NSLog("func_vehicle angles were: %v\n", angles); angles = vectoangles(first.origin - second.origin); NSLog("func_vehicle angles is now: %v\n", angles); end_pos = first.origin; end_pos[2] = m_oldOrigin[2] + 64; setorigin(this, end_pos); 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); } } #endif void func_vehicle::Spawned(void) { super::Spawned(); } void func_vehicle::OnRemoveEntity(void) { if (m_wlFL) remove(m_wlFL); if (m_wlFR) remove(m_wlFR); if (m_wlBL) remove(m_wlBL); if (m_wlBR) remove(m_wlBR); } void func_vehicle::Physics(void) { #ifdef CLIENT DriverRelink(); #endif /* if nobody is in the car, we need to run physics here * with fake input frames */ if (GetDriver() == __NULL__) { #ifdef SERVER m_flTimeLength = frametime; m_vecMoveValues = [0,0,0]; m_iMoveButtons = 0; RunVehiclePhysics(); #else setorigin(this, origin); #endif } else { //crossprint(sprintf("Driver: %s\n", GetDriver().classname)); } HandleThink(); } void func_vehicle::PlayerInput(void) { m_vecMoveValues = input_movevalues; m_iMoveButtons = input_buttons; m_flTimeLength = input_timelength; /* prediction frame... */ RunVehiclePhysics(); #ifdef SERVER /* allow us to exit */ if (m_flUseTime < time) { if (input_buttons & INPUT_BUTTON5) { eActivator = m_eDriver; OnPlayerUse(); input_buttons &= ~INPUT_BUTTON5; } } #endif //WeaponInput(); /* only allow use key */ input_buttons = (input_buttons & INPUT_BUTTON5); input_movevalues = [0,0,0]; } void func_vehicle::RunVehiclePhysics(void) { #ifdef SERVER /* eject the dead */ if (m_eDriver && m_eDriver.health <= 0) { PlayerLeave((NSClientPlayer)m_eDriver); } #endif if (m_eDriver) { float y; y = m_vecMoveValues[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 * m_flTimeLength, 1); } } else { /* straighten wheels forward over time */ if (m_flTurn < 0) { m_flTurn = min(0, m_flTurn + m_flTimeLength * m_flStraightenFactor); } else if (m_flTurn > 0) { m_flTurn = max(0, m_flTurn - m_flTimeLength * m_flStraightenFactor); } } PlayerUpdateFlags(); /* we want to null our drivers' velocity because they're sticking to us */ m_eDriver.velocity = [0,0,0]; } angles[0] = Math_FixDelta(angles[0]); angles[1] = Math_FixDelta(angles[1]); angles[2] = Math_FixDelta(angles[2]); angles[0] = bound (-45, angles[0], 45); angles[2] = bound (-15, angles[2], 15); velocity[0] = bound(-1000, velocity[0], 1000); velocity[1] = bound(-1000, velocity[1], 1000); velocity[2] = bound(-1000, velocity[2], 1000); 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); 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; if (HasSpawnFlags(FUNCVEH_FWDRIVE)) { m_wlFL.Physics(0, m_flTimeLength); m_wlFR.Physics(0, m_flTimeLength); m_wlBL.Physics(m_flTurn, m_flTimeLength); m_wlBR.Physics(m_flTurn, m_flTimeLength); } else if (HasSpawnFlags(FUNCVEH_RWDRIVE)) { m_wlFL.Physics(-m_flTurn, m_flTimeLength); m_wlFR.Physics(-m_flTurn, m_flTimeLength); m_wlBL.Physics(0, m_flTimeLength); m_wlBR.Physics(0, m_flTimeLength); } else { m_wlFL.Physics(-m_flTurn, m_flTimeLength); m_wlFR.Physics(-m_flTurn, m_flTimeLength); m_wlBL.Physics(m_flTurn, m_flTimeLength); m_wlBR.Physics(m_flTurn, m_flTimeLength); } 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); angles[0] = Math_FixDelta(angles[0]); angles[1] = Math_FixDelta(angles[1]); angles[2] = Math_FixDelta(angles[2]); /* 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(new_origin); PlayerAlign(); } #ifdef CLIENT void func_vehicle::ReceiveEntity(float flChanged, float flNew) { if (flChanged & FNCVEHNET_DRIVER) { driver_entnum = readentitynum(); DriverRelink(); } READENTITY_COORD(m_vecPlayerPos[0], FNCVEHNET_DRIVER) READENTITY_COORD(m_vecPlayerPos[1], FNCVEHNET_DRIVER) READENTITY_COORD(m_vecPlayerPos[2], FNCVEHNET_DRIVER) READENTITY_SHORT(modelindex, FNCVEHNET_MODELINDEX) READENTITY_FLOAT(m_vecSeatOffest[0], FNCVEHNET_MODELINDEX) READENTITY_FLOAT(m_vecSeatOffest[1], FNCVEHNET_MODELINDEX) READENTITY_FLOAT(m_vecSeatOffest[2], FNCVEHNET_MODELINDEX) READENTITY_FLOAT(m_flWidth, FNCVEHNET_MODELINDEX) READENTITY_FLOAT(m_flLength, FNCVEHNET_MODELINDEX) READENTITY_COORD(origin[0], FNCVEHNET_ORIGIN) READENTITY_COORD(origin[1], FNCVEHNET_ORIGIN) READENTITY_COORD(origin[2], FNCVEHNET_ORIGIN) READENTITY_ANGLE(angles[0], FNCVEHNET_ANGLES) READENTITY_ANGLE(angles[1], FNCVEHNET_ANGLES) READENTITY_ANGLE(angles[2], FNCVEHNET_ANGLES) READENTITY_COORD(velocity[0], FNCVEHNET_VELOCITY) READENTITY_COORD(velocity[1], FNCVEHNET_VELOCITY) READENTITY_COORD(velocity[2], FNCVEHNET_VELOCITY) READENTITY_INT(flags, FNCVEHNET_FLAGS) READENTITY_BYTE(solid, FNCVEHNET_SOLIDMOVETYPE) READENTITY_BYTE(movetype, FNCVEHNET_SOLIDMOVETYPE) READENTITY_INT(flags, FNCVEHNET_SOLIDMOVETYPE) READENTITY_COORD(mins[0], FNCVEHNET_SOLIDMOVETYPE) READENTITY_COORD(mins[1], FNCVEHNET_SOLIDMOVETYPE) READENTITY_COORD(mins[2], FNCVEHNET_SOLIDMOVETYPE) READENTITY_COORD(maxs[0], FNCVEHNET_SOLIDMOVETYPE) READENTITY_COORD(maxs[1], FNCVEHNET_SOLIDMOVETYPE) READENTITY_COORD(maxs[2], FNCVEHNET_SOLIDMOVETYPE) if (flChanged & FNCVEHNET_SOLIDMOVETYPE) setsize(this, mins, maxs); if (flChanged & FNCVEHNET_DRIVER) { DriverRelink(); } if (flChanged & FNCVEHNET_MODELINDEX) { //setsize( this, [-50,-50,0], [50,50,64]); } if (flChanged & FNCVEHNET_ORIGIN) { setorigin(this, origin); 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 (flNew) { drawmask = MASK_ENGINE; SetMovetype(MOVETYPE_NONE); SetSolid(SOLID_BSP); m_wlFL.velocity = m_wlFR.velocity = m_wlBL.velocity = m_wlBR.velocity = velocity = [0,0,0]; RunVehiclePhysics(); customphysics = Physics; } PredictPreFrame(); } #else void func_vehicle::EvaluateEntity(void) { EVALUATE_FIELD(m_eDriver, FNCVEHNET_DRIVER) EVALUATE_FIELD(modelindex, FNCVEHNET_MODELINDEX) EVALUATE_FIELD(origin, FNCVEHNET_ORIGIN) EVALUATE_FIELD(angles, FNCVEHNET_ANGLES) EVALUATE_FIELD(velocity, FNCVEHNET_VELOCITY) EVALUATE_FIELD(m_flTurn, FNCVEHNET_TURNING) EVALUATE_FIELD(flags, FNCVEHNET_FLAGS) EVALUATE_FIELD(solid, FNCVEHNET_SOLIDMOVETYPE) EVALUATE_FIELD(movetype, FNCVEHNET_SOLIDMOVETYPE) EVALUATE_FIELD(mins, FNCVEHNET_SOLIDMOVETYPE) EVALUATE_FIELD(maxs, FNCVEHNET_SOLIDMOVETYPE) } float func_vehicle::SendEntity(entity ePEnt, float flChanged) { if (!modelindex) return (0); if (clienttype(ePEnt) != CLIENTTYPE_REAL) return (0); WriteByte(MSG_ENTITY, ENT_VEH_BRUSH); WriteFloat(MSG_ENTITY, flChanged); SENDENTITY_ENTITY(m_eDriver, FNCVEHNET_DRIVER) SENDENTITY_COORD(m_vecPlayerPos[0], FNCVEHNET_DRIVER) SENDENTITY_COORD(m_vecPlayerPos[1], FNCVEHNET_DRIVER) SENDENTITY_COORD(m_vecPlayerPos[2], FNCVEHNET_DRIVER) SENDENTITY_SHORT(modelindex, FNCVEHNET_MODELINDEX) SENDENTITY_FLOAT(m_vecSeatOffest[0], FNCVEHNET_MODELINDEX) SENDENTITY_FLOAT(m_vecSeatOffest[1], FNCVEHNET_MODELINDEX) SENDENTITY_FLOAT(m_vecSeatOffest[2], FNCVEHNET_MODELINDEX) SENDENTITY_FLOAT(m_flWidth, FNCVEHNET_MODELINDEX) SENDENTITY_FLOAT(m_flLength, FNCVEHNET_MODELINDEX) SENDENTITY_COORD(origin[0], FNCVEHNET_ORIGIN) SENDENTITY_COORD(origin[1], FNCVEHNET_ORIGIN) SENDENTITY_COORD(origin[2], FNCVEHNET_ORIGIN) SENDENTITY_ANGLE(angles[0], FNCVEHNET_ANGLES) SENDENTITY_ANGLE(angles[1], FNCVEHNET_ANGLES) SENDENTITY_ANGLE(angles[2], FNCVEHNET_ANGLES) SENDENTITY_COORD(velocity[0], FNCVEHNET_VELOCITY) SENDENTITY_COORD(velocity[1], FNCVEHNET_VELOCITY) SENDENTITY_COORD(velocity[2], FNCVEHNET_VELOCITY) SENDENTITY_INT(flags, FNCVEHNET_FLAGS) SENDENTITY_BYTE(solid, FNCVEHNET_SOLIDMOVETYPE) SENDENTITY_BYTE(movetype, FNCVEHNET_SOLIDMOVETYPE) SENDENTITY_INT(flags, FNCVEHNET_SOLIDMOVETYPE) SENDENTITY_COORD(mins[0], FNCVEHNET_SOLIDMOVETYPE) SENDENTITY_COORD(mins[1], FNCVEHNET_SOLIDMOVETYPE) SENDENTITY_COORD(mins[2], FNCVEHNET_SOLIDMOVETYPE) SENDENTITY_COORD(maxs[0], FNCVEHNET_SOLIDMOVETYPE) SENDENTITY_COORD(maxs[1], FNCVEHNET_SOLIDMOVETYPE) SENDENTITY_COORD(maxs[2], FNCVEHNET_SOLIDMOVETYPE) return true; } #endif #ifdef CLIENT void func_vehicle_readentity(float isnew) { func_vehicle veh = (func_vehicle)self; float flags = readfloat(); if (isnew) spawnfunc_func_vehicle(); veh.ReceiveEntity(flags, isnew); } #endif