Serious-Engine/Sources/Entities/Player.es

5888 lines
200 KiB
C++
Raw Normal View History

2016-04-08 00:11:36 +02:00
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);
2016-04-08 00:11:36 +02:00
// 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(floor(m_tmEstTime)-floor(tmLevelTime), 0.0);
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;
};
};