engine/game/player.h
2024-01-07 04:36:33 +00:00

490 lines
14 KiB
C++

//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#ifndef _PLAYER_H_
#define _PLAYER_H_
#ifndef _SHAPEBASE_H_
#include "game/shapeBase.h"
#endif
#ifndef _BOXCONVEX_H_
#include "collision/boxConvex.h"
#endif
class ParticleEmitter;
class ParticleEmitterData;
class DecalData;
class SplashData;
//----------------------------------------------------------------------------
struct PlayerData: public ShapeBaseData {
typedef ShapeBaseData Parent;
enum Constants {
RecoverDelayBits = 7,
JumpDelayBits = 7,
NumSpineNodes = 6,
ImpactBits = 3,
NUM_SPLASH_EMITTERS = 3,
BUBBLE_EMITTER = 2,
};
F32 pickupRadius; // Radius around player for items (on server)
F32 maxTimeScale; // Max timeScale for action animations
F32 minLookAngle;
F32 maxLookAngle;
F32 maxFreelookAngle;
F32 runForce; // Force used to accelerate player
F32 runEnergyDrain; // Energy drain/tick
F32 minRunEnergy;
F32 maxForwardSpeed;
F32 maxBackwardSpeed;
F32 maxSideSpeed;
F32 maxUnderwaterForwardSpeed;
F32 maxUnderwaterBackwardSpeed;
F32 maxUnderwaterSideSpeed;
F32 maxStepHeight;
F32 runSurfaceAngle; // Angle from vertical in degrees
F32 horizMaxSpeed;
F32 horizResistSpeed;
F32 horizResistFactor;
F32 upMaxSpeed;
F32 upResistSpeed;
F32 upResistFactor;
S32 recoverDelay; // # tick
F32 recoverRunForceScale; // RunForce multiplier in recover state
F32 jumpForce;
F32 jumpEnergyDrain; // Energy per jump
F32 minJumpEnergy;
F32 minJumpSpeed;
F32 maxJumpSpeed;
F32 jumpSurfaceAngle; // Angle from vertical in degrees
S32 jumpDelay; // Delay time in ticks
F32 boxHeadPercentage;
F32 boxTorsoPercentage;
S32 boxHeadLeftPercentage;
S32 boxHeadRightPercentage;
S32 boxHeadBackPercentage;
S32 boxHeadFrontPercentage;
F32 minImpactSpeed;
F32 decalOffset;
F32 groundImpactMinSpeed;
VectorF groundImpactShakeFreq;
VectorF groundImpactShakeAmp;
F32 groundImpactShakeDuration;
F32 groundImpactShakeFalloff;
enum Sounds {
LFootSoft,
RFootSoft,
LFootHard,
RFootHard,
LFootMetal,
RFootMetal,
LFootSnow,
RFootSnow,
LFootShallowSplash,
RFootShallowSplash,
LFootWading,
RFootWading,
LFootUnderWater,
RFootUnderWater,
LFootBubbles,
RFootBubbles,
MoveBubbles,
WaterBreath,
ImpactSoft,
ImpactHard,
ImpactMetal,
ImpactSnow,
ImpactWaterEasy,
ImpactWaterMedium,
ImpactWaterHard,
ExitWater,
MaxSounds
};
AudioProfile* sound[MaxSounds];
Point3F boxSize; // Width, depth, heigth
// Animation and other data intialized in onAdd
struct ActionAnimationDef {
const char* name; // Sequence name
};
struct ActionAnimation {
const char* name; // Sequence name
S32 sequence;
bool death;
VectorF dir; // Dir of animation ground transform
F32 speed; // Speed in m/s
};
enum {
// *** WARNING ***
// These enum values are used to index the ActionAnimationList
// array instantiated in player.cc
// The first five are selected in the move state based on velocity
RootAnim,
RunForwardAnim,
BackBackwardAnim,
SideLeftAnim,
// These are set explicitly based on player actions
FallAnim,
JumpAnim,
LandAnim,
//
NumMoveActionAnims = SideLeftAnim + 1,
NumTableActionAnims = LandAnim + 1,
NumExtraActionAnims = 60,
NumActionAnims = NumTableActionAnims + NumExtraActionAnims,
ActionAnimBits = 8,
NullAnimation = (1 << ActionAnimBits) - 1
};
static ActionAnimationDef ActionAnimationList[NumTableActionAnims];
ActionAnimation actionList[NumActionAnims];
U32 actionCount;
U32 lookAction;
U32 skiAction;
U32 standJumpAction;
S32 spineNode[NumSpineNodes];
S32 pickupDelta; // Base off of pcikupRadius
F32 runSurfaceCos; // Angle from vertical in cos(runSurfaceAngle)
F32 jumpSurfaceCos; // Angle from vertical in cos(jumpSurfaceAngle)
enum Impacts {
ImpactNone,
ImpactNormal,
};
enum Recoil {
LightRecoil,
MediumRecoil,
HeavyRecoil,
NumRecoilSequences
};
S32 recoilSequence[NumRecoilSequences];
ParticleEmitterData * footPuffEmitter;
S32 footPuffID;
S32 footPuffNumParts;
F32 footPuffRadius;
DecalData* decalData;
S32 decalID;
ParticleEmitterData * dustEmitter;
S32 dustID;
SplashData* splash;
S32 splashId;
F32 splashVelocity;
F32 splashAngle;
F32 splashFreqMod;
F32 splashVelEpsilon;
F32 bubbleEmitTime;
F32 medSplashSoundVel;
F32 hardSplashSoundVel;
F32 exitSplashSoundVel;
F32 footSplashHeight;
ParticleEmitterData* splashEmitterList[NUM_SPLASH_EMITTERS];
S32 splashEmitterIDList[NUM_SPLASH_EMITTERS];
//
DECLARE_CONOBJECT(PlayerData);
PlayerData();
bool preload(bool server, char errorBuffer[256]);
void getGroundInfo(TSShapeInstance*,TSThread*,ActionAnimation*);
bool isTableSequence(S32 seq);
bool isJumpAction(U32 action);
static void consoleInit();
static void initPersistFields();
virtual void packData(BitStream* stream);
virtual void unpackData(BitStream* stream);
};
//----------------------------------------------------------------------------
class Player: public ShapeBase
{
typedef ShapeBase Parent;
//-------------------------------------- NOTE! DO NOT ADD ANY MORE MASK BITS TO THIS
//-------------------------------------- CLASS WITHOUT CHECKING WITH DMOORE OR MARKF
//-------------------------------------- We're totally out on the player.
enum MaskBits {
ActionMask = Parent::NextFreeMask << 0,
MoveMask = Parent::NextFreeMask << 1,
ImpactMask = Parent::NextFreeMask << 2,
NextFreeMask = Parent::NextFreeMask << 3
};
struct Range {
Range(F32 _min,F32 _max) {
min = _min;
max = _max;
delta = _max - _min;
};
F32 min,max;
F32 delta;
};
ParticleEmitter *mSplashEmitter[PlayerData::NUM_SPLASH_EMITTERS];
F32 mBubbleEmitterTime;
// Client interpolation/warp data
struct StateDelta {
Move move; // Last move from server
F32 dt; // Last interpolation time
// Interpolation data
Point3F pos;
Point3F rot;
Point3F head;
VectorF posVec;
VectorF rotVec;
VectorF headVec;
// Warp data
S32 warpTicks;
Point3F warpOffset;
Point3F rotOffset;
};
StateDelta delta;
S32 mPredictionCount; // Number of ticks to predict
// Current pos, vel etc.
Point3F mHead; // Head rotation, uses only x & z
Point3F mRot; // Body rotation, uses only z
VectorF mVelocity;
Point3F mAnchorPoint; // Pos compression anchor
static F32 mGravity;
S32 mImpactSound;
S32 mMountPending;
// Main player state
enum ActionState {
NullState,
MoveState,
RecoverState,
NumStateBits = 3
};
ActionState mState;
bool mFalling; // Falling in mid-air
S32 mJumpDelay; // Delay till next jump
S32 mContactTimer; // Ticks since last contact
Point3F mJumpSurfaceNormal;
U32 mJumpSurfaceLastContact;
F32 mWeaponBackFraction; // Amount to slide the weapon back
AUDIOHANDLE mMoveBubbleHandle;
AUDIOHANDLE mWaterBreathHandle;
SimObjectPtr<ShapeBase> mControlObject;
// Animation threads & data
struct ActionAnimation {
U32 action;
TSThread* thread;
S32 delayTicks; // before picking another.
bool forward;
bool firstPerson;
enum Advance {
Normal,
Scale,
Move
} time;
bool waitForEnd;
bool holdAtEnd;
bool animateOnServer;
bool atEnd;
} mActionAnimation;
struct ArmAnimation {
U32 action;
TSThread* thread;
} mArmAnimation;
TSThread* mArmThread;
TSThread* mHeadVThread;
TSThread* mHeadHThread;
TSThread* mRecoilThread;
static Range mArmRange;
static Range mHeadVRange;
static Range mHeadHRange;
bool mDisableMove;
bool mPilot;
bool mInMissionArea;
//
U32 mRecoverTicks;
U32 mReversePending;
U32 mVoiceTag;
U32 mDesiredVoiceTag;
bool inLiquid;
//
PlayerData* mDataBlock;
Point3F mLastPos;
Point3F mLastWaterPos;
struct ContactInfo {
bool contacted, jump, run;
VectorF contactNormal;
void clear() {contacted=jump=run=false; contactNormal.set(1,1,1);}
ContactInfo() {clear();}
} mContactInfo;
struct Death {
F32 lastPos;
Point3F posAdd;
VectorF rotate;
VectorF curNormal;
F32 curSink;
void clear() {dMemset(this, 0, sizeof(*this)); initFall();}
VectorF getPosAdd() {VectorF ret(posAdd); posAdd.set(0,0,0); return ret;}
bool haveVelocity() {return posAdd.x != 0 || posAdd.y != 0;}
void initFall() {curNormal.set(0,0,1); curSink = 0;}
Death() {clear();}
MatrixF* fallToGround(F32 adjust, const Point3F& pos, F32 zrot, F32 boxRad);
} mDeath;
// New collision
public:
OrthoBoxConvex mConvex;
Box3F mWorkingQueryBox;
protected:
void setState(ActionState state, U32 ticks=0);
void updateState();
void updateMove(const Move* move);
bool updatePos(const F32 travelTime = TickSec);
void updateLookAnimation();
void updateAnimation(F32 dt);
void updateAnimationTree(bool firstPerson);
bool step(Point3F *pos,F32 *maxStep,F32 time);
bool setArmThread(U32 action);
void setActionThread(U32 action,bool forward,bool hold = false,bool wait = false,bool fsp = false, bool forceSet = false);
void updateActionThread();
void pickActionAnimation();
void onUnmount(ShapeBase* obj,S32 node);
void setPosition(const Point3F& pos,const Point3F& viewRot);
void setRenderPosition(const Point3F& pos,const Point3F& viewRot,F32 dt=-1);
void findContact(bool* run,bool* jump,VectorF* contactNormal);
virtual void onImageRecoil(U32 imageSlot,ShapeBaseImageData::StateData::RecoilState);
virtual void updateDamageLevel();
virtual void updateDamageState();
void setControllingClient(GameConnection* client);
void calcClassRenderData();
void renderMountedImage(SceneState* state, ShapeImageRenderImage* image);
void renderImage(SceneState* state, SceneRenderImage*);
void playFootstepSound(bool triggeredLeft, S32 sound);
void playImpactSound();
bool inDeathAnim();
F32 deathDelta(Point3F &delta);
void updateDeathOffsets();
bool inSittingAnim();
void updateSplash();
void updateFroth( F32 dt );
bool pointInWater( Point3F &point );
void createSplash( Point3F &pos, F32 speed );
bool collidingWithWater( Point3F &waterHeight );
public:
DECLARE_CONOBJECT(Player);
Player();
~Player();
static void initPersistFields();
static void consoleInit();
// Transforms are all in object space
void setTransform(const MatrixF&);
void getEyeTransform(MatrixF* mat);
void getRenderEyeTransform(MatrixF* mat);
void getCameraParameters(F32 *min,F32* max,Point3F* off,MatrixF* rot);
void getMuzzleTransform(U32 imageSlot,MatrixF* mat);
void getRenderMuzzleTransform(U32 imageSlot,MatrixF* mat);
Point3F getVelocity() const;
void setVelocity(const VectorF& vel);
void applyImpulse(const Point3F& pos,const VectorF& vec);
const Point3F& getRotation() { return mRot; }
const Point3F& getHeadRotation() { return mHead; }
void getDamageLocation(const Point3F& in_rPos, const char *&out_rpVert, const char *&out_rpQuad);
bool canJump();
F32 getJetAbility(F32& thrust, F32& duration, F32& jumpSpeed);
bool haveContact() {return !mContactTimer;}
void getMuzzlePointAI(U32 imageSlot, Point3F* point);
float getMaxForwardVelocity() { return (mDataBlock != NULL ? mDataBlock->maxForwardSpeed : 0); }
virtual bool isDisplacable() const;
virtual Point3F getMomentum() const;
virtual void setMomentum(const Point3F&);
virtual F32 getMass() const;
virtual bool displaceObject(const Point3F& displaceVector);
bool checkDismountPosition(const MatrixF& oldPos, const MatrixF& newPos);
//
bool onAdd();
void onRemove();
bool onNewDataBlock(GameBaseData* dptr);
// Animation
const char* getStateName();
bool setActionThread(const char* sequence,bool hold,bool wait,bool fsp = false);
bool setArmThread(const char* sequence);
// Object control
void setControlObject(ShapeBase*);
ShapeBase* getControlObject();
//
void updateWorkingCollisionSet();
void disableMove(bool);
void setPilot(bool);
bool isPilot() const;
void processTick(const Move*);
void interpolateTick(F32 dt);
void advanceTime(F32 dt);
bool castRay(const Point3F &start, const Point3F &end, RayInfo* info);
bool buildPolyList(AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere);
void buildConvex(const Box3F& box, Convex* convex);
bool isControlObject();
void onCameraScopeQuery(NetConnection *cr, CameraScopeQuery *);
bool writePacketData(GameConnection *, BitStream *stream);
void readPacketData(GameConnection *, BitStream *stream);
U32 packUpdate(NetConnection *, U32 mask, BitStream *stream);
void unpackUpdate(NetConnection *, BitStream *stream);
};
#endif