Well, I'd hoped to polish the code a bit more than this before posting it; but it's been a busy week, and I don't want to risk getting sucked into making it *absolutely* perfect. Plus, I'm still on r561, and don't want to fall too far behind
![Razz :P](./images/smilies/icon_razz.gif)
So, heads up, massive code blocks ahead!
I've added a new entity type, called EntVehicleT; which I based on the CompanyBot entity. Here's my Vehicle.hpp:
Code: Select all
#ifndef CAFU_VEHICLE_HPP_INCLUDED
#define CAFU_VEHICLE_HPP_INCLUDED
#include "../../BaseEntity.hpp"
#include "Libs/Physics.hpp"
#include "Models/AnimExpr.hpp"
#include "btBulletDynamicsCommon.h"
#include "HumanPlayer.hpp"
#include "../../PlayerCommand.hpp"
class CafuModelT;
class EntityCreateParamsT;
class EntHumanPlayerT; //This and HumanPlayer refer to each other; not sure yet which should have the forward declaration...
class EntVehicleT : public BaseEntityT, public btMotionState
{
public:
EntVehicleT(const EntityCreateParamsT& Params);
~EntVehicleT();
void SetHeading(unsigned short h) { m_Heading = h; }
// Implement the BaseEntityT interface.
void TakeDamage(BaseEntityT* Entity, char Amount, const VectorT& ImpactDir);
void Think(float FrameTime, unsigned long ServerFrameNr);
bool GetLightSourceInfo(unsigned long& DiffuseColor, unsigned long& SpecularColor, VectorT& Position, float& Radius, bool& CastsShadows) const;
void Draw(bool FirstPersonView, float LodDist) const;
void PostDraw(float FrameTime, bool FirstPersonView);
// Implement the btMotionState interface.
void getWorldTransform(btTransform& worldTrans) const;
void setWorldTransform(const btTransform& worldTrans);
const cf::TypeSys::TypeInfoT* GetType() const;
static void* CreateInstance(const cf::TypeSys::CreateParamsT& Params);
static const cf::TypeSys::TypeInfoT TypeInfo;
//New vehicle functions
void AcceptPilot(EntHumanPlayerT* pilot);
void RemovePilot(); //EjectPilot will be a very different function from just exiting!
void AcceptCommand(PlayerCommandT command); //Just player commands for now
Vector3fT GetVector(); //Utility method for finding position (to determine player proximity)
private:
void DoDeserialize(cf::Network::InStreamT& Stream); // Override the BaseEntityT base class method.
//Set of potential states. Idle is useable, Inoperative is "dead".
enum VehicleStateT { Idle, HumanOperated, NPCOperated, Inoperative };
void AdvanceModelTime(float Time, bool Loop);
//Store commands passed from the player
ArrayT<PlayerCommandT> PlayerCommands;
VehicleStateT VehicleState; //Current state
EntHumanPlayerT* Pilot; //Referenced to be able to update view
PhysicsHelperT m_Physics;
const CafuModelT* m_VehicleModel;
IntrusivePtrT<AnimExpressionT> m_AnimExpr;
IntrusivePtrT<AnimExprStandardT> m_LastStdAE;
const CafuModelT* m_WeaponModel;
float m_TimeForLightSource;
btCollisionShape* m_CollisionShape; ///< The collision shape that is used to approximate and represent this player in the physics world.
btRigidBody* m_RigidBody; ///< The rigid body (of "kinematic" type) for use in the physics world.
};
#endif
..and the corresponding Vehicle.cpp:
Code: Select all
#include "Vehicle.hpp"
#include "EntityCreateParams.hpp"
#include "HumanPlayer.hpp"
#include "PhysicsWorld.hpp"
#include "TypeSys.hpp"
#include "Libs/LookupTables.hpp"
#include "Libs/Physics.hpp"
#include "../../GameWorld.hpp"
#include "ClipSys/ClipWorld.hpp"
#include "ClipSys/CollisionModelMan.hpp"
#include "ClipSys/TraceResult.hpp"
#include "ConsoleCommands/ConVar.hpp"
#include "MaterialSystem/Material.hpp"
#include "MaterialSystem/MaterialManager.hpp"
#include "MaterialSystem/Renderer.hpp"
#include "Models/AnimPose.hpp"
#include "Models/Model_cmdl.hpp"
#include <iostream> //For cout
// Implement the type info related code.
const cf::TypeSys::TypeInfoT* EntVehicleT::GetType() const
{ return &TypeInfo; }
void* EntVehicleT::CreateInstance(const cf::TypeSys::CreateParamsT& Params)
{ return new EntVehicleT(*static_cast<const EntityCreateParamsT*>(&Params)); }
const cf::TypeSys::TypeInfoT EntVehicleT::TypeInfo(GetBaseEntTIM(), "EntVehicleT", "BaseEntityT", EntVehicleT::CreateInstance, NULL /*MethodsList*/);
EntVehicleT::EntVehicleT(const EntityCreateParamsT& Params)
: BaseEntityT(Params,
BoundingBox3dT(Vector3dT( 600.0, 600.0, 200.0),
Vector3dT(-600.0, -600.0, -3457.6)), // Nice big bounding box
0,
EntityStateT(VectorT(),
0,
0,
0, // ModelIndex
0, // ModelSequNr
0.0, // ModelFrameNr
80, // Health
0, // Armor
0, // HaveItems
0, // HaveWeapons
0, // ActiveWeaponSlot
0, // ActiveWeaponSequNr
0.0)), // ActiveWeaponFrameNr
m_Physics(m_Origin, State.Velocity, m_Dimensions, ClipModel, GameWorld->GetClipWorld()),
m_VehicleModel(Params.GameWorld->GetModel("Games/DeathMatch/Models/Players/Sentinel/Sentinel.cmdl")), //Hard-coded, vaguely mechlike model for now
m_AnimExpr(),
m_LastStdAE(),
m_WeaponModel(Params.GameWorld->GetModel("Games/DeathMatch/Models/Weapons/DesertEagle/DesertEagle_p.cmdl")),
m_TimeForLightSource(0.0f)
{
m_LastStdAE=m_VehicleModel->GetAnimExprPool().GetStandard(State.ModelSequNr, State.ModelFrameNr);
m_AnimExpr =m_LastStdAE;
// First, create a BB of dimensions (-300.0, -300.0, -100.0) - (300.0, 300.0, 100.0).
const BoundingBox3T<double> ClearingBB(VectorT(m_Dimensions.Min.x*2, m_Dimensions.Min.y*2, -m_Dimensions.Max.z*2), m_Dimensions.Max*2);
// Move ClearingBB up to a reasonable height (if possible!), such that the *full* BB (that is, m_Dimensions) is clear of (not stuck in) solid.
cf::ClipSys::TraceResultT Result(1.0);
GameWorld->GetClipWorld().TraceBoundingBox(ClearingBB, m_Origin, VectorT(0.0, 0.0, 3000.0), MaterialT::Clip_Players, &ClipModel, Result);
const double AddHeight=3000.0*Result.Fraction;
// Move ClearingBB down as far as possible.
GameWorld->GetClipWorld().TraceBoundingBox(ClearingBB, m_Origin+VectorT(0.0, 0.0, AddHeight), VectorT(0.0, 0.0, -999999.0), MaterialT::Clip_Players, &ClipModel, Result);
const double SubHeight=999999.0*Result.Fraction;
m_Origin.z=m_Origin.z+AddHeight-SubHeight+(ClearingBB.Min.z-m_Dimensions.Min.z/*1628.8*/)+1.23456789/*Epsilon (sonst Ruckeln am Anfang!)*/;
if (CollisionModel==NULL)
{
// No "normal" collision model has been set for this company bot.
// Now simply setup a bounding box as the collision model.
CollisionModel=cf::ClipSys::CollModelMan->GetCM(m_Dimensions, MaterialManager->GetMaterial("Textures/meta/collisionmodel"));
ClipModel.SetCollisionModel(CollisionModel);
ClipModel.SetOrigin(m_Origin);
ClipModel.Register();
}
// The /1000 is because our physics world is in meters.
m_CollisionShape=new btBoxShape(conv((m_Dimensions.Max-m_Dimensions.Min)/2.0/1000.0)); // Should use a btCylinderShapeZ instead of btBoxShape?
// Our rigid body is of Bullet type "kinematic". That is, we move it ourselves, not the world dynamics.
m_RigidBody=new btRigidBody(btRigidBody::btRigidBodyConstructionInfo(0, this /*btMotionState for this body*/, m_CollisionShape, btVector3()));
m_RigidBody->setUserPointer(this); // This entity is associated to the m_RigidBody.
m_RigidBody->setCollisionFlags(m_RigidBody->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT);
m_RigidBody->setActivationState(DISABLE_DEACTIVATION);
PhysicsWorld->AddRigidBody(m_RigidBody);
}
EntVehicleT::~EntVehicleT()
{
if (m_RigidBody->isInWorld())
PhysicsWorld->RemoveRigidBody(m_RigidBody);
delete m_RigidBody;
delete m_CollisionShape;
}
void EntVehicleT::DoDeserialize(cf::Network::InStreamT& Stream)
{
const bool IsAlive=(State.ModelSequNr<18 || State.ModelSequNr>24);
btTransform WorldTrafo;
getWorldTransform(WorldTrafo);
m_RigidBody->setWorldTransform(WorldTrafo);
if (IsAlive)
{
ClipModel.SetOrigin(m_Origin);
ClipModel.Register();
if (!m_RigidBody->isInWorld())
PhysicsWorld->AddRigidBody(m_RigidBody);
}
else
{
ClipModel.Unregister();
if (m_RigidBody->isInWorld())
PhysicsWorld->RemoveRigidBody(m_RigidBody);
}
}
//Set pilot and state when a player grabs the vehicle
void EntVehicleT::AcceptPilot(EntHumanPlayerT* pilot)
{
std::cout << "Vehicle.AcceptPilot called. \n";
VehicleState = HumanOperated;
Pilot = pilot;
std::cout << "Pilot assigned! Pilot: " << pilot->Name << "\n" ;
}
//Remove pilot and switch state back
void EntVehicleT::RemovePilot()
{
std::cout << "Vehicle.RemovePilot called. \n";
VehicleState = Idle;
Pilot = 0; //Pilot = null;
std::cout << "Pilot removed.\n";
}
//Allow player to work out if vehicle is close enough to board
Vector3fT EntVehicleT::GetVector()
{ return m_Origin.AsVectorOfFloat(); }
//Pass input from Player class to player's piloted vehicle
// May need to verify that vehicle pilot matches caller at some point; just prototyping for now
void EntVehicleT::AcceptCommand(PlayerCommandT PlayerCommand)
{
//Need to cache these; makes much smoother
PlayerCommands.PushBack(PlayerCommand); //From HumanPlayer.ProcessConfig: PlayerCommands.PushBack(*((PlayerCommandT*)ConfigData));
}
void EntVehicleT::TakeDamage(BaseEntityT* Entity, char Amount, const VectorT& ImpactDir)
{
// Dead company bots can take no further damage.
if (State.ModelSequNr>=18 && State.ModelSequNr<=24) return;
State.Velocity=State.Velocity+scale(VectorT(ImpactDir.x, ImpactDir.y, 0.0), 500.0*Amount);
if (State.Health<=Amount)
{
unsigned short DeltaAngle=Entity->GetHeading()-m_Heading;
VehicleState = Inoperative; //State.Health=0; //Need to remove State.* refs...
if (DeltaAngle>=57344 || DeltaAngle< 8192) State.ModelSequNr=21; // 315° ... 45° - die forwards
else if (DeltaAngle>=8192 && DeltaAngle<16384) State.ModelSequNr=22; // 45° ... 90° - headshot
else if (DeltaAngle>=16384 && DeltaAngle<24576) State.ModelSequNr=24; // 90° ... 135° - gutshot
else if (DeltaAngle>=24576 && DeltaAngle<32768) State.ModelSequNr=19; // 135° ... 180° - die backwards1
else if (DeltaAngle>=32768 && DeltaAngle<40960) State.ModelSequNr=20; // 180° ... 225° - die backwards
else if (DeltaAngle>=40960 && DeltaAngle<49152) State.ModelSequNr=18; // 225° ... 270° - die simple
else /* (DeltaAngle>=49152&&DeltaAngle<57344)*/ State.ModelSequNr=23; // 270° ... 315° - die spin
State.ModelFrameNr=0.0;
ClipModel.Unregister(); // Dead now, don't clip no more.
PhysicsWorld->RemoveRigidBody(m_RigidBody);
// Count the frag at the "creator" entity.
BaseEntityT* FraggingEntity=Entity;
while (FraggingEntity->ParentID!=0xFFFFFFFF)
{
BaseEntityT* ParentOfFE=GameWorld->GetBaseEntityByID(FraggingEntity->ParentID);
if (ParentOfFE==NULL) break;
FraggingEntity=ParentOfFE;
}
FraggingEntity->AddFrag();
}
else
{
State.Health-=Amount;
}
}
void EntVehicleT::Think(float FrameTime, unsigned long /*ServerFrameNr*/)
{
switch (VehicleState)
{
case HumanOperated:
{
for (unsigned long PCNr=0; PCNr<PlayerCommands.Size(); PCNr++)
{
PlayerCommandT PlayerCommand = PlayerCommands[PCNr]; //Convenience
/* std::cout << "Recieved PlayerCommand:\n";
std::cout << " PlayerCommand Keys: " << PlayerCommand.Keys << "\n";
std::cout << " PlayerCommand DeltaHeading: " << PlayerCommand.DeltaHeading << "\n";
std::cout << " PlayerCommand DeltaPitch: " << PlayerCommand.DeltaPitch << "\n";
std::cout << " PlayerCommand DeltaBank: " << PlayerCommand.DeltaBank << "\n";
*/
m_Heading+=PlayerCommand.DeltaHeading;
VectorT WishVelocity;
bool WishJump=false;
const double VelX =6000.0*LookupTables::Angle16ToSin[m_Heading]; // 6000 == Client.MoveSpeed
const double VelY =6000.0*LookupTables::Angle16ToCos[m_Heading]; // 6000 == Client.MoveSpeed
const unsigned long Keys =PlayerCommands[PCNr].Keys;
if (Keys & PCK_MoveForward ) WishVelocity= VectorT( VelX, VelY, 0);
m_Physics.MoveHuman(PlayerCommands[PCNr].FrameTime, m_Heading, WishVelocity, WishVelocity, WishJump, WishJump, 470.0); //Complete magic for now; but it works - figure out details when becomes an issue
ClipModel.SetOrigin(m_Origin);
ClipModel.Register();
//Update camera (ie, HumanPlayerEntity) position. Just dupe vehicle pstn for now; figure out offset later (can I read where a bone is?)
if (Pilot) { Pilot->setPositionAndHeading(m_Origin, m_Heading); }
}
}
default: //Will be coding other states later
{ }
}
PlayerCommands.Clear();
}
static ConVarT HasLight("game_BotsHaveLight", true, ConVarT::FLAG_GAMEDLL, "Determines whether the company bot entities all carry a dynamic lightsource.");
bool EntVehicleT::GetLightSourceInfo(unsigned long& DiffuseColor, unsigned long& SpecularColor, VectorT& Position, float& Radius, bool& CastsShadows) const
{
if (!HasLight.GetValueBool()) return false;
if (m_TimeForLightSource<2.0f)
{
// 0.0 <= m_TimeForLightSource < 2.0
const float Value=1.0f-0.5f*(1.0f+LookupTables::Angle16ToCos[(unsigned short)(m_TimeForLightSource/4.0f*65536.0f)]);
const unsigned long Red =char(255.0f*Value);
const unsigned long Green=char(255.0f*Value*0.9f);
DiffuseColor =(Green << 8)+Red;
SpecularColor=0x440000+(Green << 8);
}
else
{
// 2.0 <= m_TimeForLightSource < 6.0
const float Value=0.5f*(1.0f+LookupTables::Angle16ToCos[(unsigned short)((m_TimeForLightSource-2.0f)/4.0f*65536.0f)]);
const unsigned long Green=char(255.0f*(Value*0.8f+0.1f));
DiffuseColor =(Green << 8)+0xFF;
SpecularColor=0x440000+(Green << 8);
}
const float Value=LookupTables::Angle16ToCos[(unsigned short)((m_TimeForLightSource-2.0f)/4.0f*65536.0f)];
const VectorT RelX =scale(VectorT(LookupTables::Angle16ToCos[m_Heading], LookupTables::Angle16ToSin[m_Heading], 0.0), 80.0*Value);
const VectorT RelY =scale(VectorT(LookupTables::Angle16ToSin[m_Heading], LookupTables::Angle16ToCos[m_Heading], 0.0), 500.0);
const VectorT RelZ =VectorT(0.0, 0.0, -300.0+0.5*char(DiffuseColor >> 8));
Position =m_Origin+RelX+RelY+RelZ;
Radius =10000.0;
CastsShadows=true;
return true;
}
void EntVehicleT::Draw(bool /*FirstPersonView*/, float LodDist) const
{
//Check whether pilot is local client?
MatSys::Renderer->GetCurrentLightSourcePosition()[2]+=32.0f;
MatSys::Renderer->GetCurrentEyePosition ()[2]+=32.0f;
MatSys::Renderer->Translate(MatSys::RendererI::MODEL_TO_WORLD, 0.0f, 0.0f, -32.0f);
AnimPoseT* Pose=m_VehicleModel->GetSharedPose(m_AnimExpr);
Pose->Draw(-1 /*default skin*/, LodDist);
AnimPoseT* WeaponPose=m_WeaponModel->GetSharedPose(m_WeaponModel->GetAnimExprPool().GetStandard(0, 0.0f));
WeaponPose->SetSuperPose(Pose);
WeaponPose->Draw(-1 /*default skin*/, LodDist);
WeaponPose->SetSuperPose(NULL);
}
void EntVehicleT::PostDraw(float FrameTime, bool /*FirstPersonView*/)
{
// Implicit simple "mini-prediction".
AdvanceModelTime(FrameTime, State.ModelSequNr<18 || State.ModelSequNr>24);
// Advance the time for the light source.
m_TimeForLightSource+=FrameTime;
if (m_TimeForLightSource>6.0f) m_TimeForLightSource-=4.0f;
}
void EntVehicleT::getWorldTransform(btTransform& worldTrans) const
{
Vector3dT Origin=m_Origin;
// The box shape of our physics body is equally centered around the origin point,
// whereas our m_Dimensions box is "non-uniformely displaced".
// In order to compensate, compute how far the m_Dimensions center is away from the origin.
Origin.z+=(m_Dimensions.Min.z+m_Dimensions.Max.z)/2.0;
// Return the current transformation of our rigid body to the physics world.
worldTrans.setIdentity();
worldTrans.setOrigin(conv(Origin/1000.0));
}
void EntVehicleT::setWorldTransform(const btTransform& /*worldTrans*/)
{
// Never called for a kinematic rigid body.
assert(false);
}
#undef min // See http://stackoverflow.com/questions/5004858/stdmin-gives-error
#undef max
void EntVehicleT::AdvanceModelTime(float Time, bool Loop)
{
if (State.ModelSequNr==m_LastStdAE->GetSequNr())
{
m_LastStdAE->SetFrameNr(State.ModelFrameNr);
}
else
{
const bool IsAlive=(State.ModelSequNr<18 || State.ModelSequNr>24);
float BlendTime=0.3f;
if (!IsAlive)
{
BlendTime=0.2f;
if (State.ModelSequNr>=0 && State.ModelSequNr<int(m_VehicleModel->GetAnims().Size()))
{
const CafuModelT::AnimT& Anim=m_VehicleModel->GetAnims()[State.ModelSequNr];
if (Anim.Frames.Size() > 0)
BlendTime=std::min(BlendTime, (Anim.Frames.Size()-1) * Anim.FPS * 0.5f);
}
}
m_LastStdAE=m_VehicleModel->GetAnimExprPool().GetStandard(State.ModelSequNr, 0.0f);
m_AnimExpr =m_VehicleModel->GetAnimExprPool().GetBlend(m_AnimExpr, m_LastStdAE, BlendTime);
}
m_LastStdAE->SetForceLoop(Loop);
m_AnimExpr->AdvanceTime(Time);
State.ModelFrameNr=m_LastStdAE->GetFrameNr();
}
(I stripped the big front docstrings out for the post, the files are big enough!)
HumanPlayer had some pieces spliced in as well; here's the HumanPlayer.hpp file:
Code: Select all
#ifndef CAFU_HUMAN_PLAYER_HPP_INCLUDED
#define CAFU_HUMAN_PLAYER_HPP_INCLUDED
#include "../../BaseEntity.hpp"
#include "../../PlayerCommand.hpp"
#include "Libs/Physics.hpp"
#include "btBulletDynamicsCommon.h"
//Need to be able to reference the piloted vehicle
#include "Vehicle.hpp"
class EntityCreateParamsT;
class EntVehicleT; //Forward declaration to avoid circular dependency. Done in Vehicle.hpp as well; probably not needed here...
class EntStaticDetailModelT;
namespace cf { namespace GuiSys { class GuiI; } }
class EntHumanPlayerT : public BaseEntityT, public btMotionState
{
public:
// Publicly defined enum for access from the "carried weapons".
enum EventTypesT { EVENT_TYPE_PRIMARY_FIRE, EVENT_TYPE_SECONDARY_FIRE, NUM_EVENT_TYPES };
EntHumanPlayerT(const EntityCreateParamsT& Params);
~EntHumanPlayerT();
EntityStateT& GetState() { return State; }
const EntityStateT& GetState() const { return State; }
unsigned short GetPitch() const { return m_Pitch; }
unsigned short GetBank() const { return m_Bank; }
const btRigidBody* GetRigidBody() const { return m_RigidBody; }
// Implement the BaseEntityT interface.
void TakeDamage(BaseEntityT* Entity, char Amount, const VectorT& ImpactDir);
void ProcessConfigString(const void* ConfigData, const char* ConfigString);
void Think(float FrameTime, unsigned long ServerFrameNr);
void ProcessEvent(unsigned int EventType, unsigned int NumEvents);
bool GetLightSourceInfo(unsigned long& DiffuseColor, unsigned long& SpecularColor, VectorT& Position, float& Radius, bool& CastsShadows) const;
void Draw(bool FirstPersonView, float LodDist) const;
void PostDraw(float FrameTime, bool FirstPersonView);
// Implement the btMotionState interface.
void getWorldTransform(btTransform& worldTrans) const;
void setWorldTransform(const btTransform& worldTrans);
const cf::TypeSys::TypeInfoT* GetType() const;
static void* CreateInstance(const cf::TypeSys::CreateParamsT& Params);
static const cf::TypeSys::TypeInfoT TypeInfo;
/// Allow Vehicle to update player's position and rotation. (Also useable by script)
void setPositionAndHeading(Vector3dT& Position, short Heading);
private:
/// A helper function for Think().
bool CheckGUI(EntStaticDetailModelT* GuiEnt, Vector3fT& MousePos) const;
/// For now, just vehicle-specific
bool CheckInteract(EntVehicleT* TargetEnt, Vector3fT& MousePos) const;
///Pointer to the currently piloted vehicle (if any)
EntVehicleT* PilotedVehicle;
ArrayT<PlayerCommandT> PlayerCommands;
PhysicsHelperT m_Physics;
btCollisionShape* m_CollisionShape; ///< The collision shape that is used to approximate and represent this player in the physics world.
btRigidBody* m_RigidBody; ///< The rigid body (of "kinematic" type) for use in the physics world.
mutable VectorT LastSeenAmbientColor; // This is a client-side variable, unrelated to prediction, and thus allowed.
float TimeForLightSource;
cf::GuiSys::GuiI* GuiHUD; ///< The HUD GUI for this local human player entity.
};
#endif
...and excerpts from the HumanPlayer.cpp file (it's large enough I'll just show my changes):
Code: Select all
...
#include "Vehicle.hpp"
#include "ScriptState.hpp"
#include <iostream> //For cout - handy in this early phase
...
const char StateOfExistance_FreeSpectator =3;
const char StateOfExistance_Piloting =4; //Piloting is a distinct state
...
//Allow vehicle, script, or anything else to update this entity's position and direction
// Will need to set all rotations, not just heading - later update
void EntHumanPlayerT::setPositionAndHeading(Vector3dT& Position, short Heading)
{
m_Origin = Position;
m_Heading = Heading;
}
...
//Based on CheckGUI, idea is to allow interaction with any object type
const bool EntHumanPlayerT::CheckInteract(EntVehicleT* TargetEnt, Vector3fT& MousePos) const
{
std::cout << "Checking interactability of: " << TargetEnt->Name << "\n";
//From Think
if (TargetEnt->GetType()==&EntStaticDetailModelT::TypeInfo)
{
//Let Think keep handling GUIs
return false;
}
//Just see if we're close, for now.
Vector3fT TargetOrigin;
TargetOrigin = TargetEnt->GetVector();
std::cout << "Target at x: " << TargetOrigin.x << ", y: " << TargetOrigin.y << ", z: " << TargetOrigin.z << "\n";
std::cout << "Player at x: " << m_Origin.x << ", y: " << m_Origin.y << ", z: " << m_Origin.z << "\n";
//Range of relative origins:
float rangeX = TargetOrigin.x - m_Origin.x;
float rangeY = TargetOrigin.y - m_Origin.y;
float rangeZ = TargetOrigin.z - m_Origin.z;
std::cout << "Axial distances: x: " << rangeX << ", y: " << rangeY << ", z: " << rangeZ << "\n";
float flatDist = sqrt(rangeX*rangeX+rangeY*rangeY);
std::cout << "Level distance: " << flatDist << "\n";
float distance = sqrt(flatDist*flatDist+rangeZ*rangeZ);
std::cout << "Total distance: " << distance << "\n";
if (distance > 2500) return false;
return true;
}
...
(in Think, after if (CheckGUI(GuiEnt, MousePos)) {...} call) :
//Check to see if a vehicle (or something else) can be interacted with:
if (OtherEntity->GetType()==&EntVehicleT::TypeInfo)
{
if (Keys & PCK_Use )
{
std::cout << "Use key detected by EntVehicleT object!\n";
EntVehicleT* VehicleEnt=(EntVehicleT*)OtherEntity; //Cast?
//Trying to read whether vehicle is close enough to board...
Vector3fT MousePos; //Not used for now; left for future compatibility
if (CheckInteract(VehicleEnt, MousePos))
{
VehicleEnt->AcceptPilot(this);
PilotedVehicle = VehicleEnt;
State.StateOfExistance = StateOfExistance_Piloting;
std::cout << "Pilot mode activated!\n";
}
}
}
...
(later, near end of Think(), as new switch(State.StateOfExistance) case):
case StateOfExistance_Piloting:
{
//Pass all inputs on to vehicle.
//First, remove any existing physics body
if (m_RigidBody->isInWorld())
{
std::cout << "Removing player from world... \n";
PhysicsWorld->RemoveRigidBody(m_RigidBody);
ClipModel.Unregister();
}
PilotedVehicle->AcceptCommand(PlayerCommands[PCNr]);
if (PlayerCommands[PCNr].Keys & PCK_Jump ) //Yes... for now, you jump out of the vehicle :P
{
std::cout << "Eject (jump) button detected while in Piloting mode... \n";
PilotedVehicle->RemovePilot();
PilotedVehicle = 0; //PilotedVehicle = null; //This needs to happen *after* anything involving the vehicle!
State.StateOfExistance = StateOfExistance_Alive;
std::cout << "Alive state restored\n";
//Pop back out on top of vehicle - will need to figure out how to find an open space
m_Origin.z=m_Origin.z+2000;
}
}
...
(remainder of file)
And finally, to be able to drop a vehicle into a level, a new entry for EntityClassDefs.lua:
Code: Select all
-- A base class for vehicle entities (based on Weapon above)
Vehicle=newEntClassDef(Common, Angles,
{
isPoint=true;
size ={ { -16, -16, -16 }, { 16, 16, 16 } };
color ={ 150, 200, 255 };
--accept/eject method defs
})
EntityClassDefs["vehicle"]=newEntClassDef(Vehicle, { isPoint = true; CppClass="EntVehicleT"; description="Base class for driveable vehicles"; })
If anybody has a handy file upload site, I'd also be happy to link these from there - might be easier to read! Also let me know if any parts are unclear - I don't have a lot of time right this second to review the whole posted entry...
I did figure out the issue with the non-moving clip body; but as I mention in the code, I ended up cheating on the vehicle entry conditions. I've got a couple of things I want to try later on for how players interact with objects; so I just put in some very basic range-checking for now - if the player is within 2500 units of the "vehicle" (still just a floating Sentinel model), pressing Enter will now let them drive it.