Portable Half-Life SDK. GoldSource and Xash3D. Crossplatform.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

996 lines
27 KiB

/*
Copyright (c) 1999, Cold Ice Modification.
This code has been written by SlimShady ( darcuri@optonline.net )
Use, distribution, and modification of this source code and/or resulting
object code is restricted to non-commercial enhancements to products from
Valve LLC. All other use, distribution, or modification is prohibited
without written permission from Valve LLC and from the Cold Ice team.
Please if you use this code in any public form, please give us credit.
*/
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "player.h"
#include "weapons.h"
#include "gamerules.h"
#include "skill.h"
#include "game.h"
#include "items.h"
extern DLL_GLOBAL CGameRules *g_pGameRules;
extern DLL_GLOBAL BOOL g_fGameOver;
extern int gmsgDeathMsg; // client dll messages
extern int gmsgScoreInfo;
extern int gmsgMOTD;
#define ITEM_RESPAWN_TIME 30
#define WEAPON_RESPAWN_TIME 20
#define AMMO_RESPAWN_TIME 20
//*********************************************************
// Rules for Cold Ice Railgun Arena
//*********************************************************
CRailArena :: CRailArena()
{
RefreshSkillData();
m_flIntermissionEndTime = 0;
if ( IS_DEDICATED_SERVER() )
{
// dedicated server
char *servercfgfile = (char *)CVAR_GET_STRING( "servercfgfile" );
if ( servercfgfile && servercfgfile[0] )
{
char szCommand[256];
ALERT( at_console, "Executing dedicated server config file\n" );
sprintf( szCommand, "exec %s\n", servercfgfile );
SERVER_COMMAND( szCommand );
}
}
else
{
// listen server
char *lservercfgfile = (char *)CVAR_GET_STRING( "lservercfgfile" );
if ( lservercfgfile && lservercfgfile[0] )
{
char szCommand[256];
ALERT( at_console, "Executing listen server config file\n" );
sprintf( szCommand, "exec %s\n", lservercfgfile );
SERVER_COMMAND( szCommand );
}
}
}
//=========================================================
//=========================================================
void CRailArena::RefreshSkillData( void )
{
CGameRules::RefreshSkillData();
// suitcharger
gSkillData.suitchargerCapacity = 30;
// Rocket
gSkillData.plrDmgRailgun = 145;
}
// longest the intermission can last, in seconds
#define MAX_INTERMISSION_TIME 120
//=========================================================
//=========================================================
void CRailArena :: Think ( void )
{
///// Check game rules /////
if ( g_fGameOver ) // someone else quit the game already
{
if ( m_flIntermissionEndTime < gpGlobals->time )
{
if ( m_iEndIntermissionButtonHit // check that someone has pressed a key, or the max intermission time is over
|| ((m_flIntermissionEndTime + MAX_INTERMISSION_TIME) < gpGlobals->time) )
ChangeLevel(); // intermission is over
}
return;
}
float flTimeLimit = timelimit.value * 60;
float flFragLimit = fraglimit.value;
if ( flTimeLimit != 0 && gpGlobals->time >= flTimeLimit )
{
GoToIntermission();
return;
}
if ( flFragLimit )
{
// check if any player is over the frag limit
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
{
CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
if ( pPlayer && pPlayer->pev->frags >= flFragLimit )
{
GoToIntermission();
return;
}
}
}
}
//=========================================================
//=========================================================
BOOL CRailArena::IsMultiplayer( void )
{
return TRUE;
}
//=========================================================
//=========================================================
BOOL CRailArena::IsDeathmatch( void )
{
return TRUE;
}
//=========================================================
//=========================================================
BOOL CRailArena::IsRailArena( void )
{
return TRUE;
}
//=========================================================
//=========================================================
BOOL CRailArena::IsCoOp( void )
{
return gpGlobals->coop;
}
//=========================================================
//=========================================================
BOOL CRailArena::FShouldSwitchWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pWeapon )
{
if ( !pWeapon->CanDeploy() )
{
// that weapon can't deploy anyway.
return FALSE;
}
if ( !pPlayer->m_pActiveItem )
{
// player doesn't have an active item!
return TRUE;
}
if ( !pPlayer->m_pActiveItem->CanHolster() )
{
// can't put away the active item.
return FALSE;
}
if ( pWeapon->iWeight() > pPlayer->m_pActiveItem->iWeight() )
{
return TRUE;
}
return FALSE;
}
BOOL CRailArena :: GetNextBestWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon )
{
CBasePlayerItem *pCheck;
CBasePlayerItem *pBest;// this will be used in the event that we don't find a weapon in the same category.
int iBestWeight;
int i;
iBestWeight = -1;// no weapon lower than -1 can be autoswitched to
pBest = NULL;
if ( !pCurrentWeapon->CanHolster() )
{
// can't put this gun away right now, so can't switch.
return FALSE;
}
for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ )
{
pCheck = pPlayer->m_rgpPlayerItems[ i ];
while ( pCheck )
{
if ( pCheck->iWeight() > -1 && pCheck->iWeight() == pCurrentWeapon->iWeight() && pCheck != pCurrentWeapon )
{
// this weapon is from the same category.
if ( pCheck->CanDeploy() )
{
if ( pPlayer->SwitchWeapon( pCheck ) )
{
return TRUE;
}
}
}
else if ( pCheck->iWeight() > iBestWeight && pCheck != pCurrentWeapon )// don't reselect the weapon we're trying to get rid of
{
//ALERT ( at_console, "Considering %s\n", STRING( pCheck->pev->classname ) );
// we keep updating the 'best' weapon just in case we can't find a weapon of the same weight
// that the player was using. This will end up leaving the player with his heaviest-weighted
// weapon.
if ( pCheck->CanDeploy() )
{
// if this weapon is useable, flag it as the best
iBestWeight = pCheck->iWeight();
pBest = pCheck;
}
}
pCheck = pCheck->m_pNext;
}
}
// if we make it here, we've checked all the weapons and found no useable
// weapon in the same catagory as the current weapon.
// if pBest is null, we didn't find ANYTHING. Shouldn't be possible- should always
// at least get the crowbar, but ya never know.
if ( !pBest )
{
return FALSE;
}
pPlayer->SwitchWeapon( pBest );
return TRUE;
}
//=========================================================
//=========================================================
BOOL CRailArena :: ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ] )
{
return TRUE;
}
extern int gmsgSayText;
extern int gmsgGameMode;
void CRailArena :: UpdateGameMode( CBasePlayer *pPlayer )
{
MESSAGE_BEGIN( MSG_ONE, gmsgGameMode, NULL, pPlayer->edict() );
WRITE_BYTE( 0 ); // game mode none
MESSAGE_END();
}
void CRailArena :: InitHUD( CBasePlayer *pl )
{
// notify other clients of player joining the game
UTIL_ClientPrintAll( HUD_PRINTNOTIFY, UTIL_VarArgs( "%s has joined the game\n",
( pl->pev->netname && STRING(pl->pev->netname)[0] != 0 ) ? STRING(pl->pev->netname) : "unconnected" ) );
UTIL_LogPrintf( "\"%s<%i>\" has entered the game\n", STRING( pl->pev->netname ), GETPLAYERUSERID( pl->edict() ) );
UpdateGameMode( pl );
// sending just one score makes the hud scoreboard active; otherwise
// it is just disabled for single play
MESSAGE_BEGIN( MSG_ONE, gmsgScoreInfo, NULL, pl->edict() );
WRITE_BYTE( ENTINDEX(pl->edict()) );
WRITE_SHORT( 0 );
WRITE_SHORT( 0 );
MESSAGE_END();
SendMOTDToClient( pl->edict() );
// loop through all active players and send their score info to the new client
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
{
// FIXME: Probably don't need to cast this just to read m_iDeaths
CBasePlayer *plr = (CBasePlayer *)UTIL_PlayerByIndex( i );
if ( plr )
{
MESSAGE_BEGIN( MSG_ONE, gmsgScoreInfo, NULL, pl->edict() );
WRITE_BYTE( i ); // client number
WRITE_SHORT( plr->pev->frags );
WRITE_SHORT( plr->m_iDeaths );
MESSAGE_END();
}
}
if ( g_fGameOver )
{
MESSAGE_BEGIN( MSG_ONE, SVC_INTERMISSION, NULL, pl->edict() );
MESSAGE_END();
}
pl->StartSpectator();
pl->m_nMenu = Menu_Spec;
ShowMenu(pl, 0x3, 0, 0," Welcome to Cold Ice Beta1 2x\n\nCold-Ice Railgun Arena Mode\n\n1. Join Game\n2. Observe Game");
}
//=========================================================
//=========================================================
void CRailArena :: ClientDisconnected( edict_t *pClient )
{
if ( pClient )
{
CBasePlayer *pPlayer = (CBasePlayer *)CBaseEntity::Instance( pClient );
if ( pPlayer )
{
FireTargets( "game_playerleave", pPlayer, pPlayer, USE_TOGGLE, 0 );
UTIL_LogPrintf( "\"%s<%i>\" left from Railgun Arena\n", STRING( pPlayer->pev->netname ), GETPLAYERUSERID( pPlayer->edict() ) );
pPlayer->RemoveAllItems( TRUE );// destroy all of the players weapons and items
}
}
}
//=========================================================
//=========================================================
float CRailArena :: FlPlayerFallDamage( CBasePlayer *pPlayer )
{
int iFallDamage = (int)CVAR_GET_FLOAT("mp_falldamage");
switch ( iFallDamage )
{
case 1://progressive
pPlayer->m_flFallVelocity -= PLAYER_MAX_SAFE_FALL_SPEED;
return pPlayer->m_flFallVelocity * DAMAGE_FOR_FALL_SPEED;
break;
default:
case 0:// fixed
return 10;
break;
}
}
//=========================================================
//=========================================================
BOOL CRailArena::FPlayerCanTakeDamage( CBasePlayer *pPlayer, CBaseEntity *pAttacker )
{
return TRUE;
}
//=========================================================
//=========================================================
void CRailArena :: PlayerThink( CBasePlayer *pPlayer )
{
if ( g_fGameOver )
{
// check for button presses
if ( pPlayer->m_afButtonPressed & ( IN_DUCK | IN_ATTACK | IN_ATTACK2 | IN_USE | IN_JUMP ) )
m_iEndIntermissionButtonHit = TRUE;
// clear attack/use commands from player
pPlayer->m_afButtonPressed = 0;
pPlayer->pev->button = 0;
pPlayer->m_afButtonReleased = 0;
}
}
//=========================================================
//=========================================================
BOOL CRailArena :: ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
{
if ( FStrEq( pcmd, "menuselect" ) )
{
int slot = atoi( CMD_ARGV(1) );
//=============================================================
//=============================================================
switch(pPlayer->m_nMenu)
{
case Menu_Spec:
if(slot == 1)
pPlayer->StopSpectator();
else if (slot == 2)
{
pPlayer->StartSpectator();
ShowMenu (pPlayer, 0x1, 0, 0, "Currently In Spectator Mode:\n\nHit Key 1 to exit and join the game. " );
}
break;
//=============================================================
//=============================================================
}
return TRUE;
}
//=============================================================
//=============================================================
if (FStrEq(pcmd, "rune_status" ))
{
if ( pPlayer->m_iPlayerRune == RUNE_SPEED )
ShowMenu (pPlayer, 0x1, 5, 0, "You Have Rune: Speed" );
else if ( pPlayer->m_iPlayerRune == RUNE_RESIST )
ShowMenu (pPlayer, 0x1, 5, 0, "You Have Rune: Resist" );
else if ( pPlayer->m_iPlayerRune == RUNE_STRENGTH )
ShowMenu (pPlayer, 0x1, 5, 0, "You Have Rune: Strength" );
else if ( pPlayer->m_iPlayerRune == RUNE_HEALTH )
ShowMenu (pPlayer, 0x1, 5, 0, "You Have Rune: Regeneration" );
else if ( pPlayer->m_iPlayerRune == RUNE_ROCKETARENA )
ShowMenu (pPlayer, 0x1, 5, 0, "You Have Rune: Rocket Arena Special" );
else
ShowMenu (pPlayer, 0x1, 5, 0, "You Have No Runes." );
return TRUE;
}
//=============================================================
//=============================================================
else if ( FStrEq( pcmd, "stopobserv" ) )
{
pPlayer->StopSpectator();
return TRUE;
}
//=============================================================
//=============================================================
return FALSE;
}
//=========================================================
//=========================================================
void CRailArena :: PlayerSpawn( CBasePlayer *pPlayer )
{
BOOL addDefault;
CBaseEntity *pWeaponEntity = NULL;
pPlayer->pev->weapons |= (1<<WEAPON_SUIT);
addDefault = TRUE;
while ( pWeaponEntity = UTIL_FindEntityByClassname( pWeaponEntity, "game_player_equip" ))
{
pWeaponEntity->Touch( pPlayer );
addDefault = FALSE;
}
if ( addDefault )
{
pPlayer->GiveNamedItem( "weapon_railgun" );
}
}
//=========================================================
//=========================================================
BOOL CRailArena :: FPlayerCanRespawn( CBasePlayer *pPlayer )
{
return pPlayer->m_fWantRespawn;
}
//=========================================================
//=========================================================
float CRailArena :: FlPlayerSpawnTime( CBasePlayer *pPlayer )
{
return gpGlobals->time;//now!
}
BOOL CRailArena :: AllowAutoTargetCrosshair( void )
{
return ( CVAR_GET_FLOAT( "mp_autocrosshair" ) != 0 );
}
//=========================================================
// IPointsForKill - how many points awarded to anyone
// that kills this player?
//=========================================================
int CRailArena :: IPointsForKill( CBasePlayer *pAttacker, CBasePlayer *pKilled )
{
return 1;
}
//=========================================================
// PlayerKilled - someone/something killed this player
//=========================================================
void CRailArena :: PlayerKilled( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor )
{
DeathNotice( pVictim, pKiller, pInflictor );
pVictim->m_iDeaths += 1;
FireTargets( "game_playerdie", pVictim, pVictim, USE_TOGGLE, 0 );
CBasePlayer *peKiller = NULL;
CBaseEntity *ktmp = CBaseEntity::Instance( pKiller );
if ( ktmp && (ktmp->Classify() == CLASS_PLAYER) )
peKiller = (CBasePlayer*)ktmp;
if ( pVictim->pev == pKiller )
{ // killed self
pKiller->frags -= 1;
}
else if ( ktmp && ktmp->IsPlayer() )
{
// if a player dies in a deathmatch game and the killer is a client, award the killer some points
pKiller->frags += IPointsForKill( peKiller, pVictim );
FireTargets( "game_playerkill", ktmp, ktmp, USE_TOGGLE, 0 );
}
else
{ // killed by the world
pKiller->frags -= 1;
}
// update the scores
// killed scores
MESSAGE_BEGIN( MSG_ALL, gmsgScoreInfo );
WRITE_BYTE( ENTINDEX(pVictim->edict()) );
WRITE_SHORT( pVictim->pev->frags );
WRITE_SHORT( pVictim->m_iDeaths );
MESSAGE_END();
// killers score, if it's a player
CBaseEntity *ep = CBaseEntity::Instance( pKiller );
if ( ep && ep->Classify() == CLASS_PLAYER )
{
CBasePlayer *PK = (CBasePlayer*)ep;
MESSAGE_BEGIN( MSG_ALL, gmsgScoreInfo );
WRITE_BYTE( ENTINDEX(PK->edict()) );
WRITE_SHORT( PK->pev->frags );
WRITE_SHORT( PK->m_iDeaths );
MESSAGE_END();
// let the killer paint another decal as soon as he'd like.
PK->m_flNextDecalTime = gpGlobals->time;
}
}
//=========================================================
// Deathnotice.
//=========================================================
void CRailArena::DeathNotice( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pevInflictor )
{
// Work out what killed the player, and send a message to all clients about it
CBaseEntity *Killer = CBaseEntity::Instance( pKiller );
const char *killer_weapon_name = "world"; // by default, the player is killed by the world
int killer_index = 0;
if ( pKiller->flags & FL_CLIENT )
{
killer_index = ENTINDEX(ENT(pKiller));
if ( pevInflictor )
{
if ( pevInflictor == pKiller )
{
// If the inflictor is the killer, then it must be their current weapon doing the damage
CBasePlayer *pPlayer = (CBasePlayer*)CBaseEntity::Instance( pKiller );
if ( pPlayer->m_pActiveItem )
{
killer_weapon_name = pPlayer->m_pActiveItem->pszName();
}
}
else
{
killer_weapon_name = STRING( pevInflictor->classname ); // it's just that easy
}
}
}
else
{
killer_weapon_name = STRING( pevInflictor->classname );
}
// strip the monster_* or weapon_* from the inflictor's classname
if ( strncmp( killer_weapon_name, "weapon_", 7 ) == 0 )
killer_weapon_name += 7;
else if ( strncmp( killer_weapon_name, "monster_", 8 ) == 0 )
killer_weapon_name += 8;
else if ( strncmp( killer_weapon_name, "func_", 5 ) == 0 )
killer_weapon_name += 5;
MESSAGE_BEGIN( MSG_ALL, gmsgDeathMsg );
WRITE_BYTE( killer_index ); // the killer
WRITE_BYTE( ENTINDEX(pVictim->edict()) ); // the victim
WRITE_STRING( killer_weapon_name ); // what they were killed by (should this be a string?)
MESSAGE_END();
if ( pVictim->pev == pKiller )
{ // killed self
UTIL_LogPrintf( "\"%s<%i>\" killed self with %s\n", STRING( pVictim->pev->netname ), GETPLAYERUSERID( pVictim->edict() ), killer_weapon_name );
}
else if ( pKiller->flags & FL_CLIENT )
{
UTIL_LogPrintf( "\"%s<%i>\" killed \"%s<%i>\" with %s\n", STRING( pKiller->netname ),
GETPLAYERUSERID( ENT(pKiller) ),
STRING( pVictim->pev->netname ),
GETPLAYERUSERID( pVictim->edict() ),
killer_weapon_name );
}
else
{ // killed by the world
UTIL_LogPrintf( "\"%s<%i>\" killed by world with %s\n", STRING( pVictim->pev->netname ), GETPLAYERUSERID( pVictim->edict() ), killer_weapon_name );
}
return;
}
//=========================================================
// PlayerGotWeapon - player has grabbed a weapon that was
// sitting in the world
//=========================================================
void CRailArena :: PlayerGotWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pWeapon )
{
}
//=========================================================
// FlWeaponRespawnTime - what is the time in the future
// at which this weapon may spawn?
//=========================================================
float CRailArena :: FlWeaponRespawnTime( CBasePlayerItem *pWeapon )
{
return FALSE;
}
// when we are within this close to running out of entities, items
// marked with the ITEM_FLAG_LIMITINWORLD will delay their respawn
#define ENTITY_INTOLERANCE 100
//=========================================================
// FlWeaponRespawnTime - Returns 0 if the weapon can respawn
// now, otherwise it returns the time at which it can try
// to spawn again.
//=========================================================
float CRailArena :: FlWeaponTryRespawn( CBasePlayerItem *pWeapon )
{
if ( pWeapon && pWeapon->m_iId )
{
return 0;
}
return 0;
}
//=========================================================
// VecWeaponRespawnSpot - where should this weapon spawn?
// Some game variations may choose to randomize spawn locations
//=========================================================
Vector CRailArena :: VecWeaponRespawnSpot( CBasePlayerItem *pWeapon )
{
return pWeapon->pev->origin;
}
//=========================================================
// WeaponShouldRespawn - any conditions inhibiting the
// respawning of this weapon?
//=========================================================
int CRailArena :: WeaponShouldRespawn( CBasePlayerItem *pWeapon )
{
return GR_WEAPON_RESPAWN_NO;
}
//=========================================================
// CanHaveWeapon - returns FALSE if the player is not allowed
// to pick up this weapon
//=========================================================
BOOL CRailArena::CanHavePlayerItem( CBasePlayer *pPlayer, CBasePlayerItem *pItem )
{
if ( CVAR_GET_FLOAT("mp_weaponstay") > 0 )
{
if ( pItem->iFlags() & ITEM_FLAG_LIMITINWORLD )
return CGameRules::CanHavePlayerItem( pPlayer, pItem );
// check if the player already has this weapon
for ( int i = 0 ; i < MAX_ITEM_TYPES ; i++ )
{
CBasePlayerItem *it = pPlayer->m_rgpPlayerItems[i];
while ( it != NULL )
{
if ( it->m_iId == pItem->m_iId )
{
return FALSE;
}
it = it->m_pNext;
}
}
}
return CGameRules::CanHavePlayerItem( pPlayer, pItem );
}
//=========================================================
//=========================================================
BOOL CRailArena::CanHaveItem( CBasePlayer *pPlayer, CItem *pItem )
{
return TRUE;
}
//=========================================================
//=========================================================
void CRailArena::PlayerGotItem( CBasePlayer *pPlayer, CItem *pItem )
{
}
//=========================================================
//=========================================================
int CRailArena::ItemShouldRespawn( CItem *pItem )
{
return GR_ITEM_RESPAWN_NO;
}
//=========================================================
// At what time in the future may this Item respawn?
//=========================================================
float CRailArena::FlItemRespawnTime( CItem *pItem )
{
return gpGlobals->time + ITEM_RESPAWN_TIME;
}
//=========================================================
// Where should this item respawn?
// Some game variations may choose to randomize spawn locations
//=========================================================
Vector CRailArena::VecItemRespawnSpot( CItem *pItem )
{
return pItem->pev->origin;
}
//=========================================================
//=========================================================
void CRailArena::PlayerGotAmmo( CBasePlayer *pPlayer, char *szName, int iCount )
{
}
//=========================================================
//=========================================================
BOOL CRailArena::IsAllowedToSpawn( CBaseEntity *pEntity )
{
if ( pEntity->pev->flags & FL_MONSTER )
return FALSE;
return TRUE;
}
//=========================================================
//=========================================================
int CRailArena::AmmoShouldRespawn( CBasePlayerAmmo *pAmmo )
{
return GR_AMMO_RESPAWN_NO;
}
//=========================================================
//=========================================================
float CRailArena::FlAmmoRespawnTime( CBasePlayerAmmo *pAmmo )
{
return gpGlobals->time + AMMO_RESPAWN_TIME;
}
//=========================================================
//=========================================================
Vector CRailArena::VecAmmoRespawnSpot( CBasePlayerAmmo *pAmmo )
{
return pAmmo->pev->origin;
}
//=========================================================
//=========================================================
float CRailArena::FlHealthChargerRechargeTime( void )
{
return 60;
}
float CRailArena::FlHEVChargerRechargeTime( void )
{
return 30;
}
//=========================================================
//=========================================================
int CRailArena::DeadPlayerWeapons( CBasePlayer *pPlayer )
{
return GR_PLR_DROP_GUN_ACTIVE;
}
//=========================================================
//=========================================================
int CRailArena::DeadPlayerAmmo( CBasePlayer *pPlayer )
{
return GR_PLR_DROP_AMMO_ACTIVE;
}
edict_t *CRailArena::GetPlayerSpawnSpot( CBasePlayer *pPlayer )
{
edict_t *pentSpawnSpot = CGameRules::GetPlayerSpawnSpot( pPlayer );
if ( IsMultiplayer() && pentSpawnSpot->v.target )
{
FireTargets( STRING(pentSpawnSpot->v.target), pPlayer, pPlayer, USE_TOGGLE, 0 );
}
return pentSpawnSpot;
}
//=========================================================
//=========================================================
int CRailArena::PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget )
{
// half life deathmatch has only enemies
return GR_NOTTEAMMATE;
}
//=========================================================
//=========================================================
BOOL CRailArena :: PlayFootstepSounds( CBasePlayer *pl, float fvol )
{
if ( CVAR_GET_FLOAT( "mp_footsteps" ) == 0 )
return FALSE;
if ( pl->IsOnLadder() || pl->pev->velocity.Length2D() > 220 )
return TRUE; // only make step sounds in multiplayer if the player is moving fast enough
return FALSE;
}
//=========================================================
//========================================================
BOOL CRailArena :: FAllowFlashlight( void )
{
return CVAR_GET_FLOAT( "mp_flashlight" ) != 0;
}
//=========================================================
//=========================================================
BOOL CRailArena :: FAllowMonsters( void )
{
return ( CVAR_GET_FLOAT( "mp_allowmonsters" ) != 0 );
}
//=========================================================
//======== CRailArena private functions ===========
#define INTERMISSION_TIME 6
void CRailArena :: GoToIntermission( void )
{
if ( g_fGameOver )
return; // intermission has already been triggered, so ignore.
MESSAGE_BEGIN(MSG_ALL, SVC_INTERMISSION);
MESSAGE_END();
m_flIntermissionEndTime = gpGlobals->time + INTERMISSION_TIME;
g_fGameOver = TRUE;
m_iEndIntermissionButtonHit = FALSE;
}
void CRailArena :: ChangeLevel( void )
{
char szNextMap[32];
char szFirstMapInList[32];
strcpy( szFirstMapInList, "hldm1" ); // the absolute default level is hldm1
// find the map to change to
char *mapcfile = (char*)CVAR_GET_STRING( "mapcyclefile" );
ASSERT( mapcfile != NULL );
strcpy( szNextMap, STRING(gpGlobals->mapname) );
strcpy( szFirstMapInList, STRING(gpGlobals->mapname) );
int length;
char *pFileList;
char *aFileList = pFileList = (char*)LOAD_FILE_FOR_ME( mapcfile, &length );
if ( pFileList && length )
{
// the first map name in the file becomes the default
sscanf( pFileList, " %32s", szNextMap );
if ( IS_MAP_VALID( szNextMap ) )
strcpy( szFirstMapInList, szNextMap );
// keep pulling mapnames out of the list until the current mapname
// if the current mapname isn't found, load the first map in the list
BOOL next_map_is_it = FALSE;
while ( 1 )
{
while ( *pFileList && isspace( *pFileList ) ) pFileList++; // skip over any whitespace
if ( !(*pFileList) )
break;
char cBuf[32];
int ret = sscanf( pFileList, " %32s", cBuf );
// Check the map name is valid
if ( ret != 1 || *cBuf < 13 )
break;
if ( next_map_is_it )
{
// check that it is a valid map file
if ( IS_MAP_VALID( cBuf ) )
{
strcpy( szNextMap, cBuf );
break;
}
}
if ( FStrEq( cBuf, STRING(gpGlobals->mapname) ) )
{ // we've found our map; next map is the one to change to
next_map_is_it = TRUE;
}
pFileList += strlen( cBuf );
}
FREE_FILE( aFileList );
}
if ( !IS_MAP_VALID(szNextMap) )
strcpy( szNextMap, szFirstMapInList );
g_fGameOver = TRUE;
ALERT( at_console, "CHANGE LEVEL: %s\n", szNextMap );
CHANGE_LEVEL( szNextMap, NULL );
}
#define MAX_MOTD_CHUNK 60
#define MAX_MOTD_LENGTH (MAX_MOTD_CHUNK * 4)
void CRailArena :: SendMOTDToClient( edict_t *client )
{
// read from the MOTD.txt file
int length, char_count = 0;
char *pFileList;
char *aFileList = pFileList = (char*)LOAD_FILE_FOR_ME( "rail_arena_motd.txt", &length );
// Send the message of the day
// read it chunk-by-chunk, and send it in parts
while ( pFileList && *pFileList && char_count < MAX_MOTD_LENGTH )
{
char chunk[MAX_MOTD_CHUNK+1];
if ( strlen( pFileList ) < MAX_MOTD_CHUNK )
{
strcpy( chunk, pFileList );
}
else
{
strncpy( chunk, pFileList, MAX_MOTD_CHUNK );
chunk[MAX_MOTD_CHUNK] = 0; // strncpy doesn't always append the null terminator
}
char_count += strlen( chunk );
if ( char_count < MAX_MOTD_LENGTH )
pFileList = aFileList + char_count;
else
*pFileList = 0;
MESSAGE_BEGIN( MSG_ONE, gmsgMOTD, NULL, client );
WRITE_BYTE( *pFileList ? FALSE : TRUE ); // FALSE means there is still more message to come
WRITE_STRING( chunk );
MESSAGE_END();
}
FREE_FILE( aFileList );
}