Serious-Engine/Sources/Engine/Classes/MovableEntity.es
2016-03-11 18:20:51 -06:00

3120 lines
119 KiB
C++

/* Copyright (c) 2002-2012 Croteam Ltd.
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as published by
the Free Software Foundation
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
/*
* Entity that can move and obey physics.
*/
1
%{
#include "StdH.h"
#include <Engine/Entities/InternalClasses.h>
#include <Engine/World/PhysicsProfile.h>
#include <Engine/Math/Geometry.inl>
#include <Engine/Math/Float.h>
#include <Engine/Base/Stream.h>
#include <Engine/World/World.h>
#include <Engine/Network/Network.h>
#include <Engine/Entities/EntityCollision.h>
#include <Engine/Templates/StaticArray.cpp>
#include <Engine/Templates/BSP.h>
#include <Engine/Base/ListIterator.inl>
#include <Engine/World/WorldSettings.h>
#include <Engine/World/WorldCollision.h>
#include <Engine/Math/Clipping.inl>
#include <Engine/Light/LightSource.h>
#include <Engine/Entities/LastPositions.h>
#include <Engine/Templates/StaticStackArray.cpp>
#include <Engine/Templates/DynamicArray.cpp>
#include <Engine/Base/Console.h>
#include <Engine/Base/CRC.h>
#include <Engine/Network/SessionState.h>
#include <Engine/Terrain/TerrainMisc.h>
#define CLEARMEM(var) memset(&var, 0, sizeof(var))
%}
%{
#define ANYEXCEPTION ...
template CStaticStackArray<CBrushPolygon*>;
#define MAXCOLLISIONRETRIES 4*4
extern FLOAT phy_fCollisionCacheAhead;
extern FLOAT phy_fCollisionCacheAround;
extern FLOAT cli_fPredictionFilter;
// force breakpoint (debug)
extern INDEX dbg_bBreak;
// must be in separate function to disable stupid optimizer
extern void Breakpoint(void);
CEntity *GetPredictedSafe(CEntity *pen)
{
if ((pen->en_ulFlags&(ENF_PREDICTOR|ENF_TEMPPREDICTOR)) == ENF_PREDICTOR) {
return pen->GetPredicted();
} else {
return pen;
}
}
// add acceleration to velocity
static inline void AddAcceleration(
FLOAT3D &vCurrentVelocity, const FLOAT3D &vDesiredVelocity,
FLOAT fAcceleration, FLOAT fDecceleration)
{
// if desired velocity is smaller than current velocity
if (vDesiredVelocity.Length()<vCurrentVelocity.Length()) {
fAcceleration=fDecceleration;
}
// find difference between current and desired velocities
FLOAT3D vDelta = vDesiredVelocity-vCurrentVelocity;
// accelerate in the direction of the difference with given maximum acceleration
FLOAT fDelta = vDelta.Length();
if (fDelta>fAcceleration) {
vCurrentVelocity += vDelta*(fAcceleration/fDelta);
} else {
vCurrentVelocity = vDesiredVelocity;
}
}
// add gravity acceleration to velocity along an axis
static inline void AddGAcceleration(
FLOAT3D &vCurrentVelocity, const FLOAT3D &vGDir,
FLOAT fGA, FLOAT fGV)
{
// disassemble speed
FLOAT3D vCurrentParallel, vCurrentOrthogonal;
GetParallelAndNormalComponents(vCurrentVelocity, vGDir, vCurrentOrthogonal, vCurrentParallel);
/*
IMPORTANT:
This is how this piece of code should look like:
// if not already going down at max speed
if (! (vCurrentOrthogonal%vGDir>=fGV)) {
// add accelleration to parallel speed
vCurrentOrthogonal+=vGDir*fGA;
// if going down at max speed
if (vCurrentOrthogonal%vGDir>=fGV) {
// clamp
vCurrentOrthogonal = vGDir*fGV;
}
}
But, due to need for compatibility with older versions and bad VC code generator, we use this kludge:
*/
// KLUDGE_BEGIN
if (_pNetwork->ga_ulDemoMinorVersion<=2) {
Swap(vCurrentOrthogonal, vCurrentParallel);
}
FLOAT3D vCurrentOrthogonalOrg=vCurrentOrthogonal;
// add accelleration to parallel speed
vCurrentOrthogonal+=vGDir*fGA;
// if going down at max speed
if (vCurrentOrthogonal%vGDir>=fGV) {
// clamp
vCurrentOrthogonal = vGDir*fGV;
} else {
vCurrentOrthogonalOrg = vCurrentOrthogonal;
}
if (_pNetwork->ga_ulDemoMinorVersion>2) {
vCurrentOrthogonal=vCurrentOrthogonalOrg;
}
// KLUDGE_END
// assemble speed back
vCurrentVelocity = vCurrentParallel+vCurrentOrthogonal;
}
// NOTE:
// this is pulled out into a separate function because, otherwise, VC6 generates
// invalid code when optimizing this. no clue why is that so.
#pragma inline_depth(0)
static void CheckAndAddGAcceleration(CMovableEntity *pen, FLOAT3D &vTranslationAbsolute, FLOAT fTickQuantum)
{
// if there is forcefield involved
if (pen->en_fForceA>0.01f) {
// add force acceleration
FLOAT fGV=pen->en_fForceV*fTickQuantum;
FLOAT fGA=pen->en_fForceA*fTickQuantum*fTickQuantum;
AddGAcceleration(vTranslationAbsolute, pen->en_vForceDir, fGA, fGV);
}
}
#pragma inline_depth() // see important note above
// add acceleration to velocity, but only along a plane
static inline void AddAccelerationOnPlane(
FLOAT3D &vCurrentVelocity, const FLOAT3D &vDesiredVelocity,
FLOAT fAcceleration, FLOAT fDecceleration,
const FLOAT3D &vPlaneNormal)
{
FLOAT3D vCurrentParallel, vCurrentOrthogonal;
GetParallelAndNormalComponents(vCurrentVelocity, vPlaneNormal, vCurrentOrthogonal, vCurrentParallel);
FLOAT3D vDesiredParallel;
GetNormalComponent(vDesiredVelocity, vPlaneNormal, vDesiredParallel);
AddAcceleration(vCurrentParallel, vDesiredParallel, fAcceleration, fDecceleration);
vCurrentVelocity = vCurrentParallel+vCurrentOrthogonal;
}
// add acceleration to velocity, for roller-coaster slope -- slow!
static inline void AddAccelerationOnPlane2(
FLOAT3D &vCurrentVelocity, const FLOAT3D &vDesiredVelocity,
FLOAT fAcceleration, FLOAT fDecceleration,
const FLOAT3D &vPlaneNormal, const FLOAT3D &vGravity)
{
// get down and horizontal direction
FLOAT3D vDn;
GetNormalComponent(vGravity, vPlaneNormal, vDn);
vDn.Normalize();
FLOAT3D vRt = vPlaneNormal*vDn;
vRt.Normalize();
// add only horizontal acceleration
FLOAT3D vCurrentParallel, vCurrentOrthogonal;
GetParallelAndNormalComponents(vCurrentVelocity, vRt, vCurrentParallel, vCurrentOrthogonal);
FLOAT3D vDesiredParallel;
GetParallelComponent(vDesiredVelocity, vRt, vDesiredParallel);
AddAcceleration(vCurrentParallel, vDesiredParallel, fAcceleration, fDecceleration);
vCurrentVelocity = vCurrentParallel+vCurrentOrthogonal;
}
// max number of retries during movement
static INDEX _ctTryToMoveCheckCounter;
static INDEX _ctSliding;
static FLOAT3D _vSlideOffDir; // move away direction for sliding
static FLOAT3D _vSlideDir;
static void InitTryToMove(void)
{
_ctTryToMoveCheckCounter = MAXCOLLISIONRETRIES;
_ctSliding = 0;
_vSlideOffDir = FLOAT3D(0,0,0);
_vSlideDir = FLOAT3D(0,0,0);
}
// array of forces for current entity
class CEntityForce {
public:
CEntityPointer ef_penEntity;
INDEX ef_iForceType;
FLOAT ef_fRatio; // how much of entity this force gets [0-1]
inline void Clear(void) {
ef_penEntity = NULL;
};
~CEntityForce(void) {
Clear();
};
};
static CStaticStackArray<CEntityForce> _aefForces;
void ClearMovableEntityCaches(void)
{
_aefForces.Clear();
}
%}
class export CMovableEntity : CRationalEntity {
name "MovableEntity";
thumbnail "";
properties:
// NOTE: all properties that are not marked as 'adjustable' should be threated read-only
// translation and rotation speed that this entity would like to have (in relative system)
1 FLOAT3D en_vDesiredTranslationRelative = FLOAT3D(0.0f,0.0f,0.0f),
2 ANGLE3D en_aDesiredRotationRelative = ANGLE3D(0,0,0),
// translation and rotation speed that this entity currently has in absolute system
3 FLOAT3D en_vCurrentTranslationAbsolute = FLOAT3D(0.0f,0.0f,0.0f),
4 ANGLE3D en_aCurrentRotationAbsolute = ANGLE3D(0,0,0),
6 CEntityPointer en_penReference, // reference entity (for standing on)
7 FLOAT3D en_vReferencePlane = FLOAT3D(0.0f,0.0f,0.0f), // reference plane (only for standing on)
8 INDEX en_iReferenceSurface = 0, // surface on reference entity
9 CEntityPointer en_penLastValidReference, // last valid reference entity (for impact damage)
14 FLOAT en_tmLastSignificantVerticalMovement = 0.0f, // last time entity moved significantly up/down
// swimming parameters
10 FLOAT en_tmLastBreathed = 0, // last time when entity took some air
11 FLOAT en_tmMaxHoldBreath = 5.0f, // how long can entity be without air (adjustable)
12 FLOAT en_fDensity = 5000.0f, // density of the body [kg/m3] - defines buoyancy (adjustable)
13 FLOAT en_tmLastSwimDamage = 0, // last time when entity was damaged by swimming
// content immersion parameters
20 INDEX en_iUpContent = 0,
21 INDEX en_iDnContent = 0,
22 FLOAT en_fImmersionFactor = 1.0f,
// force parameters
25 FLOAT3D en_vGravityDir = FLOAT3D(0,-1,0),
26 FLOAT en_fGravityA = 0.0f,
27 FLOAT en_fGravityV = 0.0f,
66 FLOAT3D en_vForceDir = FLOAT3D(1,0,0),
67 FLOAT en_fForceA = 0.0f,
68 FLOAT en_fForceV = 0.0f,
// jumping parameters
30 FLOAT en_tmJumped = 0, // time when entity jumped
31 FLOAT en_tmMaxJumpControl = 0.5f, // how long after jump can have control in the air [s] (adjustable)
32 FLOAT en_fJumpControlMultiplier = 0.5f, // how good is control when jumping (adjustable)
// movement parameters
35 FLOAT en_fAcceleration = 200.0f, // acc/decc [m/s2] in ideal situation (adjustable)
36 FLOAT en_fDeceleration = 40.0f,
37 FLOAT en_fStepUpHeight = 1.0f, // how high can entity step upstairs (adjustable)
42 FLOAT en_fStepDnHeight = -1.0f, // how low can entity step (negative means don't check) (adjustable)
38 FLOAT en_fBounceDampParallel = 0.5f, // damping parallel to plane at each bounce (adjustable)
39 FLOAT en_fBounceDampNormal = 0.5f, // damping normal to plane damping at each bounce (adjustable)
// collision damage control
40 FLOAT en_fCollisionSpeedLimit = 20.0f, // max. collision speed without damage (adjustable)
41 FLOAT en_fCollisionDamageFactor = 20.0f, // collision damage ammount multiplier (adjustable)
51 FLOATaabbox3D en_boxMovingEstimate = FLOATaabbox3D(FLOAT3D(0,0,0), 0.01f), // overestimate of movement in next few ticks
52 FLOATaabbox3D en_boxNearCached = FLOATaabbox3D(FLOAT3D(0,0,0), 0.01f), // box in which the polygons are cached
// intended movement in this tick
64 FLOAT3D en_vIntendedTranslation = FLOAT3D(0,0,0), // can be read on receiving a touch event, holds last velocity before touch
65 FLOATmatrix3D en_mIntendedRotation = FLOATmatrix3D(0),
{
// these are not saved via the property system
CPlacement3D en_plLastPlacement; // placement in last tick (used for lerping) (not saved)
CListNode en_lnInMovers; // node in list of moving entities (saved as bool)
CBrushPolygon *en_pbpoStandOn; // cached last polygon standing on, just for optimization
// used for caching near polygons of zoning brushes for fast collision detection
CStaticStackArray<CBrushPolygon *> en_apbpoNearPolygons; // cached polygons
FLOAT en_tmLastPredictionHead;
FLOAT3D en_vLastHead;
FLOAT3D en_vPredError;
FLOAT3D en_vPredErrorLast;
// these are really temporary - should never be used across ticks
// next placement for collision detection
FLOAT3D en_vNextPosition;
FLOATmatrix3D en_mNextRotation;
// delta for this movement
FLOAT3D en_vMoveTranslation;
FLOATmatrix3D en_mMoveRotation;
// aplied movement in this tick
FLOAT3D en_vAppliedTranslation;
FLOATmatrix3D en_mAppliedRotation;
}
components:
functions:
void ResetPredictionFilter(void)
{
en_tmLastPredictionHead = -2;
en_vLastHead = en_plPlacement.pl_PositionVector;
en_vPredError = en_vPredErrorLast = FLOAT3D(0,0,0);
}
/* Constructor. */
export void CMovableEntity(void)
{
en_pbpoStandOn = NULL;
en_apbpoNearPolygons.SetAllocationStep(5);
ResetPredictionFilter();
}
export void ~CMovableEntity(void)
{
}
/* Initialization. */
export void OnInitialize(const CEntityEvent &eeInput)
{
CRationalEntity::OnInitialize(eeInput);
ClearTemporaryData();
en_vIntendedTranslation = FLOAT3D(0,0,0);
en_mIntendedRotation.Diagonal(1.0f);
en_boxNearCached = FLOATaabbox3D();
en_boxMovingEstimate = FLOATaabbox3D();
en_pbpoStandOn = NULL;
}
/* Called before releasing entity. */
export void OnEnd(void)
{
// remove from movers if active
if (en_lnInMovers.IsLinked()) {
en_lnInMovers.Remove();
}
ClearTemporaryData();
en_boxNearCached = FLOATaabbox3D();
en_boxMovingEstimate = FLOATaabbox3D();
CRationalEntity::OnEnd();
}
export void Copy(CEntity &enOther, ULONG ulFlags)
{
CRationalEntity::Copy(enOther, ulFlags);
CMovableEntity *pmenOther = (CMovableEntity *)(&enOther);
if (ulFlags&COPY_PREDICTOR) {
en_plLastPlacement = pmenOther->en_plLastPlacement ;
en_vNextPosition = pmenOther->en_vNextPosition ;
en_mNextRotation = pmenOther->en_mNextRotation ;
///*!*/ en_vIntendedTranslation = pmenOther->en_vIntendedTranslation ;
///*!*/ en_mIntendedRotation = pmenOther->en_mIntendedRotation ;
en_vAppliedTranslation = pmenOther->en_vAppliedTranslation ;
en_mAppliedRotation = pmenOther->en_mAppliedRotation ;
en_boxNearCached = pmenOther->en_boxNearCached ;
en_boxMovingEstimate = pmenOther->en_boxMovingEstimate ;
en_pbpoStandOn = pmenOther->en_pbpoStandOn ;
en_apbpoNearPolygons = pmenOther->en_apbpoNearPolygons ;
} else {
ClearTemporaryData();
en_boxNearCached = FLOATaabbox3D();
en_boxMovingEstimate = FLOATaabbox3D();
en_pbpoStandOn = NULL;
}
ResetPredictionFilter();
en_plLastPlacement = pmenOther->en_plLastPlacement;
if (pmenOther->en_lnInMovers.IsLinked()) {
AddToMovers();
}
}
void ClearTemporaryData(void)
{
en_plLastPlacement = en_plPlacement;
// init moving parameters so that they are valid for collision if entity is not moving
en_vNextPosition = en_plPlacement.pl_PositionVector;
en_mNextRotation = en_mRotation;
///*!*/ en_vIntendedTranslation = FLOAT3D(0,0,0);
///*!*/ en_mIntendedRotation.Diagonal(1.0f);
en_vAppliedTranslation = FLOAT3D(0,0,0);
en_mAppliedRotation.Diagonal(1.0f);
ResetPredictionFilter();
// !!!! is this ok?
// en_pbpoStandOn = NULL;
// en_apbpoNearPolygons.Clear();
// en_apbpoNearPolygons.SetAllocationStep(5);
}
// create a checksum value for sync-check
export void ChecksumForSync(ULONG &ulCRC, INDEX iExtensiveSyncCheck)
{
CRationalEntity::ChecksumForSync(ulCRC, iExtensiveSyncCheck);
if (iExtensiveSyncCheck>0) {
if (en_pbpoStandOn!=NULL) {
CRC_AddLONG(ulCRC, en_pbpoStandOn->bpo_iInWorld);
}
CRC_AddFLOAT(ulCRC, en_apbpoNearPolygons.Count());
if (iExtensiveSyncCheck>2) {
for (INDEX i=0; i<en_apbpoNearPolygons.Count(); i++) {
CRC_AddLONG(ulCRC, en_apbpoNearPolygons[i]->bpo_iInWorld);
}
}
CRC_AddBlock(ulCRC, (UBYTE*)&en_vReferencePlane, sizeof(en_vReferencePlane));
CRC_AddBlock(ulCRC, (UBYTE*)&en_vDesiredTranslationRelative, sizeof(en_vDesiredTranslationRelative));
CRC_AddBlock(ulCRC, (UBYTE*)&en_aDesiredRotationRelative, sizeof(en_aDesiredRotationRelative));
CRC_AddBlock(ulCRC, (UBYTE*)&en_vCurrentTranslationAbsolute, sizeof(en_vCurrentTranslationAbsolute));
CRC_AddBlock(ulCRC, (UBYTE*)&en_aCurrentRotationAbsolute, sizeof(en_aCurrentRotationAbsolute));
}
}
// dump sync data to text file
export void DumpSync_t(CTStream &strm, INDEX iExtensiveSyncCheck) // throw char *
{
CRationalEntity::DumpSync_t(strm, iExtensiveSyncCheck);
if (iExtensiveSyncCheck>0) {
strm.FPrintF_t("standon polygon: ");
if (en_pbpoStandOn!=NULL) {
strm.FPrintF_t("%d\n", en_pbpoStandOn->bpo_iInWorld);
} else {
strm.FPrintF_t("<none>\n");
}
strm.FPrintF_t("near polygons: %d - ", en_apbpoNearPolygons.Count());
if (iExtensiveSyncCheck>2) {
for (INDEX i=0; i<en_apbpoNearPolygons.Count(); i++) {
strm.FPrintF_t("%d, ", en_apbpoNearPolygons[i]->bpo_iInWorld);
}
}
strm.FPrintF_t("\n");
strm.FPrintF_t("desired translation: %g, %g, %g (%08X %08X %08X)\n",
en_vDesiredTranslationRelative(1),
en_vDesiredTranslationRelative(2),
en_vDesiredTranslationRelative(3),
(ULONG&)en_vDesiredTranslationRelative(1),
(ULONG&)en_vDesiredTranslationRelative(2),
(ULONG&)en_vDesiredTranslationRelative(3));
strm.FPrintF_t("desired rotation: %g, %g, %g (%08X %08X %08X)\n",
en_aDesiredRotationRelative(1),
en_aDesiredRotationRelative(2),
en_aDesiredRotationRelative(3),
(ULONG&)en_aDesiredRotationRelative(1),
(ULONG&)en_aDesiredRotationRelative(2),
(ULONG&)en_aDesiredRotationRelative(3));
strm.FPrintF_t("current translation: %g, %g, %g (%08X %08X %08X)\n",
en_vCurrentTranslationAbsolute(1),
en_vCurrentTranslationAbsolute(2),
en_vCurrentTranslationAbsolute(3),
(ULONG&)en_vCurrentTranslationAbsolute(1),
(ULONG&)en_vCurrentTranslationAbsolute(2),
(ULONG&)en_vCurrentTranslationAbsolute(3));
strm.FPrintF_t("current rotation: %g, %g, %g (%08X %08X %08X)\n",
en_aCurrentRotationAbsolute(1),
en_aCurrentRotationAbsolute(2),
en_aCurrentRotationAbsolute(3),
(ULONG&)en_aCurrentRotationAbsolute(1),
(ULONG&)en_aCurrentRotationAbsolute(2),
(ULONG&)en_aCurrentRotationAbsolute(3));
strm.FPrintF_t("reference plane: %g, %g, %g (%08X %08X %08X)\n",
en_vReferencePlane(1),
en_vReferencePlane(2),
en_vReferencePlane(3),
(ULONG&)en_vReferencePlane(1),
(ULONG&)en_vReferencePlane(2),
(ULONG&)en_vReferencePlane(3));
strm.FPrintF_t("reference surface: %d\n", en_iReferenceSurface);
strm.FPrintF_t("reference entity: ");
if (en_penReference!=NULL) {
strm.FPrintF_t("id: %08X\n", en_penReference->en_ulID);
} else {
strm.FPrintF_t("none\n");
}
}
}
/* Read from stream. */
export void Read_t( CTStream *istr) // throw char *
{
CRationalEntity::Read_t(istr);
// last placement is not saved to disk - it is not neccessary!
ClearTemporaryData();
// old version didn't load this
if (istr->PeekID_t()==CChunkID("MENT")) { // 'movable entity'
istr->ExpectID_t("MENT"); // 'movable entity'
INDEX ibpo;
(*istr)>>ibpo;
en_pbpoStandOn = GetWorldPolygonPointer(ibpo);
BOOL bAnyNULLs = FALSE;
INDEX ctbpoNear;
(*istr)>>ctbpoNear;
if (ctbpoNear>0) {
en_apbpoNearPolygons.PopAll();
en_apbpoNearPolygons.Push(ctbpoNear);
for(INDEX i=0; i<ctbpoNear; i++) {
INDEX ibpo;
(*istr)>>ibpo;
en_apbpoNearPolygons[i] = GetWorldPolygonPointer(ibpo);
if (en_apbpoNearPolygons[i]==NULL) {
bAnyNULLs = TRUE;
}
}
if (bAnyNULLs) {
CPrintF("NULL saved for near polygon!\n");
en_apbpoNearPolygons.PopAll();
}
}
}
// if entity was in list of movers when saving
BOOL bWasMoving;
(*istr)>>bWasMoving;
if (bWasMoving) {
// add it to movers
AddToMovers();
}
}
/* Write to stream. */
export void Write_t( CTStream *ostr) // throw char *
{
CRationalEntity::Write_t(ostr);
ostr->WriteID_t("MENT"); // 'movable entity'
INDEX ibpo;
ibpo = GetWorldPolygonIndex(en_pbpoStandOn);
(*ostr)<<ibpo;
INDEX ctbpoNear = en_apbpoNearPolygons.Count();
(*ostr)<<ctbpoNear;
for(INDEX i=0; i<ctbpoNear; i++) {
INDEX ibpo;
ibpo = GetWorldPolygonIndex(en_apbpoNearPolygons[i]);
(*ostr)<<ibpo;
}
// last placement is not saved to disk - it is not neccessary!
// save linked state in list of movers
(*ostr)<<en_lnInMovers.IsLinked();
}
// this one is used in rendering - gets lerped placement between ticks
export CPlacement3D GetLerpedPlacement(void) const
{
// get the lerping factor
FLOAT fLerpFactor;
if (IsPredictor()) {
fLerpFactor = _pTimer->GetLerpFactor();
} else {
fLerpFactor = _pTimer->GetLerpFactor2();
}
CPlacement3D plLerped;
plLerped.Lerp(en_plLastPlacement, en_plPlacement, fLerpFactor);
CMovableEntity *penTail = (CMovableEntity *)GetPredictedSafe((CEntity*)this);
// if should filter predictions
extern BOOL _bPredictionActive;
if (_bPredictionActive) {
// add the smoothed error
FLOAT3D vError = penTail->en_vPredError;
vError*=pow(cli_fPredictionFilter, fLerpFactor);
// FLOAT fErrLen = vError.Length();
// if (fErrLen>0) {
// vError = vError/fErrLen*ClampDn(fErrLen-cli_fPredictionCorrection*fLerpFactor, 0.0f);
// }
plLerped.pl_PositionVector -= vError;
}
return plLerped;
}
/* Add yourself to list of movers. */
export void AddToMovers(void)
{
if (!en_lnInMovers.IsLinked()) {
en_pwoWorld->wo_lhMovers.AddTail(en_lnInMovers);
}
}
export void AddToMoversDuringMoving(void) // used for recursive adding
{
// if already added
if (en_lnInMovers.IsLinked()) {
// do nothing
return;
}
// add it
AddToMovers();
// mark that it was forced to add
en_ulPhysicsFlags|=EPF_FORCEADDED;
}
/* Set desired rotation speed of movable entity. */
export void SetDesiredRotation(const ANGLE3D &aRotation)
{
en_aDesiredRotationRelative = aRotation;
AddToMovers();
}
export const ANGLE3D &GetDesiredRotation(void) const { return en_aDesiredRotationRelative; };
/* Set desired translation speed of movable entity. */
export void SetDesiredTranslation(const FLOAT3D &vTranslation)
{
en_vDesiredTranslationRelative = vTranslation;
AddToMovers();
}
export const FLOAT3D &GetDesiredTranslation(void) const { return en_vDesiredTranslationRelative; };
/* Add an impulse to the current speed of the entity (used for instantaneous launching). */
export void GiveImpulseTranslationRelative(const FLOAT3D &vImpulseSpeedRelative)
{
CPlacement3D plImpulseSpeedAbsolute( vImpulseSpeedRelative, ANGLE3D(0,0,0));
plImpulseSpeedAbsolute.RelativeToAbsolute(
CPlacement3D(FLOAT3D(0.0f,0.0f,0.0f), en_plPlacement.pl_OrientationAngle));
en_vCurrentTranslationAbsolute += plImpulseSpeedAbsolute.pl_PositionVector;
AddToMovers();
}
export void GiveImpulseTranslationAbsolute(const FLOAT3D &vImpulseSpeed)
{
en_vCurrentTranslationAbsolute += vImpulseSpeed;
AddToMovers();
}
export void LaunchAsPropelledProjectile(const FLOAT3D &vImpulseSpeedRelative,
CMovableEntity *penLauncher)
{
en_vDesiredTranslationRelative = vImpulseSpeedRelative;
en_vCurrentTranslationAbsolute += vImpulseSpeedRelative*en_mRotation;
// en_vCurrentTranslationAbsolute += penLauncher->en_vCurrentTranslationAbsolute;
AddToMovers();
}
export void LaunchAsFreeProjectile(const FLOAT3D &vImpulseSpeedRelative,
CMovableEntity *penLauncher)
{
en_vCurrentTranslationAbsolute += vImpulseSpeedRelative*en_mRotation;
// en_vCurrentTranslationAbsolute += penLauncher->en_vCurrentTranslationAbsolute;
// en_fAcceleration = en_fDeceleration = 0.0f;
AddToMovers();
}
/* Stop all translation */
export void ForceStopTranslation(void) {
en_vDesiredTranslationRelative = FLOAT3D(0.0f,0.0f,0.0f);
en_vCurrentTranslationAbsolute = FLOAT3D(0.0f,0.0f,0.0f);
en_vAppliedTranslation = FLOAT3D(0.0f,0.0f,0.0f);
}
/* Stop all rotation */
export void ForceStopRotation(void) {
en_aDesiredRotationRelative = ANGLE3D(0,0,0);
en_aCurrentRotationAbsolute = ANGLE3D(0,0,0);
en_mAppliedRotation.Diagonal(1.0f);
}
/* Stop at once in place */
export void ForceFullStop(void) {
ForceStopTranslation();
ForceStopRotation();
}
/* Fake that the entity jumped (for jumppads) */
export void FakeJump(const FLOAT3D &vOrgSpeed, const FLOAT3D &vDirection, FLOAT fStrength,
FLOAT fParallelMultiplier, FLOAT fNormalMultiplier, FLOAT fMaxExitSpeed, TIME tmControl)
{
// fixup jump time for right control
en_tmJumped = _pTimer->CurrentTick()-en_tmMaxJumpControl+tmControl;
// apply parallel and normal component multipliers
FLOAT3D vCurrentNormal;
FLOAT3D vCurrentParallel;
GetParallelAndNormalComponents(vOrgSpeed, vDirection, vCurrentParallel, vCurrentNormal);
/*
CPrintF( "\nCurrent translation absolute before: %g, %g, %g\n",
vOrgSpeed(1),
vOrgSpeed(2),
vOrgSpeed(3));*/
// compile translation vector
en_vCurrentTranslationAbsolute =
vCurrentParallel*fParallelMultiplier +
vCurrentNormal*fNormalMultiplier +
vDirection*fStrength;
// clamp translation speed
FLOAT fLength = en_vCurrentTranslationAbsolute.Length();
if( fLength > fMaxExitSpeed)
{
en_vCurrentTranslationAbsolute =
en_vCurrentTranslationAbsolute/fLength*fMaxExitSpeed;
}
/*CPrintF( "Current translation absolute after: %g, %g, %g\n\n",
en_vCurrentTranslationAbsolute(1),
en_vCurrentTranslationAbsolute(2),
en_vCurrentTranslationAbsolute(3));*/
// no reference while bouncing
en_penReference = NULL;
en_pbpoStandOn = NULL;
en_vReferencePlane = FLOAT3D(0.0f, 0.0f, 0.0f);
en_iReferenceSurface = 0;
// add to movers
AddToMovers();
}
/* Get relative angles from direction angles. */
export ANGLE GetRelativeHeading(const FLOAT3D &vDirection) {
ASSERT(Abs(vDirection.Length()-1)<0.001f); // must be normalized!
// get front component of vector
FLOAT fFront =
-vDirection(1)*en_mRotation(1,3)
-vDirection(2)*en_mRotation(2,3)
-vDirection(3)*en_mRotation(3,3);
// get left component of vector
FLOAT fLeft =
-vDirection(1)*en_mRotation(1,1)
-vDirection(2)*en_mRotation(2,1)
-vDirection(3)*en_mRotation(3,1);
// relative heading is arctan of angle between front and left
return ATan2(fLeft, fFront);
}
export ANGLE GetRelativePitch(const FLOAT3D &vDirection) {
ASSERT(Abs(vDirection.Length()-1)<0.001f); // must be normalized!
// get front component of vector
FLOAT fFront =
-vDirection(1)*en_mRotation(1,3)
-vDirection(2)*en_mRotation(2,3)
-vDirection(3)*en_mRotation(3,3);
// get up component of vector
FLOAT fUp =
+vDirection(1)*en_mRotation(1,2)
+vDirection(2)*en_mRotation(2,2)
+vDirection(3)*en_mRotation(3,2);
// relative pitch is arctan of angle between front and up
return ATan2(fUp, fFront);
}
/* Get absolute direction for a heading relative to another direction. */
export void GetReferenceHeadingDirection(const FLOAT3D &vReference, ANGLE aH, FLOAT3D &vDirection) {
ASSERT(Abs(vReference.Length()-1)<0.001f); // must be normalized!
FLOAT3D vY(en_mRotation(1,2), en_mRotation(2,2), en_mRotation(3,2));
FLOAT3D vX = (vY*vReference).Normalize();
FLOAT3D vMZ = vY*vX;
vDirection = -vX*Sin(aH)+vMZ*Cos(aH);
}
/* Get absolute direction for a heading relative to current direction. */
export void GetHeadingDirection(ANGLE aH, FLOAT3D &vDirection) {
FLOAT3D vX(en_mRotation(1,1), en_mRotation(2,1), en_mRotation(3,1));
FLOAT3D vZ(en_mRotation(1,3), en_mRotation(2,3), en_mRotation(3,3));
vDirection = -vX*Sin(aH)-vZ*Cos(aH);
}
/* Get absolute direction for a pitch relative to current direction. */
export void GetPitchDirection(ANGLE aH, FLOAT3D &vDirection) {
FLOAT3D vY(en_mRotation(1,2), en_mRotation(2,2), en_mRotation(3,2));
FLOAT3D vZ(en_mRotation(1,3), en_mRotation(2,3), en_mRotation(3,3));
vDirection = -vZ*Cos(aH)+vY*Sin(aH);
}
// get a valid inflictor for misc damage (last brush or this)
CEntity *MiscDamageInflictor(void)
{
// NOTE: must be damaged by some brush if possible, because enemies are set up so
// that they cannot harm themselves.
if (en_penLastValidReference!=NULL) {
return en_penLastValidReference;
} else {
CBrushSector *pbsc = GetFirstSector();
if (pbsc==NULL) {
return this;
} else {
return pbsc->bsc_pbmBrushMip->bm_pbrBrush->br_penEntity;
}
}
}
// add the sector force
void UpdateOneSectorForce(CBrushSector &bsc, FLOAT fRatio)
{
// if not significantly
if (fRatio<0.01f) {
// just ignore it
return;
}
INDEX iForceType = bsc.GetForceType();
CEntity *penEntity = bsc.bsc_pbmBrushMip->bm_pbrBrush->br_penEntity;
/*
BOOL bAnyIn = FALSE;
CStaticArray<CMovingSphere> &absSpheres = en_pciCollisionInfo->ci_absSpheres;
// for each sphere
for(INDEX iSphere=0; iSphere<absSpheres.Count(); iSphere++) {
CMovingSphere &ms = absSpheres[iSphere];
// if the sphere is in field sector
if (bsc.bsc_bspBSPTree.TestSphere(
FLOATtoDOUBLE(ms.ms_vRelativeCenter0), ms.ms_fR)>=0) {
bAnyIn = TRUE;
break;
}
}
if (!bAnyIn) {
return;
}*/
// try to find the force in container
CEntityForce *pef = NULL;
for(INDEX iForce=0; iForce<_aefForces.Count(); iForce++) {
if (penEntity ==_aefForces[iForce].ef_penEntity
&&iForceType==_aefForces[iForce].ef_iForceType) {
pef = &_aefForces[iForce];
break;
}
}
// if field is not found
if (pef==NULL) {
// add a new one
pef = _aefForces.Push(1);
pef->ef_penEntity = penEntity;
pef->ef_iForceType = iForceType;
pef->ef_fRatio = 0.0f;
}
pef->ef_fRatio+=fRatio;
}
// test for field containment
void TestFields(INDEX &iUpContent, INDEX &iDnContent, FLOAT &fImmersionFactor)
{
// this works only for models
ASSERT(en_RenderType==RT_MODEL || en_RenderType==RT_EDITORMODEL || en_RenderType==RT_SKAMODEL || en_RenderType==RT_SKAEDITORMODEL);
iUpContent = 0;
iDnContent = 0;
FLOAT fUp = 0.0f;
FLOAT fDn = 0.0f;
FLOAT3D &vOffset = en_plPlacement.pl_PositionVector;
FLOATmatrix3D &mRotation = en_mRotation;
// project height min/max in the entity to absolute space
FLOAT3D vMin = FLOAT3D(0, en_pciCollisionInfo->ci_fMinHeight, 0);
FLOAT3D vMax = FLOAT3D(0, en_pciCollisionInfo->ci_fMaxHeight, 0);
vMin = vMin*mRotation+vOffset;
vMax = vMax*mRotation+vOffset;
// project all spheres in the entity to absolute space (for touch field testing)
CStaticArray<CMovingSphere> &absSpheres = en_pciCollisionInfo->ci_absSpheres;
FOREACHINSTATICARRAY(absSpheres, CMovingSphere, itms) {
itms->ms_vRelativeCenter0 = itms->ms_vCenter*mRotation+vOffset;
}
// clear forces
_aefForces.PopAll();
// for each sector that this entity is in
{FOREACHSRCOFDST(en_rdSectors, CBrushSector, bsc_rsEntities, pbsc)
CBrushSector &bsc = *pbsc;
// if this sector is not in first mip
if (!bsc.bsc_pbmBrushMip->IsFirstMip()) {
// skip it
continue;
}
// get entity of the sector
CEntity *penSector = bsc.bsc_pbmBrushMip->bm_pbrBrush->br_penEntity;
// if not real brush
if (penSector->en_RenderType!=RT_BRUSH) {
// skip it
continue;
}
// get min/max parameters of entity inside sector
double dMin, dMax;
bsc.bsc_bspBSPTree.FindLineMinMax(FLOATtoDOUBLE(vMin), FLOATtoDOUBLE(vMax), dMin, dMax);
// if sector content is not default
INDEX iContent = bsc.GetContentType();
if (iContent!=0) {
// if inside sector at all
if (dMax>0.0f && dMin<1.0f) {
//CPrintF("%s: %lf %lf ", bsc.bsc_strName, dMin, dMax);
// if minimum is small
if (dMin<0.01f) {
// update down content
iDnContent = iContent;
fDn = Max(fDn, FLOAT(dMax));
}
// if maximum is large
if (dMax>0.99f) {
// update up content
iUpContent = iContent;
fUp = Max(fUp, 1-FLOAT(dMin));
}
}
}
// add the sector force
UpdateOneSectorForce(bsc, dMax-dMin);
ENDFOR;}
//CPrintF("%f %d %f %d\n", fDn, iDnContent, fUp, iUpContent);
// if same contents
if (iUpContent == iDnContent) {
// trivial case
fImmersionFactor = 1.0f;
// if different contents
} else {
// calculate immersion factor
if (iUpContent==0) {
fImmersionFactor = fDn;
} else if (iDnContent==0) {
fImmersionFactor = 1-fUp;
} else {
fImmersionFactor = Max(fDn, 1-fUp);
}
// eliminate degenerate cases
if (fImmersionFactor<0.01f) {
fImmersionFactor = 1.0f;
iDnContent = iUpContent;
} else if (fImmersionFactor>0.99f) {
fImmersionFactor = 1.0f;
iUpContent = iDnContent;
}
}
// clear force container and calculate average forces
FLOAT3D vGravityA(0,0,0);
FLOAT3D vGravityV(0,0,0);
FLOAT3D vForceA(0,0,0);
FLOAT3D vForceV(0,0,0);
FLOAT fRatioSum = 0.0f;
{for(INDEX iForce=0; iForce<_aefForces.Count(); iForce++) {
CForceStrength fsGravity;
CForceStrength fsField;
_aefForces[iForce].ef_penEntity->GetForce(
_aefForces[iForce].ef_iForceType, en_plPlacement.pl_PositionVector,
fsGravity, fsField);
FLOAT fRatio = _aefForces[iForce].ef_fRatio;
fRatioSum+=fRatio;
vGravityA+=fsGravity.fs_vDirection*fsGravity.fs_fAcceleration*fRatio;
vGravityV+=fsGravity.fs_vDirection*fsGravity.fs_fVelocity*fRatio;
if (fsField.fs_fAcceleration>0) {
vForceA+=fsField.fs_vDirection*fsField.fs_fAcceleration*fRatio;
vForceV+=fsField.fs_vDirection*fsField.fs_fVelocity*fRatio;
}
_aefForces[iForce].Clear();
}}
if (fRatioSum>0) {
vGravityA/=fRatioSum;
vGravityV/=fRatioSum;
vForceA/=fRatioSum;
vForceV/=fRatioSum;
}
en_fGravityA = vGravityA.Length();
if (en_fGravityA<0.01f) {
en_fGravityA = 0;
} else {
en_fGravityV = vGravityV.Length();
en_vGravityDir = vGravityA/en_fGravityA;
}
en_fForceA = vForceA.Length();
if (en_fForceA<0.01f) {
en_fForceA = 0;
} else {
en_fForceV = vForceV.Length();
en_vForceDir = vForceA/en_fForceA;
}
_aefForces.PopAll();
}
// test entity breathing
void TestBreathing(CContentType &ctUp)
{
// if this entity doesn't breathe
if (!(en_ulPhysicsFlags&(EPF_HASLUNGS|EPF_HASGILLS))) {
// do nothing
return;
}
// find current breathing parameters
BOOL bCanBreathe =
(ctUp.ct_ulFlags&CTF_BREATHABLE_LUNGS) && (en_ulPhysicsFlags&EPF_HASLUNGS) ||
(ctUp.ct_ulFlags&CTF_BREATHABLE_GILLS) && (en_ulPhysicsFlags&EPF_HASGILLS);
TIME tmNow = _pTimer->CurrentTick();
TIME tmBreathDelay = tmNow-en_tmLastBreathed;
// if entity can breathe now
if (bCanBreathe) {
// update breathing time
en_tmLastBreathed = tmNow;
// if it was without air for some time
if (tmBreathDelay>_pTimer->TickQuantum*2) {
// notify entity that it has take air now
ETakingBreath eTakingBreath;
eTakingBreath.fBreathDelay = tmBreathDelay/en_tmMaxHoldBreath;
SendEvent(eTakingBreath);
}
// if entity can not breathe now
} else {
// if it was without air for too long
if (tmBreathDelay>en_tmMaxHoldBreath) {
// inflict drowning damage
InflictDirectDamage(this, MiscDamageInflictor(), DMT_DROWNING, ctUp.ct_fDrowningDamageAmount,
en_plPlacement.pl_PositionVector, -en_vGravityDir);
// prolongue breathing a bit, so not to come here every frame
en_tmLastBreathed = tmNow-en_tmMaxHoldBreath+ctUp.ct_tmDrowningDamageDelay;
}
}
}
void TestContentDamage(CContentType &ctDn, FLOAT fImmersion)
{
// if the content can damage by swimming
if (ctDn.ct_fSwimDamageAmount>0) {
TIME tmNow = _pTimer->CurrentTick();
// if there is a delay
if (ctDn.ct_tmSwimDamageDelay>0) {
// if not yet delayed
if (tmNow-en_tmLastSwimDamage>ctDn.ct_tmSwimDamageDelay+_pTimer->TickQuantum) {
// delay
en_tmLastSwimDamage = tmNow+ctDn.ct_tmSwimDamageDelay;
return;
}
}
if (tmNow-en_tmLastSwimDamage>ctDn.ct_tmSwimDamageFrequency) {
// inflict drowning damage
InflictDirectDamage(this, MiscDamageInflictor(),
(DamageType)ctDn.ct_iSwimDamageType, ctDn.ct_fSwimDamageAmount*fImmersion,
en_plPlacement.pl_PositionVector, -en_vGravityDir);
en_tmLastSwimDamage = tmNow;
}
}
// if the content kills
if (ctDn.ct_fKillImmersion>0 && fImmersion>=ctDn.ct_fKillImmersion
&&(en_ulFlags&ENF_ALIVE)) {
// inflict killing damage
InflictDirectDamage(this, MiscDamageInflictor(),
(DamageType)ctDn.ct_iKillDamageType, GetHealth()*10.0f,
en_plPlacement.pl_PositionVector, -en_vGravityDir);
}
}
void TestSurfaceDamage(CSurfaceType &stDn)
{
// if the surface can damage by walking
if (stDn.st_fWalkDamageAmount>0) {
TIME tmNow = _pTimer->CurrentTick();
// if there is a delay
if (stDn.st_tmWalkDamageDelay>0) {
// if not yet delayed
if (tmNow-en_tmLastSwimDamage>stDn.st_tmWalkDamageDelay+_pTimer->TickQuantum) {
// delay
en_tmLastSwimDamage = tmNow+stDn.st_tmWalkDamageDelay;
return;
}
}
if (tmNow-en_tmLastSwimDamage>stDn.st_tmWalkDamageFrequency) {
// inflict walking damage
InflictDirectDamage(this, MiscDamageInflictor(),
(DamageType)stDn.st_iWalkDamageType, stDn.st_fWalkDamageAmount,
en_plPlacement.pl_PositionVector, -en_vGravityDir);
en_tmLastSwimDamage = tmNow;
}
}
}
// send touch event to this entity and touched entity
void SendTouchEvent(const CClipMove &cmMove)
{
ETouch etouchThis;
ETouch etouchOther;
etouchThis.penOther = cmMove.cm_penHit;
etouchThis.bThisMoved = FALSE;
etouchThis.plCollision = cmMove.cm_plClippedPlane;
etouchOther.penOther = this;
etouchOther.bThisMoved = TRUE;
etouchOther.plCollision = cmMove.cm_plClippedPlane;
SendEvent(etouchThis);
cmMove.cm_penHit->SendEvent(etouchOther);
}
// send block event to this entity
void SendBlockEvent(CClipMove &cmMove)
{
EBlock eBlock;
eBlock.penOther = cmMove.cm_penHit;
eBlock.plCollision = cmMove.cm_plClippedPlane;
SendEvent(eBlock);
}
BOOL IsStandingOnPolygon(CBrushPolygon *pbpo)
{
_pfPhysicsProfile.StartTimer(CPhysicsProfile::PTI_ISSTANDINGONPOLYGON);
// if cannot optimize for standing on handle
if (en_pciCollisionInfo==NULL
||!(en_pciCollisionInfo->ci_ulFlags&CIF_CANSTANDONHANDLE)) {
// not standing on polygon
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_ISSTANDINGONPOLYGON);
return FALSE;
}
// if polygon is not valid for standing on any more (brush turned off collision)
if (en_pbpoStandOn->bpo_pbscSector->bsc_pbmBrushMip->bm_pbrBrush->br_penEntity->en_ulCollisionFlags==0) {
// not standing on polygon
return FALSE;
}
const FLOATplane3D &plPolygon = pbpo->bpo_pbplPlane->bpl_plAbsolute;
// get stand-on handle
FLOAT3D vHandle = en_plPlacement.pl_PositionVector;
vHandle(1)+=en_pciCollisionInfo->ci_fHandleY*en_mRotation(1,2);
vHandle(2)+=en_pciCollisionInfo->ci_fHandleY*en_mRotation(2,2);
vHandle(3)+=en_pciCollisionInfo->ci_fHandleY*en_mRotation(3,2);
vHandle-=((FLOAT3D&)plPolygon)*en_pciCollisionInfo->ci_fHandleR;
// if handle is not on the plane
if (plPolygon.PointDistance(vHandle)>0.01f) {
// not standing on polygon
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_ISSTANDINGONPOLYGON);
return FALSE;
}
// find major axes of the polygon plane
INDEX iMajorAxis1, iMajorAxis2;
GetMajorAxesForPlane(plPolygon, iMajorAxis1, iMajorAxis2);
// create an intersector
CIntersector isIntersector(vHandle(iMajorAxis1), vHandle(iMajorAxis2));
// for all edges in the polygon
FOREACHINSTATICARRAY(pbpo->bpo_abpePolygonEdges, CBrushPolygonEdge, itbpePolygonEdge) {
// get edge vertices (edge direction is irrelevant here!)
const FLOAT3D &vVertex0 = itbpePolygonEdge->bpe_pbedEdge->bed_pbvxVertex0->bvx_vAbsolute;
const FLOAT3D &vVertex1 = itbpePolygonEdge->bpe_pbedEdge->bed_pbvxVertex1->bvx_vAbsolute;
// pass the edge to the intersector
isIntersector.AddEdge(
vVertex0(iMajorAxis1), vVertex0(iMajorAxis2),
vVertex1(iMajorAxis1), vVertex1(iMajorAxis2));
}
// if the point is inside polygon
if (isIntersector.IsIntersecting()) {
// entity is standing on polygon
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_ISSTANDINGONPOLYGON);
return TRUE;
// if the point is outside polygon
} else {
// entity is not standing on polygon
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_ISSTANDINGONPOLYGON);
return FALSE;
}
}
// check whether a polygon is below given point, but not too far away
BOOL IsPolygonBelowPoint(CBrushPolygon *pbpo, const FLOAT3D &vPoint, FLOAT fMaxDist)
{
_pfPhysicsProfile.StartTimer(CPhysicsProfile::PTI_ISSTANDINGONPOLYGON);
// if passable or not allowed as ground
if ((pbpo->bpo_ulFlags&BPOF_PASSABLE)
||!AllowForGroundPolygon(pbpo)) {
// it cannot be below
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_ISSTANDINGONPOLYGON);
return FALSE;
}
// get polygon plane
const FLOATplane3D &plPolygon = pbpo->bpo_pbplPlane->bpl_plAbsolute;
// determine polygon orientation relative to gravity
FLOAT fCos = ((const FLOAT3D &)plPolygon)%en_vGravityDir;
// if polygon is vertical or upside down
if (fCos>-0.01f) {
// it cannot be below
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_ISSTANDINGONPOLYGON);
return FALSE;
}
// if polygon's steepness is too high
CSurfaceType &stReference = en_pwoWorld->wo_astSurfaceTypes[pbpo->bpo_bppProperties.bpp_ubSurfaceType];
if (fCos>=-stReference.st_fClimbSlopeCos&&fCos<0
||stReference.st_ulFlags&STF_SLIDEDOWNSLOPE) {
// it cannot be below
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_ISSTANDINGONPOLYGON);
return FALSE;
}
// get distance from point to the plane
FLOAT fD = plPolygon.PointDistance(vPoint);
// if the point is behind the plane
if (fD<-0.01f) {
// it cannot be below
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_ISSTANDINGONPOLYGON);
return FALSE;
}
// find distance of point from the polygon along gravity vector
FLOAT fDistance = -fD/fCos;
// if too far away
if (fDistance > fMaxDist) {
// it cannot be below
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_ISSTANDINGONPOLYGON);
return FALSE;
}
// project point to the polygon along gravity vector
FLOAT3D vProjected = vPoint + en_vGravityDir*fDistance;
// find major axes of the polygon plane
INDEX iMajorAxis1, iMajorAxis2;
GetMajorAxesForPlane(plPolygon, iMajorAxis1, iMajorAxis2);
// create an intersector
CIntersector isIntersector(vProjected(iMajorAxis1), vProjected(iMajorAxis2));
// for all edges in the polygon
FOREACHINSTATICARRAY(pbpo->bpo_abpePolygonEdges, CBrushPolygonEdge, itbpePolygonEdge) {
// get edge vertices (edge direction is irrelevant here!)
const FLOAT3D &vVertex0 = itbpePolygonEdge->bpe_pbedEdge->bed_pbvxVertex0->bvx_vAbsolute;
const FLOAT3D &vVertex1 = itbpePolygonEdge->bpe_pbedEdge->bed_pbvxVertex1->bvx_vAbsolute;
// pass the edge to the intersector
isIntersector.AddEdge(
vVertex0(iMajorAxis1), vVertex0(iMajorAxis2),
vVertex1(iMajorAxis1), vVertex1(iMajorAxis2));
}
// if the point is inside polygon
if (isIntersector.IsIntersecting()) {
// it is below
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_ISSTANDINGONPOLYGON);
return TRUE;
// if the point is outside polygon
} else {
// it is not below
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_ISSTANDINGONPOLYGON);
return FALSE;
}
}
// override this to make filtering for what can entity stand on
export virtual BOOL AllowForGroundPolygon(CBrushPolygon *pbpo)
{
return TRUE;
}
// check whether any cached near polygon is below given point
BOOL IsSomeNearPolygonBelowPoint(const FLOAT3D &vPoint, FLOAT fMaxDist)
{
// otherwise, there is none
return FALSE;
}
// check whether any polygon in a sector is below given point
BOOL IsSomeSectorPolygonBelowPoint(CBrushSector *pbsc, const FLOAT3D &vPoint, FLOAT fMaxDist)
{
// for each polygon in the sector
FOREACHINSTATICARRAY(pbsc->bsc_abpoPolygons, CBrushPolygon, itbpo) {
CBrushPolygon *pbpo = itbpo;
// if it is below
if (IsPolygonBelowPoint(pbpo, vPoint, fMaxDist)) {
// there is some
return TRUE;
}
}
// otherwise, there is none
return FALSE;
}
// check whether entity would fall if standing on next position
BOOL WouldFallInNextPosition(void)
{
// if entity doesn't care for falling
if (en_fStepDnHeight<0) {
// don't check
return FALSE;
}
// if the stand-on polygon is near below
if (en_pbpoStandOn!=NULL &&
IsPolygonBelowPoint(en_pbpoStandOn, en_vNextPosition, en_fStepDnHeight)) {
// it won't fall
return FALSE;
}
// make empty list of extra sectors to check
CListHead lhActiveSectors;
CStaticStackArray<CBrushPolygon *> &apbpo = en_apbpoNearPolygons;
// for each cached near polygon
for(INDEX iPolygon=0; iPolygon<apbpo.Count(); iPolygon++) {
CBrushPolygon *pbpo = apbpo[iPolygon];
// if it is below
if (IsPolygonBelowPoint(pbpo, en_vNextPosition, en_fStepDnHeight)) {
// it won't fall
lhActiveSectors.RemAll();
return FALSE;
}
// if the polygon's sector is not added yet
if (!pbpo->bpo_pbscSector->bsc_lnInActiveSectors.IsLinked()) {
// add it
lhActiveSectors.AddTail(pbpo->bpo_pbscSector->bsc_lnInActiveSectors);
}
}
// NOTE: We add non-zoning reference first (if existing),
// to speed up cases when standing on moving brushes.
// if the reference is a non-zoning brush
if (en_penReference!=NULL && en_penReference->en_RenderType==RT_BRUSH
&&!(en_penReference->en_ulFlags&ENF_ZONING)
&& en_penReference->en_pbrBrush!=NULL) {
// get first mip of the brush
CBrushMip *pbmMip = en_penReference->en_pbrBrush->GetFirstMip();
// for each sector in the brush mip
FOREACHINDYNAMICARRAY(pbmMip->bm_abscSectors, CBrushSector, itbsc) {
// if it is not added yet
if (!itbsc->bsc_lnInActiveSectors.IsLinked()) {
// add it
lhActiveSectors.AddTail(itbsc->bsc_lnInActiveSectors);
}
}
}
// for each zoning sector that this entity is in
{FOREACHSRCOFDST(en_rdSectors, CBrushSector, bsc_rsEntities, pbsc);
// if it is not added yet
if (!pbsc->bsc_lnInActiveSectors.IsLinked()) {
// add it
lhActiveSectors.AddTail(pbsc->bsc_lnInActiveSectors);
}
ENDFOR;}
// for each active sector
BOOL bSupportFound = FALSE;
FOREACHINLIST(CBrushSector, bsc_lnInActiveSectors, lhActiveSectors, itbsc) {
CBrushSector *pbsc = itbsc;
// if the sector is zoning
if (pbsc->bsc_pbmBrushMip->bm_pbrBrush->br_penEntity->en_ulFlags&ENF_ZONING) {
// for non-zoning brush entities in the sector
{FOREACHDSTOFSRC(pbsc->bsc_rsEntities, CEntity, en_rdSectors, pen);
if (pen->en_RenderType==CEntity::RT_TERRAIN) {
if (IsTerrainBelowPoint(pen->en_ptrTerrain, en_vNextPosition, en_fStepDnHeight, en_vGravityDir)) {
bSupportFound = TRUE;
goto out;
}
continue;
}
if (pen->en_RenderType!=CEntity::RT_BRUSH&&
pen->en_RenderType!=CEntity::RT_FIELDBRUSH) {
break; // brushes are sorted first in list
}
// get first mip of the brush
CBrushMip *pbmMip = pen->en_pbrBrush->GetFirstMip();
// for each sector in the brush mip
FOREACHINDYNAMICARRAY(pbmMip->bm_abscSectors, CBrushSector, itbscInMip) {
// if it is not added yet
if (!itbscInMip->bsc_lnInActiveSectors.IsLinked()) {
// add it
lhActiveSectors.AddTail(itbscInMip->bsc_lnInActiveSectors);
}
}
ENDFOR;}
}
// if there is a polygon below in that sector
if (IsSomeSectorPolygonBelowPoint(itbsc, en_vNextPosition, en_fStepDnHeight)) {
// it won't fall
bSupportFound = TRUE;
break;
}
}
out:;
// clear list of active sectors
lhActiveSectors.RemAll();
// if no support, it surely would fall
return !bSupportFound;
}
// clear next position to current placement
void ClearNextPosition(void)
{
en_vNextPosition = en_plPlacement.pl_PositionVector;
en_mNextRotation = en_mRotation;
}
// set current placement from next position
void SetPlacementFromNextPosition(void)
{
_pfPhysicsProfile.StartTimer(CPhysicsProfile::PTI_SETPLACEMENTFROMNEXTPOSITION);
_pfPhysicsProfile.IncrementTimerAveragingCounter(CPhysicsProfile::PTI_SETPLACEMENTFROMNEXTPOSITION);
CPlacement3D plNew;
plNew.pl_PositionVector = en_vNextPosition;
DecomposeRotationMatrixNoSnap(plNew.pl_OrientationAngle, en_mNextRotation);
FLOATmatrix3D mRotation;
MakeRotationMatrixFast(mRotation, plNew.pl_OrientationAngle);
SetPlacement_internal(plNew, mRotation, TRUE /* try to optimize for small movements */);
// use this for collision code debugging only not useful in real conditions
/*
// check that we have not ended inside a wall
if (GetRenderType()==RT_MODEL) {
extern BOOL CanEntityChangeCollisionBox(CEntity *pen, INDEX iNewCollisionBox, CEntity **ppenObstacle);
CEntity *penObstacle;
if (!CanEntityChangeCollisionBox(this, GetCollisionBoxIndex(), &penObstacle)) {
CPrintF("*** COLLISION!!!!\n");
}
}
*/
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_SETPLACEMENTFROMNEXTPOSITION);
}
BOOL TryToGoUpstairs(const FLOAT3D &vTranslationAbsolute, const CSurfaceType &stHit,
BOOL bHitStairsOrg)
{
_pfPhysicsProfile.StartTimer(CPhysicsProfile::PTI_TRYTOGOUPSTAIRS);
_pfPhysicsProfile.IncrementTimerAveragingCounter(CPhysicsProfile::PTI_TRYTOGOUPSTAIRS);
// use only horizontal components of the movement
FLOAT3D vTranslationHorizontal;
GetNormalComponent(vTranslationAbsolute, en_vGravityDir, vTranslationHorizontal);
//CPrintF("Trying: (%g) ", vTranslationHorizontal(3));
// if the movement has no substantial value
if(vTranslationHorizontal.Length()<0.001f) {
//CPrintF("no value\n");
// don't do it
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_TRYTOGOUPSTAIRS);
return FALSE;
}
FLOAT3D vTranslationHorizontalOrg = vTranslationHorizontal;
// if the surface that is climbed on is not really stairs
if (!bHitStairsOrg) {
// keep minimum speed
vTranslationHorizontal.Normalize();
vTranslationHorizontal*=0.5f;
}
// remember original placement
CPlacement3D plOriginal = en_plPlacement;
// take stairs height
FLOAT fStairsHeight = 0;
if (stHit.st_fStairsHeight>0) {
fStairsHeight = Max(stHit.st_fStairsHeight, en_fStepUpHeight);
} else if (stHit.st_fStairsHeight<0) {
fStairsHeight = Min(stHit.st_fStairsHeight, en_fStepUpHeight);
}
CContentType &ctDn = en_pwoWorld->wo_actContentTypes[en_iDnContent];
CContentType &ctUp = en_pwoWorld->wo_actContentTypes[en_iUpContent];
// if in partially in water
BOOL bGettingOutOfWater = FALSE;
if ((ctDn.ct_ulFlags&CTF_SWIMABLE) && !(ctUp.ct_ulFlags&CTF_SWIMABLE)
&& en_fImmersionFactor>0.3f) {
// add immersion height to up step
if (en_pciCollisionInfo!=NULL) {
fStairsHeight=fStairsHeight*2+en_fImmersionFactor*
(en_pciCollisionInfo->ci_fMaxHeight-en_pciCollisionInfo->ci_fMinHeight);
// remember that we are trying to get out of water
bGettingOutOfWater = TRUE;
}
}
// calculate the 3 translation directions (up, forward and down)
FLOAT3D avTranslation[3];
avTranslation[0] = en_vGravityDir*-fStairsHeight;
avTranslation[1] = vTranslationHorizontal;
avTranslation[2] = en_vGravityDir*fStairsHeight;
// for each translation step
for(INDEX iStep=0; iStep<3; iStep++) {
BOOL bStepOK = TRUE;
// create new placement with the translation step
en_vNextPosition = en_plPlacement.pl_PositionVector+avTranslation[iStep];
en_mNextRotation = en_mRotation;
// clip the movement to the entity's world
CClipMove cm(this);
en_pwoWorld->ClipMove(cm);
// if not passed
if (cm.cm_fMovementFraction<1.0f) {
// find hit surface
INDEX iSurfaceHit = 0;
BOOL bHitStairsNow = FALSE;
if (cm.cm_pbpoHit!=NULL) {
bHitStairsNow = cm.cm_pbpoHit->bpo_ulFlags&BPOF_STAIRS;
iSurfaceHit = cm.cm_pbpoHit->bpo_bppProperties.bpp_ubSurfaceType;
}
CSurfaceType &stHit = en_pwoWorld->wo_astSurfaceTypes[iSurfaceHit];
// check if hit a slope while climbing stairs
const FLOAT3D &vHitPlane = cm.cm_plClippedPlane;
FLOAT fPlaneDotG = vHitPlane%en_vGravityDir;
FLOAT fPlaneDotGAbs = Abs(fPlaneDotG);
BOOL bSlidingAllowed = (fPlaneDotGAbs>-0.01f && fPlaneDotGAbs<0.99f)&&bHitStairsOrg;
BOOL bEarlyClipAllowed =
// going up or
iStep==0 ||
// going forward and hit stairs or
iStep==1 && bHitStairsNow ||
// going down and ends on something that is not high slope
iStep==2 &&
(vHitPlane%en_vGravityDir<-stHit.st_fClimbSlopeCos ||
bHitStairsNow);
// if early clip is allowed
if (bEarlyClipAllowed || bSlidingAllowed) {
// try to go to where it is clipped (little bit before)
en_vNextPosition = en_plPlacement.pl_PositionVector +
avTranslation[iStep]*(cm.cm_fMovementFraction*0.98f);
if (bSlidingAllowed && iStep!=2) {
FLOAT3D vSliding = cm.cm_plClippedPlane.ProjectDirection(
avTranslation[iStep]*(1.0f-cm.cm_fMovementFraction))+
vHitPlane*(ClampUp(avTranslation[iStep].Length(), 0.5f)/100.0f);
en_vNextPosition += vSliding;
}
CClipMove cm(this);
en_pwoWorld->ClipMove(cm);
// if it failed
if (cm.cm_fMovementFraction<=1.0f) {
// mark that this step is unsuccessful
bStepOK = FALSE;
}
// if early clip is not allowed
} else {
// mark that this step is unsuccessful
bStepOK = FALSE;
}
}
// if the step is successful
if (bStepOK) {
// use that position
SetPlacementFromNextPosition();
// if the step failed
} else {
// restore original placement
en_vNextPosition = plOriginal.pl_PositionVector;
SetPlacementFromNextPosition();
// move is unsuccessful
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_TRYTOGOUPSTAIRS);
//CPrintF("FAILED\n");
return FALSE;
}
} // end of steps loop
// all steps passed, use the final position
// NOTE: must not keep the speed when getting out of water,
// or the player gets launched out too fast
if (!bGettingOutOfWater) {
en_vAppliedTranslation += vTranslationHorizontalOrg;
}
// move is successful
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_TRYTOGOUPSTAIRS);
//CPrintF("done\n");
return TRUE;
}
/* Try to translate the entity. Slide, climb or push others if needed. */
BOOL TryToMove(CMovableEntity *penPusher, BOOL bTranslate, BOOL bRotate)
{
//CPrintF("TryToMove(%d)\n", _ctTryToMoveCheckCounter);
// decrement the recursion counter
if (penPusher!=NULL) {
_ctTryToMoveCheckCounter--;
} else {
_ctTryToMoveCheckCounter-=4;
}
// if recursing too deep
if (_ctTryToMoveCheckCounter<0) {
// fail the move
return FALSE;
}
_pfPhysicsProfile.StartTimer(CPhysicsProfile::PTI_TRYTOTRANSLATE);
_pfPhysicsProfile.IncrementTimerAveragingCounter(CPhysicsProfile::PTI_TRYTOTRANSLATE);
_pfPhysicsProfile.IncrementCounter(CPhysicsProfile::PCI_TRYTOMOVE);
// create new placement with movement
if (bTranslate) {
en_vNextPosition = en_plPlacement.pl_PositionVector+en_vMoveTranslation;
/* STREAMDUMP START
if(GetRenderType()==RT_MODEL)
{
try
{
CTMemoryStream &strm = *GetDumpStream();
DUMPVECTOR2("Move transl:", en_vMoveTranslation);
DUMPVECTOR2("Next pos:", en_vNextPosition);
}
catch( char *strError)
{
strError;
}
}
STREAMDUMP END */
} else {
en_vNextPosition = en_plPlacement.pl_PositionVector;
}
if (bRotate) {
// CPrintF(" r:???\n");
en_mNextRotation = en_mMoveRotation*en_mRotation;
} else {
en_mNextRotation = en_mRotation;
}
// test if rotation can be ignored
ULONG ulCIFlags = en_pciCollisionInfo->ci_ulFlags;
BOOL bIgnoreRotation = !bRotate ||
((ulCIFlags&CIF_IGNOREROTATION)||
( (ulCIFlags&CIF_IGNOREHEADING) &&
(en_mMoveRotation(1,2)==0&&en_mMoveRotation(2,2)==1&&en_mMoveRotation(3,2)==0) ));
// create movement towards the new placement
CClipMove cmMove(this);
// clip the movement to the entity's world
if (!bTranslate && bIgnoreRotation) {
cmMove.cm_fMovementFraction = 2.0f;
_pfPhysicsProfile.IncrementCounter(CPhysicsProfile::PCI_TRYTOMOVE_FAST);
} else {
en_pwoWorld->ClipMove(cmMove);
}
// if the move passes
if (cmMove.cm_fMovementFraction>1.0f) {
// CPrintF(" passed\n");
//CPrintF("%d\n", MAXCOLLISIONRETRIES-(_ctTryToMoveCheckCounter+1));
// if entity is in walking control now, but it might fall of an edge
if (bTranslate && en_penReference!=NULL &&
(en_ulPhysicsFlags&EPF_TRANSLATEDBYGRAVITY) &&
!(en_ulPhysicsFlags&(EPF_ONSTEEPSLOPE|EPF_ORIENTINGTOGRAVITY|EPF_FLOATING)) &&
penPusher==NULL && WouldFallInNextPosition()) {
// fail the movement
SendEvent(EWouldFall());
//CPrintF(" wouldfall\n");
return FALSE;
}
// make entity use its new placement
SetPlacementFromNextPosition();
if (bTranslate) {
en_vAppliedTranslation += en_vMoveTranslation;
}
if (bRotate) {
en_mAppliedRotation = en_mMoveRotation*en_mAppliedRotation;
}
// move is successful
_pfPhysicsProfile.IncrementCounter(CPhysicsProfile::PCI_TRYTOMOVE_PASS);
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_TRYTOTRANSLATE);
//CPrintF(" successful\n");
return TRUE;
// if the move is clipped
} else {
_pfPhysicsProfile.IncrementCounter(CPhysicsProfile::PCI_TRYTOMOVE_CLIP);
/* STREAMDUMP START
if(GetRenderType()==RT_MODEL)
{
try
{
CTMemoryStream &strm = *GetDumpStream();
strm.FPrintF_t( "Movm Fract: %g\n", cmMove.cm_fMovementFraction);
strm.FPrintF_t( "En : ID:%08x %s\n", cmMove.cm_penHit->en_ulID, (const char*) GetName() );
DUMPVECTOR2("Pl col:", cmMove.cm_plClippedPlane);
}
catch( char *strError)
{
strError;
}
}
STREAMDUMP END */
// if must not retry
if (_ctTryToMoveCheckCounter<=0) {
// fail
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_TRYTOTRANSLATE);
return FALSE;
}
// if hit brush
if (cmMove.cm_pbpoHit!=NULL) {
// if polygon is stairs, and the entity can climb stairs
if ((cmMove.cm_pbpoHit->bpo_ulFlags&BPOF_STAIRS)
&&((en_ulPhysicsFlags&EPF_ONBLOCK_MASK)==EPF_ONBLOCK_CLIMBORSLIDE)) {
// adjust against sliding upwards
cmMove.cm_plClippedPlane = FLOATplane3D(-en_vGravityDir, 0);
}
// if cannot be damaged by impact
INDEX iSurface = cmMove.cm_pbpoHit->bpo_bppProperties.bpp_ubSurfaceType;
if (en_pwoWorld->wo_astSurfaceTypes[iSurface].st_ulFlags&STF_NOIMPACT) {
// remember that
en_ulPhysicsFlags|=EPF_NOIMPACTTHISTICK;
}
}
// if entity is translated by gravity and
// the hit plane is more orthogonal to the gravity than the last one found
if ((en_ulPhysicsFlags&EPF_TRANSLATEDBYGRAVITY) && !(en_ulPhysicsFlags&EPF_FLOATING)
&& (
((en_vGravityDir%(FLOAT3D&)cmMove.cm_plClippedPlane)
<(en_vGravityDir%en_vReferencePlane)) ) ) {
// remember touched entity as stand-on reference
en_penReference = cmMove.cm_penHit;
// CPrintF(" newreference id%08x\n", en_penReference->en_ulID);
en_vReferencePlane = (FLOAT3D&)cmMove.cm_plClippedPlane;
en_pbpoStandOn = cmMove.cm_pbpoHit; // is NULL if not hit a brush
if (cmMove.cm_pbpoHit==NULL) {
en_iReferenceSurface = 0;
} else {
en_iReferenceSurface = cmMove.cm_pbpoHit->bpo_bppProperties.bpp_ubSurfaceType;
}
}
// send touch event to this entity and to touched entity
SendTouchEvent(cmMove);
// if cannot be damaged by impact
if (cmMove.cm_penHit->en_ulPhysicsFlags&EPF_NOIMPACT) {
// remember that
en_ulPhysicsFlags|=EPF_NOIMPACTTHISTICK;
}
// if entity bounces when blocked
FLOAT3D vBounce;
BOOL bBounce = FALSE;
if ( ((en_ulPhysicsFlags&EPF_ONBLOCK_MASK)==EPF_ONBLOCK_BOUNCE) && bTranslate) {
// create translation speed for bouncing off clipping plane
FLOAT3D vParallel, vNormal;
GetParallelAndNormalComponents(en_vMoveTranslation, cmMove.cm_plClippedPlane,
vNormal, vParallel);
vNormal *= -en_fBounceDampNormal;
vParallel *= +en_fBounceDampParallel;
vBounce = vNormal+vParallel;
// if not too small bounce
if (vNormal.Length()>0.1f) {
// do bounce
bBounce = TRUE;
}
// rotate slower
en_aDesiredRotationRelative *= en_fBounceDampParallel;
if (en_aDesiredRotationRelative.Length()<10) {
en_aDesiredRotationRelative = ANGLE3D(0,0,0);
}
}
// if entity pushes when blocked and the blocking entity is pushable
if (penPusher!=NULL&&(cmMove.cm_penHit->en_ulPhysicsFlags&EPF_PUSHABLE)) {
CMovableModelEntity *penBlocking = ((CMovableModelEntity *)cmMove.cm_penHit);
// create push translation to account for rotating radius
FLOAT3D vRadius = cmMove.cm_penHit->en_plPlacement.pl_PositionVector-
penPusher->en_plPlacement.pl_PositionVector;
FLOAT3D vPush=(vRadius*penPusher->en_mMoveRotation-vRadius);
//*(1.01f-cmMove.cm_fMovementFraction);
vPush += penPusher->en_vMoveTranslation;
//*(1.01f-cmMove.cm_fMovementFraction);
penBlocking->en_vMoveTranslation = vPush;
penBlocking->en_mMoveRotation = penPusher->en_mMoveRotation;
// make sure it is added to the movers list
penBlocking->AddToMoversDuringMoving();
// push the blocking entity
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_TRYTOTRANSLATE);
BOOL bUnblocked = penBlocking->TryToMove(penPusher, bTranslate, bRotate);
_pfPhysicsProfile.StartTimer(CPhysicsProfile::PTI_TRYTOTRANSLATE);
// if it has removed itself
if (bUnblocked) {
// retry the movement
ClearNextPosition();
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_TRYTOTRANSLATE);
return TryToMove(penPusher, bTranslate, bRotate);
} else {
// move is unsuccessful
SendBlockEvent(cmMove);
ClearNextPosition();
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_TRYTOTRANSLATE);
return FALSE;
}
// if entity slides if blocked
} else if (
((en_ulPhysicsFlags&EPF_ONBLOCK_MASK)==EPF_ONBLOCK_SLIDE)||
((en_ulPhysicsFlags&EPF_ONBLOCK_MASK)==EPF_ONBLOCK_BOUNCE)||
((en_ulPhysicsFlags&EPF_ONBLOCK_MASK)==EPF_ONBLOCK_CLIMBORSLIDE)||
((en_ulPhysicsFlags&EPF_ONBLOCK_MASK)==EPF_ONBLOCK_STOPEXACT) ){
// if translating
if (bTranslate) {
// create translation for sliding along clipping plane
FLOAT3D vSliding;
// if sliding along one plane
if (_ctSliding==0) {
// remember sliding parameters from the plane
_vSlideOffDir = cmMove.cm_plClippedPlane;
// get sliding velocity
vSliding = cmMove.cm_plClippedPlane.ProjectDirection(
en_vMoveTranslation*(1.0f-cmMove.cm_fMovementFraction));
_ctSliding++;
// if second plane
} else if (_ctSliding==1) {
// off direction is away from both planes
_vSlideOffDir+=cmMove.cm_plClippedPlane;
// sliding direction is along both planes
_vSlideDir = _vSlideOffDir*(FLOAT3D&)cmMove.cm_plClippedPlane;
if (_vSlideDir.Length()>0.001f) {
_vSlideDir.Normalize();
}
_ctSliding++;
// get sliding velocity
GetParallelComponent(en_vMoveTranslation*(1.0f-cmMove.cm_fMovementFraction),
_vSlideDir, vSliding);
// if more than two planes
} else {
// off direction is away from all planes
_vSlideOffDir+=cmMove.cm_plClippedPlane;
// sliding direction is along all planes
_vSlideDir = cmMove.cm_plClippedPlane.ProjectDirection(_vSlideDir);
_ctSliding++;
// get sliding velocity
GetParallelComponent(en_vMoveTranslation*(1.0f-cmMove.cm_fMovementFraction),
_vSlideDir, vSliding);
}
ASSERT(IsValidFloat(vSliding(1)));
ASSERT(IsValidFloat(_vSlideDir(1)));
ASSERT(IsValidFloat(_vSlideOffDir(1)));
// if entity hit a brush polygon
if (cmMove.cm_pbpoHit!=NULL) {
CSurfaceType &stHit = en_pwoWorld->wo_astSurfaceTypes[
cmMove.cm_pbpoHit->bpo_bppProperties.bpp_ubSurfaceType];
// if it is not beeing pushed, and it can climb stairs
if (penPusher==NULL
&&(en_ulPhysicsFlags&EPF_ONBLOCK_MASK)==EPF_ONBLOCK_CLIMBORSLIDE) {
// NOTE: originally, the polygon's plane was considered here.
// due to sphere-polygon collision algo, it is possible for the collision
// plane to be even orthogonal to the polygon plane.
// considering polygon's plane prevented climbing up the stairs.
// so now, the collision plane is considered.
// if there are any further problems, i recommend choosing
// the plane that is more orthogonal to the movement direction.
FLOAT3D &vHitPlane = (FLOAT3D&)cmMove.cm_plClippedPlane;//cmMove.cm_pbpoHit->bpo_pbplPlane->bpl_plAbsolute;
BOOL bHitStairs = cmMove.cm_pbpoHit->bpo_ulFlags&BPOF_STAIRS;
// if the plane hit is steep enough to climb on it
// (cannot climb low slopes as if those were stairs)
if ((vHitPlane%en_vGravityDir>-stHit.st_fClimbSlopeCos)
||bHitStairs) {
// if sliding along it would be mostly horizontal
// (i.e. cannot climb up the slope)
FLOAT fSlidingVertical2 = en_vMoveTranslation%en_vGravityDir;
fSlidingVertical2*=fSlidingVertical2;
FLOAT fSliding2 = en_vMoveTranslation%en_vMoveTranslation;
if ((2*fSlidingVertical2<=fSliding2)
// if can go upstairs
&& TryToGoUpstairs(en_vMoveTranslation, stHit, bHitStairs)) {
// movement is ok
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_TRYTOTRANSLATE);
return FALSE;
}
}
}
}
// entity shouldn't really slide
if ((en_ulPhysicsFlags&EPF_ONBLOCK_MASK)==EPF_ONBLOCK_STOPEXACT) {
// kill sliding
vSliding = FLOAT3D(0,0,0);
}
ASSERT(IsValidFloat(vSliding(1)));
// add a component perpendicular to the sliding plane
vSliding += _vSlideOffDir*
(ClampUp(en_vMoveTranslation.Length(), 0.5f)/100.0f);
// if initial movement has some substantial value
if(en_vMoveTranslation.Length()>0.001f && cmMove.cm_fMovementFraction>0.002f) {
// go to where it is clipped (little bit before)
vSliding += en_vMoveTranslation*(cmMove.cm_fMovementFraction*0.985f);
}
// ignore extremely small sliding
if (vSliding.ManhattanNorm()<0.001f) {
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_TRYTOTRANSLATE);
return FALSE;
}
// recurse
en_vMoveTranslation = vSliding;
ClearNextPosition();
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_TRYTOTRANSLATE);
TryToMove(penPusher, bTranslate, bRotate);
// if bouncer
if (bBounce) {
// remember bouncing speed for next tick
en_vAppliedTranslation = vBounce;
// no reference while bouncing
en_penReference = NULL;
en_vReferencePlane = FLOAT3D(0.0f, 0.0f, 0.0f);
en_iReferenceSurface = 0;
}
// move is not entirely successful
return FALSE;
// if rotating
} else if (bRotate) {
// if bouncing entity
if ((en_ulPhysicsFlags&EPF_ONBLOCK_MASK)==EPF_ONBLOCK_BOUNCE) {
// rotate slower
en_aDesiredRotationRelative *= en_fBounceDampParallel;
if (en_aDesiredRotationRelative.Length()<10) {
en_aDesiredRotationRelative = ANGLE3D(0,0,0);
}
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_TRYTOTRANSLATE);
// move is not successful
return FALSE;
}
// create movement getting away from the collision point
en_vMoveTranslation = cmMove.cm_vClippedLine*-1.2f;
// recurse
ClearNextPosition();
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_TRYTOTRANSLATE);
TryToMove(penPusher, TRUE, bRotate);
// move is not entirely successful
return FALSE;
}
// not translating and not rotating? - move is unsuccessful
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_TRYTOTRANSLATE);
return FALSE;
// if entity has some other behaviour when blocked
} else {
// move is unsuccessful (EPF_ONBLOCK_STOP is assumed)
SendBlockEvent(cmMove);
ClearNextPosition();
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_TRYTOTRANSLATE);
return FALSE;
}
}
}
/* STREAMDUMP START
void ExportEntityPlacementAndSpeed(CMovableEntity &en, CTString strDes)
{
try
{
CTMemoryStream &strm = *GetDumpStream();
strm.FPrintF_t("%s : %s .................\n", strDes, (const char*) GetName());
DUMPPLACEMENT("En pl:", en.en_plPlacement);
DUMPVECTOR2("DesTraRel:", en.en_vDesiredTranslationRelative);
DUMPVECTOR2("CurTraAbs:", en.en_vCurrentTranslationAbsolute);
DUMPVECTOR2("IntendTra:", en.en_vIntendedTranslation);
}
catch( char *strError)
{
strError;
}
}
STREAMDUMP END */
// clear eventual temporary variables that are not persistent
export void ClearMovingTemp(void)
{
// return;
// CLEARMEM(en_vIntendedTranslation);
// CLEARMEM(en_mIntendedRotation);
ClearNextPosition();
CLEARMEM(en_vMoveTranslation);
CLEARMEM(en_mMoveRotation);
CLEARMEM(en_vAppliedTranslation);
CLEARMEM(en_mAppliedRotation);
}
// prepare parameters for moving in this tick
export void PreMoving(void)
{
//
// NOTE:
// This is one more cludge for bad VC6 code generator creating sync errors in older demos.
// We are getting rid of the whole virtual actions system ASAP when the network is recoded.
//
// FOR LICENSEES: Since you don't need to replay old demos from SSam, you may remove the whole
// PreMovingOld() function.
//
if (_pNetwork->ga_ulDemoMinorVersion<=5) {
PreMovingOld();
} else {
PreMovingNew();
}
}
void PreMovingNew(void)
{
if (en_pciCollisionInfo==NULL) {
return;
}
/* STREAMDUMP START
ExportEntityPlacementAndSpeed( *(CMovableEntity *)this, "Pre moving (start of function)");
STREAMDUMP END */
_pfPhysicsProfile.StartTimer(CPhysicsProfile::PTI_PREMOVING);
_pfPhysicsProfile.IncrementTimerAveragingCounter(CPhysicsProfile::PTI_PREMOVING);
// remember old placement for lerping
en_plLastPlacement = en_plPlacement;
// for each child of the mover
{FOREACHINLIST(CEntity, en_lnInParent, en_lhChildren, itenChild) {
// if the child is movable, yet not in movers list
if ((itenChild->en_ulPhysicsFlags&EPF_MOVABLE)
&&!((CMovableEntity*)&*itenChild)->en_lnInMovers.IsLinked()) {
CMovableEntity *penChild = ((CMovableEntity*)&*itenChild);
// remember old placement for lerping
penChild->en_plLastPlacement = penChild->en_plPlacement;
}
}}
FLOAT fTickQuantum=_pTimer->TickQuantum; // used for normalizing from SI units to game ticks
// trig break point if required
if( dbg_bBreak) {
dbg_bBreak = FALSE; // auto turn off breakpoint when triggered
try {
Breakpoint();
} catch (ANYEXCEPTION) {
CPrintF("Breakpoint!\n");
};
}
// NOTE: this limits maximum velocity of any entity in game.
// it is absolutely neccessary in order to prevent extreme slowdowns in physics.
// if you plan to increase this one radically, consider decreasing
// collision grid cell size!
// currently limited to a bit less than speed of sound (not that it is any specificaly
// relevant constant, but it is just handy)
const FLOAT fMaxSpeed = 300.0f;
en_vCurrentTranslationAbsolute(1)=Clamp(en_vCurrentTranslationAbsolute(1), -fMaxSpeed, +fMaxSpeed);
en_vCurrentTranslationAbsolute(2)=Clamp(en_vCurrentTranslationAbsolute(2), -fMaxSpeed, +fMaxSpeed);
en_vCurrentTranslationAbsolute(3)=Clamp(en_vCurrentTranslationAbsolute(3), -fMaxSpeed, +fMaxSpeed);
// if the entity is a model
if (en_RenderType==RT_MODEL || en_RenderType==RT_EDITORMODEL ||
en_RenderType==RT_SKAMODEL || en_RenderType==RT_SKAEDITORMODEL) {
// test for field containment
TestFields(en_iUpContent, en_iDnContent, en_fImmersionFactor);
// if entity has sticky feet
if (en_ulPhysicsFlags & EPF_STICKYFEET) {
// find gravity towards nearest polygon
FLOAT3D vPoint;
FLOATplane3D plPlane;
FLOAT fDistanceToEdge;
if (GetNearestPolygon(vPoint, plPlane, fDistanceToEdge)) {
en_vGravityDir = -(FLOAT3D&)plPlane;
}
}
}
CContentType &ctDn = en_pwoWorld->wo_actContentTypes[en_iDnContent];
CContentType &ctUp = en_pwoWorld->wo_actContentTypes[en_iUpContent];
// test entity breathing
TestBreathing(ctUp);
// test content damage
TestContentDamage(ctDn, en_fImmersionFactor);
// test surface damage
if (en_penReference!=NULL) {
CSurfaceType &stReference = en_pwoWorld->wo_astSurfaceTypes[en_iReferenceSurface];
TestSurfaceDamage(stReference);
}
// calculate content fluid factors
FLOAT fBouyancy = (1-
(ctDn.ct_fDensity/en_fDensity)*en_fImmersionFactor-
(ctUp.ct_fDensity/en_fDensity)*(1-en_fImmersionFactor));
FLOAT fSpeedModifier =
ctDn.ct_fSpeedMultiplier*en_fImmersionFactor+
ctUp.ct_fSpeedMultiplier*(1-en_fImmersionFactor);
FLOAT fFluidFriction =
ctDn.ct_fFluidFriction*en_fImmersionFactor+
ctUp.ct_fFluidFriction*(1-en_fImmersionFactor);
FLOAT fControlMultiplier =
ctDn.ct_fControlMultiplier*en_fImmersionFactor+
ctUp.ct_fControlMultiplier*(1-en_fImmersionFactor);
// transform relative desired translation into absolute
FLOAT3D vDesiredTranslationAbsolute = en_vDesiredTranslationRelative;
// relative absolute
if (!(en_ulPhysicsFlags & EPF_ABSOLUTETRANSLATE)) {
vDesiredTranslationAbsolute *= en_mRotation;
}
// transform translation and rotation into tick time units
vDesiredTranslationAbsolute*=fTickQuantum;
ANGLE3D aRotationRelative;
aRotationRelative(1) = en_aDesiredRotationRelative(1)*fTickQuantum;
aRotationRelative(2) = en_aDesiredRotationRelative(2)*fTickQuantum;
aRotationRelative(3) = en_aDesiredRotationRelative(3)*fTickQuantum;
// make absolute matrix rotation from relative angle rotation
FLOATmatrix3D mRotationAbsolute;
if ((en_ulPhysicsFlags&EPF_ONBLOCK_MASK)==EPF_ONBLOCK_PUSH) {
FLOATmatrix3D mNewRotation;
MakeRotationMatrixFast(mNewRotation, en_plPlacement.pl_OrientationAngle+aRotationRelative);
mRotationAbsolute = mNewRotation*!en_mRotation;
} else {
MakeRotationMatrixFast(mRotationAbsolute, aRotationRelative);
mRotationAbsolute = en_mRotation*(mRotationAbsolute*!en_mRotation);
}
// modify desired speed for fluid parameters
vDesiredTranslationAbsolute*=fSpeedModifier;
// remember jumping strength (if any)
FLOAT fJump = -en_mRotation.GetColumn(2)%vDesiredTranslationAbsolute;
BOOL bReferenceMovingInY = FALSE;
BOOL bReferenceRotatingNonY = FALSE;
// if we have a CMovableEntity for a reference entity
if (en_penReference!=NULL && (en_penReference->en_ulPhysicsFlags&EPF_MOVABLE)) {
CMovableEntity *penReference = (CMovableEntity *)(CEntity*)en_penReference;
// get reference deltas for this tick
const FLOAT3D &vReferenceTranslation = penReference->en_vIntendedTranslation;
const FLOATmatrix3D &mReferenceRotation = penReference->en_mIntendedRotation;
// calculate radius of this entity relative to reference
FLOAT3D vRadius = en_plPlacement.pl_PositionVector
-penReference->en_plPlacement.pl_PositionVector;
FLOAT3D vReferenceDelta = vReferenceTranslation + vRadius*mReferenceRotation - vRadius;
// add the deltas to this entity
vDesiredTranslationAbsolute += vReferenceDelta;
mRotationAbsolute = mReferenceRotation*mRotationAbsolute;
// remember if reference is moving in y
bReferenceMovingInY = (vReferenceDelta%en_vGravityDir != 0.0f);
bReferenceRotatingNonY = ((en_vGravityDir*mReferenceRotation)%en_vGravityDir)>0.01f;
}
FLOAT3D vTranslationAbsolute = en_vCurrentTranslationAbsolute*fTickQuantum;
// initially not orienting
en_ulPhysicsFlags&=~EPF_ORIENTINGTOGRAVITY;
// if the entity is rotated by gravity
if (en_ulPhysicsFlags&EPF_ORIENTEDBYGRAVITY) {
// find entity's down vector
FLOAT3D vDown;
vDown(1) = -en_mRotation(1,2);
vDown(2) = -en_mRotation(2,2);
vDown(3) = -en_mRotation(3,2);
// find angle entities down and gravity down
FLOAT fCos = vDown%en_vGravityDir;
// if substantial
if (fCos<0.99999f) {
// mark
en_ulPhysicsFlags|=EPF_ORIENTINGTOGRAVITY;
// limit the angle rotation
ANGLE a = ACos(fCos);
if (Abs(a)>20) {
a = 20*Sgn(a);
}
FLOAT fRad =RadAngle(a);
// make rotation axis
FLOAT3D vAxis = vDown*en_vGravityDir;
FLOAT fLen = vAxis.Length();
if (fLen<0.01f) {
vAxis(1) = en_mRotation(1,3);
vAxis(2) = en_mRotation(2,3);
vAxis(3) = en_mRotation(3,3);
// NOTE: must have this patch for smooth rocking on moving brushes
// (should infact do fRad/=fLen always)
} else if (!bReferenceRotatingNonY) {
fRad/=fLen;
}
vAxis*=fRad;
// make rotation matrix
FLOATmatrix3D mGRotation;
mGRotation(1,1) = 1; mGRotation(1,2) = -vAxis(3); mGRotation(1,3) = vAxis(2);
mGRotation(2,1) = vAxis(3); mGRotation(2,2) = 1; mGRotation(2,3) = -vAxis(1);
mGRotation(3,1) = -vAxis(2); mGRotation(3,2) = vAxis(1); mGRotation(3,3) = 1;
OrthonormalizeRotationMatrix(mGRotation);
// add the gravity rotation
mRotationAbsolute = mGRotation*mRotationAbsolute;
}
}
// initially not floating
en_ulPhysicsFlags&=~EPF_FLOATING;
FLOAT ACC=en_fAcceleration*fTickQuantum*fTickQuantum;
FLOAT DEC=en_fDeceleration*fTickQuantum*fTickQuantum;
// if the entity is not affected by gravity
if (!(en_ulPhysicsFlags&EPF_TRANSLATEDBYGRAVITY)) {
// accellerate towards desired absolute translation
if (en_ulPhysicsFlags&EPF_NOACCELERATION) {
vTranslationAbsolute = vDesiredTranslationAbsolute;
} else {
AddAcceleration(vTranslationAbsolute, vDesiredTranslationAbsolute,
ACC*fControlMultiplier,
DEC*fControlMultiplier);
}
// if swimming
} else if ((fBouyancy*en_fGravityA<0.5f && (ctDn.ct_ulFlags&(CTF_SWIMABLE|CTF_FLYABLE)))) {
// mark that
en_ulPhysicsFlags|=EPF_FLOATING;
// accellerate towards desired absolute translation
if (en_ulPhysicsFlags&EPF_NOACCELERATION) {
vTranslationAbsolute = vDesiredTranslationAbsolute;
} else {
AddAcceleration(vTranslationAbsolute, vDesiredTranslationAbsolute,
ACC*fControlMultiplier,
DEC*fControlMultiplier);
}
// add gravity acceleration
if (fBouyancy<-0.1f) {
FLOAT fGV=en_fGravityV*fTickQuantum*fSpeedModifier;
FLOAT fGA=(en_fGravityA*-fBouyancy)*fTickQuantum*fTickQuantum;
AddAcceleration(vTranslationAbsolute, en_vGravityDir*-fGV, fGA, fGA);
} else if (fBouyancy>+0.1f) {
FLOAT fGV=en_fGravityV*fTickQuantum*fSpeedModifier;
FLOAT fGA=(en_fGravityA*fBouyancy)*fTickQuantum*fTickQuantum;
AddAcceleration(vTranslationAbsolute, en_vGravityDir*fGV, fGA, fGA);
}
// if the entity is affected by gravity
} else {
BOOL bGravityAlongPolygon = TRUE;
// if there is no fixed remembered stand-on polygon or the entity is not on it anymore
if (en_pbpoStandOn==NULL || !IsStandingOnPolygon(en_pbpoStandOn) || bReferenceMovingInY
|| (en_ulPhysicsFlags&EPF_ORIENTINGTOGRAVITY)) {
// clear the stand on polygon
en_pbpoStandOn=NULL;
if (en_penReference == NULL || bReferenceMovingInY) {
bGravityAlongPolygon = FALSE;
}
}
// if gravity can cause the entity to fall
if (!bGravityAlongPolygon) {
_pfPhysicsProfile.IncrementCounter(CPhysicsProfile::PCI_GRAVITY_NONTRIVIAL);
// add gravity acceleration
FLOAT fGV=en_fGravityV*fTickQuantum*fSpeedModifier;
FLOAT fGA=(en_fGravityA*fBouyancy)*fTickQuantum*fTickQuantum;
AddGAcceleration(vTranslationAbsolute, en_vGravityDir, fGA, fGV);
// if entity can only slide down its stand-on polygon
} else {
_pfPhysicsProfile.IncrementCounter(CPhysicsProfile::PCI_GRAVITY_TRIVIAL);
// disassemble gravity to parts parallel and normal to plane
FLOAT3D vPolygonDir = -en_vReferencePlane;
// NOTE: normal to plane=paralel to plane normal vector!
FLOAT3D vGParallel, vGNormal;
GetParallelAndNormalComponents(en_vGravityDir, vPolygonDir, vGNormal, vGParallel);
// add gravity part parallel to plane
FLOAT fFactor = vGParallel.Length();
if (fFactor>0.001f) {
FLOAT fGV=en_fGravityV*fTickQuantum*fSpeedModifier;
FLOAT fGA=(en_fGravityA*fBouyancy)*fTickQuantum*fTickQuantum;
AddGAcceleration(vTranslationAbsolute, vGParallel/fFactor, fGA*fFactor, fGV*fFactor);
}
// kill your normal-to-polygon speed if towards polygon and small
FLOAT fPolyGA = (vPolygonDir%en_vGravityDir)*en_fGravityA;
FLOAT fYSpeed = vPolygonDir%vTranslationAbsolute;
if (fYSpeed>0 && fYSpeed < fPolyGA) {
vTranslationAbsolute -= vPolygonDir*fYSpeed;
}
// if a bouncer
if ((en_ulPhysicsFlags&EPF_ONBLOCK_MASK)==EPF_ONBLOCK_BOUNCE) {
// rotate slower
en_aDesiredRotationRelative *= en_fJumpControlMultiplier;
if (en_aDesiredRotationRelative.Length()<10) {
en_aDesiredRotationRelative = ANGLE3D(0,0,0);
}
}
}
CSurfaceType &stReference = en_pwoWorld->wo_astSurfaceTypes[en_iReferenceSurface];
// if it has a reference entity
if (en_penReference!=NULL) {
FLOAT fPlaneY = (en_vGravityDir%en_vReferencePlane);
FLOAT fPlaneYAbs = Abs(fPlaneY);
FLOAT fFriction = stReference.st_fFriction;
// if on a steep slope
if (fPlaneY>=-stReference.st_fClimbSlopeCos&&fPlaneY<0
||(stReference.st_ulFlags&STF_SLIDEDOWNSLOPE)&&fPlaneY>-0.99f) {
en_ulPhysicsFlags|=EPF_ONSTEEPSLOPE;
// accellerate horizontaly towards desired absolute translation
AddAccelerationOnPlane2(
vTranslationAbsolute,
vDesiredTranslationAbsolute,
ACC*fPlaneYAbs*fPlaneYAbs*fFriction*fControlMultiplier,
DEC*fPlaneYAbs*fPlaneYAbs*fFriction*fControlMultiplier,
en_vReferencePlane,
en_vGravityDir);
// if not on a steep slope
} else {
en_ulPhysicsFlags&=~EPF_ONSTEEPSLOPE;
// accellerate on plane towards desired absolute translation
AddAccelerationOnPlane(
vTranslationAbsolute,
vDesiredTranslationAbsolute,
ACC*fPlaneYAbs*fPlaneYAbs*fFriction*fControlMultiplier,
DEC*fPlaneYAbs*fPlaneYAbs*fFriction*fControlMultiplier,
en_vReferencePlane);
}
// if wants to jump and can jump
if (fJump<-0.01f && (fPlaneY<-stReference.st_fJumpSlopeCos
|| _pTimer->CurrentTick()>en_tmLastSignificantVerticalMovement+0.25f) ) {
// jump
vTranslationAbsolute += en_vGravityDir*fJump;
en_tmJumped = _pTimer->CurrentTick();
en_pbpoStandOn = NULL;
}
// if it doesn't have a reference entity
} else {//if (en_penReference==NULL)
// if can control after jump
if (_pTimer->CurrentTick()-en_tmJumped<en_tmMaxJumpControl) {
// accellerate horizontaly, but slower
AddAccelerationOnPlane(
vTranslationAbsolute,
vDesiredTranslationAbsolute,
ACC*fControlMultiplier*en_fJumpControlMultiplier,
DEC*fControlMultiplier*en_fJumpControlMultiplier,
FLOATplane3D(en_vGravityDir, 0));
}
// if wants to jump and can jump
if (fJump<-0.01f &&
_pTimer->CurrentTick()>en_tmLastSignificantVerticalMovement+0.25f) {
// jump
vTranslationAbsolute += en_vGravityDir*fJump;
en_tmJumped = _pTimer->CurrentTick();
en_pbpoStandOn = NULL;
}
}
}
// check for force-field acceleration
// NOTE: pulled out because of a bug in VC code generator, see function comments above
CheckAndAddGAcceleration(this, vTranslationAbsolute, fTickQuantum);
// if there is fluid friction involved
if (fFluidFriction>0.01f) {
// slow down
AddAcceleration(vTranslationAbsolute, FLOAT3D(0.0f, 0.0f, 0.0f),
0.0f, DEC*fFluidFriction);
}
// if may slow down spinning
if ( (en_ulPhysicsFlags& EPF_CANFADESPINNING) &&
( (ctDn.ct_ulFlags&CTF_FADESPINNING) || (ctUp.ct_ulFlags&CTF_FADESPINNING) ) ) {
// reduce desired rotation
en_aDesiredRotationRelative *= (1-fSpeedModifier*0.05f);
if (en_aDesiredRotationRelative.Length()<10) {
en_aDesiredRotationRelative = ANGLE3D(0,0,0);
}
}
// discard reference entity (will be recalculated)
if (en_pbpoStandOn==NULL && (vTranslationAbsolute.ManhattanNorm()>1E-5f ||
en_vReferencePlane%en_vGravityDir<0.0f)) {
en_penReference = NULL;
en_vReferencePlane = FLOAT3D(0.0f, 0.0f, 0.0f);
en_iReferenceSurface = 0;
}
en_vIntendedTranslation = vTranslationAbsolute;
en_mIntendedRotation = mRotationAbsolute;
//-- estimate future movements for collision caching
// make box of the entity for its current rotation
FLOATaabbox3D box;
en_pciCollisionInfo->MakeBoxAtPlacement(FLOAT3D(0,0,0), en_mRotation, box);
// if it is a light source
{CLightSource *pls = GetLightSource();
if (pls!=NULL && !(pls->ls_ulFlags&LSF_LENSFLAREONLY)) {
// expand the box to be sure that it contains light range
ASSERT(!(pls->ls_ulFlags&LSF_DIRECTIONAL));
box |= FLOATaabbox3D(FLOAT3D(0,0,0), pls->ls_rFallOff);
}}
// add a bit around it
box.ExpandByFactor( phy_fCollisionCacheAround-1.0f);
// make box go few ticks ahead of the entity
box += en_plPlacement.pl_PositionVector;
en_boxMovingEstimate = box;
box += en_vIntendedTranslation*phy_fCollisionCacheAhead;
en_boxMovingEstimate |= box;
// clear applied movement to be updated during movement
en_vAppliedTranslation = FLOAT3D(0.0f, 0.0f, 0.0f);
en_mAppliedRotation.Diagonal(1.0f);
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_PREMOVING);
// STREAMDUMP ExportEntityPlacementAndSpeed( *(CMovableEntity *)this, "Pre moving (end of function)");
}
// VC6 KLUDGE - do not change this function
// See note above in PreMoving()
/* old */ void PreMovingOld(void)
/* old */ {
/* old */ if (en_pciCollisionInfo==NULL) {
/* old */ return;
/* old */ }
/* old */
/* old */ //STREAMDUMP START
/* old */ // ExportEntityPlacementAndSpeed( *(CMovableEntity *)this, "Pre moving (start of function)");
/* old */ //STREAMDUMP END
/* old */
/* old */ _pfPhysicsProfile.StartTimer(CPhysicsProfile::PTI_PREMOVING);
/* old */ _pfPhysicsProfile.IncrementTimerAveragingCounter(CPhysicsProfile::PTI_PREMOVING);
/* old */
/* old */ // remember old placement for lerping
/* old */ en_plLastPlacement = en_plPlacement;
/* old */
/* old */ // for each child of the mover
/* old */ {FOREACHINLIST(CEntity, en_lnInParent, en_lhChildren, itenChild) {
/* old */ // if the child is movable, yet not in movers list
/* old */ if ((itenChild->en_ulPhysicsFlags&EPF_MOVABLE)
/* old */ &&!((CMovableEntity*)&*itenChild)->en_lnInMovers.IsLinked()) {
/* old */ CMovableEntity *penChild = ((CMovableEntity*)&*itenChild);
/* old */ // remember old placement for lerping
/* old */ penChild->en_plLastPlacement = penChild->en_plPlacement;
/* old */ }
/* old */ }}
/* old */
/* old */ FLOAT fTickQuantum=_pTimer->TickQuantum; // used for normalizing from SI units to game ticks
/* old */
/* old */ // trig break point if required
/* old */ if( dbg_bBreak) {
/* old */ dbg_bBreak = FALSE; // auto turn off breakpoint when triggered
/* old */ try {
/* old */ Breakpoint();
/* old */ } catch (ANYEXCEPTION) {
/* old */ CPrintF("Breakpoint!\n");
/* old */ };
/* old */ }
/* old */
/* old */ // NOTE: this limits maximum velocity of any entity in game.
/* old */ // it is absolutely neccessary in order to prevent extreme slowdowns in physics.
/* old */ // if you plan to increase this one radically, consider decreasing
/* old */ // collision grid cell size!
/* old */ // currently limited to a bit less than speed of sound (not that it is any specificaly
/* old */ // relevant constant, but it is just handy)
/* old */ const FLOAT fMaxSpeed = 300.0f;
/* old */ en_vCurrentTranslationAbsolute(1)=Clamp(en_vCurrentTranslationAbsolute(1), -fMaxSpeed, +fMaxSpeed);
/* old */ en_vCurrentTranslationAbsolute(2)=Clamp(en_vCurrentTranslationAbsolute(2), -fMaxSpeed, +fMaxSpeed);
/* old */ en_vCurrentTranslationAbsolute(3)=Clamp(en_vCurrentTranslationAbsolute(3), -fMaxSpeed, +fMaxSpeed);
/* old */
/* old */ // if the entity is a model
/* old */ if (en_RenderType==RT_MODEL || en_RenderType==RT_EDITORMODEL) {
/* old */ // test for field containment
/* old */ TestFields(en_iUpContent, en_iDnContent, en_fImmersionFactor);
/* old */ // if entity has sticky feet
/* old */ if (en_ulPhysicsFlags & EPF_STICKYFEET) {
/* old */ // find gravity towards nearest polygon
/* old */ FLOAT3D vPoint;
/* old */ FLOATplane3D plPlane;
/* old */ FLOAT fDistanceToEdge;
/* old */ if (GetNearestPolygon(vPoint, plPlane, fDistanceToEdge)) {
/* old */ en_vGravityDir = -(FLOAT3D&)plPlane;
/* old */ }
/* old */ }
/* old */ }
/* old */ CContentType &ctDn = en_pwoWorld->wo_actContentTypes[en_iDnContent];
/* old */ CContentType &ctUp = en_pwoWorld->wo_actContentTypes[en_iUpContent];
/* old */
/* old */ // test entity breathing
/* old */ TestBreathing(ctUp);
/* old */ // test content damage
/* old */ TestContentDamage(ctDn, en_fImmersionFactor);
/* old */ // test surface damage
/* old */ if (en_penReference!=NULL) {
/* old */ CSurfaceType &stReference = en_pwoWorld->wo_astSurfaceTypes[en_iReferenceSurface];
/* old */ TestSurfaceDamage(stReference);
/* old */ }
/* old */
/* old */ // calculate content fluid factors
/* old */ FLOAT fBouyancy = (1-
/* old */ (ctDn.ct_fDensity/en_fDensity)*en_fImmersionFactor-
/* old */ (ctUp.ct_fDensity/en_fDensity)*(1-en_fImmersionFactor));
/* old */ FLOAT fSpeedModifier =
/* old */ ctDn.ct_fSpeedMultiplier*en_fImmersionFactor+
/* old */ ctUp.ct_fSpeedMultiplier*(1-en_fImmersionFactor);
/* old */ FLOAT fFluidFriction =
/* old */ ctDn.ct_fFluidFriction*en_fImmersionFactor+
/* old */ ctUp.ct_fFluidFriction*(1-en_fImmersionFactor);
/* old */ FLOAT fControlMultiplier =
/* old */ ctDn.ct_fControlMultiplier*en_fImmersionFactor+
/* old */ ctUp.ct_fControlMultiplier*(1-en_fImmersionFactor);
/* old */
/* old */ // transform relative desired translation into absolute
/* old */ FLOAT3D vDesiredTranslationAbsolute = en_vDesiredTranslationRelative;
/* old */ // relative absolute
/* old */ if (!(en_ulPhysicsFlags & EPF_ABSOLUTETRANSLATE)) {
/* old */ vDesiredTranslationAbsolute *= en_mRotation;
/* old */ }
/* old */ // transform translation and rotation into tick time units
/* old */ vDesiredTranslationAbsolute*=fTickQuantum;
/* old */ ANGLE3D aRotationRelative;
/* old */ aRotationRelative(1) = en_aDesiredRotationRelative(1)*fTickQuantum;
/* old */ aRotationRelative(2) = en_aDesiredRotationRelative(2)*fTickQuantum;
/* old */ aRotationRelative(3) = en_aDesiredRotationRelative(3)*fTickQuantum;
/* old */ // make absolute matrix rotation from relative angle rotation
/* old */ FLOATmatrix3D mRotationAbsolute;
/* old */
/* old */ if ((en_ulPhysicsFlags&EPF_ONBLOCK_MASK)==EPF_ONBLOCK_PUSH) {
/* old */ FLOATmatrix3D mNewRotation;
/* old */ MakeRotationMatrixFast(mNewRotation, en_plPlacement.pl_OrientationAngle+aRotationRelative);
/* old */ mRotationAbsolute = mNewRotation*!en_mRotation;
/* old */
/* old */ } else {
/* old */ MakeRotationMatrixFast(mRotationAbsolute, aRotationRelative);
/* old */ mRotationAbsolute = en_mRotation*(mRotationAbsolute*!en_mRotation);
/* old */ }
/* old */
/* old */ // modify desired speed for fluid parameters
/* old */ vDesiredTranslationAbsolute*=fSpeedModifier;
/* old */
/* old */ // remember jumping strength (if any)
/* old */ FLOAT fJump = -en_mRotation.GetColumn(2)%vDesiredTranslationAbsolute;
/* old */
/* old */ BOOL bReferenceMovingInY = FALSE;
/* old */ BOOL bReferenceRotatingNonY = FALSE;
/* old */ // if we have a CMovableEntity for a reference entity
/* old */ if (en_penReference!=NULL && (en_penReference->en_ulPhysicsFlags&EPF_MOVABLE)) {
/* old */ CMovableEntity *penReference = (CMovableEntity *)(CEntity*)en_penReference;
/* old */ // get reference deltas for this tick
/* old */ const FLOAT3D &vReferenceTranslation = penReference->en_vIntendedTranslation;
/* old */ const FLOATmatrix3D &mReferenceRotation = penReference->en_mIntendedRotation;
/* old */ // calculate radius of this entity relative to reference
/* old */ FLOAT3D vRadius = en_plPlacement.pl_PositionVector
/* old */ -penReference->en_plPlacement.pl_PositionVector;
/* old */ FLOAT3D vReferenceDelta = vReferenceTranslation + vRadius*mReferenceRotation - vRadius;
/* old */ // add the deltas to this entity
/* old */ vDesiredTranslationAbsolute += vReferenceDelta;
/* old */ mRotationAbsolute = mReferenceRotation*mRotationAbsolute;
/* old */
/* old */ // remember if reference is moving in y
/* old */ bReferenceMovingInY = (vReferenceDelta%en_vGravityDir != 0.0f);
/* old */ bReferenceRotatingNonY = ((en_vGravityDir*mReferenceRotation)%en_vGravityDir)>0.01f;
/* old */ }
/* old */
/* old */ FLOAT3D vTranslationAbsolute = en_vCurrentTranslationAbsolute*fTickQuantum;
/* old */
/* old */ // initially not orienting
/* old */ en_ulPhysicsFlags&=~EPF_ORIENTINGTOGRAVITY;
/* old */ // if the entity is rotated by gravity
/* old */ if (en_ulPhysicsFlags&EPF_ORIENTEDBYGRAVITY) {
/* old */ // find entity's down vector
/* old */ FLOAT3D vDown;
/* old */ vDown(1) = -en_mRotation(1,2);
/* old */ vDown(2) = -en_mRotation(2,2);
/* old */ vDown(3) = -en_mRotation(3,2);
/* old */
/* old */ // find angle entities down and gravity down
/* old */ FLOAT fCos = vDown%en_vGravityDir;
/* old */ // if substantial
/* old */ if (fCos<0.99999f) {
/* old */ // mark
/* old */ en_ulPhysicsFlags|=EPF_ORIENTINGTOGRAVITY;
/* old */
/* old */ // limit the angle rotation
/* old */ ANGLE a = ACos(fCos);
/* old */ if (Abs(a)>20) {
/* old */ a = 20*Sgn(a);
/* old */ }
/* old */ FLOAT fRad =RadAngle(a);
/* old */
/* old */ // make rotation axis
/* old */ FLOAT3D vAxis = vDown*en_vGravityDir;
/* old */ FLOAT fLen = vAxis.Length();
/* old */ if (fLen<0.01f) {
/* old */ vAxis(1) = en_mRotation(1,3);
/* old */ vAxis(2) = en_mRotation(2,3);
/* old */ vAxis(3) = en_mRotation(3,3);
/* old */ // NOTE: must have this patch for smooth rocking on moving brushes
/* old */ // (should infact do fRad/=fLen always)
/* old */ } else if (!bReferenceRotatingNonY) {
/* old */ fRad/=fLen;
/* old */ }
/* old */ vAxis*=fRad;
/* old */
/* old */ // make rotation matrix
/* old */ FLOATmatrix3D mGRotation;
/* old */ mGRotation(1,1) = 1; mGRotation(1,2) = -vAxis(3); mGRotation(1,3) = vAxis(2);
/* old */ mGRotation(2,1) = vAxis(3); mGRotation(2,2) = 1; mGRotation(2,3) = -vAxis(1);
/* old */ mGRotation(3,1) = -vAxis(2); mGRotation(3,2) = vAxis(1); mGRotation(3,3) = 1;
/* old */ OrthonormalizeRotationMatrix(mGRotation);
/* old */
/* old */ // add the gravity rotation
/* old */ mRotationAbsolute = mGRotation*mRotationAbsolute;
/* old */ }
/* old */ }
/* old */
/* old */ // initially not floating
/* old */ en_ulPhysicsFlags&=~EPF_FLOATING;
/* old */
/* old */ FLOAT ACC=en_fAcceleration*fTickQuantum*fTickQuantum;
/* old */ FLOAT DEC=en_fDeceleration*fTickQuantum*fTickQuantum;
/* old */ // if the entity is not affected by gravity
/* old */ if (!(en_ulPhysicsFlags&EPF_TRANSLATEDBYGRAVITY)) {
/* old */ // accellerate towards desired absolute translation
/* old */ if (en_ulPhysicsFlags&EPF_NOACCELERATION) {
/* old */ vTranslationAbsolute = vDesiredTranslationAbsolute;
/* old */ } else {
/* old */ AddAcceleration(vTranslationAbsolute, vDesiredTranslationAbsolute,
/* old */ ACC*fControlMultiplier,
/* old */ DEC*fControlMultiplier);
/* old */ }
/* old */ // if swimming
/* old */ } else if ((fBouyancy*en_fGravityA<0.5f && (ctDn.ct_ulFlags&(CTF_SWIMABLE|CTF_FLYABLE)))) {
/* old */ // mark that
/* old */ en_ulPhysicsFlags|=EPF_FLOATING;
/* old */ // accellerate towards desired absolute translation
/* old */ if (en_ulPhysicsFlags&EPF_NOACCELERATION) {
/* old */ vTranslationAbsolute = vDesiredTranslationAbsolute;
/* old */ } else {
/* old */ AddAcceleration(vTranslationAbsolute, vDesiredTranslationAbsolute,
/* old */ ACC*fControlMultiplier,
/* old */ DEC*fControlMultiplier);
/* old */ }
/* old */
/* old */ // add gravity acceleration
/* old */ if (fBouyancy<-0.1f) {
/* old */ FLOAT fGV=en_fGravityV*fTickQuantum*fSpeedModifier;
/* old */ FLOAT fGA=(en_fGravityA*-fBouyancy)*fTickQuantum*fTickQuantum;
/* old */ AddAcceleration(vTranslationAbsolute, en_vGravityDir*-fGV, fGA, fGA);
/* old */ } else if (fBouyancy>+0.1f) {
/* old */ FLOAT fGV=en_fGravityV*fTickQuantum*fSpeedModifier;
/* old */ FLOAT fGA=(en_fGravityA*fBouyancy)*fTickQuantum*fTickQuantum;
/* old */ AddAcceleration(vTranslationAbsolute, en_vGravityDir*fGV, fGA, fGA);
/* old */ }
/* old */
/* old */ // if the entity is affected by gravity
/* old */ } else {
/* old */ BOOL bGravityAlongPolygon = TRUE;
/* old */ // if there is no fixed remembered stand-on polygon or the entity is not on it anymore
/* old */ if (en_pbpoStandOn==NULL || !IsStandingOnPolygon(en_pbpoStandOn) || bReferenceMovingInY
/* old */ || (en_ulPhysicsFlags&EPF_ORIENTINGTOGRAVITY)) {
/* old */ // clear the stand on polygon
/* old */ en_pbpoStandOn=NULL;
/* old */ if (en_penReference == NULL || bReferenceMovingInY) {
/* old */ bGravityAlongPolygon = FALSE;
/* old */ }
/* old */ }
/* old */
/* old */ // if gravity can cause the entity to fall
/* old */ if (!bGravityAlongPolygon) {
/* old */ _pfPhysicsProfile.IncrementCounter(CPhysicsProfile::PCI_GRAVITY_NONTRIVIAL);
/* old */
/* old */ // add gravity acceleration
/* old */ FLOAT fGV=en_fGravityV*fTickQuantum*fSpeedModifier;
/* old */ FLOAT fGA=(en_fGravityA*fBouyancy)*fTickQuantum*fTickQuantum;
/* old */ AddGAcceleration(vTranslationAbsolute, en_vGravityDir, fGA, fGV);
/* old */ // if entity can only slide down its stand-on polygon
/* old */ } else {
/* old */ _pfPhysicsProfile.IncrementCounter(CPhysicsProfile::PCI_GRAVITY_TRIVIAL);
/* old */
/* old */ // disassemble gravity to parts parallel and normal to plane
/* old */ FLOAT3D vPolygonDir = -en_vReferencePlane;
/* old */ // NOTE: normal to plane=paralel to plane normal vector!
/* old */ FLOAT3D vGParallel, vGNormal;
/* old */ GetParallelAndNormalComponents(en_vGravityDir, vPolygonDir, vGNormal, vGParallel);
/* old */ // add gravity part parallel to plane
/* old */ FLOAT fFactor = vGParallel.Length();
/* old */
/* old */ if (fFactor>0.001f) {
/* old */ FLOAT fGV=en_fGravityV*fTickQuantum*fSpeedModifier;
/* old */ FLOAT fGA=(en_fGravityA*fBouyancy)*fTickQuantum*fTickQuantum;
/* old */ AddGAcceleration(vTranslationAbsolute, vGParallel/fFactor, fGA*fFactor, fGV*fFactor);
/* old */ }
/* old */
/* old */ // kill your normal-to-polygon speed if towards polygon and small
/* old */ FLOAT fPolyGA = (vPolygonDir%en_vGravityDir)*en_fGravityA;
/* old */ FLOAT fYSpeed = vPolygonDir%vTranslationAbsolute;
/* old */ if (fYSpeed>0 && fYSpeed < fPolyGA) {
/* old */ vTranslationAbsolute -= vPolygonDir*fYSpeed;
/* old */ }
/* old */
/* old */ // if a bouncer
/* old */ if ((en_ulPhysicsFlags&EPF_ONBLOCK_MASK)==EPF_ONBLOCK_BOUNCE) {
/* old */ // rotate slower
/* old */ en_aDesiredRotationRelative *= en_fJumpControlMultiplier;
/* old */ if (en_aDesiredRotationRelative.Length()<10) {
/* old */ en_aDesiredRotationRelative = ANGLE3D(0,0,0);
/* old */ }
/* old */ }
/* old */ }
/* old */
/* old */ CSurfaceType &stReference = en_pwoWorld->wo_astSurfaceTypes[en_iReferenceSurface];
/* old */
/* old */ // if it has a reference entity
/* old */ if (en_penReference!=NULL) {
/* old */ FLOAT fPlaneY = (en_vGravityDir%en_vReferencePlane);
/* old */ FLOAT fPlaneYAbs = Abs(fPlaneY);
/* old */ FLOAT fFriction = stReference.st_fFriction;
/* old */ // if on a steep slope
/* old */ if (fPlaneY>=-stReference.st_fClimbSlopeCos&&fPlaneY<0
/* old */ ||(stReference.st_ulFlags&STF_SLIDEDOWNSLOPE)&&fPlaneY>-0.99f) {
/* old */ en_ulPhysicsFlags|=EPF_ONSTEEPSLOPE;
/* old */ // accellerate horizontaly towards desired absolute translation
/* old */ AddAccelerationOnPlane2(
/* old */ vTranslationAbsolute,
/* old */ vDesiredTranslationAbsolute,
/* old */ ACC*fPlaneYAbs*fPlaneYAbs*fFriction*fControlMultiplier,
/* old */ DEC*fPlaneYAbs*fPlaneYAbs*fFriction*fControlMultiplier,
/* old */ en_vReferencePlane,
/* old */ en_vGravityDir);
/* old */ // if not on a steep slope
/* old */ } else {
/* old */ en_ulPhysicsFlags&=~EPF_ONSTEEPSLOPE;
/* old */ // accellerate on plane towards desired absolute translation
/* old */ AddAccelerationOnPlane(
/* old */ vTranslationAbsolute,
/* old */ vDesiredTranslationAbsolute,
/* old */ ACC*fPlaneYAbs*fPlaneYAbs*fFriction*fControlMultiplier,
/* old */ DEC*fPlaneYAbs*fPlaneYAbs*fFriction*fControlMultiplier,
/* old */ en_vReferencePlane);
/* old */ }
/* old */ // if wants to jump and can jump
/* old */ if (fJump<-0.01f && (fPlaneY<-stReference.st_fJumpSlopeCos
/* old */ || _pTimer->CurrentTick()>en_tmLastSignificantVerticalMovement+0.25f) ) {
/* old */ // jump
/* old */ vTranslationAbsolute += en_vGravityDir*fJump;
/* old */ en_tmJumped = _pTimer->CurrentTick();
/* old */ en_pbpoStandOn = NULL;
/* old */ }
/* old */
/* old */ // if it doesn't have a reference entity
/* old */ } else {//if (en_penReference==NULL)
/* old */ // if can control after jump
/* old */ if (_pTimer->CurrentTick()-en_tmJumped<en_tmMaxJumpControl) {
/* old */ // accellerate horizontaly, but slower
/* old */ AddAccelerationOnPlane(
/* old */ vTranslationAbsolute,
/* old */ vDesiredTranslationAbsolute,
/* old */ ACC*fControlMultiplier*en_fJumpControlMultiplier,
/* old */ DEC*fControlMultiplier*en_fJumpControlMultiplier,
/* old */ FLOATplane3D(en_vGravityDir, 0));
/* old */ }
/* old */
/* old */ // if wants to jump and can jump
/* old */ if (fJump<-0.01f &&
/* old */ _pTimer->CurrentTick()>en_tmLastSignificantVerticalMovement+0.25f) {
/* old */ // jump
/* old */ vTranslationAbsolute += en_vGravityDir*fJump;
/* old */ en_tmJumped = _pTimer->CurrentTick();
/* old */ en_pbpoStandOn = NULL;
/* old */ }
/* old */ }
/* old */ }
/* old */
/* old */ // check for force-field acceleration
/* old */ // NOTE: pulled out because of a bug in VC code generator, see function comments above
/* old */ CheckAndAddGAcceleration(this, vTranslationAbsolute, fTickQuantum);
/* old */
/* old */ // if there is fluid friction involved
/* old */ if (fFluidFriction>0.01f) {
/* old */ // slow down
/* old */ AddAcceleration(vTranslationAbsolute, FLOAT3D(0.0f, 0.0f, 0.0f),
/* old */ 0.0f, DEC*fFluidFriction);
/* old */ }
/* old */
/* old */ // if may slow down spinning
/* old */ if ( (en_ulPhysicsFlags& EPF_CANFADESPINNING) &&
/* old */ ( (ctDn.ct_ulFlags&CTF_FADESPINNING) || (ctUp.ct_ulFlags&CTF_FADESPINNING) ) ) {
/* old */ // reduce desired rotation
/* old */ en_aDesiredRotationRelative *= (1-fSpeedModifier*0.05f);
/* old */ if (en_aDesiredRotationRelative.Length()<10) {
/* old */ en_aDesiredRotationRelative = ANGLE3D(0,0,0);
/* old */ }
/* old */ }
/* old */
/* old */ // discard reference entity (will be recalculated)
/* old */ if (en_pbpoStandOn==NULL) {
/* old */ en_penReference = NULL;
/* old */ en_vReferencePlane = FLOAT3D(0.0f, 0.0f, 0.0f);
/* old */ en_iReferenceSurface = 0;
/* old */ }
/* old */
/* old */ en_vIntendedTranslation = vTranslationAbsolute;
/* old */ en_mIntendedRotation = mRotationAbsolute;
/* old */
/* old */ //-- estimate future movements for collision caching
/* old */
/* old */ // make box of the entity for its current rotation
/* old */ FLOATaabbox3D box;
/* old */ en_pciCollisionInfo->MakeBoxAtPlacement(FLOAT3D(0,0,0), en_mRotation, box);
/* old */ // if it is a light source
/* old */ {CLightSource *pls = GetLightSource();
/* old */ if (pls!=NULL && !(pls->ls_ulFlags&LSF_LENSFLAREONLY)) {
/* old */ // expand the box to be sure that it contains light range
/* old */ ASSERT(!(pls->ls_ulFlags&LSF_DIRECTIONAL));
/* old */ box |= FLOATaabbox3D(FLOAT3D(0,0,0), pls->ls_rFallOff);
/* old */ }}
/* old */ // add a bit around it
/* old */ box.ExpandByFactor( phy_fCollisionCacheAround-1.0f);
/* old */ // make box go few ticks ahead of the entity
/* old */ box += en_plPlacement.pl_PositionVector;
/* old */ en_boxMovingEstimate = box;
/* old */ box += en_vIntendedTranslation*phy_fCollisionCacheAhead;
/* old */ en_boxMovingEstimate |= box;
/* old */
/* old */ // clear applied movement to be updated during movement
/* old */ en_vAppliedTranslation = FLOAT3D(0.0f, 0.0f, 0.0f);
/* old */ en_mAppliedRotation.Diagonal(1.0f);
/* old */ _pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_PREMOVING);
/* old */// STREAMDUMP ExportEntityPlacementAndSpeed( *(CMovableEntity *)this, "Pre moving (end of function)");
/* old */ }
/* Calculate physics for moving. */
export void DoMoving(void)
{
if (en_pciCollisionInfo==NULL || (en_ulPhysicsFlags&EPF_FORCEADDED)) {
return;
}
// STREAMDUMP ExportEntityPlacementAndSpeed(*(CMovableEntity *)this, "Do moving (start of function)");
_pfPhysicsProfile.StartTimer(CPhysicsProfile::PTI_DOMOVING);
_pfPhysicsProfile.IncrementTimerAveragingCounter(CPhysicsProfile::PTI_DOMOVING);
_pfPhysicsProfile.IncrementCounter(CPhysicsProfile::PCI_DOMOVING);
FLOAT fTickQuantum=_pTimer->TickQuantum; // used for normalizing from SI units to game ticks
// if rotation and translation are synchronized
if (en_ulPhysicsFlags&EPF_RT_SYNCHRONIZED) {
_pfPhysicsProfile.IncrementCounter(CPhysicsProfile::PCI_DOMOVING_SYNC);
// move both in translation and rotation
en_vMoveTranslation = en_vIntendedTranslation-en_vAppliedTranslation;
en_mMoveRotation = en_mIntendedRotation*!en_mAppliedRotation;
InitTryToMove();
CMovableEntity *penPusher = NULL;
if ((en_ulPhysicsFlags&EPF_ONBLOCK_MASK)==EPF_ONBLOCK_PUSH) {
penPusher = this;
}
BOOL bMoveSuccessfull = TryToMove(penPusher, TRUE, TRUE);
// if rotation and translation are asynchronious
} else {
ASSERT((en_ulPhysicsFlags&EPF_ONBLOCK_MASK)!=EPF_ONBLOCK_PUSH);
_pfPhysicsProfile.IncrementCounter(CPhysicsProfile::PCI_DOMOVING_ASYNC);
// if there is no reference
if (en_penReference == NULL) {
_pfPhysicsProfile.IncrementCounter(CPhysicsProfile::PCI_DOMOVING_ASYNC_SYNCTRY);
// try to do simple move both in translation and rotation
en_vMoveTranslation = en_vIntendedTranslation-en_vAppliedTranslation;
en_mMoveRotation = en_mIntendedRotation*!en_mAppliedRotation;
InitTryToMove();
_ctTryToMoveCheckCounter = 4; // no retries
BOOL bMoveSuccessfull = TryToMove(NULL, TRUE, TRUE);
// if it passes
if (bMoveSuccessfull) {
// finish
_pfPhysicsProfile.IncrementCounter(CPhysicsProfile::PCI_DOMOVING_ASYNC_SYNCPASS);
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_DOMOVING);
// STREAMDUMP ExportEntityPlacementAndSpeed(*(CMovableEntity *)this, "Do moving (return: if it passes)");
return;
}
}
_pfPhysicsProfile.IncrementCounter(CPhysicsProfile::PCI_DOMOVING_ASYNC_TRANSLATE);
// translate
en_vMoveTranslation = en_vIntendedTranslation-en_vAppliedTranslation;
InitTryToMove();
TryToMove(NULL, TRUE, FALSE);
// rotate
en_mMoveRotation = en_mIntendedRotation*!en_mAppliedRotation;
if (
en_mMoveRotation(1,1)!=1 || en_mMoveRotation(1,2)!=0 || en_mMoveRotation(1,3)!=0 ||
en_mMoveRotation(2,1)!=0 || en_mMoveRotation(2,2)!=1 || en_mMoveRotation(2,3)!=0 ||
en_mMoveRotation(3,1)!=0 || en_mMoveRotation(3,2)!=0 || en_mMoveRotation(3,3)!=1) {
_pfPhysicsProfile.IncrementCounter(CPhysicsProfile::PCI_DOMOVING_ASYNC_ROTATE);
InitTryToMove();
TryToMove(NULL, FALSE, TRUE);
}
}
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_DOMOVING);
// STREAMDUMP ExportEntityPlacementAndSpeed(*(CMovableEntity *)this, "Do moving (end of function)");
}
// calculate consequences of moving/not moving in this tick
export void PostMoving(void)
{
if (en_pciCollisionInfo==NULL) {
// mark for removing from list of movers
en_ulFlags |= ENF_INRENDERING;
return;
}
if (en_ulPhysicsFlags&EPF_FORCEADDED) {
en_ulPhysicsFlags&=~EPF_FORCEADDED;
return;
}
// STREAMDUMP ExportEntityPlacementAndSpeed(*(CMovableEntity *)this, "Post moving (start of function)");
_pfPhysicsProfile.StartTimer(CPhysicsProfile::PTI_POSTMOVING);
_pfPhysicsProfile.IncrementTimerAveragingCounter(CPhysicsProfile::PTI_POSTMOVING);
// remember valid reference if valid
if (en_penReference!=NULL) {
en_penLastValidReference = en_penReference;
}
// remember original translation
FLOAT3D vOldTranslation = en_vCurrentTranslationAbsolute;
FLOAT fTickQuantum=_pTimer->TickQuantum; // used for normalizing from SI units to game ticks
// calculate current velocity from movements applied in this tick
en_vCurrentTranslationAbsolute = en_vAppliedTranslation/fTickQuantum;
// remember significant movements
if (Abs(en_vCurrentTranslationAbsolute%en_vGravityDir)>0.1f) {
en_tmLastSignificantVerticalMovement = _pTimer->CurrentTick();
}
ClearNextPosition();
// calculate speed change between needed and possible (in m/s)
FLOAT3D vSpeedDelta = en_vIntendedTranslation - en_vAppliedTranslation;
FLOAT fSpeedDelta = vSpeedDelta.Length()/fTickQuantum;
// if it is large change and can be damaged by impact
if (fSpeedDelta>en_fCollisionSpeedLimit &&
!(en_ulPhysicsFlags&EPF_NOIMPACTTHISTICK)) {
// inflict impact damage
FLOAT fDamage = ((fSpeedDelta-en_fCollisionSpeedLimit)/en_fCollisionSpeedLimit)*en_fCollisionDamageFactor;
InflictDirectDamage(this, MiscDamageInflictor(), DMT_IMPACT, fDamage,
en_plPlacement.pl_PositionVector, -vSpeedDelta.Normalize());
}
en_ulPhysicsFlags&=~EPF_NOIMPACTTHISTICK;
// remember old speed for Touch reactions
en_vIntendedTranslation = vOldTranslation;
// if not moving anymore
if (en_vCurrentTranslationAbsolute.ManhattanNorm()<0.001f
&&(en_vDesiredTranslationRelative.ManhattanNorm()==0 || en_fAcceleration==0)
&&en_aDesiredRotationRelative.ManhattanNorm()==0) {
// if there is a reference
if (en_penReference!=NULL) {
// it the reference is movable
if (en_penReference->en_ulPhysicsFlags&EPF_MOVABLE) {
CMovableEntity *penReference = (CMovableEntity *)(CEntity*)en_penReference;
// if the reference is not in the list of movers
if (!penReference->en_lnInMovers.IsLinked()) {
// mark for removing from list of movers
en_ulFlags |= ENF_INRENDERING;
}
// if the reference is not movable
} else {
// mark for removing from list of movers
en_ulFlags |= ENF_INRENDERING;
}
// if there is no reference
} else {
// if no gravity and no forces can affect this entity
if (
(!(en_ulPhysicsFlags&(EPF_TRANSLATEDBYGRAVITY|EPF_ORIENTEDBYGRAVITY))
|| en_fGravityA==0.0f)) { // !!!! test for forces also when implemented
// mark for removing from list of movers
en_ulFlags |= ENF_INRENDERING;
}
}
// if should remove from movers list
if (en_ulFlags&ENF_INRENDERING) {
// clear last placement
en_plLastPlacement = en_plPlacement;
}
}
// remember new position for particles
if (en_plpLastPositions!=NULL) {
en_plpLastPositions->AddPosition(en_vNextPosition);
}
// if should filter predictions
extern BOOL _bPredictionActive;
if (_bPredictionActive && (IsPredictable() || IsPredictor())) {
CMovableEntity *penTail = (CMovableEntity *)GetPredictedSafe(this);
TIME tmNow = _pTimer->CurrentTick();
if (penTail->en_tmLastPredictionHead<-1) {
penTail->en_vLastHead = en_plPlacement.pl_PositionVector;
penTail->en_vPredError = FLOAT3D(0,0,0);
penTail->en_vPredErrorLast = FLOAT3D(0,0,0);
}
// if this is a predictor
if (IsPredictor()) {
// if a new prediction of old prediction head, or just started prediction
if (penTail->en_tmLastPredictionHead==tmNow || penTail->en_tmLastPredictionHead<0) {
// remember error
penTail->en_vPredErrorLast = penTail->en_vPredError;
penTail->en_vPredError +=
en_plPlacement.pl_PositionVector-penTail->en_vLastHead;
// remember last head
penTail->en_vLastHead = en_plPlacement.pl_PositionVector;
// if this is really head of prediction chain
if (IsPredictionHead()) {
// remember the time
penTail->en_tmLastPredictionHead = tmNow;
}
// if newer than last prediction head
} else if (tmNow>penTail->en_tmLastPredictionHead) {
// just remember head and time
penTail->en_vLastHead = en_plPlacement.pl_PositionVector;
penTail->en_tmLastPredictionHead = tmNow;
}
// if prediction is of for this entity
} else if (!(en_ulFlags&ENF_WILLBEPREDICTED)) {
// if it was on before
if (penTail->en_tmLastPredictionHead>0) {
// remember error
penTail->en_vPredErrorLast = penTail->en_vPredError;
penTail->en_vPredError +=
en_plPlacement.pl_PositionVector-penTail->en_vLastHead;
}
// remember this as head
penTail->en_vLastHead = en_plPlacement.pl_PositionVector;
penTail->en_tmLastPredictionHead = -1;
}
// if this is head of chain
if (IsPredictionHead()) {
// fade error
penTail->en_vPredErrorLast = penTail->en_vPredError;
penTail->en_vPredError *= cli_fPredictionFilter;
// FLOAT fErrLen = penTail->en_vPredError.Length();
// if (fErrLen>0) {
// penTail->en_vPredError=penTail->en_vPredError/fErrLen*ClampDn(fErrLen-cli_fPredictionCorrection, 0.0f);
// }
}
}
//CPrintF("\n%f", _pTimer->CurrentTick());
_pfPhysicsProfile.StopTimer(CPhysicsProfile::PTI_POSTMOVING);
// STREAMDUMP ExportEntityPlacementAndSpeed(*(CMovableEntity *)this, "Post moving (end of function)");
}
// call this if you move without collision
export void CacheNearPolygons(void)
{
CClipMove cm(this);
cm.CacheNearPolygons();
}
// returns bytes of memory used by this object
SLONG GetUsedMemory(void)
{
// init
SLONG slUsedMemory = sizeof(CMovableEntity) - sizeof(CRationalEntity) + CRationalEntity::GetUsedMemory();
// add some more
slUsedMemory += en_apbpoNearPolygons.sa_Count * sizeof(CBrushPolygon*);
return slUsedMemory;
}
procedures:
// must have at least one procedure per class
Dummy() {};
};