Serious-Engine/Sources/Entities/Player.es
Daniel Gibson b30d3d86d8 Fix compilation from changes for GCC 6.1.1
At least on my system floor() and log10() return double, so the other
arguments to Clamp() and ClampDn() didn't match anymore, now being float
instead of double.
I replaced the calls to log10f() and floorf() to avoid any ambiguities.
2016-05-30 01:06:10 +02:00

5889 lines
200 KiB
C++

401
%{
#include "Entities/StdH/StdH.h"
#include <Engine/Build.h>
#include <Engine/Network/Network.h>
#include <locale.h>
#include "Models/Player/SeriousSam/Player.h"
#include "Models/Player/SeriousSam/Body.h"
#include "Models/Player/SeriousSam/Head.h"
#include "Entities/PlayerMarker.h"
#include "Entities/PlayerWeapons.h"
#include "Entities/PlayerAnimator.h"
#include "Entities/PlayerView.h"
#include "Entities/MovingBrush.h"
#include "Entities/Switch.h"
#include "Entities/MessageHolder.h"
#include "Entities/Camera.h"
#include "Entities/WorldLink.h"
#include "Entities/HealthItem.h"
#include "Entities/ArmorItem.h"
#include "Entities/WeaponItem.h"
#include "Entities/AmmoItem.h"
#include "Entities/MessageItem.h"
#include "Entities/AmmoPack.h"
#include "Entities/KeyItem.h"
#include "Entities/MusicHolder.h"
#include "Entities/EnemyBase.h"
#include "Entities/PlayerActionMarker.h"
#include "Entities/BasicEffects.h"
#include "Entities/BackgroundViewer.h"
#include "Entities/WorldSettingsController.h"
extern void JumpFromBouncer(CEntity *penToBounce, CEntity *penBouncer);
// from game
#define GRV_SHOWEXTRAS (1L<<0) // add extra stuff like console, weapon, pause
%}
uses "Entities/WorldLink";
enum PlayerViewType {
0 PVT_PLAYEREYES "",
1 PVT_PLAYERAUTOVIEW "",
2 PVT_SCENECAMERA "",
3 PVT_3RDPERSONVIEW "",
};
enum PlayerState {
0 PST_STAND "",
1 PST_CROUCH "",
2 PST_SWIM "",
3 PST_DIVE "",
4 PST_FALL "",
};
// event for starting cinematic camera sequence
event ECameraStart {
CEntityPointer penCamera, // the camera
};
// event for ending cinematic camera sequence
event ECameraStop {
CEntityPointer penCamera, // the camera
};
// sent when needs to rebirth
event ERebirth {
};
// sent when player was disconnected from game
event EDisconnected {
};
// starts automatic player actions
event EAutoAction {
CEntityPointer penFirstMarker,
};
%{
extern void DrawHUD( const CPlayer *penPlayerCurrent, CDrawPort *pdpCurrent, BOOL bSnooping);
extern void InitHUD(void);
extern void EndHUD(void);
static CTimerValue _tvProbingLast;
// used to render certain entities only for certain players (like picked items, etc.)
ULONG _ulPlayerRenderingMask = 0;
#define NAME name
const FLOAT _fBlowUpAmmount = 70.0f;
// computer message adding flags
#define CMF_READ (1L<<0)
#define CMF_ANALYZE (1L<<1)
struct MarkerDistance {
public:
FLOAT md_fMinD;
CPlayerMarker *md_ppm;
void Clear(void);
};
int qsort_CompareMarkerDistance(const void *pv0, const void *pv1)
{
MarkerDistance &md0 = *(MarkerDistance*)pv0;
MarkerDistance &md1 = *(MarkerDistance*)pv1;
if( md0.md_fMinD<md1.md_fMinD) return +1;
else if( md0.md_fMinD>md1.md_fMinD) return -1;
else return 0;
}
static inline FLOAT IntensityAtDistance( FLOAT fFallOff, FLOAT fHotSpot, FLOAT fDistance)
{
// intensity is zero if further than fall-off range
if( fDistance>fFallOff) return 0.0f;
// intensity is maximum if closer than hot-spot range
if( fDistance<fHotSpot) return 1.0f;
// interpolate if between fall-off and hot-spot range
return (fFallOff-fDistance)/(fFallOff-fHotSpot);
}
static CTString MakeEmptyString(INDEX ctLen, char ch=' ')
{
char ach[2];
ach[0] = ch;
ach[1] = 0;
CTString strSpaces;
for (INDEX i=0; i<ctLen; i++) {
strSpaces+=ach;
}
return strSpaces;
}
// take a two line string and align into one line of minimum given length
static int _ctAlignWidth = 20;
static CTString AlignString(const CTString &strOrg)
{
// split into two lines
CTString strL = strOrg;
strL.OnlyFirstLine();
CTString strR = strOrg;
strR.RemovePrefix(strL);
strR.DeleteChar(0);
// get their lengths
INDEX iLenL = strL.LengthNaked();
INDEX iLenR = strR.LengthNaked();
// find number of spaces to insert
INDEX ctSpaces = _ctAlignWidth-(iLenL+iLenR);
if (ctSpaces<1) {
ctSpaces=1;
}
// make aligned string
return strL+MakeEmptyString(ctSpaces)+strR;
}
static CTString CenterString(const CTString &str)
{
INDEX ctSpaces = (_ctAlignWidth-str.LengthNaked())/2;
if (ctSpaces<0) {
ctSpaces=0;
}
return MakeEmptyString(ctSpaces)+str;
}
static CTString PadStringRight(const CTString &str, INDEX iLen)
{
INDEX ctSpaces = iLen-str.LengthNaked();
if (ctSpaces<0) {
ctSpaces=0;
}
return str+MakeEmptyString(ctSpaces);
}
static CTString PadStringLeft(const CTString &str, INDEX iLen)
{
INDEX ctSpaces = iLen-str.LengthNaked();
if (ctSpaces<0) {
ctSpaces=0;
}
return MakeEmptyString(ctSpaces)+str;
}
static void KillAllEnemies(CEntity *penKiller)
{
// for each entity in the world
{FOREACHINDYNAMICCONTAINER(penKiller->GetWorld()->wo_cenEntities, CEntity, iten) {
CEntity *pen = iten;
if (IsDerivedFromClass(pen, "Enemy Base") && !IsOfClass(pen, "Devil")) {
CEnemyBase *penEnemy = (CEnemyBase *)pen;
if (penEnemy->m_penEnemy==NULL) {
continue;
}
penKiller->InflictDirectDamage(pen, penKiller, DMT_BULLET,
penEnemy->GetHealth()+1, pen->GetPlacement().pl_PositionVector, FLOAT3D(0,1,0));
}
}}
}
#define HEADING_MAX 45.0f
#define PITCH_MAX 90.0f
#define BANKING_MAX 45.0f
// player flags
#define PLF_INITIALIZED (1UL<<0) // set when player entity is ready to function
#define PLF_VIEWROTATIONCHANGED (1UL<<1) // for adjusting view rotation separately from legs
#define PLF_JUMPALLOWED (1UL<<2) // if jumping is allowed
#define PLF_SYNCWEAPON (1UL<<3) // weapon model needs to be synchronized before rendering
#define PLF_AUTOMOVEMENTS (1UL<<4) // complete automatic control of movements
#define PLF_DONTRENDER (1UL<<5) // don't render view (used at end of level)
#define PLF_CHANGINGLEVEL (1UL<<6) // mark that we next are to appear at start of new level
#define PLF_APPLIEDACTION (1UL<<7) // used to detect when player is not connected
#define PLF_NOTCONNECTED (1UL<<8) // set if the player is not connected
#define PLF_LEVELSTARTED (1UL<<9) // marks that level start time was recorded
#define PLF_RESPAWNINPLACE (1UL<<10) // don't move to marker when respawning (for current death only)
// defines representing flags used to fill player buttoned actions
#define PLACT_FIRE (1L<<0)
#define PLACT_RELOAD (1L<<1)
#define PLACT_WEAPON_NEXT (1L<<2)
#define PLACT_WEAPON_PREV (1L<<3)
#define PLACT_WEAPON_FLIP (1L<<4)
#define PLACT_USE (1L<<5)
#define PLACT_COMPUTER (1L<<6)
#define PLACT_3RD_PERSON_VIEW (1L<<7)
#define PLACT_CENTER_VIEW (1L<<8)
#define PLACT_SELECT_WEAPON_SHIFT (9)
#define PLACT_SELECT_WEAPON_MASK (0x1FL<<PLACT_SELECT_WEAPON_SHIFT)
#define MAX_WEAPONS 30
#define PICKEDREPORT_TIME (2.0f) // how long (picked-up) message stays on screen
// is player spying another player
//extern TIME _tmSnoopingStarted;
//extern CEntity *_penTargeting;
struct PlayerControls {
FLOAT3D aRotation;
FLOAT3D aViewRotation;
FLOAT3D vTranslation;
BOOL bMoveForward;
BOOL bMoveBackward;
BOOL bMoveLeft;
BOOL bMoveRight;
BOOL bMoveUp;
BOOL bMoveDown;
BOOL bTurnLeft;
BOOL bTurnRight;
BOOL bTurnUp;
BOOL bTurnDown;
BOOL bTurnBankingLeft;
BOOL bTurnBankingRight;
BOOL bCenterView;
BOOL bLookLeft;
BOOL bLookRight;
BOOL bLookUp;
BOOL bLookDown;
BOOL bLookBankingLeft;
BOOL bLookBankingRight;
BOOL bSelectWeapon[MAX_WEAPONS+1];
BOOL bWeaponNext;
BOOL bWeaponPrev;
BOOL bWeaponFlip;
BOOL bWalk;
BOOL bStrafe;
BOOL bStrafeFB;
BOOL bFire;
BOOL bReload;
BOOL bUse;
BOOL bComputer;
BOOL bUseOrComputer;
BOOL bUseOrComputerLast; // for internal use
BOOL b3rdPersonView;
};
static struct PlayerControls pctlCurrent;
// cheats
static INDEX cht_iGoToMarker = -1;
static INDEX cht_bKillAll = FALSE;
static INDEX cht_bGiveAll = FALSE;
static INDEX cht_bOpen = FALSE;
static INDEX cht_bAllMessages= FALSE;
static INDEX cht_bRefresh = FALSE;
INDEX cht_bGod = FALSE;
INDEX cht_bFly = FALSE;
INDEX cht_bGhost = FALSE;
INDEX cht_bInvisible = FALSE;
FLOAT cht_fTranslationMultiplier = 1.0f;
INDEX cht_bEnable = 0;
// interface control
static INDEX hud_bShowAll = TRUE; // used internaly in menu/console
INDEX hud_bShowWeapon = TRUE;
INDEX hud_bShowMessages = TRUE;
INDEX hud_bShowInfo = TRUE;
INDEX hud_bShowLatency = FALSE;
INDEX hud_iShowPlayers = -1; // auto
INDEX hud_iSortPlayers = -1; // auto
FLOAT hud_fOpacity = 0.9f;
FLOAT hud_fScaling = 1.0f;
FLOAT hud_tmWeaponsOnScreen = 3.0f;
FLOAT hud_tmLatencySnapshot = 1.0f;
FLOAT plr_fBreathingStrength = 0.0f;
extern FLOAT plr_tmSnoopingTime;
INDEX cht_bKillFinalBoss = FALSE;
INDEX cht_bDebugFinalBoss = FALSE;
INDEX cht_bDumpFinalBossData = FALSE;
INDEX cht_bDebugFinalBossAnimations = FALSE;
INDEX cht_bDumpPlayerShading = FALSE;
FLOAT wpn_fRecoilSpeed[17] = {0};
FLOAT wpn_fRecoilLimit[17] = {0};
FLOAT wpn_fRecoilDampUp[17] = {0};
FLOAT wpn_fRecoilDampDn[17] = {0};
FLOAT wpn_fRecoilOffset[17] = {0};
FLOAT wpn_fRecoilFactorP[17] = {0};
FLOAT wpn_fRecoilFactorZ[17] = {0};
// misc
static FLOAT plr_fAcceleration = 100.0f;
static FLOAT plr_fDeceleration = 60.0f;
static FLOAT plr_fSpeedForward = 10.0f;
static FLOAT plr_fSpeedBackward = 10.0f;
static FLOAT plr_fSpeedSide = 10.0f;
static FLOAT plr_fSpeedUp = 11.0f;
static FLOAT plr_fViewHeightStand = 1.9f;
static FLOAT plr_fViewHeightCrouch = 0.7f;
static FLOAT plr_fViewHeightSwim = 0.4f;
static FLOAT plr_fViewHeightDive = 0.0f;
FLOAT plr_fViewDampFactor = 0.4f;
FLOAT plr_fViewDampLimitGroundUp = 0.1f;
FLOAT plr_fViewDampLimitGroundDn = 0.4f;
FLOAT plr_fViewDampLimitWater = 0.1f;
static FLOAT plr_fFrontClipDistance = 0.25f;
static FLOAT plr_fFOV = 90.0f;
static FLOAT net_tmLatencyAvg;
INDEX plr_bRenderPicked = FALSE;
INDEX plr_bRenderPickedParticles = FALSE;
INDEX plr_bOnlySam = FALSE;
CTString plr_strLastLevel = "";
INDEX ent_bReportBrokenChains = FALSE;
FLOAT ent_tmMentalIn = 0.5f;
FLOAT ent_tmMentalOut = 0.75f;
FLOAT ent_tmMentalFade = 0.5f;
// prediction control vars
FLOAT cli_fPredictPlayersRange = 0.0f;
FLOAT cli_fPredictItemsRange = 3.0f;
FLOAT cli_tmPredictFoe = 10.0f;
FLOAT cli_tmPredictAlly = 10.0f;
FLOAT cli_tmPredictEnemy = 10.0f;
static FLOAT plr_fSwimSoundDelay = 0.8f;
static FLOAT plr_fDiveSoundDelay = 1.6f;
static FLOAT plr_fWalkSoundDelay = 0.5f;
static FLOAT plr_fRunSoundDelay = 0.3f;
static FLOAT ctl_tmComputerDoubleClick = 0.5f; // double click delay for calling computer
static FLOAT _tmLastUseOrCompPressed = -10.0f; // for computer doubleclick
// speeds for button rotation
static FLOAT ctl_fButtonRotationSpeedH = 300.0f;
static FLOAT ctl_fButtonRotationSpeedP = 150.0f;
static FLOAT ctl_fButtonRotationSpeedB = 150.0f;
// modifier for axis strafing
static FLOAT ctl_fAxisStrafingModifier = 1.0f;
// !=NULL if some player wants to call computer
DECL_DLL class CPlayer *cmp_ppenPlayer = NULL;
// !=NULL for rendering computer on secondary display in dualhead
DECL_DLL class CPlayer *cmp_ppenDHPlayer = NULL;
// set to update current message in background mode (for dualhead)
DECL_DLL BOOL cmp_bUpdateInBackground = FALSE;
// set for initial calling computer without rendering game
DECL_DLL BOOL cmp_bInitialStart = FALSE;
// game sets this for player hud and statistics and hiscore sound playing
DECL_DLL INDEX plr_iHiScore = 0.0f;
// these define address and size of player controls structure
DECL_DLL void *ctl_pvPlayerControls = &pctlCurrent;
DECL_DLL const SLONG ctl_slPlayerControlsSize = sizeof(pctlCurrent);
// called to compose action packet from current controls
DECL_DLL void ctl_ComposeActionPacket(const CPlayerCharacter &pc, CPlayerAction &paAction, BOOL bPreScan)
{
// allow double axis controls
paAction.pa_aRotation += paAction.pa_aViewRotation;
CPlayerSettings *pps = (CPlayerSettings *)pc.pc_aubAppearance;
// CPrintF("compose: prescan %d, x:%g\n", bPreScan, paAction.pa_aRotation(1));
// if strafing
if (pctlCurrent.bStrafe) {
// move rotation left/right into translation left/right
paAction.pa_vTranslation(1) = -paAction.pa_aRotation(1)*ctl_fAxisStrafingModifier;
paAction.pa_aRotation(1) = 0;
}
// if strafing forward/backward
if (pctlCurrent.bStrafeFB) {
// move rotation up/down into translation forward/backward
paAction.pa_vTranslation(3) = -paAction.pa_aRotation(2)*ctl_fAxisStrafingModifier;
paAction.pa_aRotation(2) = 0;
}
// if centering view
if (pctlCurrent.bCenterView) {
// don't allow moving view up/down
paAction.pa_aRotation(2) = 0;
}
// multiply axis actions with speed
paAction.pa_vTranslation(1) *= plr_fSpeedSide;
paAction.pa_vTranslation(2) *= plr_fSpeedUp;
if (paAction.pa_vTranslation(3)<0) {
paAction.pa_vTranslation(3) *= plr_fSpeedForward;
} else {
paAction.pa_vTranslation(3) *= plr_fSpeedBackward;
}
// find local player, if any
CPlayer *penThis = NULL;
INDEX ctPlayers = CEntity::GetMaxPlayers();
for (INDEX iPlayer = 0; iPlayer<ctPlayers; iPlayer++) {
CPlayer *pen=(CPlayer *)CEntity::GetPlayerEntity(iPlayer);
if (pen!=NULL && pen->en_pcCharacter==pc) {
penThis = pen;
break;
}
}
// if not found
if (penThis==NULL) {
// do nothing
return;
}
// accumulate local rotation
penThis->m_aLocalRotation +=paAction.pa_aRotation;
penThis->m_aLocalViewRotation+=paAction.pa_aViewRotation;
penThis->m_vLocalTranslation +=paAction.pa_vTranslation;
// if prescanning
if (bPreScan) {
// no button checking
return;
}
// add button movement/rotation/look actions to the axis actions
if(pctlCurrent.bMoveForward || (pctlCurrent.bStrafeFB&&pctlCurrent.bTurnUp) ) paAction.pa_vTranslation(3) -= plr_fSpeedForward;
if(pctlCurrent.bMoveBackward || (pctlCurrent.bStrafeFB&&pctlCurrent.bTurnDown)) paAction.pa_vTranslation(3) += plr_fSpeedBackward;
if(pctlCurrent.bMoveLeft || (pctlCurrent.bStrafe&&pctlCurrent.bTurnLeft) ) paAction.pa_vTranslation(1) -= plr_fSpeedSide;
if(pctlCurrent.bMoveRight || (pctlCurrent.bStrafe&&pctlCurrent.bTurnRight)) paAction.pa_vTranslation(1) += plr_fSpeedSide;
if(pctlCurrent.bMoveUp ) paAction.pa_vTranslation(2) += plr_fSpeedUp;
if(pctlCurrent.bMoveDown ) paAction.pa_vTranslation(2) -= plr_fSpeedUp;
const FLOAT fQuantum = _pTimer->TickQuantum;
if(pctlCurrent.bTurnLeft && !pctlCurrent.bStrafe) penThis->m_aLocalRotation(1) += ctl_fButtonRotationSpeedH*fQuantum;
if(pctlCurrent.bTurnRight && !pctlCurrent.bStrafe) penThis->m_aLocalRotation(1) -= ctl_fButtonRotationSpeedH*fQuantum;
if(pctlCurrent.bTurnUp && !pctlCurrent.bStrafeFB) penThis->m_aLocalRotation(2) += ctl_fButtonRotationSpeedP*fQuantum;
if(pctlCurrent.bTurnDown && !pctlCurrent.bStrafeFB) penThis->m_aLocalRotation(2) -= ctl_fButtonRotationSpeedP*fQuantum;
if(pctlCurrent.bTurnBankingLeft ) penThis->m_aLocalRotation(3) += ctl_fButtonRotationSpeedB*fQuantum;
if(pctlCurrent.bTurnBankingRight ) penThis->m_aLocalRotation(3) -= ctl_fButtonRotationSpeedB*fQuantum;
if(pctlCurrent.bLookLeft ) penThis->m_aLocalViewRotation(1) += ctl_fButtonRotationSpeedH*fQuantum;
if(pctlCurrent.bLookRight ) penThis->m_aLocalViewRotation(1) -= ctl_fButtonRotationSpeedH*fQuantum;
if(pctlCurrent.bLookUp ) penThis->m_aLocalViewRotation(2) += ctl_fButtonRotationSpeedP*fQuantum;
if(pctlCurrent.bLookDown ) penThis->m_aLocalViewRotation(2) -= ctl_fButtonRotationSpeedP*fQuantum;
if(pctlCurrent.bLookBankingLeft ) penThis->m_aLocalViewRotation(3) += ctl_fButtonRotationSpeedB*fQuantum;
if(pctlCurrent.bLookBankingRight ) penThis->m_aLocalViewRotation(3) -= ctl_fButtonRotationSpeedB*fQuantum;
// use current accumulated rotation
paAction.pa_aRotation = penThis->m_aLocalRotation;
paAction.pa_aViewRotation = penThis->m_aLocalViewRotation;
//paAction.pa_vTranslation = penThis->m_vLocalTranslation;
// if walking
if(pctlCurrent.bWalk) {
// make forward/backward and sidestep speeds slower
paAction.pa_vTranslation(3) /= 2.0f;
paAction.pa_vTranslation(1) /= 2.0f;
}
// reset all button actions
paAction.pa_ulButtons = 0;
// set weapon selection bits
for(INDEX i=1; i<MAX_WEAPONS; i++) {
if(pctlCurrent.bSelectWeapon[i]) {
paAction.pa_ulButtons = i<<PLACT_SELECT_WEAPON_SHIFT;
break;
}
}
// set button pressed flags
if(pctlCurrent.bWeaponNext) paAction.pa_ulButtons |= PLACT_WEAPON_NEXT;
if(pctlCurrent.bWeaponPrev) paAction.pa_ulButtons |= PLACT_WEAPON_PREV;
if(pctlCurrent.bWeaponFlip) paAction.pa_ulButtons |= PLACT_WEAPON_FLIP;
if(pctlCurrent.bFire) paAction.pa_ulButtons |= PLACT_FIRE;
if(pctlCurrent.bReload) paAction.pa_ulButtons |= PLACT_RELOAD;
if(pctlCurrent.bUse) paAction.pa_ulButtons |= PLACT_USE;
if(pctlCurrent.bComputer) paAction.pa_ulButtons |= PLACT_COMPUTER;
if(pctlCurrent.b3rdPersonView) paAction.pa_ulButtons |= PLACT_3RD_PERSON_VIEW;
if(pctlCurrent.bCenterView) paAction.pa_ulButtons |= PLACT_CENTER_VIEW;
// if userorcomp just pressed
if(pctlCurrent.bUseOrComputer && !pctlCurrent.bUseOrComputerLast) {
// if double-click is off
if (ctl_tmComputerDoubleClick==0 || (pps->ps_ulFlags&PSF_COMPSINGLECLICK)) {
// press both
paAction.pa_ulButtons |= PLACT_USE|PLACT_COMPUTER;
// if double-click is on
} else {
// if double click
if (_pTimer->GetRealTimeTick()<=_tmLastUseOrCompPressed+ctl_tmComputerDoubleClick) {
// computer pressed
paAction.pa_ulButtons |= PLACT_COMPUTER;
// if single click
} else {
// use pressed
paAction.pa_ulButtons |= PLACT_USE;
}
}
_tmLastUseOrCompPressed = _pTimer->GetRealTimeTick();
}
// remember old userorcomp pressed state
pctlCurrent.bUseOrComputerLast = pctlCurrent.bUseOrComputer;
};
void CPlayer_Precache(void)
{
CDLLEntityClass *pdec = &CPlayer_DLLClass;
// precache view
extern void CPlayerView_Precache(void);
CPlayerView_Precache();
// precache all player sounds
pdec->PrecacheSound(SOUND_WATER_ENTER );
pdec->PrecacheSound(SOUND_WATER_LEAVE );
pdec->PrecacheSound(SOUND_WALK_L );
pdec->PrecacheSound(SOUND_WALK_R );
pdec->PrecacheSound(SOUND_WALK_SAND_L );
pdec->PrecacheSound(SOUND_WALK_SAND_R );
pdec->PrecacheSound(SOUND_SWIM_L );
pdec->PrecacheSound(SOUND_SWIM_R );
pdec->PrecacheSound(SOUND_DIVE_L );
pdec->PrecacheSound(SOUND_DIVE_R );
pdec->PrecacheSound(SOUND_DIVEIN );
pdec->PrecacheSound(SOUND_DIVEOUT );
pdec->PrecacheSound(SOUND_DROWN );
pdec->PrecacheSound(SOUND_INHALE0 );
pdec->PrecacheSound(SOUND_JUMP );
pdec->PrecacheSound(SOUND_LAND );
pdec->PrecacheSound(SOUND_WOUNDWEAK );
pdec->PrecacheSound(SOUND_WOUNDMEDIUM );
pdec->PrecacheSound(SOUND_WOUNDSTRONG );
pdec->PrecacheSound(SOUND_WOUNDWATER );
pdec->PrecacheSound(SOUND_DEATH );
pdec->PrecacheSound(SOUND_DEATHWATER );
pdec->PrecacheSound(SOUND_WATERAMBIENT );
pdec->PrecacheSound(SOUND_WATERBUBBLES );
pdec->PrecacheSound(SOUND_WATERWALK_L );
pdec->PrecacheSound(SOUND_WATERWALK_R );
pdec->PrecacheSound(SOUND_INHALE1 );
pdec->PrecacheSound(SOUND_INHALE2 );
pdec->PrecacheSound(SOUND_INFO );
// pdec->PrecacheSound(SOUND_HIGHSCORE );
pdec->PrecacheClass(CLASS_BASIC_EFFECT, BET_TELEPORT);
pdec->PrecacheModel(MODEL_FLESH);
pdec->PrecacheModel(MODEL_FLESH_APPLE);
pdec->PrecacheModel(MODEL_FLESH_BANANA);
pdec->PrecacheModel(MODEL_FLESH_BURGER);
pdec->PrecacheTexture(TEXTURE_FLESH_RED);
pdec->PrecacheTexture(TEXTURE_FLESH_GREEN);
pdec->PrecacheTexture(TEXTURE_FLESH_APPLE);
pdec->PrecacheTexture(TEXTURE_FLESH_BANANA);
pdec->PrecacheTexture(TEXTURE_FLESH_BURGER);
pdec->PrecacheTexture(TEXTURE_FLESH_LOLLY);
pdec->PrecacheTexture(TEXTURE_FLESH_ORANGE);
pdec->PrecacheClass(CLASS_BASIC_EFFECT, BET_BLOODSPILL);
pdec->PrecacheClass(CLASS_BASIC_EFFECT, BET_BLOODSTAIN);
pdec->PrecacheClass(CLASS_BASIC_EFFECT, BET_BLOODSTAINGROW);
pdec->PrecacheClass(CLASS_BASIC_EFFECT, BET_BLOODEXPLODE);
}
void CPlayer_OnInitClass(void)
{
// clear current player controls
memset(&pctlCurrent, 0, sizeof(pctlCurrent));
// declare player control variables
_pShell->DeclareSymbol("user INDEX ctl_bMoveForward;", &pctlCurrent.bMoveForward);
_pShell->DeclareSymbol("user INDEX ctl_bMoveBackward;", &pctlCurrent.bMoveBackward);
_pShell->DeclareSymbol("user INDEX ctl_bMoveLeft;", &pctlCurrent.bMoveLeft);
_pShell->DeclareSymbol("user INDEX ctl_bMoveRight;", &pctlCurrent.bMoveRight);
_pShell->DeclareSymbol("user INDEX ctl_bMoveUp;", &pctlCurrent.bMoveUp);
_pShell->DeclareSymbol("user INDEX ctl_bMoveDown;", &pctlCurrent.bMoveDown);
_pShell->DeclareSymbol("user INDEX ctl_bTurnLeft;", &pctlCurrent.bTurnLeft);
_pShell->DeclareSymbol("user INDEX ctl_bTurnRight;", &pctlCurrent.bTurnRight);
_pShell->DeclareSymbol("user INDEX ctl_bTurnUp;", &pctlCurrent.bTurnUp);
_pShell->DeclareSymbol("user INDEX ctl_bTurnDown;", &pctlCurrent.bTurnDown);
_pShell->DeclareSymbol("user INDEX ctl_bTurnBankingLeft;", &pctlCurrent.bTurnBankingLeft);
_pShell->DeclareSymbol("user INDEX ctl_bTurnBankingRight;", &pctlCurrent.bTurnBankingRight);
_pShell->DeclareSymbol("user INDEX ctl_bCenterView;", &pctlCurrent.bCenterView);
_pShell->DeclareSymbol("user INDEX ctl_bLookLeft;", &pctlCurrent.bLookLeft);
_pShell->DeclareSymbol("user INDEX ctl_bLookRight;", &pctlCurrent.bLookRight);
_pShell->DeclareSymbol("user INDEX ctl_bLookUp;", &pctlCurrent.bLookUp);
_pShell->DeclareSymbol("user INDEX ctl_bLookDown;", &pctlCurrent.bLookDown);
_pShell->DeclareSymbol("user INDEX ctl_bLookBankingLeft;", &pctlCurrent.bLookBankingLeft);
_pShell->DeclareSymbol("user INDEX ctl_bLookBankingRight;", &pctlCurrent.bLookBankingRight );
_pShell->DeclareSymbol("user INDEX ctl_bWalk;", &pctlCurrent.bWalk);
_pShell->DeclareSymbol("user INDEX ctl_bStrafe;", &pctlCurrent.bStrafe);
_pShell->DeclareSymbol("user INDEX ctl_bStrafeFB;", &pctlCurrent.bStrafeFB);
_pShell->DeclareSymbol("user INDEX ctl_bFire;", &pctlCurrent.bFire);
_pShell->DeclareSymbol("user INDEX ctl_bReload;", &pctlCurrent.bReload);
_pShell->DeclareSymbol("user INDEX ctl_bUse;", &pctlCurrent.bUse);
_pShell->DeclareSymbol("user INDEX ctl_bComputer;", &pctlCurrent.bComputer);
_pShell->DeclareSymbol("user INDEX ctl_bUseOrComputer;", &pctlCurrent.bUseOrComputer);
_pShell->DeclareSymbol("user INDEX ctl_b3rdPersonView;", &pctlCurrent.b3rdPersonView);
_pShell->DeclareSymbol("user INDEX ctl_bWeaponNext;", &pctlCurrent.bWeaponNext);
_pShell->DeclareSymbol("user INDEX ctl_bWeaponPrev;", &pctlCurrent.bWeaponPrev);
_pShell->DeclareSymbol("user INDEX ctl_bWeaponFlip;", &pctlCurrent.bWeaponFlip);
_pShell->DeclareSymbol("user INDEX ctl_bSelectWeapon[30+1];", &pctlCurrent.bSelectWeapon);
_pShell->DeclareSymbol("persistent user FLOAT ctl_tmComputerDoubleClick;", &ctl_tmComputerDoubleClick);
_pShell->DeclareSymbol("persistent user FLOAT ctl_fButtonRotationSpeedH;", &ctl_fButtonRotationSpeedH);
_pShell->DeclareSymbol("persistent user FLOAT ctl_fButtonRotationSpeedP;", &ctl_fButtonRotationSpeedP);
_pShell->DeclareSymbol("persistent user FLOAT ctl_fButtonRotationSpeedB;", &ctl_fButtonRotationSpeedB);
_pShell->DeclareSymbol("persistent user FLOAT ctl_fAxisStrafingModifier;", &ctl_fAxisStrafingModifier);
_pShell->DeclareSymbol("user FLOAT plr_fSwimSoundDelay;", &plr_fSwimSoundDelay);
_pShell->DeclareSymbol("user FLOAT plr_fDiveSoundDelay;", &plr_fDiveSoundDelay);
_pShell->DeclareSymbol("user FLOAT plr_fWalkSoundDelay;", &plr_fWalkSoundDelay);
_pShell->DeclareSymbol("user FLOAT plr_fRunSoundDelay;", &plr_fRunSoundDelay);
_pShell->DeclareSymbol("persistent user FLOAT cli_fPredictPlayersRange;",&cli_fPredictPlayersRange);
_pShell->DeclareSymbol("persistent user FLOAT cli_fPredictItemsRange;", &cli_fPredictItemsRange );
_pShell->DeclareSymbol("persistent user FLOAT cli_tmPredictFoe;", &cli_tmPredictFoe );
_pShell->DeclareSymbol("persistent user FLOAT cli_tmPredictAlly;", &cli_tmPredictAlly );
_pShell->DeclareSymbol("persistent user FLOAT cli_tmPredictEnemy;", &cli_tmPredictEnemy );
_pShell->DeclareSymbol(" INDEX hud_bShowAll;", &hud_bShowAll);
_pShell->DeclareSymbol("user INDEX hud_bShowInfo;", &hud_bShowInfo);
_pShell->DeclareSymbol("user const FLOAT net_tmLatencyAvg;", &net_tmLatencyAvg);
_pShell->DeclareSymbol("persistent user INDEX hud_bShowLatency;", &hud_bShowLatency);
_pShell->DeclareSymbol("persistent user INDEX hud_iShowPlayers;", &hud_iShowPlayers);
_pShell->DeclareSymbol("persistent user INDEX hud_iSortPlayers;", &hud_iSortPlayers);
_pShell->DeclareSymbol("persistent user INDEX hud_bShowWeapon;", &hud_bShowWeapon);
_pShell->DeclareSymbol("persistent user INDEX hud_bShowMessages;",&hud_bShowMessages);
_pShell->DeclareSymbol("persistent user FLOAT hud_fScaling;", &hud_fScaling);
_pShell->DeclareSymbol("persistent user FLOAT hud_fOpacity;", &hud_fOpacity);
_pShell->DeclareSymbol("persistent user FLOAT hud_tmWeaponsOnScreen;", &hud_tmWeaponsOnScreen);
_pShell->DeclareSymbol("persistent user FLOAT hud_tmLatencySnapshot;", &hud_tmLatencySnapshot);
_pShell->DeclareSymbol("persistent user FLOAT plr_fBreathingStrength;", &plr_fBreathingStrength);
_pShell->DeclareSymbol("INDEX cht_bKillFinalBoss;", &cht_bKillFinalBoss);
_pShell->DeclareSymbol("INDEX cht_bDebugFinalBoss;", &cht_bDebugFinalBoss);
_pShell->DeclareSymbol("INDEX cht_bDumpFinalBossData;", &cht_bDumpFinalBossData);
_pShell->DeclareSymbol("INDEX cht_bDebugFinalBossAnimations;", &cht_bDebugFinalBossAnimations);
_pShell->DeclareSymbol("INDEX cht_bDumpPlayerShading;", &cht_bDumpPlayerShading);
_pShell->DeclareSymbol("persistent user FLOAT wpn_fRecoilSpeed[17];", &wpn_fRecoilSpeed);
_pShell->DeclareSymbol("persistent user FLOAT wpn_fRecoilLimit[17];", &wpn_fRecoilLimit);
_pShell->DeclareSymbol("persistent user FLOAT wpn_fRecoilDampUp[17];", &wpn_fRecoilDampUp);
_pShell->DeclareSymbol("persistent user FLOAT wpn_fRecoilDampDn[17];", &wpn_fRecoilDampDn);
_pShell->DeclareSymbol("persistent user FLOAT wpn_fRecoilOffset[17];", &wpn_fRecoilOffset);
_pShell->DeclareSymbol("persistent user FLOAT wpn_fRecoilFactorP[17];", &wpn_fRecoilFactorP);
_pShell->DeclareSymbol("persistent user FLOAT wpn_fRecoilFactorZ[17];", &wpn_fRecoilFactorZ);
// cheats
_pShell->DeclareSymbol("user INDEX cht_bGod;", &cht_bGod);
_pShell->DeclareSymbol("user INDEX cht_bFly;", &cht_bFly);
_pShell->DeclareSymbol("user INDEX cht_bGhost;", &cht_bGhost);
_pShell->DeclareSymbol("user INDEX cht_bInvisible;", &cht_bInvisible);
_pShell->DeclareSymbol("user INDEX cht_bGiveAll;", &cht_bGiveAll);
_pShell->DeclareSymbol("user INDEX cht_bKillAll;", &cht_bKillAll);
_pShell->DeclareSymbol("user INDEX cht_bOpen;", &cht_bOpen);
_pShell->DeclareSymbol("user INDEX cht_bAllMessages;", &cht_bAllMessages);
_pShell->DeclareSymbol("user FLOAT cht_fTranslationMultiplier ;", &cht_fTranslationMultiplier);
_pShell->DeclareSymbol("user INDEX cht_bRefresh;", &cht_bRefresh);
// this one is masqueraded cheat enable variable
_pShell->DeclareSymbol("INDEX cht_bEnable;", &cht_bEnable);
// this cheat is always enabled
_pShell->DeclareSymbol("user INDEX cht_iGoToMarker;", &cht_iGoToMarker);
// player speed and view parameters, not declared except in internal build
#if 0
_pShell->DeclareSymbol("user FLOAT plr_fViewHeightStand;", &plr_fViewHeightStand);
_pShell->DeclareSymbol("user FLOAT plr_fViewHeightCrouch;",&plr_fViewHeightCrouch);
_pShell->DeclareSymbol("user FLOAT plr_fViewHeightSwim;", &plr_fViewHeightSwim);
_pShell->DeclareSymbol("user FLOAT plr_fViewHeightDive;", &plr_fViewHeightDive);
_pShell->DeclareSymbol("user FLOAT plr_fViewDampFactor;", &plr_fViewDampFactor);
_pShell->DeclareSymbol("user FLOAT plr_fViewDampLimitGroundUp;", &plr_fViewDampLimitGroundUp);
_pShell->DeclareSymbol("user FLOAT plr_fViewDampLimitGroundDn;", &plr_fViewDampLimitGroundDn);
_pShell->DeclareSymbol("user FLOAT plr_fViewDampLimitWater;", &plr_fViewDampLimitWater);
_pShell->DeclareSymbol("user FLOAT plr_fAcceleration;", &plr_fAcceleration);
_pShell->DeclareSymbol("user FLOAT plr_fDeceleration;", &plr_fDeceleration);
_pShell->DeclareSymbol("user FLOAT plr_fSpeedForward;", &plr_fSpeedForward);
_pShell->DeclareSymbol("user FLOAT plr_fSpeedBackward;", &plr_fSpeedBackward);
_pShell->DeclareSymbol("user FLOAT plr_fSpeedSide;", &plr_fSpeedSide);
_pShell->DeclareSymbol("user FLOAT plr_fSpeedUp;", &plr_fSpeedUp);
#endif
_pShell->DeclareSymbol("persistent user FLOAT plr_fFOV;", (void*) &plr_fFOV);
_pShell->DeclareSymbol("persistent user FLOAT plr_fFrontClipDistance;", (void*) &plr_fFrontClipDistance);
_pShell->DeclareSymbol("persistent user INDEX plr_bRenderPicked;", (void*) &plr_bRenderPicked);
_pShell->DeclareSymbol("persistent user INDEX plr_bRenderPickedParticles;", (void*) &plr_bRenderPickedParticles);
_pShell->DeclareSymbol("persistent user INDEX plr_bOnlySam;", (void*) &plr_bOnlySam);
_pShell->DeclareSymbol("persistent user INDEX ent_bReportBrokenChains;", (void*) &ent_bReportBrokenChains);
_pShell->DeclareSymbol("persistent user FLOAT ent_tmMentalIn ;", (void*) &ent_tmMentalIn );
_pShell->DeclareSymbol("persistent user FLOAT ent_tmMentalOut ;", (void*) &ent_tmMentalOut );
_pShell->DeclareSymbol("persistent user FLOAT ent_tmMentalFade;", (void*) &ent_tmMentalFade);
_pShell->DeclareSymbol("persistent user CTString plr_strLastLevel;", (void*) &plr_strLastLevel);
// player appearance interface
_pShell->DeclareSymbol("INDEX SetPlayerAppearance(INDEX, INDEX, INDEX, INDEX);", (void *)&SetPlayerAppearance);
// call player weapons persistant variable initialization
extern void CPlayerWeapons_Init(void);
CPlayerWeapons_Init();
// initialize HUD
InitHUD();
// precache
CPlayer_Precache();
}
// clean up
void CPlayer_OnEndClass(void)
{
EndHUD();
}
CTString GetDifficultyString(void)
{
if (GetSP()->sp_bMental) { return TRANS("Mental"); }
switch (GetSP()->sp_gdGameDifficulty) {
case CSessionProperties::GD_TOURIST: return TRANS("Tourist");
case CSessionProperties::GD_EASY: return TRANS("Easy");
default:
case CSessionProperties::GD_NORMAL: return TRANS("Normal");
case CSessionProperties::GD_HARD: return TRANS("Hard");
case CSessionProperties::GD_EXTREME: return TRANS("Serious");
}
}
// armor & health constants getters
FLOAT MaxArmor(void)
{
if (GetSP()->sp_gdGameDifficulty<=CSessionProperties::GD_EASY) {
return 300;
} else {
return 200;
}
}
FLOAT TopArmor(void)
{
if (GetSP()->sp_gdGameDifficulty<=CSessionProperties::GD_EASY) {
return 200;
} else {
return 100;
}
}
FLOAT MaxHealth(void)
{
if (GetSP()->sp_gdGameDifficulty<=CSessionProperties::GD_EASY) {
return 300;
} else {
return 200;
}
}
FLOAT TopHealth(void)
{
if (GetSP()->sp_gdGameDifficulty<=CSessionProperties::GD_EASY) {
return 200;
} else {
return 100;
}
}
// info structure
static EntityInfo eiPlayerGround = {
EIBT_FLESH, 80.0f,
0.0f, 1.7f, 0.0f, // source (eyes)
0.0f, 1.0f, 0.0f, // target (body)
};
static EntityInfo eiPlayerCrouch = {
EIBT_FLESH, 80.0f,
0.0f, 1.2f, 0.0f, // source (eyes)
0.0f, 0.7f, 0.0f, // target (body)
};
static EntityInfo eiPlayerSwim = {
EIBT_FLESH, 40.0f,
0.0f, 0.0f, 0.0f, // source (eyes)
0.0f, 0.0f, 0.0f, // target (body)
};
// animation light specific
#define LIGHT_ANIM_MINIGUN 2
#define LIGHT_ANIM_TOMMYGUN 3
#define LIGHT_ANIM_COLT_SHOTGUN 4
#define LIGHT_ANIM_NONE 5
const char *NameForState(PlayerState pst)
{
switch(pst) {
case PST_STAND: return "stand";
case PST_CROUCH: return "crouch";
case PST_FALL: return "fall";
case PST_SWIM: return "swim";
case PST_DIVE: return "dive";
default: return "???";
};
}
// print explanation on how a player died
void PrintPlayerDeathMessage(CPlayer *ppl, const EDeath &eDeath)
{
CTString strMyName = ppl->GetPlayerName();
CEntity *penKiller = eDeath.eLastDamage.penInflictor;
// if killed by a valid entity
if (penKiller!=NULL) {
// if killed by a player
if (IsOfClass(penKiller, "Player")) {
// if not self
if (penKiller!=ppl) {
CTString strKillerName = ((CPlayer*)penKiller)->GetPlayerName();
if(eDeath.eLastDamage.dmtType==DMT_TELEPORT) {
CPrintF(TRANS("%s telefragged %s\n"), (const char*)strKillerName, (const char*)strMyName);
} else if(eDeath.eLastDamage.dmtType==DMT_CLOSERANGE) {
CPrintF(TRANS("%s cut %s into pieces\n"), (const char*)strKillerName, (const char*)strMyName);
} else if(eDeath.eLastDamage.dmtType==DMT_BULLET) {
CPrintF(TRANS("%s poured lead into %s\n"), (const char*)strKillerName, (const char*)strMyName);
} else if(eDeath.eLastDamage.dmtType==DMT_PROJECTILE || eDeath.eLastDamage.dmtType==DMT_EXPLOSION) {
CPrintF(TRANS("%s blew %s away\n"), (const char*)strKillerName, (const char*)strMyName);
} else if(eDeath.eLastDamage.dmtType==DMT_CANNONBALL) {
CPrintF(TRANS("%s smashed %s with a cannon\n"), (const char*)strKillerName, (const char*)strMyName);
} else if(eDeath.eLastDamage.dmtType==DMT_CANNONBALL_EXPLOSION) {
CPrintF(TRANS("%s nuked %s\n"), (const char*)strKillerName, (const char*)strMyName);
} else {
CPrintF(TRANS("%s killed %s\n"), (const char*)strKillerName, (const char*)strMyName);
}
} else {
// make message from damage type
switch(eDeath.eLastDamage.dmtType) {
case DMT_DROWNING: CPrintF(TRANS("%s drowned\n"), (const char*)strMyName); break;
case DMT_BURNING: CPrintF(TRANS("%s burst into flames\n"), (const char*)strMyName); break;
case DMT_SPIKESTAB: CPrintF(TRANS("%s fell into a spike-hole\n"), (const char*)strMyName); break;
case DMT_FREEZING: CPrintF(TRANS("%s has frozen\n"), (const char*)strMyName); break;
case DMT_ACID: CPrintF(TRANS("%s dissolved\n"), (const char*)strMyName); break;
case DMT_PROJECTILE:
case DMT_EXPLOSION:
CPrintF(TRANS("%s blew himself away\n"), (const char*)strMyName); break;
default: CPrintF(TRANS("%s has committed suicide\n"), (const char*)strMyName);
}
}
// if killed by an enemy
} else if (IsDerivedFromClass(penKiller, "Enemy Base")) {
// check for telefrag first
if(eDeath.eLastDamage.dmtType==DMT_TELEPORT) {
CPrintF(TRANS("%s was telefragged\n"), (const char*)strMyName);
return;
}
// describe how this enemy killed player
CPrintF("%s\n", (const char*)((CEnemyBase*)penKiller)->GetPlayerKillDescription(strMyName, eDeath));
// if killed by some other entity
} else {
// make message from damage type
switch(eDeath.eLastDamage.dmtType) {
case DMT_SPIKESTAB: CPrintF(TRANS("%s was pierced\n"), (const char*)strMyName); break;
case DMT_BRUSH: CPrintF(TRANS("%s was squashed\n"), (const char*)strMyName); break;
case DMT_ABYSS: CPrintF(TRANS("%s went over the edge\n"), (const char*)strMyName); break;
case DMT_IMPACT: CPrintF(TRANS("%s swashed\n"), (const char*)strMyName); break;
case DMT_HEAT: CPrintF(TRANS("%s stood in the sun for too long\n"), (const char*)strMyName); break;
default: CPrintF(TRANS("%s passed away\n"), (const char*)strMyName);
}
}
// if no entity pointer (shouldn't happen)
} else {
CPrintF(TRANS("%s is missing in action\n"), (const char*)strMyName);
}
}
%}
class export CPlayer : CPlayerEntity {
name "Player";
thumbnail "";
features "ImplementsOnInitClass", "ImplementsOnEndClass", "CanBePredictable";
properties:
1 CTString m_strName "Name" = "<unnamed player>",
2 COLOR m_ulLastButtons = 0x0, // buttons last pressed
3 FLOAT m_fArmor = 0.0f, // armor
4 CTString m_strGroup = "", // group name for world change
5 INDEX m_ulKeys = 0, // mask for all picked-up keys
6 FLOAT m_fMaxHealth = 1, // default health supply player can have
7 INDEX m_ulFlags = 0, // various flags
16 CEntityPointer m_penWeapons, // player weapons
17 CEntityPointer m_penAnimator, // player animator
18 CEntityPointer m_penView, // player view
19 CEntityPointer m_pen3rdPersonView, // player 3rd person view
20 INDEX m_iViewState=PVT_PLAYEREYES, // view state
21 INDEX m_iLastViewState=PVT_PLAYEREYES, // last view state
26 CAnimObject m_aoLightAnimation, // light animation object
27 FLOAT m_fDamageAmmount = 0.0f, // how much was last wound
28 FLOAT m_tmWoundedTime = 0.0f, // when was last wound
29 FLOAT m_tmScreamTime = 0.0f, // when was last wound sound played
34 enum PlayerState m_pstState = PST_STAND, // current player state
35 FLOAT m_fFallTime = 0.0f, // time passed when falling
36 FLOAT m_fSwimTime = 0.0f, // time when started swimming
45 FLOAT m_tmOutOfWater = 0.0f, // time when got out of water last time
37 FLOAT m_tmMoveSound = 0.0f, // last time move sound was played
38 BOOL m_bMoveSoundLeft = TRUE, // left or right walk channel is current
39 FLOAT m_tmNextAmbientOnce = 0.0f, // next time to play local ambient sound
43 FLOAT m_tmMouthSoundLast = 0.0f, // time last played some repeating mouth sound
40 CEntityPointer m_penCamera, // camera for current cinematic sequence, or null
41 CTString m_strCenterMessage="", // center message
42 FLOAT m_tmCenterMessageEnd = 0.0f, // last time to show centered message
48 BOOL m_bPendingMessage = FALSE, // message sound pending to be played
47 FLOAT m_tmMessagePlay = 0.0f, // when to play the message sound
49 FLOAT m_tmAnalyseEnd = 0.0f, // last time to show analysation
50 BOOL m_bComputerInvoked = FALSE, // set if computer was invoked at least once
57 FLOAT m_tmAnimateInbox = -100.0f, // show animation of inbox icon animation
44 CEntityPointer m_penMainMusicHolder,
51 FLOAT m_tmLastDamage = -1.0f,
52 FLOAT m_fMaxDamageAmmount = 0.0f,
53 FLOAT3D m_vDamage = FLOAT3D(0,0,0),
54 FLOAT m_tmSpraySpawned = -1.0f,
55 FLOAT m_fSprayDamage = 0.0f,
56 CEntityPointer m_penSpray,
// sounds
60 CSoundObject m_soWeapon0,
61 CSoundObject m_soWeapon1,
62 CSoundObject m_soWeapon2,
63 CSoundObject m_soWeapon3,
70 CSoundObject m_soMouth, // breating, yelling etc.
71 CSoundObject m_soFootL, // walking etc.
72 CSoundObject m_soFootR,
73 CSoundObject m_soBody, // splashing etc.
74 CSoundObject m_soLocalAmbientLoop, // local ambient that only this player hears
75 CSoundObject m_soLocalAmbientOnce, // local ambient that only this player hears
76 CSoundObject m_soMessage, // message sounds
77 CSoundObject m_soHighScore, // high score sound
78 CSoundObject m_soSpeech, // for quotes
81 INDEX m_iMana = 0, // current score worth for killed player
94 FLOAT m_fManaFraction = 0.0f,// fractional part of mana, for slow increase with time
84 INDEX m_iHighScore = 0, // internal hiscore for demo playing
85 INDEX m_iBeatenHighScore = 0, // hiscore that was beaten
89 FLOAT m_tmLatency = 0.0f, // player-server latency (in seconds)
// for latency averaging
88 FLOAT m_tmLatencyLastAvg = 0.0f,
87 FLOAT m_tmLatencyAvgSum = 0.0f,
86 INDEX m_ctLatencyAvg = 0,
96 BOOL m_bEndOfLevel = FALSE,
97 BOOL m_bEndOfGame = FALSE,
98 INDEX m_iMayRespawn = 0, // must get to 2 to be able to respawn
99 FLOAT m_tmSpawned = 0.0f, // when player was spawned
100 FLOAT3D m_vDied = FLOAT3D(0,0,0), // where player died (for respawn in-place)
101 FLOAT3D m_aDied = FLOAT3D(0,0,0),
// statistics
103 FLOAT m_tmEstTime = 0.0f, // time estimated for this level
105 INDEX m_iTimeScore = 0,
106 INDEX m_iStartTime = 0, // game start time (ansi c time_t type)
107 INDEX m_iEndTime = 0, // game end time (ansi c time_t type)
108 FLOAT m_tmLevelStarted = 0.0f, // game time when level started
93 CTString m_strLevelStats = "", // detailed statistics for each level
// auto action vars
110 CEntityPointer m_penActionMarker, // current marker for auto actions
111 FLOAT m_fAutoSpeed = 0.0f, // speed to go towards the marker
112 INDEX m_iAutoOrgWeapon = 0, // original weapon for autoactions
113 FLOAT3D m_vAutoSpeed = FLOAT3D(0,0,0),
114 FLOAT m_tmSpiritStart = 0.0f,
115 FLOAT m_tmFadeStart = 0.0f,
// 'picked up' display vars
120 FLOAT m_tmLastPicked = -10000.0f, // when something was last picked up
121 CTString m_strPickedName = "", // name of item picked
122 FLOAT m_fPickedAmmount = 0.0f, // total picked ammount
123 FLOAT m_fPickedMana = 0.0f, // total picked mana
// shaker values
130 INDEX m_iLastHealth = 0,
131 INDEX m_iLastArmor = 0,
132 INDEX m_iLastAmmo = 0,
135 FLOAT m_tmHealthChanged = -9,
136 FLOAT m_tmArmorChanged = -9,
137 FLOAT m_tmAmmoChanged = -9,
138 FLOAT m_tmMinigunAutoFireStart = -1.0f,
150 FLOAT3D m_vLastStain = FLOAT3D(0,0,0), // where last stain was left
// for mouse lag elimination via prescanning
151 ANGLE3D m_aLastRotation = FLOAT3D(0,0,0),
152 ANGLE3D m_aLastViewRotation = FLOAT3D(0,0,0),
153 FLOAT3D m_vLastTranslation = FLOAT3D(0,0,0),
154 ANGLE3D m_aLocalRotation = FLOAT3D(0,0,0),
155 ANGLE3D m_aLocalViewRotation = FLOAT3D(0,0,0),
156 FLOAT3D m_vLocalTranslation = FLOAT3D(0,0,0),
{
ShellLaunchData ShellLaunchData_array; // array of data describing flying empty shells
INDEX m_iFirstEmptySLD; // index of last added empty shell
ULONG ulButtonsNow; ULONG ulButtonsBefore;
ULONG ulNewButtons;
ULONG ulReleasedButtons;
// listener
CSoundListener sliSound;
// light
CLightSource m_lsLightSource;
TIME m_tmPredict; // time to predict the entity to
// all messages in the inbox
CDynamicStackArray<CCompMessageID> m_acmiMessages;
INDEX m_ctUnreadMessages;
// statistics
PlayerStats m_psLevelStats;
PlayerStats m_psLevelTotal;
PlayerStats m_psGameStats;
PlayerStats m_psGameTotal;
CModelObject m_moRender; // model object to render - this one can be customized
}
components:
1 class CLASS_PLAYER_WEAPONS "Classes\\PlayerWeapons.ecl",
2 class CLASS_PLAYER_ANIMATOR "Classes\\PlayerAnimator.ecl",
3 class CLASS_PLAYER_VIEW "Classes\\PlayerView.ecl",
4 class CLASS_BASIC_EFFECT "Classes\\BasicEffect.ecl",
5 class CLASS_BLOOD_SPRAY "Classes\\BloodSpray.ecl",
// sounds
150 sound SOUND_WATER_ENTER "Sounds\\Player\\WaterEnter.wav",
151 sound SOUND_WATER_LEAVE "Sounds\\Player\\WaterLeave.wav",
152 sound SOUND_WALK_L "Sounds\\Player\\WalkL.wav",
153 sound SOUND_WALK_R "Sounds\\Player\\WalkR.wav",
154 sound SOUND_SWIM_L "Sounds\\Player\\SwimL.wav",
155 sound SOUND_SWIM_R "Sounds\\Player\\SwimR.wav",
156 sound SOUND_DIVE_L "Sounds\\Player\\Dive.wav",
157 sound SOUND_DIVE_R "Sounds\\Player\\Dive.wav",
158 sound SOUND_DIVEIN "Sounds\\Player\\DiveIn.wav",
159 sound SOUND_DIVEOUT "Sounds\\Player\\DiveOut.wav",
160 sound SOUND_DROWN "Sounds\\Player\\Drown.wav",
161 sound SOUND_INHALE0 "Sounds\\Player\\Inhale00.wav",
162 sound SOUND_JUMP "Sounds\\Player\\Jump.wav",
163 sound SOUND_LAND "Sounds\\Player\\Land.wav",
166 sound SOUND_DEATH "Sounds\\Player\\Death.wav",
167 sound SOUND_DEATHWATER "Sounds\\Player\\DeathWater.wav",
168 sound SOUND_WATERAMBIENT "Sounds\\Player\\Underwater.wav",
169 sound SOUND_WATERBUBBLES "Sounds\\Player\\Bubbles.wav",
170 sound SOUND_WATERWALK_L "Sounds\\Player\\WalkWaterL.wav",
171 sound SOUND_WATERWALK_R "Sounds\\Player\\WalkWaterR.wav",
172 sound SOUND_INHALE1 "Sounds\\Player\\Inhale01.wav",
173 sound SOUND_INHALE2 "Sounds\\Player\\Inhale02.wav",
174 sound SOUND_INFO "Sounds\\Player\\Info.wav",
175 sound SOUND_WALK_SAND_L "Sounds\\Player\\WalkSandL.wav",
176 sound SOUND_WALK_SAND_R "Sounds\\Player\\WalkSandR.wav",
//178 sound SOUND_HIGHSCORE "Sounds\\Player\\HighScore.wav",
180 sound SOUND_WOUNDWEAK "Sounds\\Player\\WoundWeak.wav",
181 sound SOUND_WOUNDMEDIUM "Sounds\\Player\\WoundMedium.wav",
182 sound SOUND_WOUNDSTRONG "Sounds\\Player\\WoundStrong.wav",
185 sound SOUND_WOUNDWATER "Sounds\\Player\\WoundWater.wav",
// ************** FLESH PARTS **************
210 model MODEL_FLESH "Models\\Effects\\Debris\\Flesh\\Flesh.mdl",
211 model MODEL_FLESH_APPLE "Models\\Effects\\Debris\\Fruits\\Apple.mdl",
212 model MODEL_FLESH_BANANA "Models\\Effects\\Debris\\Fruits\\Banana.mdl",
213 model MODEL_FLESH_BURGER "Models\\Effects\\Debris\\Fruits\\CheeseBurger.mdl",
214 model MODEL_FLESH_LOLLY "Models\\Effects\\Debris\\Fruits\\LollyPop.mdl",
215 model MODEL_FLESH_ORANGE "Models\\Effects\\Debris\\Fruits\\Orange.mdl",
220 texture TEXTURE_FLESH_RED "Models\\Effects\\Debris\\Flesh\\FleshRed.tex",
221 texture TEXTURE_FLESH_GREEN "Models\\Effects\\Debris\\Flesh\\FleshGreen.tex",
222 texture TEXTURE_FLESH_APPLE "Models\\Effects\\Debris\\Fruits\\Apple.tex",
223 texture TEXTURE_FLESH_BANANA "Models\\Effects\\Debris\\Fruits\\Banana.tex",
224 texture TEXTURE_FLESH_BURGER "Models\\Effects\\Debris\\Fruits\\CheeseBurger.tex",
225 texture TEXTURE_FLESH_LOLLY "Models\\Effects\\Debris\\Fruits\\LollyPop.tex",
226 texture TEXTURE_FLESH_ORANGE "Models\\Effects\\Debris\\Fruits\\Orange.tex",
functions:
void AddBouble( FLOAT3D vPos, FLOAT3D vSpeedRelative)
{
ShellLaunchData &sld = m_asldData[m_iFirstEmptySLD];
sld.sld_vPos = vPos;
const FLOATmatrix3D &m = GetRotationMatrix();
FLOAT3D vUp( m(1,2), m(2,2), m(3,2));
sld.sld_vUp = vUp;
sld.sld_vSpeed = vSpeedRelative*m;
sld.sld_tmLaunch = _pTimer->CurrentTick();
sld.sld_estType = ESL_BUBBLE;
// move to next shell position
m_iFirstEmptySLD = (m_iFirstEmptySLD+1) % MAX_FLYING_SHELLS;
}
void ClearShellLaunchData( void)
{
// clear flying shells data array
m_iFirstEmptySLD = 0;
for( INDEX iShell=0; iShell<MAX_FLYING_SHELLS; iShell++)
{
m_asldData[iShell].sld_tmLaunch = -100.0f;
}
}
void CPlayer(void)
{
// clear flying shells data array
ClearShellLaunchData();
m_tmPredict = 0;
}
class CPlayerWeapons *GetPlayerWeapons(void)
{
ASSERT(m_penWeapons!=NULL);
return (CPlayerWeapons *)&*m_penWeapons;
}
class CPlayerAnimator *GetPlayerAnimator(void)
{
ASSERT(m_penAnimator!=NULL);
return (CPlayerAnimator *)&*m_penAnimator;
}
CPlayerSettings *GetSettings(void)
{
return (CPlayerSettings *)en_pcCharacter.pc_aubAppearance;
}
export void Copy(CEntity &enOther, ULONG ulFlags)
{
CPlayerEntity::Copy(enOther, ulFlags);
CPlayer *penOther = (CPlayer *)(&enOther);
m_moRender.Copy(penOther->m_moRender);
m_psLevelStats = penOther->m_psLevelStats;
m_psLevelTotal = penOther->m_psLevelTotal;
m_psGameStats = penOther->m_psGameStats ;
m_psGameTotal = penOther->m_psGameTotal ;
// if creating predictor
if (ulFlags&COPY_PREDICTOR)
{
// copy positions of launched empty shells
memcpy( m_asldData, penOther->m_asldData, sizeof( m_asldData));
m_iFirstEmptySLD = penOther->m_iFirstEmptySLD;
// all messages in the inbox
m_acmiMessages.Clear();
m_ctUnreadMessages = 0;
//m_lsLightSource;
SetupLightSource(); //? is this ok !!!!
// if normal copying
} else {
// copy messages
m_acmiMessages = penOther->m_acmiMessages;
m_ctUnreadMessages = penOther->m_ctUnreadMessages;
}
}
// update smoothed (average latency)
void UpdateLatency(FLOAT tmLatencyNow)
{
TIME tmNow = _pTimer->GetHighPrecisionTimer().GetSeconds();
// if not enough time passed
if (tmNow<m_tmLatencyLastAvg+hud_tmLatencySnapshot) {
// just sum
m_tmLatencyAvgSum += tmLatencyNow;
m_ctLatencyAvg++;
// if enough time passed
} else {
// calculate average
m_tmLatency = m_tmLatencyAvgSum/m_ctLatencyAvg;
// reset counters
m_tmLatencyAvgSum = 0.0f;
m_ctLatencyAvg = 0;
m_tmLatencyLastAvg = tmNow;
}
if (_pNetwork->IsPlayerLocal(this)) {
en_tmPing = m_tmLatency;
net_tmLatencyAvg = en_tmPing;
}
}
// check character data for invalid values
void ValidateCharacter(void)
{
// if in single player or flyover
if (GetSP()->sp_bSinglePlayer) {
// always use default model
CPlayerSettings *pps = (CPlayerSettings *)en_pcCharacter.pc_aubAppearance;
memset(pps->ps_achModelFile, 0, sizeof(pps->ps_achModelFile));
}
}
void CheckHighScore(void)
{
// if not playing a demo
if (!_pNetwork->IsPlayingDemo()) {
// update our local high score with the external
if (plr_iHiScore>m_iHighScore) {
m_iHighScore = plr_iHiScore;
}
}
// if current score is better than highscore
if (m_psGameStats.ps_iScore>m_iHighScore) {
// if it is a highscore greater than the last one beaten
if (m_iHighScore>m_iBeatenHighScore) {
// remember that it was beaten
m_iBeatenHighScore = m_iHighScore;
// tell that to player
m_soHighScore.Set3DParameters(25.0f, 5.0f, 1.0f, 1.0f);
//PlaySound(m_soHighScore, SOUND_HIGHSCORE, 0); !!!!####!!!!
}
}
}
CTString GetPredictName(void) const
{
if (IsPredicted()) {
return "PREDICTED";
} else if (IsPredictor()) {
return "predictor";
} else if (GetFlags()&ENF_WILLBEPREDICTED){
return "WILLBEPREDICTED";
} else {
return "no prediction";
}
}
/* Write to stream. */
void Write_t( CTStream *ostr) // throw char *
{
CPlayerEntity::Write_t(ostr);
// save array of messages
ostr->WriteID_t("MSGS");
INDEX ctMsg = m_acmiMessages.Count();
(*ostr)<<ctMsg;
for(INDEX iMsg=0; iMsg<ctMsg; iMsg++) {
m_acmiMessages[iMsg].Write_t(*ostr);
}
ostr->Write_t(&m_psLevelStats, sizeof(m_psLevelStats));
ostr->Write_t(&m_psLevelTotal, sizeof(m_psLevelTotal));
ostr->Write_t(&m_psGameStats , sizeof(m_psGameStats ));
ostr->Write_t(&m_psGameTotal , sizeof(m_psGameTotal ));
}
/* Read from stream. */
void Read_t( CTStream *istr) // throw char *
{
CPlayerEntity::Read_t(istr);
// clear flying shells data array
ClearShellLaunchData();
// load array of messages
istr->ExpectID_t("MSGS");
INDEX ctMsg;
(*istr)>>ctMsg;
m_acmiMessages.Clear();
m_ctUnreadMessages = 0;
if( ctMsg>0) {
m_acmiMessages.Push(ctMsg);
for(INDEX iMsg=0; iMsg<ctMsg; iMsg++) {
m_acmiMessages[iMsg].Read_t(*istr);
if (!m_acmiMessages[iMsg].cmi_bRead) {
m_ctUnreadMessages++;
}
}
}
istr->Read_t(&m_psLevelStats, sizeof(m_psLevelStats));
istr->Read_t(&m_psLevelTotal, sizeof(m_psLevelTotal));
istr->Read_t(&m_psGameStats , sizeof(m_psGameStats ));
istr->Read_t(&m_psGameTotal , sizeof(m_psGameTotal ));
// set your real appearance if possible
ValidateCharacter();
CTString strDummy;
SetPlayerAppearance(&m_moRender, &en_pcCharacter, strDummy, /*bPreview=*/FALSE);
m_ulFlags |= PLF_SYNCWEAPON;
// setup light source
SetupLightSource();
};
/* Get static light source information. */
CLightSource *GetLightSource(void)
{
if (!IsPredictor()) {
return &m_lsLightSource;
} else {
return NULL;
}
};
// called by other entities to set time prediction parameter
void SetPredictionTime(TIME tmAdvance) // give time interval in advance to set
{
m_tmPredict = _pTimer->CurrentTick()+tmAdvance;
}
// called by engine to get the upper time limit
TIME GetPredictionTime(void) // return moment in time up to which to predict this entity
{
return m_tmPredict;
}
// get maximum allowed range for predicting this entity
FLOAT GetPredictionRange(void)
{
return cli_fPredictPlayersRange;
}
// add to prediction any entities that this entity depends on
void AddDependentsToPrediction(void)
{
m_penWeapons->AddToPrediction();
m_penAnimator->AddToPrediction();
m_penView->AddToPrediction();
m_pen3rdPersonView->AddToPrediction();
}
// get in-game time for statistics
TIME GetStatsInGameTimeLevel(void)
{
if(m_bEndOfLevel) {
return m_psLevelStats.ps_tmTime;
} else {
return _pNetwork->GetGameTime()-m_tmLevelStarted;
}
}
TIME GetStatsInGameTimeGame(void)
{
if(m_bEndOfLevel) {
return m_psGameStats.ps_tmTime;
} else {
return m_psGameStats.ps_tmTime + (_pNetwork->GetGameTime()-m_tmLevelStarted);
}
}
FLOAT GetStatsRealWorldTime(void)
{
time_t timeNow;
if(m_bEndOfLevel) {
timeNow = m_iEndTime;
} else {
time(&timeNow);
}
return (FLOAT)difftime( timeNow, m_iStartTime);
}
CTString GetStatsRealWorldStarted(void)
{
struct tm *newtime;
STUBBED("this isn't 64-bit clean");
time_t t = (time_t) m_iStartTime;
newtime = localtime(&t);
setlocale(LC_ALL, "");
CTString strTimeline;
char achTimeLine[256];
strftime( achTimeLine, sizeof(achTimeLine)-1, "%a %x %H:%M", newtime);
strTimeline = achTimeLine;
setlocale(LC_ALL, "C");
return strTimeline;
}
// fill in player statistics
export void GetStats( CTString &strStats, const CompStatType csType, INDEX ctCharsPerRow)
{
// get proper type of stats
if( csType==CST_SHORT) {
GetShortStats(strStats);
} else {
ASSERT(csType==CST_DETAIL);
strStats = "\n";
_ctAlignWidth = Min(ctCharsPerRow, INDEX(60));
if (GetSP()->sp_bCooperative) {
if (GetSP()->sp_bSinglePlayer) {
GetDetailStatsSP(strStats, 0);
} else {
GetDetailStatsCoop(strStats);
}
} else {
GetDetailStatsDM(strStats);
}
}
}
// get short one-line statistics - used for savegame descriptions and similar
void GetShortStats(CTString &strStats)
{
strStats.PrintF( TRANS("%s %s Score: %d Kills: %d/%d"),
(const char*)GetDifficultyString(), (const char*)TimeToString(GetStatsInGameTimeLevel()),
m_psLevelStats.ps_iScore, m_psLevelStats.ps_iKills, m_psLevelTotal.ps_iKills);
}
// get detailed statistics for deathmatch game
void GetDetailStatsDM(CTString &strStats)
{
extern INDEX SetAllPlayersStats( INDEX iSortKey);
extern CPlayer *_apenPlayers[NET_MAXGAMEPLAYERS];
// determine type of game
const BOOL bFragMatch = GetSP()->sp_bUseFrags;
// fill players table
const INDEX ctPlayers = SetAllPlayersStats(bFragMatch?5:3); // sort by frags or by score
// get time elapsed since the game start
strStats+=AlignString(CTString(0, "^cFFFFFF%s:^r\n%s", (const char*)TRANS("TIME"), (const char*)TimeToString(_pNetwork->GetGameTime())));
strStats+="\n";
// find maximum frags/score that one player has
INDEX iMaxFrags = LowerLimit(INDEX(0));
INDEX iMaxScore = LowerLimit(INDEX(0));
{for(INDEX iPlayer=0; iPlayer<ctPlayers; iPlayer++) {
CPlayer *penPlayer = _apenPlayers[iPlayer];
iMaxFrags = Max(iMaxFrags, penPlayer->m_psLevelStats.ps_iKills);
iMaxScore = Max(iMaxScore, penPlayer->m_psLevelStats.ps_iScore);
}}
// print game limits
const CSessionProperties &sp = *GetSP();
if (sp.sp_iTimeLimit>0) {
FLOAT fTimeLeft = ClampDn(sp.sp_iTimeLimit*60.0f - _pNetwork->GetGameTime(), 0.0f);
strStats+=AlignString(CTString(0, "^cFFFFFF%s:^r\n%s", TRANS("TIME LEFT"), (const char*)TimeToString(fTimeLeft)));
strStats+="\n";
}
if (bFragMatch && sp.sp_iFragLimit>0) {
INDEX iFragsLeft = ClampDn(sp.sp_iFragLimit-iMaxFrags, INDEX(0));
strStats+=AlignString(CTString(0, "^cFFFFFF%s:^r\n%d", TRANS("FRAGS LEFT"), iFragsLeft));
strStats+="\n";
}
if (!bFragMatch && sp.sp_iScoreLimit>0) {
INDEX iScoreLeft = ClampDn(sp.sp_iScoreLimit-iMaxScore, INDEX(0));
strStats+=AlignString(CTString(0, "^cFFFFFF%s:^r\n%d", TRANS("SCORE LEFT"), iScoreLeft));
strStats+="\n";
}
strStats += "\n";
CTString strRank = TRANS("NO.");
CTString strFrag = bFragMatch ? TRANS("FRAGS"):TRANS("SCORE");
CTString strPing = TRANS("PING");
CTString strName = TRANS("PLAYER");
INDEX ctRankChars = Max(strRank.Length(), INDEX(3)) ;
INDEX ctFragChars = Max(strFrag.Length(), INDEX(7)) ;
INDEX ctPingChars = Max(strPing.Length(), INDEX(5)) ;
INDEX ctNameChars = Max(strName.Length(), INDEX(20));
// header
strStats += "^cFFFFFF";
strStats += PadStringRight(strRank, ctRankChars)+" ";
strStats += PadStringLeft (strFrag, ctFragChars)+" ";
strStats += PadStringLeft (strPing, ctPingChars)+" ";
strStats += PadStringRight(strName, ctNameChars)+" ";
strStats += "^r";
strStats += "\n\n";
{for(INDEX iPlayer=0; iPlayer<ctPlayers; iPlayer++) {
CTString strLine;
CPlayer *penPlayer = _apenPlayers[iPlayer];
INDEX iPing = ceil(penPlayer->en_tmPing*1000.0f);
INDEX iScore = bFragMatch ? penPlayer->m_psLevelStats.ps_iKills : penPlayer->m_psLevelStats.ps_iScore;
CTString strName = penPlayer->GetPlayerName();
strStats += PadStringRight(CTString(0, "%d", iPlayer+1), ctRankChars)+" ";
strStats += PadStringLeft (CTString(0, "%d", iScore), ctFragChars)+" ";
strStats += PadStringLeft (CTString(0, "%d", iPing), ctPingChars)+" ";
strStats += PadStringRight(strName, ctNameChars)+" ";
strStats += "\n";
}}
}
// get singleplayer statistics
void GetDetailStatsCoop(CTString &strStats)
{
// first put in your full stats
strStats += "^b"+CenterString(TRANS("YOUR STATS"))+"^r\n";
strStats+="\n";
GetDetailStatsSP(strStats, 1);
// get stats from all players
extern INDEX SetAllPlayersStats( INDEX iSortKey);
extern CPlayer *_apenPlayers[NET_MAXGAMEPLAYERS];
const INDEX ctPlayers = SetAllPlayersStats(3); // sort by score
// for each player
PlayerStats psSquadLevel = PlayerStats();
PlayerStats psSquadGame = PlayerStats();
{for( INDEX iPlayer=0; iPlayer<ctPlayers; iPlayer++) {
CPlayer *penPlayer = _apenPlayers[iPlayer];
// add values to squad stats
ASSERT( penPlayer!=NULL);
PlayerStats psLevel = penPlayer->m_psLevelStats;
PlayerStats psGame = penPlayer->m_psGameStats ;
psSquadLevel.ps_iScore += psLevel.ps_iScore ;
psSquadLevel.ps_iKills += psLevel.ps_iKills ;
psSquadLevel.ps_iDeaths += psLevel.ps_iDeaths ;
psSquadLevel.ps_iSecrets += psLevel.ps_iSecrets ;
psSquadGame.ps_iScore += psGame.ps_iScore ;
psSquadGame.ps_iKills += psGame.ps_iKills ;
psSquadGame.ps_iDeaths += psGame.ps_iDeaths ;
psSquadGame.ps_iSecrets += psGame.ps_iSecrets ;
}}
// add squad stats
strStats+="\n";
strStats += "^b"+CenterString(TRANS("SQUAD TOTAL"))+"^r\n";
strStats+="\n";
strStats+=CTString(0, "^cFFFFFF%s^r", TranslateConst(en_pwoWorld->GetName(), 0));
strStats+="\n";
strStats+=AlignString(CTString(0, " %s:\n%d", TRANS("SCORE"), psSquadLevel.ps_iScore));
strStats+="\n";
strStats+=AlignString(CTString(0, " %s:\n%d", TRANS("DEATHS"), psSquadLevel.ps_iDeaths));
strStats+="\n";
strStats+=AlignString(CTString(0, " %s:\n%d/%d", TRANS("KILLS"), psSquadLevel.ps_iKills, m_psLevelTotal.ps_iKills));
strStats+="\n";
strStats+=AlignString(CTString(0, " %s:\n%d/%d", TRANS("SECRETS"), psSquadLevel.ps_iSecrets, m_psLevelTotal.ps_iSecrets));
strStats+="\n";
strStats+="\n";
strStats+=CTString("^cFFFFFF")+TRANS("TOTAL")+"^r\n";
strStats+=AlignString(CTString(0, " %s:\n%d", TRANS("SCORE"), psSquadGame.ps_iScore));
strStats+="\n";
strStats+=AlignString(CTString(0, " %s:\n%d", TRANS("DEATHS"), psSquadGame.ps_iDeaths));
strStats+="\n";
strStats+=AlignString(CTString(0, " %s:\n%d/%d", TRANS("KILLS"), psSquadGame.ps_iKills, m_psGameTotal.ps_iKills));
strStats+="\n";
strStats+=AlignString(CTString(0, " %s:\n%d/%d", TRANS("SECRETS"), psSquadGame.ps_iSecrets, m_psGameTotal.ps_iSecrets));
strStats+="\n";
strStats+="\n";
strStats+="\n";
strStats += "^b"+CenterString(TRANS("OTHER PLAYERS"))+"^r\n";
strStats+="\n";
// for each player
{for(INDEX iPlayer=0; iPlayer<ctPlayers; iPlayer++) {
CPlayer *penPlayer = _apenPlayers[iPlayer];
// if this one
if (penPlayer==this) {
// skip it
continue;
}
// add his stats short
strStats+="^cFFFFFF"+CenterString(penPlayer->GetPlayerName())+"^r\n\n";
penPlayer->GetDetailStatsSP(strStats, 2);
strStats+="\n";
}}
}
// get singleplayer statistics
void GetDetailStatsSP(CTString &strStats, INDEX iCoopType)
{
if (iCoopType<=1) {
if (m_bEndOfGame) {
if (GetWorld()->wo_fnmFileName==plr_strLastLevel) {
strStats+=CTString(TRANS("^f4YOU HAVE FINISHED THE LIMITED EDITION!^F\n"))+
TRANS("8 more levels are available in\nthe full version of\n'Serious Sam: The First Encounter'!\n\n");
} else if (GetSP()->sp_gdGameDifficulty==CSessionProperties::GD_EXTREME) {
strStats+=TRANS("^f4SERIOUS GAME FINISHED,\nMENTAL MODE IS NOW ENABLED!^F\n\n");
} else if (GetSP()->sp_bMental) {
strStats+=TRANS("^f4YOU HAVE MASTERED THE GAME!^F\n\n");
}
}
}
if (iCoopType<=1) {
// report total score info
strStats+=AlignString(CTString(0, "^cFFFFFF%s:^r\n%d", TRANS("TOTAL SCORE"), m_psGameStats.ps_iScore));
strStats+="\n";
strStats+=AlignString(CTString(0, "^cFFFFFF%s:^r\n%s", TRANS("DIFFICULTY"), (const char*)GetDifficultyString()));
strStats+="\n";
strStats+=AlignString(CTString(0, "^cFFFFFF%s:^r\n%s", TRANS("STARTED"), (const char*)GetStatsRealWorldStarted()));
strStats+="\n";
strStats+=AlignString(CTString(0, "^cFFFFFF%s:^r\n%s", TRANS("PLAYING TIME"), (const char*)TimeToString(GetStatsRealWorldTime())));
strStats+="\n";
if( m_psGameStats.ps_iScore<=plr_iHiScore) {
strStats+=AlignString(CTString(0, "^cFFFFFF%s:^r\n%d", TRANS("HI-SCORE"), plr_iHiScore));
} else {
strStats+=TRANS("YOU BEAT THE HI-SCORE!");
}
strStats+="\n\n";
}
// report this level statistics
strStats+=CTString(0, "^cFFFFFF%s^r", TranslateConst(en_pwoWorld->GetName(), 0));
strStats+="\n";
if (iCoopType<=1) {
if( m_bEndOfLevel) {
strStats+=AlignString(CTString(0, " %s:\n%s", TRANS("ESTIMATED TIME"), (const char*)TimeToString(m_tmEstTime)));
strStats+="\n";
strStats+=AlignString(CTString(0, " %s:\n%d", TRANS("TIME BONUS"), m_iTimeScore));
strStats+="\n";
strStats+="\n";
}
// } else {
// strStats+=CTString("^cFFFFFF")+TRANS("THIS LEVEL")+"^r\n";
}
strStats+=AlignString(CTString(0, " %s:\n%d", TRANS("SCORE"), m_psLevelStats.ps_iScore));
strStats+="\n";
strStats+=AlignString(CTString(0, " %s:\n%d/%d", TRANS("KILLS"), m_psLevelStats.ps_iKills, m_psLevelTotal.ps_iKills));
strStats+="\n";
if (iCoopType>=1) {
strStats+=AlignString(CTString(0, " %s:\n%d", TRANS("DEATHS"), m_psLevelStats.ps_iDeaths, m_psLevelTotal.ps_iDeaths));
strStats+="\n";
}
strStats+=AlignString(CTString(0, " %s:\n%d/%d", TRANS("SECRETS"), m_psLevelStats.ps_iSecrets, m_psLevelTotal.ps_iSecrets));
strStats+="\n";
if (iCoopType<=1) {
strStats+=AlignString(CTString(0, " %s:\n%s", TRANS("TIME"), (const char*)TimeToString(GetStatsInGameTimeLevel())));
strStats+="\n";
}
strStats+="\n";
// report total game statistics
strStats+=CTString("^cFFFFFF")+TRANS("TOTAL")+"^r";
strStats+="\n";
strStats+=AlignString(CTString(0, " %s:\n%d", TRANS("SCORE"), m_psGameStats.ps_iScore));
strStats+="\n";
strStats+=AlignString(CTString(0, " %s:\n%d/%d", TRANS("KILLS"), m_psGameStats.ps_iKills, m_psGameTotal.ps_iKills));
strStats+="\n";
if (iCoopType>=1) {
strStats+=AlignString(CTString(0, " %s:\n%d", TRANS("DEATHS"), m_psGameStats.ps_iDeaths, m_psGameTotal.ps_iDeaths));
strStats+="\n";
}
strStats+=AlignString(CTString(0, " %s:\n%d/%d", TRANS("SECRETS"), m_psGameStats.ps_iSecrets, m_psGameTotal.ps_iSecrets));
strStats+="\n";
if (iCoopType<=1) {
strStats+=AlignString(CTString(0, " %s:\n%s", TRANS("GAME TIME"),(const char*) TimeToString(GetStatsInGameTimeGame())));
strStats+="\n";
}
strStats+="\n";
// set per level outputs
if (iCoopType<=1) {
if(m_strLevelStats!="") {
strStats += CTString("^cFFFFFF")+TRANS("Per level statistics") +"^r\n\n" + m_strLevelStats;
}
}
}
// provide info for GameSpy enumeration
void GetGameSpyPlayerInfo( INDEX iPlayer, CTString &strOut)
{
CTString strKey;
strKey.PrintF("\\player_%d\\%s", iPlayer, (const char*)GetPlayerName());
strOut+=strKey;
if (GetSP()->sp_bUseFrags) {
strKey.PrintF("\\frags_%d\\%d", iPlayer, m_psLevelStats.ps_iKills);
strOut+=strKey;
} else {
strKey.PrintF("\\frags_%d\\%d", iPlayer, m_psLevelStats.ps_iScore);
strOut+=strKey;
}
strKey.PrintF("\\ping_%d\\%d", iPlayer, INDEX(ceil(en_tmPing*1000.0f)));
strOut+=strKey;
};
// check if message is in inbox
BOOL HasMessage( const CTFileName &fnmMessage)
{
ULONG ulHash = fnmMessage.GetHash();
INDEX ctMsg = m_acmiMessages.Count();
for(INDEX iMsg=0; iMsg<ctMsg; iMsg++) {
if (m_acmiMessages[iMsg].cmi_ulHash == ulHash &&
m_acmiMessages[iMsg].cmi_fnmFileName == fnmMessage) {
return TRUE;
}
}
return FALSE;
}
// receive a computer message and put it in inbox if not already there
void ReceiveComputerMessage(const CTFileName &fnmMessage, ULONG ulFlags)
{
// if already received
if (HasMessage(fnmMessage)) {
// do nothing
return;
}
// add it to array
CCompMessageID &cmi = m_acmiMessages.Push();
cmi.NewMessage(fnmMessage);
cmi.cmi_bRead = ulFlags&CMF_READ;
if (!(ulFlags&CMF_READ)) {
m_ctUnreadMessages++;
cmp_bUpdateInBackground = TRUE;
}
if (!(ulFlags&CMF_READ) && (ulFlags&CMF_ANALYZE)) {
m_tmAnalyseEnd = _pTimer->CurrentTick()+2.0f;
m_soMessage.Set3DParameters(25.0f, 5.0f, 1.0f, 1.0f);
PlaySound(m_soMessage, SOUND_INFO, SOF_3D|SOF_VOLUMETRIC|SOF_LOCAL);
}
}
void SayVoiceMessage(const CTFileName &fnmMessage)
{
if (GetSettings()->ps_ulFlags&PSF_NOQUOTES) {
return;
}
SetSpeakMouthPitch();
PlaySound( m_soSpeech, fnmMessage, SOF_3D|SOF_VOLUMETRIC);
}
// receive all messages in one directory - cheat
void CheatAllMessagesDir(const CTString &strDir)
{
// list the directory
CDynamicStackArray<CTFileName> afnmDir;
MakeDirList(afnmDir, strDir, CTFileName(CTString("*.txt")), DLI_RECURSIVE);
// for each file in the directory
for (INDEX i=0; i<afnmDir.Count(); i++) {
CTFileName fnm = afnmDir[i];
// add the message
ReceiveComputerMessage(fnm, 0);
}
}
// receive all messages - cheat
void CheatAllMessages(void)
{
CheatAllMessagesDir("Data\\Messages\\information\\");
CheatAllMessagesDir("Data\\Messages\\weapons\\");
CheatAllMessagesDir("Data\\Messages\\enemies\\");
CheatAllMessagesDir("Data\\Messages\\background\\");
CheatAllMessagesDir("Data\\Messages\\statistics\\");
}
// mark that an item was picked
void ItemPicked(const CTString &strName, FLOAT fAmmount)
{
// if nothing picked too long
if (_pTimer->CurrentTick() > m_tmLastPicked+PICKEDREPORT_TIME) {
// kill the name
m_strPickedName = "";
// reset picked mana
m_fPickedMana = 0;
}
// if different than last picked
if (m_strPickedName!=strName) {
// remember name
m_strPickedName = strName;
// reset picked ammount
m_fPickedAmmount = 0;
}
// increase ammount
m_fPickedAmmount+=fAmmount;
m_tmLastPicked = _pTimer->CurrentTick();
}
// Setup light source
void SetupLightSource(void)
{
// setup light source
CLightSource lsNew;
lsNew.ls_ulFlags = LSF_NONPERSISTENT|LSF_DYNAMIC;
lsNew.ls_rHotSpot = 1.0f;
lsNew.ls_colColor = C_WHITE;
lsNew.ls_rFallOff = 2.5f;
lsNew.ls_plftLensFlare = NULL;
lsNew.ls_ubPolygonalMask = 0;
lsNew.ls_paoLightAnimation = &m_aoLightAnimation;
m_lsLightSource.ls_penEntity = this;
m_lsLightSource.SetLightSource(lsNew);
};
// play light animation
void PlayLightAnim(INDEX iAnim, ULONG ulFlags) {
if (m_aoLightAnimation.GetData()!=NULL) {
m_aoLightAnimation.PlayAnim(iAnim, ulFlags);
}
};
BOOL AdjustShadingParameters(FLOAT3D &vLightDirection, COLOR &colLight, COLOR &colAmbient)
{
if( cht_bDumpPlayerShading)
{
ANGLE3D a3dHPB;
DirectionVectorToAngles(-vLightDirection, a3dHPB);
UBYTE ubAR, ubAG, ubAB;
UBYTE ubCR, ubCG, ubCB;
ColorToRGB(colAmbient, ubAR, ubAG, ubAB);
ColorToRGB(colLight, ubCR, ubCG, ubCB);
CPrintF("Ambient: %d,%d,%d, Color: %d,%d,%d, Direction HPB (%g,%g,%g)\n",
ubAR, ubAG, ubAB, ubCR, ubCG, ubCB, a3dHPB(1), a3dHPB(2), a3dHPB(3));
}
return CPlayerEntity::AdjustShadingParameters(vLightDirection, colLight, colAmbient);
};
// get a different model object for rendering
CModelObject *GetModelForRendering(void)
{
// if not yet initialized
if(!(m_ulFlags&PLF_INITIALIZED)) {
// return base model
return GetModelObject();
}
// lerp player viewpoint
CPlacement3D plView;
plView.Lerp(en_plLastViewpoint, en_plViewpoint, _pTimer->GetLerpFactor());
// body and head attachment animation
((CPlayerAnimator&)*m_penAnimator).BodyAndHeadOrientation(plView);
((CPlayerAnimator&)*m_penAnimator).OnPreRender();
// synchronize your appearance with the default model
m_moRender.Synchronize(*GetModelObject());
if (m_ulFlags&PLF_SYNCWEAPON) {
m_ulFlags &= ~PLF_SYNCWEAPON;
GetPlayerAnimator()->SyncWeapon();
}
FLOAT tmNow = _pTimer->GetLerpedCurrentTick();
FLOAT fFading = 1.0f;
if (m_tmFadeStart!=0) {
FLOAT fFactor = (tmNow-m_tmFadeStart)/5.0f;
fFactor = Clamp(fFactor, 0.0f, 1.0f);
fFading*=fFactor;
}
// if invunerable after spawning
FLOAT tmSpawnInvulnerability = GetSP()->sp_tmSpawnInvulnerability;
if (tmSpawnInvulnerability>0 && tmNow-m_tmSpawned<tmSpawnInvulnerability) {
// blink fast
FLOAT fDelta = tmNow-m_tmSpawned;
fFading *= 0.75f+0.25f*Sin(fDelta/0.5f*360);
}
COLOR colAlpha = m_moRender.mo_colBlendColor;
colAlpha = (colAlpha&0xffffff00) + (COLOR(fFading*0xff)&0xff);
m_moRender.mo_colBlendColor = colAlpha;
// if not connected
if (m_ulFlags&PLF_NOTCONNECTED) {
// pluse slowly
fFading *= 0.25f+0.25f*Sin(tmNow/2.0f*360);
}
// use the appearance for rendering
return &m_moRender;
}
// wrapper for action marker getting
class CPlayerActionMarker *GetActionMarker(void) {
return (CPlayerActionMarker *)&*m_penActionMarker;
}
// find main music holder if not remembered
void FindMusicHolder(void)
{
if (m_penMainMusicHolder==NULL) {
m_penMainMusicHolder = _pNetwork->GetEntityWithName("MusicHolder", 0);
}
}
// update per-level stats
void UpdateLevelStats(void)
{
// clear stats for this level
m_psLevelStats = PlayerStats();
// get music holder
if (m_penMainMusicHolder==NULL) {
return;
}
CMusicHolder &mh = (CMusicHolder&)*m_penMainMusicHolder;
// assure proper count enemies in current world
if (mh.m_ctEnemiesInWorld==0) {
mh.CountEnemies();
}
// set totals for level and increment for game
m_psLevelTotal.ps_iKills = mh.m_ctEnemiesInWorld;
m_psGameTotal.ps_iKills += mh.m_ctEnemiesInWorld;
m_psLevelTotal.ps_iSecrets = mh.m_ctSecretsInWorld;
m_psGameTotal.ps_iSecrets += mh.m_ctSecretsInWorld;
}
// check if there is fuss
BOOL IsFuss(void)
{
// if no music holder
if (m_penMainMusicHolder==NULL) {
// no fuss
return FALSE;
}
// if no enemies - no fuss
return ((CMusicHolder*)&*m_penMainMusicHolder)->m_cenFussMakers.Count()>0;
}
void SetDefaultMouthPitch(void)
{
m_soMouth.Set3DParameters(50.0f, 10.0f, 1.0f, 1.0f);
}
void SetRandomMouthPitch(FLOAT fMin, FLOAT fMax)
{
m_soMouth.Set3DParameters(50.0f, 10.0f, 1.0f, Lerp(fMin, fMax, FRnd()));
}
void SetSpeakMouthPitch(void)
{
m_soSpeech.Set3DParameters(50.0f, 10.0f, 2.0f, 1.0f);
}
void ApplyShaking(CPlacement3D &plViewer)
{
CWorldSettingsController *pwsc = NULL;
// obtain bcg viewer
CBackgroundViewer *penBcgViewer = (CBackgroundViewer *) GetWorld()->GetBackgroundViewer();
if( penBcgViewer != NULL) {
// obtain world settings controller
pwsc = (CWorldSettingsController *) &*penBcgViewer->m_penWorldSettingsController;
}
if (pwsc==NULL || pwsc->m_tmShakeStarted<0) {
return;
}
TIME tm = _pTimer->GetLerpedCurrentTick()-pwsc->m_tmShakeStarted;
if (tm<0) {
return;
}
FLOAT fDistance = (plViewer.pl_PositionVector-pwsc->m_vShakePos).Length();
FLOAT fIntensity = IntensityAtDistance(pwsc->m_fShakeFalloff, 0, fDistance);
FLOAT fShakeY = SinFast(tm*pwsc->m_tmShakeFrequencyY*360.0f)*
exp(-tm*(pwsc->m_fShakeFade))*
fIntensity*pwsc->m_fShakeIntensityY;
FLOAT fShakeB = SinFast(tm*pwsc->m_tmShakeFrequencyB*360.0f)*
exp(-tm*(pwsc->m_fShakeFade))*
fIntensity*pwsc->m_fShakeIntensityB;
FLOAT fShakeZ = SinFast(tm*pwsc->m_tmShakeFrequencyZ*360.0f)*
exp(-tm*(pwsc->m_fShakeFade))*
fIntensity*pwsc->m_fShakeIntensityZ;
plViewer.pl_PositionVector(2) += fShakeY;
plViewer.pl_PositionVector(3) += fShakeZ;
plViewer.pl_OrientationAngle(3) += fShakeB;
}
COLOR GetWorldGlaring(void)
{
CWorldSettingsController *pwsc = NULL;
// obtain bcg viewer
CBackgroundViewer *penBcgViewer = (CBackgroundViewer *) GetWorld()->GetBackgroundViewer();
if( penBcgViewer != NULL) {
// obtain world settings controller
pwsc = (CWorldSettingsController *) &*penBcgViewer->m_penWorldSettingsController;
}
if (pwsc==NULL || pwsc->m_tmGlaringStarted<0) {
return 0;
}
TIME tm = _pTimer->GetLerpedCurrentTick();
FLOAT fRatio = CalculateRatio(tm, pwsc->m_tmGlaringStarted, pwsc->m_tmGlaringEnded,
pwsc->m_fGlaringFadeInRatio, pwsc->m_fGlaringFadeOutRatio);
COLOR colResult = C_WHITE|(UBYTE(fRatio*255.0f));
return colResult;
}
/************************************************************
* RENDER GAME VIEW *
************************************************************/
// setup viewing parameters for viewing from player or camera
void SetupView(CDrawPort *pdp, CAnyProjection3D &apr, CEntity *&penViewer,
CPlacement3D &plViewer, COLOR &colBlend, BOOL bCamera)
{
// read the exact placement of the view for this tick
GetLerpedAbsoluteViewPlacement(plViewer);
ASSERT(IsValidFloat(plViewer.pl_OrientationAngle(1))&&IsValidFloat(plViewer.pl_OrientationAngle(2))&&IsValidFloat(plViewer.pl_OrientationAngle(3)) );
// get current entity that the player views from
penViewer = GetViewEntity();
INDEX iViewState = m_iViewState;
if (m_penCamera!=NULL && bCamera) {
iViewState = PVT_SCENECAMERA;
plViewer = m_penCamera->GetLerpedPlacement();
penViewer = m_penCamera;
}
// init projection parameters
CPerspectiveProjection3D prPerspectiveProjection;
plr_fFOV = Clamp( plr_fFOV, 1.0f, 160.0f);
ANGLE aFOV = plr_fFOV;
// disable zoom in deathmatch
if (!GetSP()->sp_bCooperative) {
aFOV = 90.0f;
}
if (m_pstState==PST_DIVE && iViewState == PVT_PLAYEREYES) {
TIME tmNow = _pTimer->GetLerpedCurrentTick();
aFOV+=sin(tmNow*0.79f)*2.0f;
}
ApplyShaking(plViewer);
colBlend = 0;
if (iViewState == PVT_SCENECAMERA) {
CCamera *pcm = (CCamera*)&*m_penCamera;
prPerspectiveProjection.FOVL() =
Lerp(pcm->m_fLastFOV, pcm->m_fFOV, _pTimer->GetLerpFactor());
if (pcm->m_tmDelta>0.001f) {
FLOAT fFactor = (_pTimer->GetLerpedCurrentTick()-pcm->m_tmAtMarker)/pcm->m_tmDelta;
fFactor = Clamp( fFactor, 0.0f, 1.0f);
colBlend = LerpColor( pcm->m_colFade0, pcm->m_colFade1, fFactor);
} else {
colBlend = pcm->m_colFade0;
}
} else {
prPerspectiveProjection.FOVL() = aFOV;
}
prPerspectiveProjection.ScreenBBoxL() = FLOATaabbox2D(
FLOAT2D(0.0f, 0.0f),
FLOAT2D((FLOAT)pdp->GetWidth(), (FLOAT)pdp->GetHeight())
);
// determine front clip plane
plr_fFrontClipDistance = Clamp( plr_fFrontClipDistance, 0.05f, 0.30f);
FLOAT fFCD = plr_fFrontClipDistance;
// adjust front clip plane if swimming
if( m_pstState==PST_SWIM && iViewState==PVT_PLAYEREYES) { fFCD *= 0.6666f; }
prPerspectiveProjection.FrontClipDistanceL() = fFCD;
prPerspectiveProjection.AspectRatioL() = 1.0f;
// set up viewer position
apr = prPerspectiveProjection;
apr->ViewerPlacementL() = plViewer;
}
// listen from a given viewer
void ListenFromEntity(CEntity *penListener, const CPlacement3D &plSound)
{
FLOATmatrix3D mRotation;
MakeRotationMatrixFast(mRotation, plSound.pl_OrientationAngle);
sliSound.sli_vPosition = plSound.pl_PositionVector;
sliSound.sli_mRotation = mRotation;
sliSound.sli_fVolume = 1.0f;
sliSound.sli_vSpeed = en_vCurrentTranslationAbsolute;
sliSound.sli_penEntity = penListener;
if (m_pstState == PST_DIVE) {
sliSound.sli_fFilter = 20.0f;
} else {
sliSound.sli_fFilter = 0.0f;
}
INDEX iEnv = 0;
CBrushSector *pbsc = penListener->GetSectorFromPoint(plSound.pl_PositionVector);
// for each sector around listener
if (pbsc!=NULL) {
iEnv = pbsc->GetEnvironmentType();
}
// get the environment
CEnvironmentType &et = GetWorld()->wo_aetEnvironmentTypes[iEnv];
sliSound.sli_iEnvironmentType = et.et_iType;
sliSound.sli_fEnvironmentSize = et.et_fSize;
_pSound->Listen(sliSound);
}
// render dummy view (not connected yet)
void RenderDummyView(CDrawPort *pdp)
{
// clear screen
pdp->Fill( C_BLACK|CT_OPAQUE);
// if not single player
if (!GetSP()->sp_bSinglePlayer) {
// print a message
PIX pixDPWidth = pdp->GetWidth();
PIX pixDPHeight = pdp->GetHeight();
FLOAT fScale = (FLOAT)pixDPWidth/640.0f;
pdp->SetFont( _pfdDisplayFont);
pdp->SetTextScaling( fScale);
pdp->SetTextAspect( 1.0f);
CTString strMsg;
strMsg.PrintF(TRANS("%s connected"), (const char*)GetPlayerName());
pdp->PutTextCXY( strMsg, pixDPWidth*0.5f, pixDPHeight*0.5f, C_GREEN|CT_OPAQUE);
}
}
// render view from player
void RenderPlayerView(CDrawPort *pdp, BOOL bShowExtras)
{
// setup view settings
CAnyProjection3D apr;
CEntity *penViewer;
CPlacement3D plViewer;
COLOR colBlend;
SetupView(pdp, apr, penViewer, plViewer, colBlend, FALSE);
// render the view
ASSERT(IsValidFloat(plViewer.pl_OrientationAngle(1))&&IsValidFloat(plViewer.pl_OrientationAngle(2))&&IsValidFloat(plViewer.pl_OrientationAngle(3)));
_ulPlayerRenderingMask = 1<<GetMyPlayerIndex();
RenderView(*en_pwoWorld, *penViewer, apr, *pdp);
_ulPlayerRenderingMask = 0;
// listen from here
ListenFromEntity(this, plViewer);
if(hud_bShowAll && bShowExtras) {
// let the player entity render its interface
CPlacement3D plLight(_vViewerLightDirection, ANGLE3D(0,0,0));
plLight.AbsoluteToRelative(plViewer);
RenderHUD( *(CPerspectiveProjection3D *)(CProjection3D *)apr, pdp,
plLight.pl_PositionVector, _colViewerLight, _colViewerAmbient,
penViewer==this && (GetFlags()&ENF_ALIVE));
}
// determine and cache main drawport, size and relative scale
PIX pixDPWidth = pdp->GetWidth();
PIX pixDPHeight = pdp->GetHeight();
FLOAT fScale = (FLOAT)pixDPWidth/640.0f;
// print center message
if (_pTimer->CurrentTick()<m_tmCenterMessageEnd) {
pdp->SetFont( _pfdDisplayFont);
pdp->SetTextScaling( fScale);
pdp->SetTextAspect( 1.0f);
pdp->PutTextCXY( m_strCenterMessage, pixDPWidth*0.5f, pixDPHeight*0.85f, C_WHITE|0xDD);
// print picked item
} else if (_pTimer->CurrentTick()<m_tmLastPicked+PICKEDREPORT_TIME) {
pdp->SetFont( _pfdDisplayFont);
pdp->SetTextScaling( fScale);
pdp->SetTextAspect( 1.0f);
CTString strPicked;
if (m_fPickedAmmount==0) {
strPicked = m_strPickedName;
} else {
strPicked.PrintF("%s +%d", (const char*)m_strPickedName, int(m_fPickedAmmount));
}
pdp->PutTextCXY( strPicked, pixDPWidth*0.5f, pixDPHeight*0.82f, C_WHITE|0xDD);
if (!GetSP()->sp_bCooperative && !GetSP()->sp_bUseFrags && m_fPickedMana>=1) {
CTString strValue;
strValue.PrintF("%s +%d", TRANS("Value"), INDEX(m_fPickedMana));
pdp->PutTextCXY( strValue, pixDPWidth*0.5f, pixDPHeight*0.85f, C_WHITE|0xDD);
}
}
if (_pTimer->CurrentTick()<m_tmAnalyseEnd) {
pdp->SetFont( _pfdDisplayFont);
pdp->SetTextScaling( fScale);
pdp->SetTextAspect( 1.0f);
UBYTE ubA = int(sin(_pTimer->CurrentTick()*10.0f)*127+128);
pdp->PutTextCXY( TRANS("Analyzing..."), pixDPWidth*0.5f, pixDPHeight*0.2f, C_GREEN|ubA);
}
}
// render view from camera
void RenderCameraView(CDrawPort *pdp, BOOL bListen)
{
CDrawPort dpCamera;
CDrawPort *pdpCamera = pdp;
if (m_penCamera!=NULL && ((CCamera&)*m_penCamera).m_bWideScreen) {
pdp->MakeWideScreen(&dpCamera);
pdpCamera = &dpCamera;
}
pdp->Unlock();
pdpCamera->Lock();
// setup view settings
CAnyProjection3D apr;
CEntity *penViewer;
CPlacement3D plViewer;
COLOR colBlend;
SetupView(pdpCamera, apr, penViewer, plViewer, colBlend, TRUE);
// render the view
ASSERT(IsValidFloat(plViewer.pl_OrientationAngle(1))&&IsValidFloat(plViewer.pl_OrientationAngle(2))&&IsValidFloat(plViewer.pl_OrientationAngle(3)));
_ulPlayerRenderingMask = 1<<GetMyPlayerIndex();
RenderView(*en_pwoWorld, *penViewer, apr, *pdpCamera);
_ulPlayerRenderingMask = 0;
// listen from there if needed
if (bListen) {
ListenFromEntity(penViewer, plViewer);
}
pdpCamera->Unlock();
pdp->Lock();
// camera fading
if ((colBlend&CT_AMASK)!=0) {
pdp->Fill(colBlend);
}
// print center message
if (_pTimer->CurrentTick()<m_tmCenterMessageEnd) {
PIX pixDPWidth = pdp->GetWidth();
PIX pixDPHeight = pdp->GetHeight();
FLOAT fScale = (FLOAT)pixDPWidth/640.0f;
pdp->SetFont( _pfdDisplayFont);
pdp->SetTextScaling( fScale);
pdp->SetTextAspect( 1.0f);
pdp->PutTextCXY( m_strCenterMessage, pixDPWidth*0.5f, pixDPHeight*0.85f, C_WHITE|0xDD);
}
}
void RenderGameView(CDrawPort *pdp, void *pvUserData)
{
BOOL bShowExtras = (size_t(pvUserData)&GRV_SHOWEXTRAS);
// if not yet initialized
if(!(m_ulFlags&PLF_INITIALIZED) || (m_ulFlags&PLF_DONTRENDER)) {
// render dummy view on the right drawport
CDrawPort dpView(pdp, TRUE);
if(dpView.Lock()) {
RenderDummyView(&dpView);
dpView.Unlock();
}
return;
}
// if rendering real game view (not thumbnail, or similar)
if (pvUserData!=0) {
// if rendered a game view recently
CTimerValue tvNow = _pTimer->GetHighPrecisionTimer();
if ((tvNow-_tvProbingLast).GetSeconds()<0.1) {
// allow probing
_pGfx->gl_bAllowProbing = TRUE;
}
_tvProbingLast = tvNow;
}
// CPrintF("%s: render\n", GetPredictName());
// check for dualhead
BOOL bDualHead =
pdp->IsDualHead() &&
GetSP()->sp_gmGameMode!=CSessionProperties::GM_FLYOVER &&
m_penActionMarker==NULL;
// if dualhead, or no camera active
if (bDualHead||m_penCamera==NULL) {
// make left player view
CDrawPort dpView(pdp, TRUE);
if(dpView.Lock()) {
// draw it
RenderPlayerView(&dpView, bShowExtras);
dpView.Unlock();
}
}
// if camera active
if (m_penCamera!=NULL) {
// make left or right camera view
CDrawPort dpView(pdp, m_penActionMarker!=NULL);
if(dpView.Lock()) {
// draw it, listen if not dualhead
RenderCameraView(&dpView, !bDualHead);
dpView.Unlock();
}
// if camera is not active
} else {
// if dualhead
if (bDualHead) {
// render computer on secondary display
cmp_ppenDHPlayer = this;
}
}
};
/************************************************************
* PRE/DO/POST MOVING *
************************************************************/
// premoving for soft player up-down movement
void PreMoving(void) {
/*CPrintF("pos(%s): %g,%g,%g\n", GetPredictName(),
GetPlacement().pl_PositionVector(1),
GetPlacement().pl_PositionVector(2),
GetPlacement().pl_PositionVector(3));
*/
((CPlayerAnimator&)*m_penAnimator).StoreLast();
CPlayerEntity::PreMoving();
};
// do moving
void DoMoving(void) {
CPlayerEntity::DoMoving();
((CPlayerAnimator&)*m_penAnimator).AnimateBanking();
if (m_penView!=NULL) {
((CPlayerView&)*m_penView).DoMoving();
}
if (m_pen3rdPersonView!=NULL) {
((CPlayerView&)*m_pen3rdPersonView).DoMoving();
}
};
// postmoving for soft player up-down movement
void PostMoving(void)
{
CPlayerEntity::PostMoving();
// never allow a player to be removed from the list of movers
en_ulFlags &= ~ENF_INRENDERING;
((CPlayerAnimator&)*m_penAnimator).AnimateSoftEyes();
//((CPlayerAnimator&)*m_penAnimator).AnimateRecoilPitch();
// slowly increase mana with time, faster if player is not moving
m_fManaFraction +=
ClampDn( 1.0f-en_vCurrentTranslationAbsolute.Length()/20.0f, 0.0f) * 20.0f
* _pTimer->TickQuantum;
INDEX iNewMana = m_fManaFraction;
m_iMana += iNewMana;
m_fManaFraction -= iNewMana;
// if in tourist mode
if (GetSP()->sp_gdGameDifficulty==CSessionProperties::GD_TOURIST) {
// slowly increase health with time
FLOAT fHealth = GetHealth();
FLOAT fTopHealth = TopHealth();
if (fHealth<fTopHealth) {
SetHealth(ClampUp(fHealth+_pTimer->TickQuantum, fTopHealth)); // one unit per second
}
}
// update ray hit for weapon target
GetPlayerWeapons()->UpdateTargetingInfo();
if (m_pen3rdPersonView!=NULL) {
((CPlayerView&)*m_pen3rdPersonView).PostMoving();
}
if (m_penView!=NULL) {
((CPlayerView&)*m_penView).PostMoving();
}
// if didn't have any action in this tick
if (!(m_ulFlags&PLF_APPLIEDACTION)) {
// means we are not connected
SetUnconnected();
}
// clear action indicator
m_ulFlags&=~PLF_APPLIEDACTION;
}
// set player parameters for unconnected state (between the server loads and player reconnects)
void SetUnconnected(void)
{
if (m_ulFlags&PLF_NOTCONNECTED) {
return;
}
m_ulFlags |= PLF_NOTCONNECTED;
// reset to a dummy state
ForceFullStop();
SetPhysicsFlags(GetPhysicsFlags() & ~(EPF_TRANSLATEDBYGRAVITY|EPF_ORIENTEDBYGRAVITY));
SetCollisionFlags(GetCollisionFlags() & ~((ECBI_BRUSH|ECBI_MODEL)<<ECB_TEST));
en_plLastViewpoint.pl_OrientationAngle = en_plViewpoint.pl_OrientationAngle = ANGLE3D(0,0,0);
StartModelAnim(PLAYER_ANIM_STAND, 0);
GetPlayerAnimator()->BodyAnimationTemplate(
BODY_ANIM_NORMALWALK, BODY_ANIM_COLT_STAND, BODY_ANIM_SHOTGUN_STAND, BODY_ANIM_MINIGUN_STAND,
AOF_LOOPING|AOF_NORESTART);
}
// set player parameters for connected state
void SetConnected(void)
{
if (!(m_ulFlags&PLF_NOTCONNECTED)) {
return;
}
m_ulFlags &= ~PLF_NOTCONNECTED;
SetPhysicsFlags(GetPhysicsFlags() | (EPF_TRANSLATEDBYGRAVITY|EPF_ORIENTEDBYGRAVITY));
SetCollisionFlags(GetCollisionFlags() | ((ECBI_BRUSH|ECBI_MODEL)<<ECB_TEST));
}
// check if player is connected or not
BOOL IsConnected(void) const
{
return !(m_ulFlags&PLF_NOTCONNECTED);
}
// create a checksum value for sync-check
void ChecksumForSync(ULONG &ulCRC, INDEX iExtensiveSyncCheck)
{
CPlayerEntity::ChecksumForSync(ulCRC, iExtensiveSyncCheck);
CRC_AddLONG(ulCRC, m_psLevelStats.ps_iScore);
CRC_AddLONG(ulCRC, m_iMana);
if (iExtensiveSyncCheck>0) {
CRC_AddFLOAT(ulCRC, m_fManaFraction);
}
CRC_AddFLOAT(ulCRC, m_fArmor);
}
// dump sync data to text file
void DumpSync_t(CTStream &strm, INDEX iExtensiveSyncCheck) // throw char *
{
CPlayerEntity::DumpSync_t(strm, iExtensiveSyncCheck);
strm.FPrintF_t("Score: %d\n", m_psLevelStats.ps_iScore);
strm.FPrintF_t("m_iMana: %d\n", m_iMana);
strm.FPrintF_t("m_fManaFraction: %g(%08x)\n", m_fManaFraction, (ULONG&)m_fManaFraction);
strm.FPrintF_t("m_fArmor: %g(%08x)\n", m_fArmor, (ULONG&)m_fArmor);
}
/************************************************************
* DAMAGE OVERRIDE (PLAYER HAS ARMOR) *
************************************************************/
// leave stain
virtual void LeaveStain( BOOL bGrow)
{
ESpawnEffect ese;
FLOAT3D vPoint;
FLOATplane3D vPlaneNormal;
FLOAT fDistanceToEdge;
// get your size
FLOATaabbox3D box;
GetBoundingBox(box);
// on plane
if( GetNearestPolygon(vPoint, vPlaneNormal, fDistanceToEdge)) {
// if near to polygon and away from last stain point
if( (vPoint-GetPlacement().pl_PositionVector).Length()<0.5f
&& (m_vLastStain-vPoint).Length()>1.0f ) {
m_vLastStain = vPoint;
FLOAT fStretch = box.Size().Length();
ese.colMuliplier = C_WHITE|CT_OPAQUE;
// stain
if (bGrow) {
ese.betType = BET_BLOODSTAINGROW;
ese.vStretch = FLOAT3D( fStretch*1.5f, fStretch*1.5f, 1.0f);
} else {
ese.betType = BET_BLOODSTAIN;
ese.vStretch = FLOAT3D( fStretch*0.75f, fStretch*0.75f, 1.0f);
}
ese.vNormal = FLOAT3D( vPlaneNormal);
ese.vDirection = FLOAT3D( 0, 0, 0);
FLOAT3D vPos = vPoint+ese.vNormal/50.0f*(FRnd()+0.5f);
CEntityPointer penEffect = CreateEntity( CPlacement3D(vPos, ANGLE3D(0,0,0)), CLASS_BASIC_EFFECT);
penEffect->Initialize(ese);
}
}
};
void DamageImpact(enum DamageType dmtType,
FLOAT fDamageAmmount, const FLOAT3D &vHitPoint, const FLOAT3D &vDirection)
{
// if exploded
if (GetRenderType()!=RT_MODEL) {
// do nothing
return;
}
if (dmtType == DMT_ABYSS || dmtType == DMT_SPIKESTAB) {
return;
}
fDamageAmmount = Clamp(fDamageAmmount, 0.0f, 5000.0f);
FLOAT fKickDamage = fDamageAmmount;
if( (dmtType == DMT_EXPLOSION) || (dmtType == DMT_IMPACT) || (dmtType == DMT_CANNONBALL_EXPLOSION) )
{
fKickDamage*=1.5;
}
if (dmtType==DMT_DROWNING || dmtType==DMT_CLOSERANGE) {
fKickDamage /= 10;
}
// get passed time since last damage
TIME tmNow = _pTimer->CurrentTick();
TIME tmDelta = tmNow-m_tmLastDamage;
m_tmLastDamage = tmNow;
// fade damage out
if (tmDelta>=_pTimer->TickQuantum*3) {
m_vDamage=FLOAT3D(0,0,0);
}
// add new damage
FLOAT3D vDirectionFixed;
if (vDirection.ManhattanNorm()>0.5f) {
vDirectionFixed = vDirection;
} else {
vDirectionFixed = -en_vGravityDir;
}
FLOAT3D vDamageOld = m_vDamage;
m_vDamage+=(vDirectionFixed/*-en_vGravityDir/2*/)*fKickDamage;
FLOAT fOldLen = vDamageOld.Length();
FLOAT fNewLen = m_vDamage.Length();
FLOAT fOldRootLen = Sqrt(fOldLen);
FLOAT fNewRootLen = Sqrt(fNewLen);
FLOAT fMassFactor = 200.0f/((EntityInfo*)GetEntityInfo())->fMass;
if( !(en_ulFlags & ENF_ALIVE))
{
fMassFactor /= 3;
}
switch( dmtType)
{
case DMT_CLOSERANGE:
case DMT_DROWNING:
case DMT_IMPACT:
case DMT_BRUSH:
case DMT_BURNING:
// do nothing
break;
default:
{
if(fOldLen != 0.0f)
{
// cancel last push
GiveImpulseTranslationAbsolute( -vDamageOld/fOldRootLen*fMassFactor);
}
/*
FLOAT3D vImpuls = m_vDamage/fNewRootLen*fMassFactor;
CPrintF( "Applied absolute translation impuls: (%g%g%g)\n",
vImpuls(1),vImpuls(2),vImpuls(3));*/
// push it back
GiveImpulseTranslationAbsolute( m_vDamage/fNewRootLen*fMassFactor);
}
}
if( m_fMaxDamageAmmount<fDamageAmmount)
{
m_fMaxDamageAmmount = fDamageAmmount;
}
// if it has no spray, or if this damage overflows it
if ((m_tmSpraySpawned<=_pTimer->CurrentTick()-_pTimer->TickQuantum*8 ||
m_fSprayDamage+fDamageAmmount>50.0f)) {
// spawn blood spray
CPlacement3D plSpray = CPlacement3D( vHitPoint, ANGLE3D(0, 0, 0));
m_penSpray = CreateEntity( plSpray, CLASS_BLOOD_SPRAY);
m_penSpray->SetParent( this);
ESpawnSpray eSpawnSpray;
if( m_fMaxDamageAmmount > 10.0f)
{
eSpawnSpray.fDamagePower = 3.0f;
}
else if(m_fSprayDamage+fDamageAmmount>50.0f)
{
eSpawnSpray.fDamagePower = 2.0f;
}
else
{
eSpawnSpray.fDamagePower = 1.0f;
}
eSpawnSpray.sptType = SPT_BLOOD;
eSpawnSpray.fSizeMultiplier = 1.0f;
// setup direction of spray
FLOAT3D vHitPointRelative = vHitPoint - GetPlacement().pl_PositionVector;
FLOAT3D vReflectingNormal;
GetNormalComponent( vHitPointRelative, en_vGravityDir, vReflectingNormal);
vReflectingNormal.Normalize();
vReflectingNormal(1)/=5.0f;
FLOAT3D vProjectedComponent = vReflectingNormal*(vDirection%vReflectingNormal);
FLOAT3D vSpilDirection = vDirection-vProjectedComponent*2.0f-en_vGravityDir*0.5f;
eSpawnSpray.vDirection = vSpilDirection;
eSpawnSpray.penOwner = this;
// initialize spray
m_penSpray->Initialize( eSpawnSpray);
m_tmSpraySpawned = _pTimer->CurrentTick();
m_fSprayDamage = 0.0f;
m_fMaxDamageAmmount = 0.0f;
}
m_fSprayDamage+=fDamageAmmount;
}
/* Receive damage */
void ReceiveDamage( CEntity *penInflictor, enum DamageType dmtType,
FLOAT fDamageAmmount, const FLOAT3D &vHitPoint, const FLOAT3D &vDirection)
{
// don't harm yourself with knife or with rocket in easy/tourist mode
if( penInflictor==this && (dmtType==DMT_CLOSERANGE ||
((dmtType==DMT_EXPLOSION||dmtType==DMT_CANNONBALL_EXPLOSION||dmtType==DMT_PROJECTILE) &&
GetSP()->sp_gdGameDifficulty<=CSessionProperties::GD_EASY)) )
{
return;
}
// if not connected
if (m_ulFlags&PLF_NOTCONNECTED) {
// noone can harm you
return;
}
// god mode -> no one can harm you
if( cht_bGod && CheatsEnabled() ) { return; }
// if invunerable after spawning
FLOAT tmSpawnInvulnerability = GetSP()->sp_tmSpawnInvulnerability;
if (tmSpawnInvulnerability>0 && _pTimer->CurrentTick()-m_tmSpawned<tmSpawnInvulnerability) {
// ignore damage
return;
}
// check for friendly fire
if (!GetSP()->sp_bFriendlyFire && GetSP()->sp_bCooperative) {
if (IsOfClass(penInflictor, "Player") && penInflictor!=this) {
return;
}
}
// adjust for difficulty
FLOAT fDifficultyDamage = GetSP()->sp_fDamageStrength;
if( fDifficultyDamage<=1.0f || penInflictor!=this) {
fDamageAmmount *= fDifficultyDamage;
}
// ignore zero damages
if (fDamageAmmount<=0) {
return;
}
FLOAT fSubHealth, fSubArmor;
if( dmtType == DMT_DROWNING) {
// drowning
fSubHealth = fDamageAmmount;
}
else {
// damage and armor
fSubArmor = fDamageAmmount*2.0f/3.0f; // 2/3 on armor damage
fSubHealth = fDamageAmmount - fSubArmor; // 1/3 on health damage
m_fArmor -= fSubArmor; // decrease armor
if( m_fArmor<0) { // armor below zero -> add difference to health damage
fSubHealth -= m_fArmor;
m_fArmor = 0.0f;
}
}
// if any damage
if( fSubHealth>0) {
// if camera is active
if (m_penCamera!=NULL) {
// if the camera has onbreak
CEntity *penOnBreak = ((CCamera&)*m_penCamera).m_penOnBreak;
if (penOnBreak!=NULL) {
// trigger it
SendToTarget(penOnBreak, EET_TRIGGER, this);
// if it doesn't
} else {
// just deactivate camera
m_penCamera = NULL;
}
}
}
// if the player is doing autoactions
if (m_penActionMarker!=NULL) {
// ignore all damage
return;
}
DamageImpact(dmtType, fSubHealth, vHitPoint, vDirection);
// receive damage
CPlayerEntity::ReceiveDamage( penInflictor, dmtType, fSubHealth, vHitPoint, vDirection);
// red screen and hit translation
if( fDamageAmmount>1.0f) {
// !!!! this is obsolete, DamageImpact is used instead!
if( dmtType==DMT_EXPLOSION || dmtType==DMT_PROJECTILE || dmtType==DMT_BULLET
|| dmtType==DMT_IMPACT || dmtType==DMT_CANNONBALL || dmtType==DMT_CANNONBALL_EXPLOSION) {
// GiveImpulseTranslationAbsolute( vDirection*(fDamageAmmount/7.5f)
// -en_vGravityDir*(fDamageAmmount/15.0f));
}
if( GetFlags()&ENF_ALIVE) {
m_fDamageAmmount += fDamageAmmount;
m_tmWoundedTime = _pTimer->CurrentTick();
}
}
// yell (this hurts)
ESound eSound;
eSound.EsndtSound = SNDT_PLAYER;
eSound.penTarget = this;
SendEventInRange( eSound, FLOATaabbox3D( GetPlacement().pl_PositionVector, 10.0f));
// play hurting sound
if( dmtType==DMT_DROWNING) {
SetRandomMouthPitch( 0.9f, 1.1f);
PlaySound( m_soMouth, SOUND_DROWN, SOF_3D);
if(_pNetwork->IsPlayerLocal(this)) {IFeel_PlayEffect("WoundWater");}
m_tmMouthSoundLast = _pTimer->CurrentTick();
PlaySound( m_soLocalAmbientOnce, SOUND_WATERBUBBLES, SOF_3D|SOF_VOLUMETRIC|SOF_LOCAL);
m_soLocalAmbientOnce.Set3DParameters( 25.0f, 5.0f, 2.0f, Lerp(0.5f, 1.5f, FRnd()) );
SpawnBubbles( 10+INDEX(FRnd()*10));
} else if( m_fDamageAmmount>1.0f) {
// if not dead
if (GetFlags()&ENF_ALIVE) {
// determine corresponding sound
INDEX iSound;
const char *strIFeel = NULL;
if( m_fDamageAmmount<5.0f) {
iSound = SOUND_WOUNDWEAK;
strIFeel = "WoundWeak";
}
else if( m_fDamageAmmount<25.0f) {
iSound = SOUND_WOUNDMEDIUM;
strIFeel = "WoundMedium";
}
else {
iSound = SOUND_WOUNDSTRONG;
strIFeel = "WoundStrong";
}
if( m_pstState==PST_DIVE) {
iSound = SOUND_WOUNDWATER;
strIFeel = "WoundWater";
} // override for diving
SetRandomMouthPitch( 0.9f, 1.1f);
// give some pause inbetween screaming
TIME tmNow = _pTimer->CurrentTick();
if( (tmNow-m_tmScreamTime) > 1.0f) {
m_tmScreamTime = tmNow;
PlaySound( m_soMouth, iSound, SOF_3D);
if(_pNetwork->IsPlayerLocal(this)) {IFeel_PlayEffect(strIFeel);}
}
}
}
};
// should this player blow up (spawn debris)
BOOL ShouldBlowUp(void)
{
// blow up if
return
// allowed
GetSP()->sp_bGibs &&
// dead and
(GetHealth()<=0) &&
// has received large enough damage lately and
(m_vDamage.Length() > _fBlowUpAmmount) &&
// is not blown up already
GetRenderType()==RT_MODEL;
};
// spawn body parts
void BlowUp(void)
{
FLOAT3D vNormalizedDamage = m_vDamage-m_vDamage*(_fBlowUpAmmount/m_vDamage.Length());
vNormalizedDamage /= Sqrt(vNormalizedDamage.Length());
vNormalizedDamage *= 0.75f;
FLOAT3D vBodySpeed = en_vCurrentTranslationAbsolute-en_vGravityDir*(en_vGravityDir%en_vCurrentTranslationAbsolute);
const FLOAT fBlowUpSize = 2.0f;
// readout blood type
const INDEX iBloodType = GetSP()->sp_iBlood;
// determine debris texture (color)
ULONG ulFleshTexture = TEXTURE_FLESH_GREEN;
ULONG ulFleshModel = MODEL_FLESH;
if( iBloodType==2) { ulFleshTexture = TEXTURE_FLESH_RED; }
// spawn debris
Debris_Begin( EIBT_FLESH, DPT_BLOODTRAIL, BET_BLOODSTAIN, fBlowUpSize, vNormalizedDamage, vBodySpeed, 1.0f, 0.0f);
for( INDEX iDebris=0; iDebris<4; iDebris++) {
// flowerpower mode?
if( iBloodType==3) {
switch( IRnd()%5) {
case 1: { ulFleshModel = MODEL_FLESH_APPLE; ulFleshTexture = TEXTURE_FLESH_APPLE; break; }
case 2: { ulFleshModel = MODEL_FLESH_BANANA; ulFleshTexture = TEXTURE_FLESH_BANANA; break; }
case 3: { ulFleshModel = MODEL_FLESH_BURGER; ulFleshTexture = TEXTURE_FLESH_BURGER; break; }
case 4: { ulFleshModel = MODEL_FLESH_LOLLY; ulFleshTexture = TEXTURE_FLESH_LOLLY; break; }
default: { ulFleshModel = MODEL_FLESH_ORANGE; ulFleshTexture = TEXTURE_FLESH_ORANGE; break; }
}
}
Debris_Spawn( this, this, ulFleshModel, ulFleshTexture, 0, 0, 0, IRnd()%4, 0.5f,
FLOAT3D(FRnd()*0.6f+0.2f, FRnd()*0.6f+0.2f, FRnd()*0.6f+0.2f));
}
// leave a stain beneath
LeaveStain(FALSE);
// hide yourself (must do this after spawning debris)
SwitchToEditorModel();
FLOAT fSpeedOrg = en_vCurrentTranslationAbsolute.Length();
const FLOAT fSpeedMax = 30.0f;
if (fSpeedOrg>fSpeedMax) {
en_vCurrentTranslationAbsolute *= fSpeedMax/fSpeedOrg;
}
// SetPhysicsFlags(EPF_MODEL_IMMATERIAL);
// SetCollisionFlags(ECF_IMMATERIAL);
};
/************************************************************
* OVERRIDEN FUNCTIONS *
************************************************************/
/* Entity info */
void *GetEntityInfo(void) {
switch (m_pstState) {
case PST_STAND: case PST_FALL:
return &eiPlayerGround;
break;
case PST_CROUCH:
return &eiPlayerCrouch;
break;
case PST_SWIM: case PST_DIVE:
return &eiPlayerSwim;
break;
}
return &eiPlayerGround;
};
/* Receive item */
BOOL ReceiveItem(const CEntityEvent &ee)
{
// *********** HEALTH ***********
if( ee.ee_slEvent == EVENTCODE_EHealth)
{
// determine old and new health values
FLOAT fHealthOld = GetHealth();
FLOAT fHealthNew = fHealthOld + ((EHealth&)ee).fHealth;
if( ((EHealth&)ee).bOverTopHealth) {
fHealthNew = ClampUp( fHealthNew, MaxHealth());
} else {
fHealthNew = ClampUp( fHealthNew, TopHealth());
}
// if value can be changed
if( ceil(fHealthNew) > ceil(fHealthOld)) {
// receive it
SetHealth(fHealthNew);
ItemPicked( TRANS("Health"), ((EHealth&)ee).fHealth);
m_iMana += (INDEX)(((EHealth&)ee).fHealth);
m_fPickedMana += ((EHealth&)ee).fHealth;
return TRUE;
}
}
// *********** ARMOR ***********
else if( ee.ee_slEvent == EVENTCODE_EArmor)
{
// determine old and new health values
FLOAT fArmorOld = m_fArmor;
FLOAT fArmorNew = fArmorOld + ((EArmor&)ee).fArmor;
if( ((EArmor&)ee).bOverTopArmor) {
fArmorNew = ClampUp( fArmorNew, MaxArmor());
} else {
fArmorNew = ClampUp( fArmorNew, TopArmor());
}
// if value can be changed
if( ceil(fArmorNew) > ceil(fArmorOld)) {
// receive it
m_fArmor = fArmorNew;
ItemPicked( TRANS("Armor"), ((EArmor&)ee).fArmor);
m_iMana += (INDEX)(((EArmor&)ee).fArmor);
m_fPickedMana += ((EArmor&)ee).fArmor;
return TRUE;
}
}
// *********** MESSAGE ***********
else if (ee.ee_slEvent == EVENTCODE_EMessageItem) {
EMessageItem &eMI = (EMessageItem &)ee;
ReceiveComputerMessage(eMI.fnmMessage, CMF_ANALYZE);
ItemPicked(TRANS("Ancient papyrus"), 0);
return TRUE;
}
// *********** WEAPON ***********
else if (ee.ee_slEvent == EVENTCODE_EWeaponItem) {
return ((CPlayerWeapons&)*m_penWeapons).ReceiveWeapon(ee);
}
// *********** AMMO ***********
else if (ee.ee_slEvent == EVENTCODE_EAmmoItem) {
return ((CPlayerWeapons&)*m_penWeapons).ReceiveAmmo(ee);
}
else if (ee.ee_slEvent == EVENTCODE_EAmmoPackItem) {
return ((CPlayerWeapons&)*m_penWeapons).ReceivePackAmmo(ee);
}
// *********** KEYS ***********
else if (ee.ee_slEvent == EVENTCODE_EKey) {
// make key mask
ULONG ulKey = 1<<INDEX(((EKey&)ee).kitType);
EKey &eKey = (EKey&)ee;
if(eKey.kitType == KIT_ANKHGOLDDUMMY || eKey.kitType == KIT_SCARABDUMMY)
{
ulKey = 0;
}
// if key is already in inventory
if (m_ulKeys&ulKey) {
// ignore it
return FALSE;
// if key is not in inventory
} else {
// pick it up
m_ulKeys |= ulKey;
CTString strKey = GetKeyName(((EKey&)ee).kitType);
ItemPicked(strKey, 0);
// if in cooperative
if (GetSP()->sp_bCooperative && !GetSP()->sp_bSinglePlayer) {
CPrintF(TRANS("^cFFFFFF%s - %s^r\n"), (const char*)GetPlayerName(), (const char*)strKey);
}
return TRUE;
}
}
// nothing picked
return FALSE;
};
// Change Player view
void ChangePlayerView()
{
// change from eyes to 3rd person
if (m_iViewState == PVT_PLAYEREYES) {
// spawn 3rd person view camera
ASSERT(m_pen3rdPersonView == NULL);
if (m_pen3rdPersonView == NULL) {
m_pen3rdPersonView = CreateEntity(GetPlacement(), CLASS_PLAYER_VIEW);
EViewInit eInit;
eInit.penOwner = this;
eInit.penCamera = NULL;
eInit.vtView = VT_3RDPERSONVIEW;
eInit.bDeathFixed = FALSE;
m_pen3rdPersonView ->Initialize(eInit);
}
m_iViewState = PVT_3RDPERSONVIEW;
// change from 3rd person to eyes
} else if (m_iViewState == PVT_3RDPERSONVIEW) {
m_iViewState = PVT_PLAYEREYES;
// kill 3rd person view
if (m_pen3rdPersonView != NULL) {
((CPlayerView&)*m_pen3rdPersonView).SendEvent(EEnd());
m_pen3rdPersonView = NULL;
}
}
};
// if computer is pressed
void ComputerPressed(void)
{
// call computer
if (cmp_ppenPlayer==NULL && _pNetwork->IsPlayerLocal(this)) {
cmp_ppenPlayer = this;
}
m_bComputerInvoked = TRUE;
// clear analyses message
m_tmAnalyseEnd = 0;
m_bPendingMessage = FALSE;
m_tmMessagePlay = 0;
}
// if use is pressed
void UsePressed(BOOL bOrComputer)
{
// cast ray from weapon
CPlayerWeapons *penWeapons = GetPlayerWeapons();
CEntity *pen = penWeapons->m_penRayHit;
BOOL bSomethingToUse = FALSE;
// if hit
if (pen!=NULL) {
// check switch/messageholder relaying by moving brush
if (IsOfClass( pen, "Moving Brush")) {
if (((CMovingBrush&)*pen).m_penSwitch!=NULL) {
pen = ((CMovingBrush&)*pen).m_penSwitch;
}
}
// if switch and near enough
if (IsOfClass( pen, "Switch") && penWeapons->m_fRayHitDistance < 2.0f) {
CSwitch &enSwitch = (CSwitch&)*pen;
// if switch is useable
if (enSwitch.m_bUseable) {
// send it a trigger event
SendToTarget(pen, EET_TRIGGER, this);
bSomethingToUse = TRUE;
}
}
// if analyzable
if (IsOfClass( pen, "MessageHolder")
&& penWeapons->m_fRayHitDistance<((CMessageHolder*)&*pen)->m_fDistance
&& ((CMessageHolder*)&*pen)->m_bActive) {
const CTFileName &fnmMessage = ((CMessageHolder*)&*pen)->m_fnmMessage;
// if player doesn't have that message in database
if (!HasMessage(fnmMessage)) {
// add the message
ReceiveComputerMessage(fnmMessage, CMF_ANALYZE);
bSomethingToUse = TRUE;
}
}
}
// if nothing usable under cursor, and may call computer
if (!bSomethingToUse && bOrComputer) {
// call computer
ComputerPressed();
}
}
/************************************************************
* PLAYER ACTIONS *
************************************************************/
void SetGameEnd(void)
{
_pNetwork->SetGameFinished();
// start console for first player possible
for(INDEX iPlayer=0; iPlayer<GetMaxPlayers(); iPlayer++) {
CEntity *pen = GetPlayerEntity(iPlayer);
if (pen!=NULL) {
if (cmp_ppenPlayer==NULL && _pNetwork->IsPlayerLocal(pen)) {
cmp_ppenPlayer = (CPlayer*)pen;
}
}
}
}
// check if game should be finished
void CheckGameEnd(void)
{
BOOL bFinished = FALSE;
// if time limit is out
INDEX iTimeLimit = GetSP()->sp_iTimeLimit;
if (iTimeLimit>0 && _pTimer->CurrentTick()>=iTimeLimit*60.0f) {
bFinished = TRUE;
}
// if frag limit is out
INDEX iFragLimit = GetSP()->sp_iFragLimit;
if (iFragLimit>0 && m_psLevelStats.ps_iKills>=iFragLimit) {
bFinished = TRUE;
}
// if score limit is out
INDEX iScoreLimit = GetSP()->sp_iScoreLimit;
if (iScoreLimit>0 && m_psLevelStats.ps_iScore>=iScoreLimit) {
bFinished = TRUE;
}
if (bFinished) {
SetGameEnd();
}
}
// Preapply the action packet for local mouselag elimination
void PreapplyAction( const CPlayerAction &paAction)
{
}
// Called to apply player action to player entity each tick.
void ApplyAction( const CPlayerAction &paOriginal, FLOAT tmLatency)
{
if(!(m_ulFlags&PLF_INITIALIZED)) { return; }
// CPrintF("---APPLY: %g\n", paOriginal.pa_aRotation(1));
// if was not connected
if (m_ulFlags&PLF_NOTCONNECTED) {
// set connected state
SetConnected();
}
// mark that the player is connected
m_ulFlags |= PLF_APPLIEDACTION;
// make a copy of action for adjustments
CPlayerAction paAction = paOriginal;
//CPrintF("applying(%s-%08x): %g\n", GetPredictName(), int(paAction.pa_llCreated),
// paAction.pa_vTranslation(3));
// calculate delta from last received actions
ANGLE3D aDeltaRotation = paAction.pa_aRotation -m_aLastRotation;
ANGLE3D aDeltaViewRotation = paAction.pa_aViewRotation-m_aLastViewRotation;
//FLOAT3D vDeltaTranslation = paAction.pa_vTranslation -m_vLastTranslation;
m_aLastRotation = paAction.pa_aRotation;
m_aLastViewRotation = paAction.pa_aViewRotation;
//m_vLastTranslation = paAction.pa_vTranslation;
paAction.pa_aRotation = aDeltaRotation;
paAction.pa_aViewRotation = aDeltaViewRotation;
//paAction.pa_vTranslation = vDeltaTranslation;
// adjust rotations per tick
paAction.pa_aRotation /= _pTimer->TickQuantum;
paAction.pa_aViewRotation /= _pTimer->TickQuantum;
// adjust prediction for remote players only
CEntity *penMe = this;
if (IsPredictor()) {
penMe = penMe->GetPredicted();
}
SetPredictable(!_pNetwork->IsPlayerLocal(penMe));
// check for end of game
if (!IsPredictor()) {
CheckGameEnd();
}
// limit speeds against abusing
paAction.pa_vTranslation(1) = Clamp( paAction.pa_vTranslation(1), -plr_fSpeedSide, plr_fSpeedSide);
paAction.pa_vTranslation(2) = Clamp( paAction.pa_vTranslation(2), -plr_fSpeedUp, plr_fSpeedUp);
paAction.pa_vTranslation(3) = Clamp( paAction.pa_vTranslation(3), -plr_fSpeedForward, plr_fSpeedBackward);
// if speeds are like walking
if (Abs(paAction.pa_vTranslation(3))< plr_fSpeedForward/1.99f
&&Abs(paAction.pa_vTranslation(1))< plr_fSpeedSide/1.99f) {
// don't allow falling
en_fStepDnHeight = 1.5f;
// if speeds are like running
} else {
// allow falling
en_fStepDnHeight = -1;
}
// limit diagonal speed against abusing
FLOAT3D &v = paAction.pa_vTranslation;
FLOAT fDiag = Sqrt(v(1)*v(1)+v(3)*v(3));
if (fDiag>0.01f) {
FLOAT fDiagLimited = Min(fDiag, plr_fSpeedForward);
FLOAT fFactor = fDiagLimited/fDiag;
v(1)*=fFactor;
v(3)*=fFactor;
}
ulButtonsNow = paAction.pa_ulButtons;
ulButtonsBefore = m_ulLastButtons;
ulNewButtons = ulButtonsNow&~ulButtonsBefore;
ulReleasedButtons = (~ulButtonsNow)&(ulButtonsBefore);
m_ulLastButtons = ulButtonsNow; // remember last buttons
en_plLastViewpoint = en_plViewpoint; // remember last view point for lerping
// if alive
if (GetFlags() & ENF_ALIVE) {
// if not in auto-action mode
if (m_penActionMarker==NULL) {
// apply actions
AliveActions(paAction);
// if in auto-action mode
} else {
// do automatic actions
AutoActions(paAction);
}
// if not alive rotate camera view and rebirth on fire
} else {
DeathActions(paAction);
}
if (Abs(_pTimer->CurrentTick()-m_tmAnalyseEnd)<_pTimer->TickQuantum*2) {
m_tmAnalyseEnd = 0;
m_bPendingMessage = TRUE;
m_tmMessagePlay = 0;
}
if (m_bPendingMessage && !IsFuss()) {
m_bPendingMessage = FALSE;
m_tmMessagePlay = _pTimer->CurrentTick()+1.0f;
m_tmAnimateInbox = _pTimer->CurrentTick();
}
if (Abs(_pTimer->CurrentTick()-m_tmMessagePlay)<_pTimer->TickQuantum*2) {
m_bPendingMessage = FALSE;
m_tmAnalyseEnd = 0;
if (!m_bComputerInvoked && GetSP()->sp_bSinglePlayer) {
PrintCenterMessage(this, this,
TRANS("Press USE to read the message!"), 5.0f, MSS_NONE);
}
}
// wanna cheat a bit?
if (CheatsEnabled()) {
Cheats();
}
// if teleporting to marker (this cheat is enabled in all versions)
if (cht_iGoToMarker>0 && (GetFlags()&ENF_ALIVE)) {
// rebirth player, and it will teleport
m_iLastViewState = m_iViewState;
SendEvent(ERebirth());
}
// keep latency for eventual printout
UpdateLatency(tmLatency);
// check if highscore has changed
CheckHighScore();
};
// Called when player is disconnected
void Disconnect(void)
{
// remember name
m_strName = GetPlayerName();
// clear the character, so we don't get re-connected to same entity
en_pcCharacter = CPlayerCharacter();
// make main loop exit
SendEvent(EDisconnected());
};
// Called when player character is changed
void CharacterChanged(const CPlayerCharacter &pcNew)
{
// remember original character
CPlayerCharacter pcOrg = en_pcCharacter;
// set the new character
en_pcCharacter = pcNew;
ValidateCharacter();
// if the name has changed
if (pcOrg.GetName()!=pcNew.GetName()) {
// report that
CPrintF(TRANS("%s is now known as %s\n"),
(const char*)pcOrg.GetNameForPrinting(), (const char*)pcNew.GetNameForPrinting());
}
// if the team has changed
if (pcOrg.GetTeam()!=pcNew.GetTeam()) {
// report that
CPrintF(TRANS("%s switched to team %s\n"),
(const char*) pcNew.GetNameForPrinting(), (const char*)pcNew.GetTeamForPrinting());
}
// if appearance changed
CPlayerSettings *ppsOrg = (CPlayerSettings *)pcOrg.pc_aubAppearance;
CPlayerSettings *ppsNew = (CPlayerSettings *)pcNew.pc_aubAppearance;
if (memcmp(ppsOrg->ps_achModelFile, ppsNew->ps_achModelFile, sizeof(ppsOrg->ps_achModelFile))!=0) {
// update your real appearance if possible
CTString strNewLook;
BOOL bSuccess = SetPlayerAppearance(&m_moRender, &en_pcCharacter, strNewLook, /*bPreview=*/FALSE);
// if succeeded
if (bSuccess) {
// report that
CPrintF(TRANS("%s now appears as %s\n"),
(const char*)pcNew.GetNameForPrinting(),(const char*) strNewLook);
// if failed
} else {
// report that
CPrintF(TRANS("Cannot change appearance for %s: setting '%s' is unavailable\n"),
(const char*)pcNew.GetNameForPrinting(), (const char*)ppsNew->GetModelFilename());
}
// attach weapon to new appearance
GetPlayerAnimator()->SyncWeapon();
}
BOOL b3RDPersonOld = ppsOrg->ps_ulFlags&PSF_PREFER3RDPERSON;
BOOL b3RDPersonNew = ppsNew->ps_ulFlags&PSF_PREFER3RDPERSON;
if ((b3RDPersonOld && !b3RDPersonNew && m_iViewState==PVT_3RDPERSONVIEW)
||(b3RDPersonNew && !b3RDPersonOld && m_iViewState==PVT_PLAYEREYES) ) {
ChangePlayerView();
}
};
// Alive actions
void AliveActions(const CPlayerAction &pa)
{
CPlayerAction paAction = pa;
// if camera is active
if (m_penCamera!=NULL) {
// ignore keyboard/mouse/joystick commands
paAction.pa_vTranslation = FLOAT3D(0,0,0);
paAction.pa_aRotation = ANGLE3D(0,0,0);
paAction.pa_aViewRotation = ANGLE3D(0,0,0);
// if fire or use is pressed
if (ulNewButtons&(PLACT_FIRE|PLACT_USE)) {
// stop camera
m_penCamera=NULL;
}
} else {
ButtonsActions(paAction);
}
// do the actions
ActiveActions(paAction);
// if less than few seconds elapsed since last damage
FLOAT tmSinceWounding = _pTimer->CurrentTick() - m_tmWoundedTime;
if( tmSinceWounding<4.0f) {
// decrease damage ammount
m_fDamageAmmount *= 1.0f - tmSinceWounding/4.0f;
} else {
// reset damage ammount
m_fDamageAmmount = 0.0f;
}
}
// Auto-actions
void AutoActions(const CPlayerAction &pa)
{
// if fire, use or computer is pressed
if (ulNewButtons&(PLACT_FIRE|PLACT_USE|PLACT_COMPUTER)) {
if (m_penCamera!=NULL) {
CEntity *penOnBreak = ((CCamera&)*m_penCamera).m_penOnBreak;
if (penOnBreak!=NULL) {
SendToTarget(penOnBreak, EET_TRIGGER, this);
}
}
}
CPlayerAction paAction = pa;
// ignore keyboard/mouse/joystick commands
paAction.pa_vTranslation = FLOAT3D(0,0,0);
paAction.pa_aRotation = ANGLE3D(0,0,0);
paAction.pa_aViewRotation = ANGLE3D(0,0,0);
// if moving towards the marker is enabled
if (m_fAutoSpeed>0) {
FLOAT3D vDelta =
m_penActionMarker->GetPlacement().pl_PositionVector-
GetPlacement().pl_PositionVector;
FLOAT fDistance = vDelta.Length();
if (fDistance>0.1f) {
vDelta/=fDistance;
ANGLE aDH = GetRelativeHeading(vDelta);
// if should hit the marker exactly
FLOAT fSpeed = m_fAutoSpeed;
if (GetActionMarker()->m_paaAction==PAA_RUNANDSTOP) {
// adjust speed
fSpeed = Min(fSpeed, fDistance/_pTimer->TickQuantum);
}
// adjust rotation
if (Abs(aDH)>5.0f) {
if (fSpeed>m_fAutoSpeed-0.1f) {
aDH = Clamp(aDH, -30.0f, 30.0f);
}
paAction.pa_aRotation = ANGLE3D(aDH/_pTimer->TickQuantum,0,0);
}
// set forward speed
paAction.pa_vTranslation = FLOAT3D(0,0,-fSpeed);
}
} else {
paAction.pa_vTranslation = m_vAutoSpeed;
}
CPlayerActionMarker *ppam = GetActionMarker();
ASSERT( ppam != NULL);
if( ppam->m_paaAction == PAA_LOGO_FIRE_MINIGUN)
{
if( m_tmMinigunAutoFireStart != -1)
{
FLOAT tmDelta = _pTimer->CurrentTick()-m_tmMinigunAutoFireStart;
FLOAT aDH=0.0f;
FLOAT aDP=0.0f;
if( tmDelta>=0.0f && tmDelta<=0.75f)
{
aDH = 0.0f;
}
else if( tmDelta>=0.75f)
{
FLOAT fDT = tmDelta-0.75f;
aDH = 1.0f*cos(fDT+PI/2.0f);
aDP = 0.5f*cos(fDT);
}
paAction.pa_aRotation = ANGLE3D(aDH/_pTimer->TickQuantum, aDP/_pTimer->TickQuantum,0);
}
}
// do the actions
if (!(m_ulFlags&PLF_AUTOMOVEMENTS)) {
ActiveActions(paAction);
}
}
void GetLerpedWeaponPosition( FLOAT3D vRel, CPlacement3D &pl)
{
pl = CPlacement3D( vRel, ANGLE3D(0,0,0));
CPlacement3D plView;
GetLerpedAbsoluteViewPlacement(plView);
pl.RelativeToAbsolute( plView);
}
void SpawnBubbles( INDEX ctBubbles)
{
for( INDEX iBouble=0; iBouble<ctBubbles; iBouble++)
{
FLOAT3D vRndRel = FLOAT3D( (FRnd()-0.5f)*0.25f, -0.25f, -0.5f+FRnd()/10.0f);
ANGLE3D aDummy = ANGLE3D(0,0,0);
CPlacement3D plMouth = CPlacement3D( vRndRel, aDummy);
plMouth.RelativeToAbsolute( en_plViewpoint);
plMouth.RelativeToAbsolute( GetPlacement());
FLOAT3D vRndSpd = FLOAT3D( (FRnd()-0.5f)*0.25f, (FRnd()-0.5f)*0.25f, (FRnd()-0.5f)*0.25f);
AddBouble( plMouth.pl_PositionVector, vRndSpd);
}
}
void ActiveActions(const CPlayerAction &paAction)
{
// translation
FLOAT3D vTranslation = paAction.pa_vTranslation;
// turbo speed cheat
if (cht_fTranslationMultiplier && CheatsEnabled()) {
vTranslation *= cht_fTranslationMultiplier;
}
// enable faster moving if holding knife in DM
if( ((CPlayerWeapons&)*m_penWeapons).m_iCurrentWeapon==WEAPON_KNIFE &&
!GetSP()->sp_bCooperative)
{
vTranslation*=1.30f;
}
en_fAcceleration = plr_fAcceleration;
en_fDeceleration = plr_fDeceleration;
if( !GetSP()->sp_bCooperative)
{
vTranslation(1)*=1.35f;
vTranslation(3)*=1.35f;
//en_fDeceleration *= 0.8f;
}
CContentType &ctUp = GetWorld()->wo_actContentTypes[en_iUpContent];
CContentType &ctDn = GetWorld()->wo_actContentTypes[en_iDnContent];
PlayerState pstWanted = PST_STAND;
BOOL bUpSwimable = (ctUp.ct_ulFlags&CTF_SWIMABLE) && en_fImmersionFactor<=0.99f;
BOOL bDnSwimable = (ctDn.ct_ulFlags&CTF_SWIMABLE) && en_fImmersionFactor>=0.5f;
// if considerably inside swimable content
if (bUpSwimable || bDnSwimable) {
// allow jumping
m_ulFlags|=PLF_JUMPALLOWED;
//CPrintF("swimable %f", en_fImmersionFactor);
// if totaly inside
if (en_fImmersionFactor>=0.99f || bUpSwimable) {
// want to dive
pstWanted = PST_DIVE;
// if only partially inside
} else {
// want to swim
pstWanted = PST_SWIM;
}
// if not in swimable content
} else {
// if has reference
if (en_penReference!=NULL) {
// reset fall timer
m_fFallTime = 0.0f;
// if no reference
} else {
// increase fall time
m_fFallTime += _pTimer->TickQuantum;
}
// if not wanting to jump
if (vTranslation(2)<0.1f) {
// allow jumping
m_ulFlags|=PLF_JUMPALLOWED;
}
// if falling
if (m_fFallTime >= 0.5f) {
// wants to fall
pstWanted = PST_FALL;
// if not falling
} else {
// if holding down and really not in air
if (vTranslation(2)<-0.01f/* && m_fFallTime<0.5f*/) {
// wants to crouch
pstWanted = PST_CROUCH;
// if not holding down
} else {
// wants to stand
pstWanted = PST_STAND;
}
}
}
//CPrintF("c - %s w - %s", NameForState(m_pstState), NameForState(pstWanted));
// flying mode - rotate whole player
if (!(GetPhysicsFlags()&EPF_TRANSLATEDBYGRAVITY)) {
SetDesiredRotation(paAction.pa_aRotation);
StartModelAnim(PLAYER_ANIM_STAND, AOF_LOOPING|AOF_NORESTART);
SetDesiredTranslation(vTranslation);
// normal mode
} else {
PlayerState pstOld = m_pstState;
// if different state needed
if (pstWanted!=m_pstState) {
// check state wanted
switch(pstWanted) {
// if wanting to stand
case PST_STAND: {
// if can stand here
if (ChangeCollisionBoxIndexNow(PLAYER_COLLISION_BOX_STAND)) {
en_plViewpoint.pl_PositionVector(2) = plr_fViewHeightStand;
if (m_pstState==PST_CROUCH) {
((CPlayerAnimator&)*m_penAnimator).Rise();
} else {
((CPlayerAnimator&)*m_penAnimator).Stand();
}
m_pstState = PST_STAND;
}
} break;
// if wanting to crouch
case PST_CROUCH: {
// if can crouch here
if (ChangeCollisionBoxIndexNow(PLAYER_COLLISION_BOX_CROUCH)) {
m_pstState = PST_CROUCH;
en_plViewpoint.pl_PositionVector(2) = plr_fViewHeightCrouch;
((CPlayerAnimator&)*m_penAnimator).Crouch();
}
} break;
// if wanting to swim
case PST_SWIM: {
// if can swim here
if (ChangeCollisionBoxIndexNow(PLAYER_COLLISION_BOX_SWIMSMALL)) {
ChangeCollisionBoxIndexWhenPossible(PLAYER_COLLISION_BOX_SWIM);
m_pstState = PST_SWIM;
en_plViewpoint.pl_PositionVector(2) = plr_fViewHeightSwim;
((CPlayerAnimator&)*m_penAnimator).Swim();
m_fSwimTime = _pTimer->CurrentTick();
}
} break;
// if wanting to dive
case PST_DIVE: {
// if can dive here
if (ChangeCollisionBoxIndexNow(PLAYER_COLLISION_BOX_SWIMSMALL)) {
ChangeCollisionBoxIndexWhenPossible(PLAYER_COLLISION_BOX_SWIM);
m_pstState = PST_DIVE;
en_plViewpoint.pl_PositionVector(2) = plr_fViewHeightDive;
((CPlayerAnimator&)*m_penAnimator).Swim();
}
} break;
// if wanting to fall
case PST_FALL: {
// if can fall here
if (ChangeCollisionBoxIndexNow(PLAYER_COLLISION_BOX_STAND)) {
m_pstState = PST_FALL;
en_plViewpoint.pl_PositionVector(2) = plr_fViewHeightStand;
((CPlayerAnimator&)*m_penAnimator).Fall();
}
} break;
}
}
// if state changed
if (m_pstState!=pstOld) {
// check water entering/leaving
BOOL bWasInWater = (pstOld==PST_SWIM||pstOld==PST_DIVE);
BOOL bIsInWater = (m_pstState==PST_SWIM||m_pstState==PST_DIVE);
// if entered water
if (bIsInWater && !bWasInWater) {
PlaySound(m_soBody, SOUND_WATER_ENTER, SOF_3D);
// if left water
} else if (!bIsInWater && bWasInWater) {
PlaySound(m_soBody, SOUND_WATER_LEAVE, SOF_3D);
m_tmOutOfWater = _pTimer->CurrentTick();
//CPrintF("gotout ");
// if in water
} else if (bIsInWater) {
// if dived in
if (pstOld==PST_SWIM && m_pstState == PST_DIVE) {
PlaySound(m_soFootL, SOUND_DIVEIN, SOF_3D);
if(_pNetwork->IsPlayerLocal(this)) {IFeel_PlayEffect("DiveIn");}
m_bMoveSoundLeft = TRUE;
m_tmMoveSound = _pTimer->CurrentTick();
// if dived out
} else if (m_pstState==PST_SWIM && pstOld==PST_DIVE) {
PlaySound(m_soFootL, SOUND_DIVEOUT, SOF_3D);
m_bMoveSoundLeft = TRUE;
m_tmMoveSound = _pTimer->CurrentTick();
}
}
// if just fell to ground
if (pstOld==PST_FALL && (m_pstState==PST_STAND||m_pstState==PST_CROUCH)) {
PlaySound(m_soFootL, SOUND_LAND, SOF_3D);
if(_pNetwork->IsPlayerLocal(this)) {IFeel_PlayEffect("Land");}
}
// change ambience sounds
if (m_pstState==PST_DIVE) {
m_soLocalAmbientLoop.Set3DParameters(50.0f, 10.0f, 0.25f, 1.0f);
PlaySound(m_soLocalAmbientLoop, SOUND_WATERAMBIENT,
SOF_LOOP|SOF_3D|SOF_VOLUMETRIC|SOF_LOCAL);
} else if (pstOld==PST_DIVE) {
m_soLocalAmbientLoop.Stop();
}
}
// if just jumped
if (en_tmJumped+_pTimer->TickQuantum>=_pTimer->CurrentTick() &&
en_tmJumped<=_pTimer->CurrentTick() && en_penReference==NULL) {
// play jump sound
SetDefaultMouthPitch();
PlaySound(m_soMouth, SOUND_JUMP, SOF_3D);
if(_pNetwork->IsPlayerLocal(this)) {IFeel_PlayEffect("Jump");}
// disallow jumping
m_ulFlags&=~PLF_JUMPALLOWED;
}
// set density
if (m_pstState == PST_SWIM || pstWanted == PST_SWIM
||(pstWanted == PST_DIVE && m_pstState != pstWanted)) {
en_fDensity = 500.0f; // lower density than water
} else {
en_fDensity = 1000.0f; // same density as water
}
if (_pTimer->CurrentTick()>=m_tmNextAmbientOnce)
{
if (m_pstState == PST_DIVE)
{
PlaySound(m_soLocalAmbientOnce, SOUND_WATERBUBBLES,
SOF_3D|SOF_VOLUMETRIC|SOF_LOCAL);
m_soLocalAmbientOnce.Set3DParameters(25.0f, 5.0f, 2.0f, Lerp(0.5f, 1.5f, FRnd()) );
SpawnBubbles( 5+INDEX(FRnd()*5));
}
m_tmNextAmbientOnce = _pTimer->CurrentTick()+5.0f+FRnd();
}
// if crouching
if (m_pstState == PST_CROUCH) {
// go slower
vTranslation /= 2.5f;
// don't go down
vTranslation(2) = 0.0f;
}
// if diving
if (m_pstState == PST_DIVE) {
// translate up/down with view pitch
FLOATmatrix3D mPitch;
MakeRotationMatrixFast(mPitch, FLOAT3D(0,en_plViewpoint.pl_OrientationAngle(2),0));
FLOAT fZ = vTranslation(3);
vTranslation(3) = 0.0f;
vTranslation += FLOAT3D(0,0,fZ)*mPitch;
// if swimming
} else if (m_pstState == PST_SWIM) {
// translate down with view pitch if large
FLOATmatrix3D mPitch;
FLOAT fPitch = en_plViewpoint.pl_OrientationAngle(2);
if (fPitch>-30.0f) {
fPitch = 0;
}
MakeRotationMatrixFast(mPitch, FLOAT3D(0,fPitch,0));
FLOAT fZ = vTranslation(3);
vTranslation(3) = 0.0f;
vTranslation += FLOAT3D(0,0,fZ)*mPitch;
}
// if swimming or diving
if (m_pstState == PST_SWIM || m_pstState == PST_DIVE) {
// up/down is slower than on ground
vTranslation(2)*=0.5f;
}
// if just started swimming
if ((m_pstState == PST_SWIM && _pTimer->CurrentTick()<m_fSwimTime+0.5f)
||_pTimer->CurrentTick()<m_tmOutOfWater+0.5f) {
// no up/down change
vTranslation(2)=0;
//CPrintF(" noup");
}
//CPrintF("\n");
// disable consecutive jumps
if (!(m_ulFlags&PLF_JUMPALLOWED) && vTranslation(2)>0) {
vTranslation(2) = 0.0f;
}
// set translation
SetDesiredTranslation(vTranslation);
// set pitch and banking from the normal rotation into the view rotation
en_plViewpoint.Rotate_HPB(ANGLE3D(
(ANGLE)((FLOAT)paAction.pa_aRotation(1)*_pTimer->TickQuantum),
(ANGLE)((FLOAT)paAction.pa_aRotation(2)*_pTimer->TickQuantum),
(ANGLE)((FLOAT)paAction.pa_aRotation(3)*_pTimer->TickQuantum)));
// pitch and banking boundaries
RoundViewAngle(en_plViewpoint.pl_OrientationAngle(2), PITCH_MAX);
RoundViewAngle(en_plViewpoint.pl_OrientationAngle(3), BANKING_MAX);
// translation rotate player for heading
if (vTranslation.Length() > 0.1f) {
SetDesiredRotation(ANGLE3D(en_plViewpoint.pl_OrientationAngle(1)/_pTimer->TickQuantum, 0.0f, 0.0f));
if (m_ulFlags&PLF_VIEWROTATIONCHANGED) {
m_ulFlags&=~PLF_VIEWROTATIONCHANGED;
FLOATmatrix3D mViewRot;
MakeRotationMatrixFast(mViewRot, ANGLE3D(en_plViewpoint.pl_OrientationAngle(1),0,0));
FLOAT3D vTransRel = vTranslation*mViewRot;
SetDesiredTranslation(vTransRel);
}
en_plViewpoint.pl_OrientationAngle(1) = 0.0f;
// rotate head, body and legs
} else {
m_ulFlags |= PLF_VIEWROTATIONCHANGED;
SetDesiredRotation(ANGLE3D(0.0f, 0.0f, 0.0f));
ANGLE aDiff = en_plViewpoint.pl_OrientationAngle(1) - HEADING_MAX;
if (aDiff > 0.0f) {
SetDesiredRotation(ANGLE3D(aDiff/_pTimer->TickQuantum, 0.0f, 0.0f));
}
aDiff = en_plViewpoint.pl_OrientationAngle(1) + HEADING_MAX;
if (aDiff < 0.0f) {
SetDesiredRotation(ANGLE3D(aDiff/_pTimer->TickQuantum, 0.0f, 0.0f));
}
RoundViewAngle(en_plViewpoint.pl_OrientationAngle(1), HEADING_MAX);
}
// play moving sounds
FLOAT fWantSpeed = en_vDesiredTranslationRelative.Length();
FLOAT fGoesSpeed = en_vCurrentTranslationAbsolute.Length();
BOOL bOnGround = (m_pstState == PST_STAND)||(m_pstState == PST_CROUCH);
BOOL bRunning = bOnGround && fWantSpeed>5.0f && fGoesSpeed>5.0f;
BOOL bWalking = bOnGround && !bRunning && fWantSpeed>2.0f && fGoesSpeed>2.0f;
BOOL bSwimming = (m_pstState == PST_SWIM) && fWantSpeed>2.0f && fGoesSpeed>2.0f;
BOOL bDiving = (m_pstState == PST_DIVE) && fWantSpeed>2.0f && fGoesSpeed>2.0f;
TIME tmNow = _pTimer->CurrentTick();
INDEX iSoundWalkL = SOUND_WALK_L;
INDEX iSoundWalkR = SOUND_WALK_R;
if ((ctDn.ct_ulFlags&CTF_SWIMABLE) && en_fImmersionFactor>=0.1f) {
iSoundWalkL = SOUND_WATERWALK_L;
iSoundWalkR = SOUND_WATERWALK_R;
} else if (en_pbpoStandOn!=NULL &&
en_pbpoStandOn->bpo_bppProperties.bpp_ubSurfaceType==SURFACE_SAND) {
iSoundWalkL = SOUND_WALK_SAND_L;
iSoundWalkR = SOUND_WALK_SAND_R;
} else if (en_pbpoStandOn!=NULL &&
en_pbpoStandOn->bpo_bppProperties.bpp_ubSurfaceType==SURFACE_RED_SAND) {
iSoundWalkL = SOUND_WALK_SAND_L;
iSoundWalkR = SOUND_WALK_SAND_R;
}
else {
}
if (bRunning) {
if (tmNow>m_tmMoveSound+plr_fRunSoundDelay) {
m_tmMoveSound = tmNow;
m_bMoveSoundLeft = !m_bMoveSoundLeft;
if (m_bMoveSoundLeft) {
PlaySound(m_soFootL, iSoundWalkL, SOF_3D);
} else {
PlaySound(m_soFootR, iSoundWalkR, SOF_3D);
}
}
} else if (bWalking) {
if (tmNow>m_tmMoveSound+plr_fWalkSoundDelay) {
m_tmMoveSound = tmNow;
m_bMoveSoundLeft = !m_bMoveSoundLeft;
if (m_bMoveSoundLeft) {
PlaySound(m_soFootL, iSoundWalkL, SOF_3D);
} else {
PlaySound(m_soFootR, iSoundWalkR, SOF_3D);
}
}
} else if (bDiving) {
if (tmNow>m_tmMoveSound+plr_fDiveSoundDelay) {
m_tmMoveSound = tmNow;
m_bMoveSoundLeft = !m_bMoveSoundLeft;
if (m_bMoveSoundLeft) {
PlaySound(m_soFootL, SOUND_DIVE_L, SOF_3D);
} else {
PlaySound(m_soFootR, SOUND_DIVE_R, SOF_3D);
}
}
} else if (bSwimming) {
if (tmNow>m_tmMoveSound+plr_fSwimSoundDelay) {
m_tmMoveSound = tmNow;
m_bMoveSoundLeft = !m_bMoveSoundLeft;
if (m_bMoveSoundLeft) {
PlaySound(m_soFootL, SOUND_SWIM_L, SOF_3D);
} else {
PlaySound(m_soFootR, SOUND_SWIM_R, SOF_3D);
}
}
}
// if player is almost out of air
TIME tmBreathDelay = tmNow-en_tmLastBreathed;
if (en_tmMaxHoldBreath-tmBreathDelay<20.0f) {
// play drowning sound once in a while
if (m_tmMouthSoundLast+2.0f<tmNow) {
m_tmMouthSoundLast = tmNow;
SetRandomMouthPitch(0.9f, 1.1f);
PlaySound(m_soMouth, SOUND_DROWN, SOF_3D);
}
}
// animate player
((CPlayerAnimator&)*m_penAnimator).AnimatePlayer();
}
};
// Round view angle
void RoundViewAngle(ANGLE &aViewAngle, ANGLE aRound) {
if (aViewAngle > aRound) {
aViewAngle = aRound;
}
if (aViewAngle < -aRound) {
aViewAngle = -aRound;
}
};
// Death actions
void DeathActions(const CPlayerAction &paAction) {
// set heading, pitch and banking from the normal rotation into the camera view rotation
if (m_penView!=NULL) {
ASSERT((IsPredicted()&&m_penView->IsPredicted()) || (IsPredictor()&&m_penView->IsPredictor())
|| (!IsPredicted()&&!m_penView->IsPredicted()&&!IsPredictor()&&!m_penView->IsPredictor()));
en_plViewpoint.pl_PositionVector = FLOAT3D(0, 1, 0);
en_plViewpoint.pl_OrientationAngle += (ANGLE3D(
(ANGLE)((FLOAT)paAction.pa_aRotation(1)*_pTimer->TickQuantum),
(ANGLE)((FLOAT)paAction.pa_aRotation(2)*_pTimer->TickQuantum),
(ANGLE)((FLOAT)paAction.pa_aRotation(3)*_pTimer->TickQuantum)));
}
// if death is finished and fire just released again and this is not a predictor
if (m_iMayRespawn==2 && (ulReleasedButtons&PLACT_FIRE) && !IsPredictor()) {
// if singleplayer
if( GetSP()->sp_bSinglePlayer) {
// load quick savegame
_pShell->Execute("gam_bQuickLoad=1;");
// if deathmatch or similar
} else if( !GetSP()->sp_bCooperative) {
// rebirth
SendEvent(EEnd());
// if cooperative
} else {
// if holding down reload button
if (m_ulLastButtons&PLACT_RELOAD) {
// forbid respawning in-place
m_ulFlags &= ~PLF_RESPAWNINPLACE;
}
// if playing on credits
if (GetSP()->sp_ctCredits!=0) {
// if playing on infinite credits or some credits left
if (GetSP()->sp_ctCredits==-1 || GetSP()->sp_ctCreditsLeft!=0) {
// decrement credits
if (GetSP()->sp_ctCredits!=-1) {
((CSessionProperties*)GetSP())->sp_ctCreditsLeft--;
}
// initiate respawn
CPrintF(TRANS("%s is riding the gun again\n"), (const char*)GetPlayerName());
SendEvent(EEnd());
// report number of credits left
if (GetSP()->sp_ctCredits>0) {
if (GetSP()->sp_ctCreditsLeft==0) {
CPrintF(TRANS(" no more credits left!\n"));
} else {
CPrintF(TRANS(" %d credits left\n"), GetSP()->sp_ctCreditsLeft);
}
}
// if no more credits left
} else {
// report that you cannot respawn
CPrintF(TRANS("%s rests in peace - out of credits\n"), (const char*)GetPlayerName());
}
}
}
}
// check fire released once after death
if (m_iMayRespawn==1 && !(ulButtonsNow&PLACT_FIRE)) {
m_iMayRespawn=2;
}
};
// Buttons actions
void ButtonsActions( CPlayerAction &paAction)
{
// if selecting a new weapon select it
if((ulNewButtons&PLACT_SELECT_WEAPON_MASK)!=0) {
ESelectWeapon eSelect;
eSelect.iWeapon = (ulNewButtons&PLACT_SELECT_WEAPON_MASK)>>PLACT_SELECT_WEAPON_SHIFT;
((CPlayerWeapons&)*m_penWeapons).SendEvent(eSelect);
}
if(ulNewButtons&PLACT_WEAPON_NEXT) {
ESelectWeapon eSelect;
eSelect.iWeapon = -1;
((CPlayerWeapons&)*m_penWeapons).SendEvent(eSelect);
}
if(ulNewButtons&PLACT_WEAPON_PREV) {
ESelectWeapon eSelect;
eSelect.iWeapon = -2;
((CPlayerWeapons&)*m_penWeapons).SendEvent(eSelect);
}
if(ulNewButtons&PLACT_WEAPON_FLIP) {
ESelectWeapon eSelect;
eSelect.iWeapon = -3;
((CPlayerWeapons&)*m_penWeapons).SendEvent(eSelect);
}
// if fire is pressed
if (ulNewButtons&PLACT_FIRE) {
((CPlayerWeapons&)*m_penWeapons).SendEvent(EFireWeapon());
}
// if fire is released
if (ulReleasedButtons&PLACT_FIRE) {
((CPlayerWeapons&)*m_penWeapons).SendEvent(EReleaseWeapon());
}
// if reload is pressed
if (ulReleasedButtons&PLACT_RELOAD) {
((CPlayerWeapons&)*m_penWeapons).SendEvent(EReloadWeapon());
}
// if use is pressed
if (ulNewButtons&PLACT_USE) {
UsePressed(ulNewButtons&PLACT_COMPUTER);
// if computer is pressed
} else if (ulNewButtons&PLACT_COMPUTER) {
ComputerPressed();
}
// if 3rd person view is pressed
if (ulNewButtons&PLACT_3RD_PERSON_VIEW) {
ChangePlayerView();
}
// apply center view
if( ulButtonsNow&PLACT_CENTER_VIEW) {
// center view with speed of 45 degrees per 1/20 seconds
paAction.pa_aRotation(2) += Clamp( -en_plViewpoint.pl_OrientationAngle(2)/_pTimer->TickQuantum, -900.0f, +900.0f);
}
};
// check if cheats can be active
BOOL CheatsEnabled(void)
{
return (GetSP()->sp_ctMaxPlayers==1||GetSP()->sp_bQuickTest) && m_penActionMarker==NULL;
}
// Cheats
void Cheats(void)
{
BOOL bFlyOn = cht_bFly || cht_bGhost;
// fly mode
BOOL bIsFlying = !(GetPhysicsFlags() & EPF_TRANSLATEDBYGRAVITY);
if (bFlyOn && !bIsFlying) {
SetPhysicsFlags(GetPhysicsFlags() & ~(EPF_TRANSLATEDBYGRAVITY|EPF_ORIENTEDBYGRAVITY));
en_plViewpoint.pl_OrientationAngle = ANGLE3D(0, 0, 0);
} else if (!bFlyOn && bIsFlying) {
SetPhysicsFlags(GetPhysicsFlags() | EPF_TRANSLATEDBYGRAVITY|EPF_ORIENTEDBYGRAVITY);
en_plViewpoint.pl_OrientationAngle = ANGLE3D(0, 0, 0);
}
// ghost mode
BOOL bIsGhost = !(GetCollisionFlags() & ((ECBI_BRUSH|ECBI_MODEL)<<ECB_TEST));
if (cht_bGhost && !bIsGhost) {
SetCollisionFlags(GetCollisionFlags() & ~((ECBI_BRUSH|ECBI_MODEL)<<ECB_TEST));
} else if (!cht_bGhost && bIsGhost) {
SetCollisionFlags(GetCollisionFlags() | ((ECBI_BRUSH|ECBI_MODEL)<<ECB_TEST));
}
// invisible mode
if (cht_bInvisible) {
SetFlags(GetFlags() | ENF_INVISIBLE);
} else {
SetFlags(GetFlags() & ~ENF_INVISIBLE);
}
// cheat
if (cht_bGiveAll) {
cht_bGiveAll = FALSE;
((CPlayerWeapons&)*m_penWeapons).CheatGiveAll();
}
if (cht_bKillAll) {
cht_bKillAll = FALSE;
KillAllEnemies(this);
}
if (cht_bOpen) {
cht_bOpen = FALSE;
((CPlayerWeapons&)*m_penWeapons).CheatOpen();
}
if (cht_bAllMessages) {
cht_bAllMessages = FALSE;
CheatAllMessages();
}
if (cht_bRefresh) {
cht_bRefresh = FALSE;
SetHealth(TopHealth());
}
};
/************************************************************
* END OF PLAYER ACTIONS *
************************************************************/
// Get current placement that the player views from in absolute space.
void GetLerpedAbsoluteViewPlacement(CPlacement3D &plView) {
if (!(m_ulFlags&PLF_INITIALIZED)) {
plView = GetPlacement();
return;
}
BOOL bSharpTurning =
(GetSettings()->ps_ulFlags&PSF_SHARPTURNING) &&
_pNetwork->IsPlayerLocal((CPlayer*)GetPredictionTail());
// lerp player viewpoint
FLOAT fLerpFactor = _pTimer->GetLerpFactor();
plView.Lerp(en_plLastViewpoint, en_plViewpoint, fLerpFactor);
// moving banking and soft eyes
((CPlayerAnimator&)*m_penAnimator).ChangeView(plView);
// body and head attachment animation
((CPlayerAnimator&)*m_penAnimator).BodyAndHeadOrientation(plView);
// return player eyes view
if (m_iViewState == PVT_PLAYEREYES) {
CPlacement3D plPosLerped = GetLerpedPlacement();
if (bSharpTurning) {
// get your prediction tail
CPlayer *pen = (CPlayer*)GetPredictionTail();
// add local rotation
plView.pl_OrientationAngle = pen->en_plViewpoint.pl_OrientationAngle + (pen->m_aLocalRotation-pen->m_aLastRotation);
// make sure it doesn't go out of limits
RoundViewAngle(plView.pl_OrientationAngle(2), PITCH_MAX);
RoundViewAngle(plView.pl_OrientationAngle(3), BANKING_MAX);
// compensate for rotations that happen to the player without his/hers will
// (rotating brushes, weird gravities...)
// (these need to be lerped)
ANGLE3D aCurr = pen->GetPlacement().pl_OrientationAngle;
ANGLE3D aLast = pen->en_plLastPlacement.pl_OrientationAngle;
ANGLE3D aDesired = pen->en_aDesiredRotationRelative*_pTimer->TickQuantum;
FLOATmatrix3D mCurr; MakeRotationMatrixFast(mCurr, aCurr);
FLOATmatrix3D mLast; MakeRotationMatrixFast(mLast, aLast);
FLOATmatrix3D mDesired; MakeRotationMatrixFast(mDesired, aDesired);
mDesired = en_mRotation*(mDesired*!en_mRotation);
FLOATmatrix3D mForced = !mDesired*mCurr*!mLast; // = aCurr-aLast-aDesired;
ANGLE3D aForced; DecomposeRotationMatrixNoSnap(aForced, mForced);
if (aForced.MaxNorm()<1E-2) {
aForced = ANGLE3D(0,0,0);
}
FLOATquat3D qForced; qForced.FromEuler(aForced);
FLOATquat3D qZero; qZero.FromEuler(ANGLE3D(0,0,0));
FLOATquat3D qLerped = Slerp(fLerpFactor, qZero, qForced);
FLOATmatrix3D m;
qLerped.ToMatrix(m);
m=m*mDesired*mLast;
DecomposeRotationMatrixNoSnap(plPosLerped.pl_OrientationAngle, m);
}
plView.RelativeToAbsoluteSmooth(plPosLerped);
// 3rd person view
} else if (m_iViewState == PVT_3RDPERSONVIEW) {
plView = m_pen3rdPersonView->GetLerpedPlacement();
// camera view for player auto actions
} else if (m_iViewState == PVT_PLAYERAUTOVIEW) {
plView = m_penView->GetLerpedPlacement();
// camera view for stored sequences
} else {
ASSERTALWAYS("Unknown player view");
}
};
// Get current entity that the player views from.
CEntity *GetViewEntity(void) {
// player eyes
if (m_iViewState == PVT_PLAYEREYES) {
return this;
// 3rd person view
} else if (m_iViewState == PVT_3RDPERSONVIEW) {
if (((CPlayerView&)*m_pen3rdPersonView).m_fDistance>2.0f) {
return m_pen3rdPersonView;
} else {
return this;
}
// camera
} else if (m_iViewState == PVT_PLAYERAUTOVIEW) {
if (((CPlayerView&)*m_penView).m_fDistance>2.0f) {
return m_penView;
} else {
return this;
}
// invalid view
} else {
ASSERTALWAYS("Unknown player view");
return NULL;
}
};
// Draw player interface on screen.
void RenderHUD( CPerspectiveProjection3D &prProjection, CDrawPort *pdp,
FLOAT3D vViewerLightDirection, COLOR colViewerLight, COLOR colViewerAmbient,
BOOL bRenderWeapon)
{
// render weapon models if needed
BOOL bRenderModels = _pShell->GetINDEX("gfx_bRenderModels");
if( hud_bShowWeapon && bRenderModels) {
// render weapons only if view is from player eyes
((CPlayerWeapons&)*m_penWeapons).RenderWeaponModel(prProjection, pdp,
vViewerLightDirection, colViewerLight, colViewerAmbient, bRenderWeapon);
}
// render crosshair
CPlacement3D plView;
if (m_iViewState == PVT_PLAYEREYES) {
// player view
plView = en_plViewpoint;
plView.RelativeToAbsolute(GetPlacement());
} else if (m_iViewState == PVT_3RDPERSONVIEW) {
// camera view
plView = ((CPlayerView&)*m_pen3rdPersonView).GetPlacement();
}
((CPlayerWeapons&)*m_penWeapons).RenderCrosshair(prProjection, pdp, plView);
// get your prediction tail
CPlayer *pen = (CPlayer*)GetPredictionTail();
// do screen blending
ULONG ulR=255, ulG=0, ulB=0; // red for wounding
ULONG ulA = pen->m_fDamageAmmount*5.0f;
// if less than few seconds elapsed since last damage
FLOAT tmSinceWounding = _pTimer->CurrentTick() - pen->m_tmWoundedTime;
if( tmSinceWounding<4.0f) {
// decrease damage ammount
if( tmSinceWounding<0.001f) { ulA = (ulA+64)/2; }
}
// add rest of blend ammount
ulA = ClampUp( ulA, (ULONG)224);
if (m_iViewState == PVT_PLAYEREYES) {
pdp->dp_ulBlendingRA += ulR*ulA;
pdp->dp_ulBlendingGA += ulG*ulA;
pdp->dp_ulBlendingBA += ulB*ulA;
pdp->dp_ulBlendingA += ulA;
}
// add world glaring
{
COLOR colGlare = GetWorldGlaring();
UBYTE ubR, ubG, ubB, ubA;
ColorToRGBA(colGlare, ubR, ubG, ubB, ubA);
if (ubA!=0) {
pdp->dp_ulBlendingRA += ULONG(ubR)*ULONG(ubA);
pdp->dp_ulBlendingGA += ULONG(ubG)*ULONG(ubA);
pdp->dp_ulBlendingBA += ULONG(ubB)*ULONG(ubA);
pdp->dp_ulBlendingA += ULONG(ubA);
}
}
// do all queued screen blendings
pdp->BlendScreen();
// render status info line (if needed)
if( hud_bShowInfo) {
// get player or its predictor
BOOL bSnooping = FALSE;
CPlayer *penHUDPlayer = this;
if (penHUDPlayer->IsPredicted()) {
penHUDPlayer = (CPlayer *)penHUDPlayer->GetPredictor();
}
// check if snooping is needed
CPlayerWeapons *pen = (CPlayerWeapons*)&*penHUDPlayer->m_penWeapons;
TIME tmDelta = _pTimer->CurrentTick() - pen->m_tmSnoopingStarted;
if( tmDelta<plr_tmSnoopingTime) {
ASSERT( pen->m_penTargeting!=NULL);
penHUDPlayer = (CPlayer*)&*pen->m_penTargeting;
bSnooping = TRUE;
}
DrawHUD( penHUDPlayer, pdp, bSnooping);
}
}
/************************************************************
* SPECIAL FUNCTIONS *
************************************************************/
// try to find start marker for deathmatch (re)spawning
CEntity *GetDeathmatchStartMarker(void)
{
// get number of markers
CTString strPlayerStart = "Player Start - ";
INDEX ctMarkers = _pNetwork->GetNumberOfEntitiesWithName(strPlayerStart);
// if none
if (ctMarkers==0) {
// fail
return NULL;
}
// if only one
if (ctMarkers==1) {
// get that one
return _pNetwork->GetEntityWithName(strPlayerStart, 0);
}
// if at least two markers found...
// create tables of markers and their distances from players
CStaticArray<MarkerDistance> amdMarkers;
amdMarkers.New(ctMarkers);
// for each marker
{for(INDEX iMarker=0; iMarker<ctMarkers; iMarker++) {
amdMarkers[iMarker].md_ppm = (CPlayerMarker*)_pNetwork->GetEntityWithName(strPlayerStart, iMarker);
if (amdMarkers[iMarker].md_ppm==NULL) {
return NULL; // (if there is any invalidity, fail completely)
}
// get min distance from any player
FLOAT fMinD = UpperLimit(0.0f);
for (INDEX iPlayer=0; iPlayer<GetMaxPlayers(); iPlayer++) {
CPlayer *ppl = (CPlayer *)&*GetPlayerEntity(iPlayer);
if (ppl==NULL) {
continue;
}
FLOAT fD =
(amdMarkers[iMarker].md_ppm->GetPlacement().pl_PositionVector-
ppl->GetPlacement().pl_PositionVector).Length();
if (fD<fMinD) {
fMinD = fD;
}
}
amdMarkers[iMarker].md_fMinD = fMinD;
}}
// now sort the list
qsort(&amdMarkers[0], ctMarkers, sizeof(amdMarkers[0]), &qsort_CompareMarkerDistance);
ASSERT(amdMarkers[0].md_fMinD>=amdMarkers[ctMarkers-1].md_fMinD);
// choose marker among one of the 50% farthest
INDEX ctFarMarkers = ctMarkers/2;
ASSERT(ctFarMarkers>0);
INDEX iStartMarker = IRnd()%ctFarMarkers;
// find first next marker that was not used lately
INDEX iMarker=iStartMarker;
FOREVER{
if (_pTimer->CurrentTick()>amdMarkers[iMarker].md_ppm->m_tmLastSpawned+1.0f) {
break;
}
iMarker = (iMarker+1)%ctMarkers;
if (iMarker==iStartMarker) {
break;
}
}
// return that
return amdMarkers[iMarker].md_ppm;
}
/************************************************************
* INITIALIZE PLAYER *
************************************************************/
void InitializePlayer() {
// set viewpoint position inside the entity
en_plViewpoint.pl_OrientationAngle = ANGLE3D(0,0,0);
en_plViewpoint.pl_PositionVector = FLOAT3D(0.0f, plr_fViewHeightStand, 0.0f);
en_plLastViewpoint = en_plViewpoint;
// clear properties
m_ulFlags &= PLF_INITIALIZED|PLF_LEVELSTARTED|PLF_RESPAWNINPLACE; // must not clear initialized flag
m_fFallTime = 0.0f;
m_pstState = PST_STAND;
m_fDamageAmmount = 0.0f;
m_tmWoundedTime = 0.0f;
// initialize animator
((CPlayerAnimator&)*m_penAnimator).Initialize();
// restart weapons if needed
GetPlayerWeapons()->SendEvent(EStart());
// set flags
SetPhysicsFlags(EPF_MODEL_WALKING|EPF_HASLUNGS);
SetCollisionFlags(ECF_MODEL|((ECBI_PLAYER)<<ECB_IS));
SetFlags(GetFlags()|ENF_ALIVE);
// animation
StartModelAnim(PLAYER_ANIM_STAND, AOF_LOOPING);
TeleportPlayer(WLT_FIXED);
};
FLOAT3D GetTeleportingOffset(void)
{
// find player index
INDEX iPlayer = GetMyPlayerIndex();
// create offset from marker
const FLOAT fOffsetY = 0.1f; // how much to offset up (as precaution not to spawn in floor)
FLOAT3D vOffsetRel = FLOAT3D(0,fOffsetY,0);
if (GetSP()->sp_bCooperative && !GetSP()->sp_bSinglePlayer) {
INDEX iRow = iPlayer/4;
INDEX iCol = iPlayer%4;
vOffsetRel = FLOAT3D(-3.0f+iCol*2.0f, fOffsetY, -3.0f+iRow*2.0f);
}
return vOffsetRel;
}
void TeleportPlayer(enum WorldLinkType EwltType)
{
INDEX iLevel = -1;
CTString strLevelName = GetWorld()->wo_fnmFileName.FileName();
strLevelName.ScanF("%02d_", &iLevel);
if (iLevel>0) {
((CSessionProperties*)GetSP())->sp_ulLevelsMask|=1<<(iLevel-1);
}
// find player index
INDEX iPlayer = GetMyPlayerIndex();
// player placement
CPlacement3D plSet = GetPlacement();
// teleport in dummy space to avoid auto teleport frag
Teleport(CPlacement3D(FLOAT3D(32000.0f+100.0f*iPlayer, 32000.0f, 0), ANGLE3D(0, 0, 0)));
// force yourself to standing state
ForceCollisionBoxIndexChange(PLAYER_COLLISION_BOX_STAND);
en_plViewpoint.pl_PositionVector(2) = plr_fViewHeightStand;
((CPlayerAnimator&)*m_penAnimator).m_bDisableAnimating = FALSE;
((CPlayerAnimator&)*m_penAnimator).Stand();
m_pstState = PST_STAND;
// create offset from marker
FLOAT3D vOffsetRel = GetTeleportingOffset();
// no player start initially
BOOL bSetHealth = FALSE; // for getting health from marker
BOOL bAdjustHealth = FALSE; // for getting adjusting health to 50-100 interval
CEntity *pen = NULL;
if (GetSP()->sp_bCooperative) {
if (cht_iGoToMarker>=0) {
// try to find fast go marker
CTString strPlayerStart;
strPlayerStart.PrintF("Player Start - %d", (INDEX)cht_iGoToMarker);
pen = _pNetwork->GetEntityWithName(strPlayerStart, 0);
pen->SendEvent(ETrigger());
cht_iGoToMarker = -1;
bSetHealth = TRUE;
bAdjustHealth = FALSE;
// if there is coop respawn marker
} else if (m_penMainMusicHolder!=NULL && !(m_ulFlags&PLF_CHANGINGLEVEL)) {
CMusicHolder *pmh = (CMusicHolder *)&*m_penMainMusicHolder;
if (pmh->m_penRespawnMarker!=NULL) {
// get it
pen = pmh->m_penRespawnMarker;
bSetHealth = TRUE;
bAdjustHealth = FALSE;
}
}
// if quick start is enabled (in wed)
if (pen==NULL && GetSP()->sp_bQuickTest && m_strGroup=="") {
// try to find quick start marker
CTString strPlayerStart;
strPlayerStart.PrintF("Player Quick Start");
pen = _pNetwork->GetEntityWithName(strPlayerStart, 0);
bSetHealth = TRUE;
bAdjustHealth = FALSE;
}
// if no start position yet
if (pen==NULL) {
// try to find normal start marker
CTString strPlayerStart;
strPlayerStart.PrintF("Player Start - %s", (const char*)m_strGroup);
pen = _pNetwork->GetEntityWithName(strPlayerStart, 0);
if (m_strGroup=="") {
bSetHealth = TRUE;
bAdjustHealth = FALSE;
} else {
if (EwltType==WLT_FIXED) {
bSetHealth = FALSE;
bAdjustHealth = TRUE;
} else {
bSetHealth = FALSE;
bAdjustHealth = FALSE;
}
}
}
// if no start position yet
if (pen==NULL) {
// try to find normal start marker without group anyway
CTString strPlayerStart;
strPlayerStart.PrintF("Player Start - ");
pen = _pNetwork->GetEntityWithName(strPlayerStart, 0);
bSetHealth = TRUE;
bAdjustHealth = FALSE;
}
} else {
bSetHealth = TRUE;
bAdjustHealth = FALSE;
// try to find start marker by random
pen = GetDeathmatchStartMarker();
if (pen!=NULL) {
((CPlayerMarker&)*pen).m_tmLastSpawned = _pTimer->CurrentTick();
}
}
// if respawning in place
if (m_ulFlags&PLF_RESPAWNINPLACE) {
m_ulFlags &= ~PLF_RESPAWNINPLACE;
// set default params
SetHealth(TopHealth());
m_iMana = GetSP()->sp_iInitialMana;
m_fArmor = 0.0f;
// teleport where you were when you were killed
Teleport(CPlacement3D(m_vDied, m_aDied));
// if start marker is found
} else if (pen!=NULL) {
// if there is no respawn marker yet
if (m_penMainMusicHolder!=NULL) {
CMusicHolder *pmh = (CMusicHolder *)&*m_penMainMusicHolder;
if (pmh->m_penRespawnMarker==NULL) {
// set it
pmh->m_penRespawnMarker = pen;
}
}
CPlayerMarker &CpmStart = (CPlayerMarker&)*pen;
// set player characteristics
if (bSetHealth) {
SetHealth(CpmStart.m_fHealth/100.0f*TopHealth());
m_iMana = GetSP()->sp_iInitialMana;
m_fArmor = CpmStart.m_fShield;
} else if (bAdjustHealth) {
FLOAT fHealth = GetHealth();
FLOAT fTopHealth = TopHealth();
if( fHealth < fTopHealth) {
SetHealth(ClampUp(fHealth+fTopHealth/2.0f, fTopHealth));
}
}
// if should start in computer
if (CpmStart.m_bStartInComputer && GetSP()->sp_bSinglePlayer) {
// mark that
if (_pNetwork->IsPlayerLocal(this)) {
cmp_ppenPlayer = this;
}
cmp_bInitialStart = TRUE;
}
// start with first message linked to the marker
CMessageHolder *penMessage = (CMessageHolder *)&*CpmStart.m_penMessage;
// while there are some messages to add
while (penMessage!=NULL && IsOfClass(penMessage, "MessageHolder")) {
const CTFileName &fnmMessage = penMessage->m_fnmMessage;
// if player doesn't have that message in database
if (!HasMessage(fnmMessage)) {
// add the message
ReceiveComputerMessage(fnmMessage, 0);
}
// go to next message holder in list
penMessage = (CMessageHolder *)&*penMessage->m_penNext;
}
// set weapons
if (!GetSP()->sp_bCooperative) {
((CPlayerWeapons&)*m_penWeapons).InitializeWeapons(CpmStart.m_iGiveWeapons, 0, 0,
CpmStart.m_fMaxAmmoRatio);
} else {
((CPlayerWeapons&)*m_penWeapons).InitializeWeapons(CpmStart.m_iGiveWeapons, CpmStart.m_iTakeWeapons,
GetSP()->sp_bInfiniteAmmo?0:CpmStart.m_iTakeAmmo, CpmStart.m_fMaxAmmoRatio);
}
// start position relative to link
if (EwltType == WLT_RELATIVE) {
plSet.AbsoluteToRelative(_SwcWorldChange.plLink); // relative to link position
plSet.RelativeToAbsolute(CpmStart.GetPlacement()); // absolute to start marker position
Teleport(plSet);
// fixed start position
} else if (EwltType == WLT_FIXED) {
CPlacement3D plNew = CpmStart.GetPlacement();
vOffsetRel*=CpmStart.en_mRotation;
plNew.pl_PositionVector += vOffsetRel;
Teleport(plNew);
// error -> teleport to zero
} else {
ASSERTALWAYS("Unknown world link type");
Teleport(CPlacement3D(FLOAT3D(0, 0, 0)+vOffsetRel, ANGLE3D(0, 0, 0)));
}
// if there is a start trigger target
if(CpmStart.m_penTarget!=NULL) {
SendToTarget(CpmStart.m_penTarget, EET_TRIGGER, this);
}
// default start position
} else {
// set player characteristics
SetHealth(TopHealth());
m_iMana = GetSP()->sp_iInitialMana;
m_fArmor = 0.0f;
// set weapons
((CPlayerWeapons&)*m_penWeapons).InitializeWeapons(0, 0, 0, 0);
// start position
Teleport(CPlacement3D(FLOAT3D(0, 0, 0)+vOffsetRel, ANGLE3D(0, 0, 0)));
}
// send teleport event to all entities in range
SendEventInRange(ETeleport(), FLOATaabbox3D(GetPlacement().pl_PositionVector, 200.0f));
// stop moving
ForceFullStop();
// remember maximum health
m_fMaxHealth = TopHealth();
// if in singleplayer mode
if (GetSP()->sp_bSinglePlayer && GetSP()->sp_gmGameMode!=CSessionProperties::GM_FLYOVER) {
// save quick savegame
_pShell->Execute("gam_bQuickSave=1;");
}
// remember level start time
if (!(m_ulFlags&PLF_LEVELSTARTED)) {
m_ulFlags |= PLF_LEVELSTARTED;
m_tmLevelStarted = _pNetwork->GetGameTime();
}
// reset model appearance
CTString strDummy;
SetPlayerAppearance(GetModelObject(), NULL, strDummy, /*bPreview=*/FALSE);
ValidateCharacter();
SetPlayerAppearance(&m_moRender, &en_pcCharacter, strDummy, /*bPreview=*/FALSE);
GetPlayerAnimator()->SetWeapon();
m_ulFlags |= PLF_SYNCWEAPON;
// spawn teleport effect
SpawnTeleport();
// return from editor model (if was fragged into pieces)
SwitchToModel();
m_tmSpawned = _pTimer->CurrentTick();
en_tmLastBreathed = _pTimer->CurrentTick()+0.1f; // do not take breath when spawned in air
};
// note: set estimated time in advance
void RecordEndOfLevelData(void)
{
// must not be called multiple times
ASSERT(!m_bEndOfLevel);
// clear analyses message
m_tmAnalyseEnd = 0;
m_bPendingMessage = FALSE;
m_tmMessagePlay = 0;
// mark end of level
m_iMayRespawn = 0;
m_bEndOfLevel = TRUE;
// remember end time
time_t t;
time(&t);
m_iEndTime = (INDEX)t;
// add time score
TIME tmLevelTime = _pTimer->CurrentTick()-m_tmLevelStarted;
m_psLevelStats.ps_tmTime = tmLevelTime;
m_psGameStats.ps_tmTime += tmLevelTime;
FLOAT fTimeDelta = ClampDn(floorf(m_tmEstTime)-floorf(tmLevelTime), 0.0f);
m_iTimeScore = floor(fTimeDelta*100.0f);
m_psLevelStats.ps_iScore+=m_iTimeScore;
m_psGameStats.ps_iScore+=m_iTimeScore;
// record stats for this level and add to global table
CTString strStats;
strStats.PrintF(TRANS("%s\n Time: %s\n Score: %9d\n Kills: %03d/%03d\n Secrets: %02d/%02d\n"),
(const char*)TranslateConst(en_pwoWorld->GetName(), 0), (const char*)TimeToString(tmLevelTime),
m_psLevelStats.ps_iScore,
m_psLevelStats.ps_iKills, m_psLevelTotal.ps_iKills,
m_psLevelStats.ps_iSecrets, m_psLevelTotal.ps_iSecrets);
m_strLevelStats += strStats;
}
// spawn teleport effect
void SpawnTeleport(void)
{
// if in singleplayer
if (GetSP()->sp_bSinglePlayer) {
// no spawn effects
return;
}
ESpawnEffect ese;
ese.colMuliplier = C_WHITE|CT_OPAQUE;
ese.betType = BET_TELEPORT;
ese.vNormal = FLOAT3D(0,1,0);
FLOATaabbox3D box;
GetBoundingBox(box);
FLOAT fEntitySize = box.Size().MaxNorm()*2;
ese.vStretch = FLOAT3D(fEntitySize, fEntitySize, fEntitySize);
CEntityPointer penEffect = CreateEntity(GetPlacement(), CLASS_BASIC_EFFECT);
penEffect->Initialize(ese);
}
// render particles
void RenderParticles(void)
{
// render empty shells
Particles_EmptyShells( this, m_asldData);
if (Particle_GetViewer()==this) {
Particles_ViewerLocal(this);
}
// spirit particles
if( m_tmSpiritStart != 0.0f)
{
Particles_Appearing(this, m_tmSpiritStart);
}
}
void TeleportToAutoMarker(CPlayerActionMarker *ppam)
{
// if we are in coop
if (GetSP()->sp_bCooperative && !GetSP()->sp_bSinglePlayer) {
// for each player
for(INDEX iPlayer=0; iPlayer<GetMaxPlayers(); iPlayer++) {
CPlayer *ppl = (CPlayer*)GetPlayerEntity(iPlayer);
if (ppl!=NULL) {
// put it at marker
CPlacement3D pl = ppam->GetPlacement();
FLOAT3D vOffsetRel = ppl->GetTeleportingOffset();
pl.pl_PositionVector += vOffsetRel*ppam->en_mRotation;
ppl->Teleport(pl, FALSE);
// remember new respawn place
ppl->m_vDied = pl.pl_PositionVector;
ppl->m_aDied = pl.pl_OrientationAngle;
}
}
// otherwise
} else {
// put yourself at marker
CPlacement3D pl = ppam->GetPlacement();
FLOAT3D vOffsetRel = GetTeleportingOffset();
pl.pl_PositionVector += vOffsetRel*ppam->en_mRotation;
Teleport(pl, FALSE);
}
}
// check whether this time we respawn in place or on marker
void CheckDeathForRespawnInPlace(EDeath eDeath)
{
// if respawning in place is not allowed
if (!GetSP()->sp_bRespawnInPlace) {
// skip further checks
return;
}
// if killed by a player or enemy
CEntity *penKiller = eDeath.eLastDamage.penInflictor;
if (IsOfClass(penKiller, "Player") || IsDerivedFromClass(penKiller, "Enemy Base")) {
// mark for respawning in place
m_ulFlags |= PLF_RESPAWNINPLACE;
m_vDied = GetPlacement().pl_PositionVector;
m_aDied = GetPlacement().pl_OrientationAngle;
}
}
procedures:
/************************************************************
* WOUNDED *
************************************************************/
Wounded(EDamage eDamage) {
return;
};
/************************************************************
* WORLD CHANGE *
************************************************************/
WorldChange() {
// if in single player
if (GetSP()->sp_bSinglePlayer) {
// mark world as visited
CTString strDummy("1");
SaveStringVar(GetWorld()->wo_fnmFileName.NoExt()+".vis", strDummy);
}
// find music holder on new world
FindMusicHolder();
// store group name
m_strGroup = _SwcWorldChange.strGroup;
TeleportPlayer((WorldLinkType)_SwcWorldChange.iType);
// setup light source
SetupLightSource();
// update per-level stats
UpdateLevelStats();
m_ulFlags |= PLF_INITIALIZED;
m_ulFlags &= ~PLF_CHANGINGLEVEL;
return;
};
WorldChangeDead()
{
// forbid respawning in-place when changing levels while dead
m_ulFlags &= ~PLF_RESPAWNINPLACE;
// if in single player
if (GetSP()->sp_bSinglePlayer) {
// mark world as visited
CTString strDummy("1");
SaveStringVar(GetWorld()->wo_fnmFileName.NoExt()+".vis", strDummy);
}
// find music holder on new world
FindMusicHolder();
// store group name
autocall Rebirth() EReturn;
// setup light source
SetupLightSource();
// update per-level stats
UpdateLevelStats();
m_ulFlags |= PLF_INITIALIZED;
m_ulFlags &= ~PLF_CHANGINGLEVEL;
return;
}
/************************************************************
* D E A T H *
************************************************************/
Death(EDeath eDeath) {
// stop firing when dead
((CPlayerWeapons&)*m_penWeapons).SendEvent(EReleaseWeapon());
// stop all looping ifeel effects
if(_pNetwork->IsPlayerLocal(this))
{
IFeel_StopEffect("Minigun_rotate");
}
// if in single player, or if this is a predictor entity
if (GetSP()->sp_bSinglePlayer || IsPredictor()) {
// do not print anything
NOTHING;
// if in cooperative, but not single player
} else if (GetSP()->sp_bCooperative) {
// just print death message, no score updating
PrintPlayerDeathMessage(this, eDeath);
// check whether this time we respawn in place or on marker
CheckDeathForRespawnInPlace(eDeath);
// increase number of deaths
m_psLevelStats.ps_iDeaths += 1;
m_psGameStats.ps_iDeaths += 1;
// if not in cooperative, and not single player
} else {
// print death message
PrintPlayerDeathMessage(this, eDeath);
// get the killer pointer
CEntity *penKiller = eDeath.eLastDamage.penInflictor;
// initially, not killed by a player
CPlayer *pplKillerPlayer = NULL;
// if killed by some entity
if (penKiller!=NULL) {
// if killed by player
if (IsOfClass(penKiller, "Player")) {
// if someone other then you
if (penKiller!=this) {
pplKillerPlayer = (CPlayer*)penKiller;
EReceiveScore eScore;
eScore.iPoints = m_iMana;
eDeath.eLastDamage.penInflictor->SendEvent(eScore);
eDeath.eLastDamage.penInflictor->SendEvent(EKilledEnemy());
// if it was yourself
} else {
m_psLevelStats.ps_iScore -= m_iMana;
m_psGameStats.ps_iScore -= m_iMana;
m_psLevelStats.ps_iKills -= 1;
m_psGameStats.ps_iKills -= 1;
}
// if killed by non-player
} else {
m_psLevelStats.ps_iScore -= m_iMana;
m_psGameStats.ps_iScore -= m_iMana;
m_psLevelStats.ps_iKills -= 1;
m_psGameStats.ps_iKills -= 1;
}
// if killed by NULL (shouldn't happen, but anyway)
} else {
m_psLevelStats.ps_iScore -= m_iMana;
m_psGameStats.ps_iScore -= m_iMana;
m_psLevelStats.ps_iKills -= 1;
m_psGameStats.ps_iKills -= 1;
}
// if playing scorematch
if (!GetSP()->sp_bUseFrags) {
// if killed by a player
if (pplKillerPlayer!=NULL) {
// print how much that player gained
CPrintF(TRANS(" %s: +%d points\n"), (const char*)pplKillerPlayer->GetPlayerName(), m_iMana);
// if it was a suicide, or an accident
} else {
// print how much you lost
CPrintF(TRANS(" %s: -%d points\n"), (const char*)GetPlayerName(), m_iMana);
}
}
// increase number of deaths
m_psLevelStats.ps_iDeaths += 1;
m_psGameStats.ps_iDeaths += 1;
}
// store last view
m_iLastViewState = m_iViewState;
// mark player as death
SetFlags(GetFlags()&~ENF_ALIVE);
// stop player
SetDesiredTranslation(FLOAT3D(0.0f, 0.0f, 0.0f));
SetDesiredRotation(ANGLE3D(0.0f, 0.0f, 0.0f));
// remove weapon from hand
((CPlayerAnimator&)*m_penAnimator).RemoveWeapon();
// kill weapon animations
GetPlayerWeapons()->SendEvent(EStop());
// if in deathmatch
if (!GetSP()->sp_bCooperative) {
// drop current weapon as item so others can pick it
GetPlayerWeapons()->DropWeapon();
}
// play death
INDEX iAnim1;
INDEX iAnim2;
if (m_pstState == PST_SWIM || m_pstState == PST_DIVE) {
iAnim1 = PLAYER_ANIM_DEATH_UNDERWATER;
iAnim2 = BODY_ANIM_DEATH_UNDERWATER;
} else if (eDeath.eLastDamage.dmtType==DMT_SPIKESTAB) {
iAnim1 = PLAYER_ANIM_DEATH_SPIKES;
iAnim2 = BODY_ANIM_DEATH_SPIKES;
} else if (eDeath.eLastDamage.dmtType==DMT_ABYSS) {
iAnim1 = PLAYER_ANIM_ABYSSFALL;
iAnim2 = BODY_ANIM_ABYSSFALL;
} else {
FLOAT3D vFront;
GetHeadingDirection(0, vFront);
FLOAT fDamageDir = m_vDamage%vFront;
if (fDamageDir<0) {
if (Abs(fDamageDir)<10.0f) {
iAnim1 = PLAYER_ANIM_DEATH_EASYFALLBACK;
iAnim2 = BODY_ANIM_DEATH_EASYFALLBACK;
} else {
iAnim1 = PLAYER_ANIM_DEATH_BACK;
iAnim2 = BODY_ANIM_DEATH_BACK;
}
} else {
if (Abs(fDamageDir)<10.0f) {
iAnim1 = PLAYER_ANIM_DEATH_EASYFALLFORWARD;
iAnim2 = BODY_ANIM_DEATH_EASYFALLFORWARD;
} else {
iAnim1 = PLAYER_ANIM_DEATH_FORWARD;
iAnim2 = BODY_ANIM_DEATH_FORWARD;
}
}
}
en_plViewpoint.pl_OrientationAngle = ANGLE3D(0,0,0);
StartModelAnim(iAnim1, 0);
CModelObject &moBody = GetModelObject()->GetAttachmentModel(PLAYER_ATTACHMENT_TORSO)->amo_moModelObject;
moBody.PlayAnim(iAnim2, 0);
// set physic flags
SetPhysicsFlags(EPF_MODEL_CORPSE);
SetCollisionFlags(ECF_CORPSE);
// set density to float out of water
en_fDensity = 400.0f;
// play sound
if (m_pstState==PST_DIVE) {
SetDefaultMouthPitch();
PlaySound(m_soMouth, SOUND_DEATHWATER, SOF_3D);
if(_pNetwork->IsPlayerLocal(this)) {IFeel_PlayEffect("DeathWater");}
} else {
SetDefaultMouthPitch();
PlaySound(m_soMouth, SOUND_DEATH, SOF_3D);
if(_pNetwork->IsPlayerLocal(this)) {IFeel_PlayEffect("Death");}
}
// initialize death camera view
ASSERT(m_penView == NULL);
if (m_penView == NULL) {
m_penView = CreateEntity(GetPlacement(), CLASS_PLAYER_VIEW);
EViewInit eInit;
eInit.penOwner = this;
eInit.penCamera = NULL;
eInit.vtView = VT_PLAYERDEATH;
eInit.bDeathFixed = eDeath.eLastDamage.dmtType==DMT_ABYSS;
m_penView->Initialize(eInit);
}
if (ShouldBlowUp()) {
BlowUp();
} else {
// leave a stain beneath
LeaveStain(TRUE);
}
m_iMayRespawn = 0;
// wait for anim of death
wait (1.2f) {
on (EBegin) : {
// set new view status
m_iViewState = PVT_PLAYERAUTOVIEW;
resume;
}
// when anim is finished
on (ETimer) : {
// allow respawning
m_iMayRespawn = 1;
resume;
}
// when damaged
on (EDamage eDamage) : {
if (eDamage.dmtType==DMT_ABYSS) {
if (m_penView!=NULL) {
((CPlayerView*)&*m_penView)->m_bFixed = TRUE;
}
}
// if should blow up now (and not already blown up)
if (ShouldBlowUp()) {
// do it
BlowUp();
}
resume;
}
on (EDeath) : { resume; }
// if player pressed fire
on (EEnd) : {
// NOTE: predictors must never respawn since player markers for respawning are not predicted
// if this is not predictor
if (!IsPredictor()) {
// stop waiting
stop;
}
}
// if autoaction is received
on (EAutoAction eAutoAction) : {
// if we are in coop
if (GetSP()->sp_bCooperative && !GetSP()->sp_bSinglePlayer) {
// if the marker is teleport marker
if (eAutoAction.penFirstMarker!=NULL &&
((CPlayerActionMarker*)&*eAutoAction.penFirstMarker)->m_paaAction == PAA_TELEPORT) {
// teleport there
TeleportToAutoMarker((CPlayerActionMarker*)&*eAutoAction.penFirstMarker);
}
}
// ignore the actions
resume;
}
on (EDisconnected) : { pass; }
on (EReceiveScore) : { pass; }
on (EPreLevelChange) : { pass; }
on (EPostLevelChange) : { pass; }
otherwise() : { resume; }
}
return ERebirth();
};
TheEnd() {
// if not playing demo
if (!_pNetwork->IsPlayingDemo()) {
// record high score in single player only
if (GetSP()->sp_bSinglePlayer) {
_pShell->Execute("gam_iRecordHighScore=0;");
}
}
// if current difficulty is serious
if (GetSP()->sp_gdGameDifficulty==CSessionProperties::GD_EXTREME) {
// activate the mental mode
_pShell->Execute("sam_bMentalActivated=1;");
}
// stop firing when end
((CPlayerWeapons&)*m_penWeapons).SendEvent(EReleaseWeapon());
// mark player as dead
SetFlags(GetFlags()&~ENF_ALIVE);
// stop player
SetDesiredTranslation(FLOAT3D(0.0f, 0.0f, 0.0f));
SetDesiredRotation(ANGLE3D(0.0f, 0.0f, 0.0f));
// look straight
StartModelAnim(PLAYER_ANIM_STAND, 0);
((CPlayerAnimator&)*m_penAnimator).BodyAnimationTemplate(
BODY_ANIM_NORMALWALK, BODY_ANIM_COLT_STAND, BODY_ANIM_SHOTGUN_STAND, BODY_ANIM_MINIGUN_STAND,
AOF_LOOPING|AOF_NORESTART);
en_plViewpoint.pl_OrientationAngle = ANGLE3D(0,0,0);
// call computer
m_bEndOfGame = TRUE;
SetGameEnd();
wait () {
on (EBegin) : { resume; }
on (EReceiveScore) : { pass; }
on (ECenterMessage) : { pass; }
otherwise() : { resume; }
}
};
/************************************************************
* R E B I R T H *
************************************************************/
FirstInit() {
// restore last view
m_iViewState = m_iLastViewState;
// stop and kill camera
if (m_penView != NULL) {
((CPlayerView&)*m_penView).SendEvent(EEnd());
m_penView = NULL;
}
FindMusicHolder();
// update per-level stats
UpdateLevelStats();
// initialize player (from PlayerMarker)
InitializePlayer();
// add statistics message
ReceiveComputerMessage(CTFILENAME("Data\\Messages\\Statistics\\Statistics.txt"), CMF_READ);
if (GetSettings()->ps_ulFlags&PSF_PREFER3RDPERSON) {
ChangePlayerView();
}
return;
};
Rebirth() {
// restore last view
m_iViewState = m_iLastViewState;
// clear ammunition
if (!(m_ulFlags&PLF_RESPAWNINPLACE)) {
GetPlayerWeapons()->ClearWeapons();
}
// stop and kill camera
if (m_penView != NULL) {
((CPlayerView&)*m_penView).SendEvent(EEnd());
m_penView = NULL;
}
FindMusicHolder();
// initialize player (from PlayerMarker)
InitializePlayer();
return EReturn();
};
// auto action - go to current marker
AutoGoToMarker(EVoid)
{
ULONG ulFlags = AOF_LOOPING|AOF_NORESTART;
INDEX iAnim = GetModelObject()->GetAnim();
if( iAnim!=PLAYER_ANIM_STAND)
{
ulFlags |= AOF_SMOOTHCHANGE;
}
CPlayerAnimator &plan = (CPlayerAnimator&)*m_penAnimator;
plan.BodyWalkAnimation();
if (m_fAutoSpeed>plr_fSpeedForward/2) {
StartModelAnim(PLAYER_ANIM_RUN, ulFlags);
} else {
StartModelAnim(PLAYER_ANIM_NORMALWALK, ulFlags);
}
// while not at marker
while (
(m_penActionMarker->GetPlacement().pl_PositionVector-
GetPlacement().pl_PositionVector).Length()>1.0f) {
// wait a bit
autowait(_pTimer->TickQuantum);
}
// return to auto-action loop
return EReturn();
}
// auto action - go to current marker and stop there
AutoGoToMarkerAndStop(EVoid)
{
ULONG ulFlags = AOF_LOOPING|AOF_NORESTART;
INDEX iAnim = GetModelObject()->GetAnim();
if( iAnim!=PLAYER_ANIM_STAND)
{
ulFlags |= AOF_SMOOTHCHANGE;
}
CPlayerAnimator &plan = (CPlayerAnimator&)*m_penAnimator;
plan.BodyWalkAnimation();
if (m_fAutoSpeed>plr_fSpeedForward/2) {
StartModelAnim(PLAYER_ANIM_RUN, ulFlags);
} else {
StartModelAnim(PLAYER_ANIM_NORMALWALK, ulFlags);
}
// while not at marker
while (
(m_penActionMarker->GetPlacement().pl_PositionVector-
GetPlacement().pl_PositionVector).Length()>m_fAutoSpeed*_pTimer->TickQuantum*2.00f) {
// wait a bit
autowait(_pTimer->TickQuantum);
}
// disable auto speed
m_fAutoSpeed = 0.0f;
CPlayerAnimator &plan = (CPlayerAnimator&)*m_penAnimator;
plan.BodyStillAnimation();
StartModelAnim(PLAYER_ANIM_STAND, AOF_LOOPING|AOF_NORESTART);
// stop moving
ForceFullStop();
// return to auto-action loop
return EReturn();
}
// auto action - use an item
AutoUseItem(EVoid)
{
// start pulling the item
CPlayerAnimator &plan = (CPlayerAnimator&)*m_penAnimator;
plan.BodyPullItemAnimation();
//StartModelAnim(PLAYER_ANIM_STATUE_PULL, 0);
autowait(0.2f);
// item appears
CPlayerActionMarker *ppam = GetActionMarker();
if (IsOfClass(ppam->m_penItem, "KeyItem")) {
CModelObject &moItem = ppam->m_penItem->GetModelObject()->GetAttachmentModel(0)->amo_moModelObject;
GetPlayerAnimator()->SetItem(&moItem);
}
autowait(2.20f-0.2f);
// the item is in place
CPlayerAnimator &plan = (CPlayerAnimator&)*m_penAnimator;
plan.BodyRemoveItem();
// if marker points to a trigger
if (GetActionMarker()->m_penTrigger!=NULL) {
// trigger it
SendToTarget(GetActionMarker()->m_penTrigger, EET_TRIGGER, this);
}
// fake that player has passed through the door controller
if (GetActionMarker()->m_penDoorController!=NULL) {
EPass ePass;
ePass.penOther = this;
GetActionMarker()->m_penDoorController->SendEvent(ePass);
}
autowait(3.25f-2.20f);
CPlayerAnimator &plan = (CPlayerAnimator&)*m_penAnimator;
plan.BodyRemoveItem();
// return to auto-action loop
return EReturn();
}
// auto action - pick an item
AutoPickItem(EVoid)
{
// start pulling the item
CPlayerAnimator &plan = (CPlayerAnimator&)*m_penAnimator;
plan.BodyPickItemAnimation();
StartModelAnim(PLAYER_ANIM_KEYLIFT, 0);
autowait(1.2f);
// if marker points to a trigger
if (GetActionMarker()->m_penTrigger!=NULL) {
// trigger it
SendToTarget(GetActionMarker()->m_penTrigger, EET_TRIGGER, this);
}
// item appears
CPlayerActionMarker *ppam = GetActionMarker();
if (IsOfClass(ppam->m_penItem, "KeyItem")) {
CModelObject &moItem = ppam->m_penItem->GetModelObject()->GetAttachmentModel(0)->amo_moModelObject;
GetPlayerAnimator()->SetItem(&moItem);
EPass ePass;
ePass.penOther = this;
ppam->m_penItem->SendEvent(ePass);
}
autowait(3.6f-1.2f+GetActionMarker()->m_tmWait);
CPlayerAnimator &plan = (CPlayerAnimator&)*m_penAnimator;
plan.BodyRemoveItem();
// return to auto-action loop
return EReturn();
}
AutoFallDown(EVoid)
{
StartModelAnim(PLAYER_ANIM_BRIDGEFALLPOSE, 0);
CModelObject &moBody = GetModelObject()->GetAttachmentModel(PLAYER_ATTACHMENT_TORSO)->amo_moModelObject;
moBody.PlayAnim(BODY_ANIM_BRIDGEFALLPOSE, 0);
autowait(GetActionMarker()->m_tmWait);
// return to auto-action loop
return EReturn();
}
AutoFallToAbys(EVoid)
{
StartModelAnim(PLAYER_ANIM_ABYSSFALL, AOF_LOOPING);
CModelObject &moBody = GetModelObject()->GetAttachmentModel(PLAYER_ATTACHMENT_TORSO)->amo_moModelObject;
moBody.PlayAnim(BODY_ANIM_ABYSSFALL, AOF_LOOPING);
autowait(GetActionMarker()->m_tmWait);
// return to auto-action loop
return EReturn();
}
// auto action - look around
AutoLookAround(EVoid)
{
StartModelAnim(PLAYER_ANIM_BACKPEDAL, 0);
m_vAutoSpeed = FLOAT3D(0,0,plr_fSpeedForward/4/0.75f);
CModelObject &moBody = GetModelObject()->GetAttachmentModel(PLAYER_ATTACHMENT_TORSO)->amo_moModelObject;
moBody.PlayAnim(BODY_ANIM_NORMALWALK, 0);
autowait(GetModelObject()->GetCurrentAnimLength()/2);
m_vAutoSpeed = FLOAT3D(0,0,0);
// start looking around
StartModelAnim(PLAYER_ANIM_STAND, 0);
CModelObject &moBody = GetModelObject()->GetAttachmentModel(PLAYER_ATTACHMENT_TORSO)->amo_moModelObject;
moBody.PlayAnim(BODY_ANIM_LOOKAROUND, 0);
CPlayerAnimator &plan = (CPlayerAnimator&)*m_penAnimator;
// wait given time
autowait(moBody.GetCurrentAnimLength()+0.1f);
// return to auto-action loop
return EReturn();
}
AutoTeleport(EVoid)
{
// teleport there
TeleportToAutoMarker(GetActionMarker());
// return to auto-action loop
return EReturn();
}
AutoAppear(EVoid)
{
// hide the model
SwitchToEditorModel();
// put it at marker
Teleport(GetActionMarker()->GetPlacement());
// make it rotate in spawnpose
SetPhysicsFlags(GetPhysicsFlags() & ~(EPF_TRANSLATEDBYGRAVITY|EPF_ORIENTEDBYGRAVITY));
m_ulFlags|=PLF_AUTOMOVEMENTS;
SetDesiredRotation(ANGLE3D(60,0,0));
StartModelAnim(PLAYER_ANIM_SPAWNPOSE, AOF_LOOPING);
CModelObject &moBody = GetModelObject()->GetAttachmentModel(PLAYER_ATTACHMENT_TORSO)->amo_moModelObject;
moBody.PlayAnim(BODY_ANIM_SPAWNPOSE, AOF_LOOPING);
// start stardust appearing
m_tmSpiritStart = _pTimer->CurrentTick();
// wait till it appears
autowait(5);
// start model appearing
SwitchToModel();
m_tmFadeStart = _pTimer->CurrentTick();
// wait till it appears
autowait(5);
// fixate full opacity
COLOR colAlpha = GetModelObject()->mo_colBlendColor;
GetModelObject()->mo_colBlendColor = colAlpha|0xFF;
// put it to normal state
SetPhysicsFlags(GetPhysicsFlags() | EPF_TRANSLATEDBYGRAVITY|EPF_ORIENTEDBYGRAVITY);
SetDesiredRotation(ANGLE3D(0,0,0));
m_ulFlags&=~PLF_AUTOMOVEMENTS;
// play animation to fall down
StartModelAnim(PLAYER_ANIM_SPAWN_FALLDOWN, 0);
CModelObject &moBody = GetModelObject()->GetAttachmentModel(PLAYER_ATTACHMENT_TORSO)->amo_moModelObject;
moBody.PlayAnim(BODY_ANIM_SPAWN_FALLDOWN, 0);
autowait(GetModelObject()->GetCurrentAnimLength());
// play animation to get up
StartModelAnim(PLAYER_ANIM_SPAWN_GETUP, AOF_SMOOTHCHANGE);
CModelObject &moBody = GetModelObject()->GetAttachmentModel(PLAYER_ATTACHMENT_TORSO)->amo_moModelObject;
moBody.PlayAnim(BODY_ANIM_SPAWN_GETUP, AOF_SMOOTHCHANGE);
autowait(GetModelObject()->GetCurrentAnimLength());
// return to auto-action loop
return EReturn();
}
TravellingInBeam()
{
// put it at marker
Teleport(GetActionMarker()->GetPlacement());
// make it rotate in spawnpose
SetPhysicsFlags(GetPhysicsFlags() & ~(EPF_TRANSLATEDBYGRAVITY|EPF_ORIENTEDBYGRAVITY));
m_ulFlags|=PLF_AUTOMOVEMENTS;
SetDesiredRotation(ANGLE3D(60,0,0));
SetDesiredTranslation(ANGLE3D(0,20.0f,0));
StartModelAnim(PLAYER_ANIM_SPAWNPOSE, AOF_LOOPING);
CModelObject &moBody = GetModelObject()->GetAttachmentModel(PLAYER_ATTACHMENT_TORSO)->amo_moModelObject;
moBody.PlayAnim(BODY_ANIM_SPAWNPOSE, AOF_LOOPING);
// wait till it appears
autowait(8.0f);
// switch to model
SwitchToEditorModel();
// return to auto-action loop
return EReturn();
}
LogoFireMinigun(EVoid)
{
// put it at marker
CPlacement3D pl = GetActionMarker()->GetPlacement();
pl.pl_PositionVector += FLOAT3D(0, 0.01f, 0)*GetActionMarker()->en_mRotation;
Teleport(pl);
en_plViewpoint.pl_OrientationAngle(1) = 20.0f;
en_plLastViewpoint.pl_OrientationAngle = en_plViewpoint.pl_OrientationAngle;
// stand in pose
StartModelAnim(PLAYER_ANIM_INTRO, AOF_LOOPING);
// remember time for rotating view start
m_tmMinigunAutoFireStart = _pTimer->CurrentTick();
// wait some time for fade in and to look from left to right with out fireing
//autowait(0.75f);
((CPlayerWeapons&)*m_penWeapons).SendEvent(EFireWeapon());
autowait(2.5f);
((CPlayerWeapons&)*m_penWeapons).SendEvent(EReleaseWeapon());
// stop minigun shaking
CModelObject &moBody = GetModelObject()->GetAttachmentModel(PLAYER_ATTACHMENT_TORSO)->amo_moModelObject;
moBody.PlayAnim(BODY_ANIM_MINIGUN_STAND, 0);
autowait(0.5f);
// ---------- Apply shake
CWorldSettingsController *pwsc = NULL;
// obtain bcg viewer
CBackgroundViewer *penBcgViewer = (CBackgroundViewer *) GetWorld()->GetBackgroundViewer();
if( penBcgViewer != NULL)
{
pwsc = (CWorldSettingsController *) &*penBcgViewer->m_penWorldSettingsController;
pwsc->m_tmShakeStarted = _pTimer->CurrentTick();
pwsc->m_vShakePos = GetPlacement().pl_PositionVector;
pwsc->m_fShakeFalloff = 250.0f;
pwsc->m_fShakeFade = 3.0f;
pwsc->m_fShakeIntensityZ = 0.1f*2.0f;
pwsc->m_tmShakeFrequencyZ = 5.0f;
pwsc->m_fShakeIntensityY = 0.0f;
pwsc->m_fShakeIntensityB = 0.0f;
/*
pwsc->m_fShakeIntensityY = 0.1f*2.0f;
pwsc->m_tmShakeFrequencyY = 5.0f;
pwsc->m_fShakeIntensityB = 2.5f*1.5f;
pwsc->m_tmShakeFrequencyB = 7.2f;
*/
}
// stop rotating body
m_tmMinigunAutoFireStart = -1;
autowait(10.0f);
return EReturn();
}
AutoStoreWeapon(EVoid)
{
// store current weapon slowly
CPlayerAnimator &plan = (CPlayerAnimator&)*m_penAnimator;
plan.BodyAnimationTemplate(BODY_ANIM_WAIT,
BODY_ANIM_COLT_REDRAWSLOW, BODY_ANIM_SHOTGUN_REDRAWSLOW, BODY_ANIM_MINIGUN_REDRAWSLOW,
0);
autowait(plan.m_fBodyAnimTime);
m_iAutoOrgWeapon = ((CPlayerWeapons&)*m_penWeapons).m_iCurrentWeapon;
((CPlayerWeapons&)*m_penWeapons).m_iCurrentWeapon = WEAPON_NONE;
((CPlayerWeapons&)*m_penWeapons).m_iWantedWeapon = WEAPON_NONE;
// sync apperances
GetPlayerAnimator()->SyncWeapon();
// remove weapon attachment
CPlayerAnimator &plan = (CPlayerAnimator&)*m_penAnimator;
plan.m_iWeaponLast = m_iAutoOrgWeapon;
plan.RemoveWeapon();
GetPlayerAnimator()->SyncWeapon();
((CPlayerWeapons&)*m_penWeapons).m_iCurrentWeapon = (WeaponType) m_iAutoOrgWeapon;
plan.BodyAnimationTemplate(BODY_ANIM_WAIT,
BODY_ANIM_COLT_DEACTIVATETOWALK, BODY_ANIM_SHOTGUN_DEACTIVATETOWALK, BODY_ANIM_MINIGUN_DEACTIVATETOWALK, AOF_SMOOTHCHANGE);
((CPlayerWeapons&)*m_penWeapons).m_iCurrentWeapon = WEAPON_NONE;
autowait(plan.m_fBodyAnimTime);
// return to auto-action loop
return EReturn();
}
// perform player auto actions
DoAutoActions(EVoid)
{
// don't look up/down
en_plViewpoint.pl_OrientationAngle = ANGLE3D(0,0,0);
// disable playeranimator animating
CPlayerAnimator &plan = (CPlayerAnimator&)*m_penAnimator;
plan.m_bDisableAnimating = TRUE;
// while there is some marker
while (m_penActionMarker!=NULL && IsOfClass(m_penActionMarker, "PlayerActionMarker")) {
// if should wait
if (GetActionMarker()->m_paaAction==PAA_WAIT) {
// play still anim
CModelObject &moBody = GetModelObject()->GetAttachmentModel(PLAYER_ATTACHMENT_TORSO)->amo_moModelObject;
moBody.PlayAnim(BODY_ANIM_WAIT, AOF_NORESTART|AOF_LOOPING);
// wait given time
autowait(GetActionMarker()->m_tmWait);
// if should teleport here
} else if (GetActionMarker()->m_paaAction==PAA_APPEARING) {
autocall AutoAppear() EReturn;
} else if (GetActionMarker()->m_paaAction==PAA_TRAVELING_IN_BEAM) {
autocall TravellingInBeam() EReturn;
} else if (GetActionMarker()->m_paaAction==PAA_LOGO_FIRE_MINIGUN) {
autocall LogoFireMinigun() EReturn;
// if should appear here
} else if (GetActionMarker()->m_paaAction==PAA_TELEPORT) {
autocall AutoTeleport() EReturn;
// if should wait for trigger
} else if (GetActionMarker()->m_paaAction==PAA_WAITFOREVER) {
// wait forever
wait() {
on (EBegin) : { resume; }
otherwise() : { pass; }
}
// if should store weapon
} else if (GetActionMarker()->m_paaAction==PAA_STOREWEAPON) {
autocall AutoStoreWeapon() EReturn;
// if should draw weapon
} else if (GetActionMarker()->m_paaAction==PAA_DRAWWEAPON) {
// order playerweapons to select best weapon
ESelectWeapon eSelect;
eSelect.iWeapon = -4;
((CPlayerWeapons&)*m_penWeapons).SendEvent(eSelect);
// if should wait
} else if (GetActionMarker()->m_paaAction==PAA_LOOKAROUND) {
autocall AutoLookAround() EReturn;
// if should use item
} else if (GetActionMarker()->m_paaAction==PAA_USEITEM) {
// use it
autocall AutoUseItem() EReturn;
// if should pick item
} else if (GetActionMarker()->m_paaAction==PAA_PICKITEM) {
// pick it
autocall AutoPickItem() EReturn;
// if falling from bridge
} else if (GetActionMarker()->m_paaAction==PAA_FALLDOWN) {
// fall
autocall AutoFallDown() EReturn;
// if releasing player
} else if (GetActionMarker()->m_paaAction==PAA_RELEASEPLAYER) {
if (m_penCamera!=NULL) {
((CCamera*)&*m_penCamera)->m_bStopMoving=TRUE;
}
m_penCamera = NULL;
// if currently not having any weapon in hand
if (GetPlayerWeapons()->m_iCurrentWeapon == WEAPON_NONE) {
// order playerweapons to select best weapon
ESelectWeapon eSelect;
eSelect.iWeapon = -4;
((CPlayerWeapons&)*m_penWeapons).SendEvent(eSelect);
}
// sync weapon, just in case
m_ulFlags |= PLF_SYNCWEAPON;
m_tmSpiritStart = 0;
// if start computer
} else if (GetActionMarker()->m_paaAction==PAA_STARTCOMPUTER) {
// mark that
if (_pNetwork->IsPlayerLocal(this) && GetSP()->sp_bSinglePlayer) {
cmp_ppenPlayer = this;
cmp_bInitialStart = TRUE;
}
// if start introscroll
} else if (GetActionMarker()->m_paaAction==PAA_STARTINTROSCROLL) {
_pShell->Execute("sam_iStartCredits=1;");
// if start credits
} else if (GetActionMarker()->m_paaAction==PAA_STARTCREDITS) {
_pShell->Execute("sam_iStartCredits=2;");
// if stop scroller
} else if (GetActionMarker()->m_paaAction==PAA_STOPSCROLLER) {
_pShell->Execute("sam_iStartCredits=-1;");
// if should run to the marker
} else if (GetActionMarker()->m_paaAction==PAA_RUN) {
// go to it
m_fAutoSpeed = plr_fSpeedForward*GetActionMarker()->m_fSpeed;
autocall AutoGoToMarker() EReturn;
// if should run to the marker and stop exactly there
} else if (GetActionMarker()->m_paaAction==PAA_RUNANDSTOP) {
// go to it
m_fAutoSpeed = plr_fSpeedForward*GetActionMarker()->m_fSpeed;
autocall AutoGoToMarkerAndStop() EReturn;
// if should record end-of-level stats
} else if (GetActionMarker()->m_paaAction==PAA_RECORDSTATS) {
if ((GetSP()->sp_bSinglePlayer || GetSP()->sp_bPlayEntireGame)) {
// remeber estimated time
m_tmEstTime = GetActionMarker()->m_tmWait;
// record stats
RecordEndOfLevelData();
} else {
SetGameEnd();
}
// if should show statistics to the player
} else if (GetActionMarker()->m_paaAction==PAA_SHOWSTATS) {
// !! FIXUP for the 'half' edition of the first encounter
if (GetWorld()->wo_fnmFileName==plr_strLastLevel) {
jump TheEnd();
}
// call computer
if (cmp_ppenPlayer==NULL && _pNetwork->IsPlayerLocal(this) && GetSP()->sp_bSinglePlayer) {
m_bEndOfLevel = TRUE;
cmp_ppenPlayer = this;
m_ulFlags|=PLF_DONTRENDER;
while(m_bEndOfLevel) {
wait(_pTimer->TickQuantum) {
on (ETimer) : { stop; }
on (EReceiveScore) : { pass; }
on (ECenterMessage) : { pass; }
otherwise() : { resume; }
}
}
m_ulFlags&=!PLF_DONTRENDER;
}
// if end of entire game
} else if (GetActionMarker()->m_paaAction==PAA_ENDOFGAME) {
// record stats
jump TheEnd();
} else if (GetActionMarker()->m_paaAction==PAA_NOGRAVITY) {
SetPhysicsFlags(GetPhysicsFlags() & ~(EPF_TRANSLATEDBYGRAVITY|EPF_ORIENTEDBYGRAVITY));
if( GetActionMarker()->GetParent() != NULL)
{
SetParent(GetActionMarker()->GetParent());
}
} else if (GetActionMarker()->m_paaAction==PAA_TURNONGRAVITY) {
SetPhysicsFlags(GetPhysicsFlags()|EPF_TRANSLATEDBYGRAVITY|EPF_ORIENTEDBYGRAVITY);
SetParent(NULL);
}
else if (TRUE) {
ASSERT(FALSE);
}
// if marker points to a trigger
if (GetActionMarker()->m_penTrigger!=NULL &&
GetActionMarker()->m_paaAction!=PAA_PICKITEM) {
// trigger it
SendToTarget(GetActionMarker()->m_penTrigger, EET_TRIGGER, this);
}
// get next marker
m_penActionMarker = GetActionMarker()->m_penTarget;
}
// must clear marker, in case it was invalid
m_penActionMarker = NULL;
// enable playeranimator animating
CPlayerAnimator &plan = (CPlayerAnimator&)*m_penAnimator;
plan.m_bDisableAnimating = FALSE;
// return to main loop
return EVoid();
}
/************************************************************
* M A I N *
************************************************************/
Main(EVoid evoid)
{
// remember start time
time_t t;
time(&t);
m_iStartTime = (INDEX)t;
m_ctUnreadMessages = 0;
SetFlags(GetFlags()|ENF_CROSSESLEVELS|ENF_NOTIFYLEVELCHANGE);
InitAsEditorModel();
// set default model for physics etc
CTString strDummy;
SetPlayerAppearance(GetModelObject(), NULL, strDummy, /*bPreview=*/FALSE);
// set your real appearance if possible
ValidateCharacter();
SetPlayerAppearance(&m_moRender, &en_pcCharacter, strDummy, /*bPreview=*/FALSE);
// if unsuccessful
if (GetModelObject()->GetData()==NULL) {
// never proceed with initialization - player cannot work
return;
}
//const FLOAT fSize = 2.1f/1.85f;
//GetModelObject()->StretchModel(FLOAT3D(fSize, fSize, fSize));
ModelChangeNotify();
// wait a bit to allow other entities to start
wait(0.2f) { // this is 4 ticks, it has to be at least more than musicchanger for enemy counting
on (EBegin) : { resume; }
on (ETimer) : { stop; }
on (EDisconnected) : {
Destroy();
return;
}
}
// do not use predictor if not yet initialized
if (IsPredictor()) { // !!!!####
Destroy();
return;
}
// appear
SwitchToModel();
m_ulFlags|=PLF_INITIALIZED;
// set initial vars
en_tmMaxHoldBreath = 60.0f;
en_fDensity = 1000.0f; // same density as water - to be able to dive freely
ModelChangeNotify();
// spawn weapons
m_penWeapons = CreateEntity(GetPlacement(), CLASS_PLAYER_WEAPONS);
EWeaponsInit eInitWeapons;
eInitWeapons.penOwner = this;
m_penWeapons->Initialize(eInitWeapons);
// spawn animator
m_penAnimator = CreateEntity(GetPlacement(), CLASS_PLAYER_ANIMATOR);
EAnimatorInit eInitAnimator;
eInitAnimator.penPlayer = this;
m_penAnimator->Initialize(eInitAnimator);
// set sound default parameters
m_soMouth.Set3DParameters(50.0f, 10.0f, 1.0f, 1.0f);
m_soFootL.Set3DParameters(20.0f, 2.0f, 1.0f, 1.0f);
m_soFootR.Set3DParameters(20.0f, 2.0f, 1.0f, 1.0f);
m_soBody.Set3DParameters(25.0f, 5.0f, 1.0f, 1.0f);
m_soMessage.Set3DParameters(25.0f, 5.0f, 1.0f, 1.0f);
// setup light source
SetupLightSource();
// set light animation if available
try {
m_aoLightAnimation.SetData_t(CTFILENAME("Animations\\BasicEffects.ani"));
} catch (char *strError) {
WarningMessage(TRANS("Cannot load Animations\\BasicEffects.ani: %s"), strError);
}
PlayLightAnim(LIGHT_ANIM_NONE, 0);
wait() {
on (EBegin) : { call FirstInit(); }
on (ERebirth) : { call Rebirth(); }
on (EDeath eDeath) : { call Death(eDeath); }
on (EDamage eDamage) : { call Wounded(eDamage); }
on (EPreLevelChange) : {
m_ulFlags&=~PLF_INITIALIZED;
m_ulFlags|=PLF_CHANGINGLEVEL;
m_ulFlags &= ~PLF_LEVELSTARTED;
resume;
}
on (EPostLevelChange) : {
if (GetSP()->sp_bSinglePlayer || (GetFlags()&ENF_ALIVE)) {
call WorldChange();
} else {
call WorldChangeDead();
}
}
on (ETakingBreath eTakingBreath ) : {
SetDefaultMouthPitch();
if (eTakingBreath.fBreathDelay<0.2f) {
PlaySound(m_soMouth, SOUND_INHALE0, SOF_3D);
} else if (eTakingBreath.fBreathDelay<0.8f) {
PlaySound(m_soMouth, SOUND_INHALE1, SOF_3D);
} else {
PlaySound(m_soMouth, SOUND_INHALE2, SOF_3D);
}
resume;
}
on (ECameraStart eStart) : {
m_penCamera = eStart.penCamera;
// stop player
if (m_penActionMarker==NULL) {
SetDesiredTranslation(FLOAT3D(0.0f, 0.0f, 0.0f));
SetDesiredRotation(ANGLE3D(0.0f, 0.0f, 0.0f));
}
// stop firing
((CPlayerWeapons&)*m_penWeapons).SendEvent(EReleaseWeapon());
resume;
}
on (ECameraStop eCameraStop) : {
if (m_penCamera==eCameraStop.penCamera) {
m_penCamera = NULL;
}
resume;
}
on (ECenterMessage eMsg) : {
m_strCenterMessage = eMsg.strMessage;
m_tmCenterMessageEnd = _pTimer->CurrentTick()+eMsg.tmLength;
if (eMsg.mssSound==MSS_INFO) {
m_soMessage.Set3DParameters(25.0f, 5.0f, 1.0f, 1.0f);
PlaySound(m_soMessage, SOUND_INFO, SOF_3D|SOF_VOLUMETRIC|SOF_LOCAL);
}
resume;
}
on (EComputerMessage eMsg) : {
ReceiveComputerMessage(eMsg.fnmMessage, CMF_ANALYZE);
resume;
}
on (EVoiceMessage eMsg) : {
SayVoiceMessage(eMsg.fnmMessage);
resume;
}
on (EAutoAction eAutoAction) : {
// remember first marker
m_penActionMarker = eAutoAction.penFirstMarker;
// do the actions
call DoAutoActions();
}
on (EReceiveScore eScore) : {
m_psLevelStats.ps_iScore += eScore.iPoints;
m_psGameStats.ps_iScore += eScore.iPoints;
m_iMana += eScore.iPoints*GetSP()->sp_fManaTransferFactor;
CheckHighScore();
resume;
}
on (EKilledEnemy) : {
m_psLevelStats.ps_iKills += 1;
m_psGameStats.ps_iKills += 1;
resume;
}
on (ESecretFound) : {
m_psLevelStats.ps_iSecrets += 1;
m_psGameStats.ps_iSecrets += 1;
resume;
}
// EEnd should not arrive here
on (EEnd) : {
ASSERT(FALSE);
resume;
}
// if player is disconnected
on (EDisconnected) : {
// exit the loop
stop;
}
// support for jumping using bouncers
on (ETouch eTouch) : {
if (IsOfClass(eTouch.penOther, "Bouncer")) {
JumpFromBouncer(this, eTouch.penOther);
// play jump sound
SetDefaultMouthPitch();
PlaySound(m_soMouth, SOUND_JUMP, SOF_3D);
if(_pNetwork->IsPlayerLocal(this)) {IFeel_PlayEffect("Jump");}
}
resume;
}
}
// we get here if the player is disconnected from the server
// if we have some keys
if (!IsPredictor() && m_ulKeys!=0) {
// find first live player
CPlayer *penNextPlayer = NULL;
for(INDEX iPlayer=0; iPlayer<GetMaxPlayers(); iPlayer++) {
CPlayer *pen = (CPlayer*)&*GetPlayerEntity(iPlayer);
if (pen!=NULL && pen!=this && (pen->GetFlags()&ENF_ALIVE) && !(pen->GetFlags()&ENF_DELETED) ) {
penNextPlayer = pen;
}
}
// if any found
if (penNextPlayer!=NULL) {
// transfer keys to that player
CPrintF(TRANS("%s leaving, all keys transfered to %s\n"),
(const char*)m_strName, (const char*)penNextPlayer->GetPlayerName());
penNextPlayer->m_ulKeys |= m_ulKeys;
}
}
// spawn teleport effect
SpawnTeleport();
// cease to exist
m_penWeapons->Destroy();
m_penAnimator->Destroy();
if (m_penView!=NULL) {
m_penView->Destroy();
}
if (m_pen3rdPersonView!=NULL) {
m_pen3rdPersonView->Destroy();
}
Destroy();
return;
};
};