/* * 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 prop_vehicle_driveable (0 0 1) (-50 -50 0) (50 50 70) Point entity defining a 4-wheel vehicle that you can drive. -------- KEYS -------- "targetname" : Name -------- SPAWNFLAGS -------- -------- NOTES -------- -------- TRIVIA -------- This entity was introduced in Half-Life 2 (2004). */ #define VEH_SKIDDING FL_USE_RELEASED #define VEHSF_NOFLIP 2048 enumflags { VEHFL_DRIVER, VEHFL_MODELINDEX, VEHFL_ORIGIN, VEHFL_ANGLES, VEHFL_VELOCITY, VEHFL_TURNING, VEHFL_FLAGS }; class prop_vehicle_driveable_wheel { void() prop_vehicle_driveable_wheel; float m_flSuspension; float m_flSuspensionForce; #ifdef CLIENT vector origin_net; vector velocity_net; vector angles_net; virtual void(void) PredictPreFrame; virtual void(void) PredictPostFrame; #endif virtual void(float) UpdateSuspension; virtual void(float) Move; virtual void(vector) Bounce; virtual void(float, float m_flTurn) Accel; virtual void(float, float) Physics; }; class prop_vehicle_driveable: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_flTimeLength; vector m_vecSeatOffest; /* collision boxes */ NSEntity m_eCollBox1; NSEntity m_eCollBox2; prop_vehicle_driveable_wheel m_wlFL; prop_vehicle_driveable_wheel m_wlFR; prop_vehicle_driveable_wheel m_wlBL; prop_vehicle_driveable_wheel m_wlBR; vector m_vecControlMins; vector m_vecControlMaxs; PREDICTED_FLOAT(m_flTurn); float m_flBRWheelAxel; float m_flBLWheelAxel; float m_flFLWheelAxel; float m_flFRWheelAxel; float m_flUseTime; void(void) prop_vehicle_driveable; virtual void(void) Spawned; virtual void(void) Physics; virtual void(void) RunVehiclePhysics; virtual void(void) WeaponInput; virtual void(void) PlayerInput; virtual void(void) OnRemoveEntity; #ifdef CLIENT virtual bool(void) HideViewWeapon; virtual void(void) PredictPreFrame; virtual void(void) PredictPostFrame; virtual void(float, float) ReceiveEntity; virtual void(void) UpdateView; #else virtual void(void) Respawn; virtual void(void) OnPlayerUse; virtual void(void) EvaluateEntity; virtual float(entity, float) SendEntity; #endif }; void prop_vehicle_driveable::prop_vehicle_driveable(void) { m_eDriver = __NULL__; m_flBounceFactor = 1.25f; m_flAcceleration = 600.0f; m_flSkidSpeed = 256.0f; m_flTraction = 5.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; if (!m_eCollBox1) m_eCollBox1 = spawn(NSEntity); if (!m_eCollBox2) m_eCollBox2 = spawn(NSEntity); { m_eCollBox1.SetSize([-32,-32,0], [32,32,32]); m_eCollBox2.SetSize([-32,-32,0], [32,32,32]); } m_eCollBox1.hitcontentsmaski = hitcontentsmaski; m_eCollBox2.hitcontentsmaski = hitcontentsmaski; if (!m_wlFL) m_wlFL = spawn(prop_vehicle_driveable_wheel); if (!m_wlFR) m_wlFR = spawn(prop_vehicle_driveable_wheel); if (!m_wlBL) m_wlBL = spawn(prop_vehicle_driveable_wheel); if (!m_wlBR) m_wlBR = spawn(prop_vehicle_driveable_wheel); m_eCollBox1.owner = m_eCollBox2.owner = \ m_wlFL.owner = m_wlFR.owner = \ m_wlBL.owner = m_wlBR.owner = this; customphysics = Physics; } #ifdef CLIENT bool prop_vehicle_driveable::HideViewWeapon(void) { return true; } #endif void prop_vehicle_driveable::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 prop_vehicle_driveable::OnRemoveEntity(void) { remove(m_wlFL); remove(m_wlFR); remove(m_wlBL); remove(m_wlBR); remove(m_eCollBox1); remove(m_eCollBox2); } #ifdef CLIENT void prop_vehicle_driveable::UpdateView(void) { vector vecStart, vecEnd; pSeat->m_vecPredictedOrigin = origin; makevectors(view_angles); vecStart = [pSeat->m_vecPredictedOrigin[0], pSeat->m_vecPredictedOrigin[1], pSeat->m_vecPredictedOrigin[2] + 16] + (v_right * 4); vecEnd = vecStart + (v_forward * -256) + [0,0,16] + (v_right * 4); other = world; traceline(vecStart, vecEnd, MOVE_OTHERONLY, this); g_view.SetCameraOrigin(trace_endpos + (v_forward * 16)); g_view.SetClientAngle(view_angles); } void prop_vehicle_driveable_wheel::PredictPreFrame(void) { SAVE_STATE(angles); SAVE_STATE(origin); SAVE_STATE(velocity); } void prop_vehicle_driveable_wheel::PredictPostFrame(void) { ROLL_BACK(angles); ROLL_BACK(origin); ROLL_BACK(velocity); } #endif void prop_vehicle_driveable_wheel::Bounce(vector normal) { prop_vehicle_driveable vehParent; vehParent = (prop_vehicle_driveable)owner; vector vecBounce = (velocity * normal) * normal * vehParent.m_flBounceFactor; velocity -= vecBounce; vehParent.velocity = velocity; #ifdef SERVER float flStregth = vlen((velocity * normal) * normal); if (flStregth > 96) { Sound_Play(vehParent, CHAN_VOICE, "prop_vehicle_driveable.bounce"); } #endif } void prop_vehicle_driveable_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] += 8; 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 prop_vehicle_driveable_wheel::Accel(float flMoveTime, float m_flTurn) { prop_vehicle_driveable vehParent; entity eDriver; float flTraction; vector vecAngle; vehParent = (prop_vehicle_driveable)owner; eDriver = vehParent.m_eDriver; vecAngle = vehParent.angles; makevectors(vecAngle); if (m_flTurn) { /* cripple turnrate by our current velocity */ float turnbreak = bound(0, vlen(velocity), 100) * 0.5; /* rotates v_forward */ rotatevectorsbyangle([ 0, m_flTurn * turnbreak, 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; } /* test if this car is skidding */ float skid = (velocity * v_right); if ( fabs(skid) > vehParent.m_flSkidSpeed ) { vehParent.flags |= VEH_SKIDDING; } /* 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; tracebox(origin, mins * 4.0, maxs * 4.0, origin, MOVE_NORMAL, owner); if (trace_ent && trace_ent != vehParent.m_eDriver) { int iImpactDamage = vlen(velocity) / 10; if (iImpactDamage > 10) { trace_ent.velocity = velocity * 2.0 + [0,0,500]; velocity *= 0.25f; #ifdef SERVER if (trace_ent.takedamage) { NSSurfacePropEntity foo = (NSSurfacePropEntity)trace_ent; Damage_Apply(foo, vehParent.m_eDriver, iImpactDamage, 0, DMG_VEHICLE); //print(sprintf("Delivering %i impact damage\n", iImpactDamage)); } #endif } } } void prop_vehicle_driveable_wheel::UpdateSuspension(float flTimeLength) { float flDamp; float flForce; if (fabs(m_flSuspension) > 0.001 || fabs(m_flSuspensionForce) > 0.001) { m_flSuspension += m_flSuspensionForce * flTimeLength; flForce = bound(0, flTimeLength * 65, 2); flDamp = 1 - (flTimeLength * 4); if (flDamp < 0) { flDamp = 0; } m_flSuspensionForce *= flDamp; m_flSuspensionForce -= m_flSuspension * flForce; m_flSuspension = bound(-15, m_flSuspension, 15); } } void prop_vehicle_driveable_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_NORMAL, owner); setorigin(this, trace_endpos); /* see if we're in-air */ other = world; tracebox(origin, mins, maxs, origin - [0,0,1], MOVE_OTHERONLY, this); if (!trace_startsolid) { if ((trace_fraction < 1) && (trace_plane_normal[2] > 0.7)) { flags |= FL_ONGROUND; } else { flags &= ~FL_ONGROUND; m_flSuspensionForce += flTimeLength * 200.0; } } Accel(flTimeLength / 2, turnrate); Move(flTimeLength); Accel(flTimeLength / 2, turnrate); UpdateSuspension(flTimeLength); //print(sprintf("suspension: %d, force: %d\n", m_flSuspension, m_flSuspensionForce)); } void prop_vehicle_driveable_wheel::prop_vehicle_driveable_wheel(void) { mins = [-8,-8,-35]; maxs = [8,8,8]; hitcontentsmaski = CONTENTBIT_BODY | CONTENTBITS_POINTSOLID | CONTENTBIT_VEHICLECLIP; } #ifdef CLIENT void prop_vehicle_driveable::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 prop_vehicle_driveable::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(); } #endif void prop_vehicle_driveable::WeaponInput(void) { } void prop_vehicle_driveable::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); } void prop_vehicle_driveable::RunVehiclePhysics(void) { #if 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(); } if (spawnflags & VEHSF_NOFLIP) { angles[0] = bound (-45, angles[0], 45); angles[2] = bound (-45, angles[2], 45); } flags &= ~VEH_SKIDDING; angles[0] = Math_FixDelta(angles[0]); angles[1] = Math_FixDelta(angles[1]); angles[2] = Math_FixDelta(angles[2]); 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_wlFL, origin ); setorigin( m_wlBL, m_wlFL.origin - v_forward * 85 ); setorigin( m_wlFL, m_wlFL.origin + v_forward * 85 ); setorigin( m_wlFR, m_wlFL.origin + v_right * 40 ); setorigin( m_wlFL, m_wlFL.origin - v_right * 40 ); setorigin( m_wlBR, m_wlBL.origin + v_right * 40 ); setorigin( m_wlBL, m_wlBL.origin - v_right * 40 ); m_wlFL.Physics( this.m_flTurn, m_flTimeLength); m_wlFR.Physics( this.m_flTurn, m_flTimeLength); m_wlBL.Physics( 0, m_flTimeLength); m_wlBR.Physics( 0, 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 ); /* 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); makevectors(angles); m_eCollBox1.SetOrigin(origin + (v_forward * 64)); m_eCollBox2.SetOrigin(origin + (v_forward * -72)); m_eCollBox1.SetSolid(SOLID_BBOX); m_eCollBox2.SetSolid(SOLID_BBOX); PlayerAlign(); /* actiony stuff */ } #ifdef SERVER void prop_vehicle_driveable::OnPlayerUse(void) { if (m_flUseTime > time) return; if (m_eDriver == eActivator) { PlayerLeave((NSClientPlayer)eActivator); setorigin(eActivator, GetExitPos()); } else if (!m_eDriver) { PlayerEnter((NSClientPlayer)eActivator); m_vecPlayerPos = [0,0,0]; } m_flUseTime = time + 2.0f; } void prop_vehicle_driveable::Respawn(void) { SetMovetype(MOVETYPE_NONE); SetSolid(SOLID_BBOX); SetOrigin(GetSpawnOrigin() + [0,0,32]); SetAngles(GetSpawnAngles()); SetModel(GetSpawnModel()); m_flBRWheelAxel = gettagindex( this, "RRWheelAxel" ); m_flBLWheelAxel = gettagindex( this, "RLWheelAxel" ); m_flFLWheelAxel = gettagindex( this, "FLWheelAxel" ); m_flFRWheelAxel = gettagindex( this, "FRWheelAxel" ); m_wlFL.velocity = m_wlFR.velocity = m_wlBL.velocity = m_wlBR.velocity = velocity = [0,0,0]; PlayerUse = OnPlayerUse; setsize( this, [-50,-50,0], [50,50,64]); if (m_eDriver) PlayerLeave((NSClientPlayer)m_eDriver); SendFlags = -1; } #endif #ifdef CLIENT void prop_vehicle_driveable::ReceiveEntity(float flSendFlags, float flNew) { if (flSendFlags & VEHFL_DRIVER) { driver_entnum = readentitynum(); DriverRelink(); } if (flSendFlags & VEHFL_MODELINDEX) { modelindex = readshort(); m_vecSeatOffest[0] = readfloat(); m_vecSeatOffest[1] = readfloat(); m_vecSeatOffest[2] = readfloat(); setsize( this, [-50,-50,0], [50,50,64]); } if (flSendFlags & VEHFL_ORIGIN) { origin[0] = readcoord(); origin[1] = readcoord(); origin[2] = readcoord(); setorigin(this, origin); makevectors(angles); setorigin( m_wlFL, origin ); setorigin( m_wlBL, m_wlFL.origin - v_forward * 85 ); setorigin( m_wlFL, m_wlFL.origin + v_forward * 85 ); setorigin( m_wlFR, m_wlFL.origin + v_right * 40 ); setorigin( m_wlFL, m_wlFL.origin - v_right * 40 ); setorigin( m_wlBR, m_wlBL.origin + v_right * 40 ); setorigin( m_wlBL, m_wlBL.origin - v_right * 40 ); m_eCollBox1.SetOrigin(origin + (v_forward * 64)); m_eCollBox2.SetOrigin(origin + (v_forward * -72)); } if (flSendFlags & VEHFL_ANGLES) { angles[0] = readfloat(); angles[1] = readfloat(); angles[2] = readfloat(); } if (flSendFlags & VEHFL_VELOCITY) { velocity[0] = readfloat(); velocity[1] = readfloat(); velocity[2] = readfloat(); } if (flSendFlags & VEHFL_TURNING) m_flTurn = readfloat(); if (flSendFlags & VEHFL_FLAGS) flags = readfloat(); if (flNew) { drawmask = MASK_ENGINE; SetMovetype(MOVETYPE_NONE); SetSolid(SOLID_BBOX); m_flBRWheelAxel = gettagindex( this, "RRWheelAxel" ); m_flBLWheelAxel = gettagindex( this, "RLWheelAxel" ); m_flFLWheelAxel = gettagindex( this, "FLWheelAxel" ); m_flFRWheelAxel = gettagindex( this, "FRWheelAxel" ); m_wlFL.velocity = m_wlFR.velocity = m_wlBL.velocity = m_wlBR.velocity = velocity = [0,0,0]; RunVehiclePhysics(); customphysics = Physics; } PredictPreFrame(); } #else void prop_vehicle_driveable::EvaluateEntity(void) { /* while the engine is still handling physics for these, we can't * predict when origin/angle might change */ if (ATTR_CHANGED(origin)) SetSendFlags(VEHFL_ORIGIN); if (ATTR_CHANGED(angles)) { angles[0] = Math_FixDelta(angles[0]); angles[1] = Math_FixDelta(angles[1]); angles[2] = Math_FixDelta(angles[2]); SetSendFlags(VEHFL_ANGLES); } if (ATTR_CHANGED(modelindex)) SetSendFlags(VEHFL_MODELINDEX); if (ATTR_CHANGED(velocity)) SetSendFlags(VEHFL_VELOCITY); if (ATTR_CHANGED(m_flTurn)) SetSendFlags(VEHFL_TURNING); if (ATTR_CHANGED(m_eDriver)) SetSendFlags(VEHFL_DRIVER); if (ATTR_CHANGED(flags)) SetSendFlags(VEHFL_FLAGS); SAVE_STATE(origin); SAVE_STATE(angles); SAVE_STATE(modelindex); SAVE_STATE(velocity); SAVE_STATE(m_flTurn); SAVE_STATE(m_eDriver); SAVE_STATE(flags); } float prop_vehicle_driveable::SendEntity(entity ePVSent, float flSendFlags) { WriteByte(MSG_ENTITY, ENT_VEH_4WHEEL); WriteFloat(MSG_ENTITY, flSendFlags); if (!modelindex) return (false); if (flSendFlags & VEHFL_DRIVER) { WriteEntity(MSG_ENTITY, m_eDriver); } if (flSendFlags & VEHFL_MODELINDEX) { WriteShort(MSG_ENTITY, modelindex); WriteFloat(MSG_ENTITY, m_vecSeatOffest[0]); WriteFloat(MSG_ENTITY, m_vecSeatOffest[1]); WriteFloat(MSG_ENTITY, m_vecSeatOffest[2]); } if (flSendFlags & VEHFL_ORIGIN) { WriteCoord(MSG_ENTITY, origin[0]); WriteCoord(MSG_ENTITY, origin[1]); WriteCoord(MSG_ENTITY, origin[2]); } if (flSendFlags & VEHFL_ANGLES) { WriteFloat(MSG_ENTITY, angles[0]); WriteFloat(MSG_ENTITY, angles[1]); WriteFloat(MSG_ENTITY, angles[2]); } if (flSendFlags & VEHFL_VELOCITY) { WriteFloat(MSG_ENTITY, velocity[0]); WriteFloat(MSG_ENTITY, velocity[1]); WriteFloat(MSG_ENTITY, velocity[2]); } if (flSendFlags & VEHFL_TURNING) WriteFloat(MSG_ENTITY, m_flTurn); if (flSendFlags & VEHFL_FLAGS) WriteFloat(MSG_ENTITY, flags); return true; } #endif void prop_vehicle_driveable::Spawned(void) { super::Spawned(); #ifdef SERVER Sound_Precache("prop_vehicle_driveable.bounce"); #endif } #ifdef CLIENT void prop_vehicle_driveable_readentity(float isnew) { prop_vehicle_driveable veh = (prop_vehicle_driveable)self; float flags = readfloat(); if (isnew) spawnfunc_prop_vehicle_driveable(); veh.ReceiveEntity(flags, isnew); } #endif