/***
*
* Copyright ( c ) 1996 - 2002 , Valve LLC . All rights reserved .
*
* This product contains software technology licensed from Id
* Software , Inc . ( " Id Technology " ) . Id Technology ( c ) 1996 Id Software , Inc .
* All Rights Reserved .
*
* 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 .
*
* * * */
//
// teamplay_gamerules.cpp
//
# 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"
# if !NO_VOICEGAMEMGR
# include "voice_gamemgr.h"
# endif
# include "hltv.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 ;
extern int gmsgServerName ;
extern int g_teamplay ;
# define ITEM_RESPAWN_TIME 30
# define WEAPON_RESPAWN_TIME 20
# define AMMO_RESPAWN_TIME 20
float g_flIntermissionStartTime = 0 ;
# if !NO_VOICEGAMEMGR
CVoiceGameMgr g_VoiceGameMgr ;
class CMultiplayGameMgrHelper : public IVoiceGameMgrHelper
{
public :
virtual bool CanPlayerHearPlayer ( CBasePlayer * pListener , CBasePlayer * pTalker )
{
if ( g_teamplay )
{
if ( g_pGameRules - > PlayerRelationship ( pListener , pTalker ) ! = GR_TEAMMATE )
{
return false ;
}
}
return true ;
}
} ;
static CMultiplayGameMgrHelper g_GameMgrHelper ;
# endif
//*********************************************************
// Rules for the half-life multiplayer game.
//*********************************************************
CHalfLifeMultiplay : : CHalfLifeMultiplay ( )
{
# if !NO_VOICEGAMEMGR
g_VoiceGameMgr . Init ( & g_GameMgrHelper , gpGlobals - > maxClients ) ;
# endif
RefreshSkillData ( ) ;
m_flIntermissionEndTime = 0 ;
g_flIntermissionStartTime = 0 ;
// 11/8/98
// Modified by YWB: Server .cfg file is now a cvar, so that
// server ops can run multiple game servers, with different server .cfg files,
// from a single installed directory.
// Mapcyclefile is already a cvar.
// 3/31/99
// Added lservercfg file cvar, since listen and dedicated servers should not
// share a single config file. (sjb)
if ( IS_DEDICATED_SERVER ( ) )
{
// dedicated server
/*const char *servercfgfile = 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 ) ;
}
*/
// this code has been moved into engine, to only run server.cfg once
}
else
{
// listen server
const char * lservercfgfile = 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 ) ;
}
}
}
BOOL CHalfLifeMultiplay : : ClientCommand ( CBasePlayer * pPlayer , const char * pcmd )
{
# if !NO_VOICEGAMEMGR
if ( g_VoiceGameMgr . ClientCommand ( pPlayer , pcmd ) )
return TRUE ;
# endif
return CGameRules : : ClientCommand ( pPlayer , pcmd ) ;
}
//=========================================================
//=========================================================
void CHalfLifeMultiplay : : RefreshSkillData ( void )
{
// load all default values
CGameRules : : RefreshSkillData ( ) ;
// override some values for multiplay.
// suitcharger
gSkillData . suitchargerCapacity = 30 ;
// Crowbar whack
gSkillData . plrDmgCrowbar = 25 ;
// Glock Round
gSkillData . plrDmg9MM = 12 ;
// 357 Round
gSkillData . plrDmg357 = 40 ;
// MP5 Round
gSkillData . plrDmgMP5 = 12 ;
// M203 grenade
gSkillData . plrDmgM203Grenade = 100 ;
// Shotgun buckshot
gSkillData . plrDmgBuckshot = 20 ; // fewer pellets in deathmatch
// Crossbow
gSkillData . plrDmgCrossbowClient = 20 ;
// RPG
gSkillData . plrDmgRPG = 120 ;
// Egon
gSkillData . plrDmgEgonWide = 20 ;
gSkillData . plrDmgEgonNarrow = 10 ;
// Hand Grendade
gSkillData . plrDmgHandGrenade = 100 ;
// Satchel Charge
gSkillData . plrDmgSatchel = 120 ;
// Tripmine
gSkillData . plrDmgTripmine = 150 ;
// hornet
gSkillData . plrDmgHornet = 10 ;
}
// longest the intermission can last, in seconds
# define MAX_INTERMISSION_TIME 120
extern cvar_t timeleft , fragsleft ;
extern cvar_t mp_chattime ;
//=========================================================
//=========================================================
void CHalfLifeMultiplay : : Think ( void )
{
# if !NO_VOICEGAMEMGR
g_VoiceGameMgr . Update ( gpGlobals - > frametime ) ;
# endif
///// Check game rules /////
static int last_frags ;
static int last_time ;
int frags_remaining = 0 ;
int time_remaining = 0 ;
if ( g_fGameOver ) // someone else quit the game already
{
// bounds check
int time = ( int ) CVAR_GET_FLOAT ( " mp_chattime " ) ;
if ( time < 1 )
CVAR_SET_STRING ( " mp_chattime " , " 1 " ) ;
else if ( time > MAX_INTERMISSION_TIME )
CVAR_SET_STRING ( " mp_chattime " , UTIL_dtos1 ( MAX_INTERMISSION_TIME ) ) ;
m_flIntermissionEndTime = g_flIntermissionStartTime + mp_chattime . value ;
// check to see if we should change levels now
if ( m_flIntermissionEndTime < gpGlobals - > time )
{
if ( m_iEndIntermissionButtonHit // check that someone has pressed a key, or the max intermission time is over
| | ( ( g_flIntermissionStartTime + MAX_INTERMISSION_TIME ) < gpGlobals - > time ) )
ChangeLevel ( ) ; // intermission is over
}
return ;
}
float flTimeLimit = timelimit . value * 60 ;
float flFragLimit = fraglimit . value ;
time_remaining = ( int ) ( flTimeLimit ? ( flTimeLimit - gpGlobals - > time ) : 0 ) ;
if ( flTimeLimit ! = 0 & & gpGlobals - > time > = flTimeLimit )
{
GoToIntermission ( ) ;
return ;
}
if ( flFragLimit )
{
int bestfrags = 9999 ;
int remain ;
// 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 ;
}
if ( pPlayer )
{
remain = ( int ) ( flFragLimit - pPlayer - > pev - > frags ) ;
if ( remain < bestfrags )
{
bestfrags = remain ;
}
}
}
frags_remaining = bestfrags ;
}
// Updates when frags change
if ( frags_remaining ! = last_frags )
{
g_engfuncs . pfnCvar_DirectSet ( & fragsleft , UTIL_VarArgs ( " %i " , frags_remaining ) ) ;
}
// Updates once per second
if ( timeleft . value ! = last_time )
{
g_engfuncs . pfnCvar_DirectSet ( & timeleft , UTIL_VarArgs ( " %i " , time_remaining ) ) ;
}
last_frags = frags_remaining ;
last_time = time_remaining ;
}
//=========================================================
//=========================================================
BOOL CHalfLifeMultiplay : : IsMultiplayer ( void )
{
return TRUE ;
}
//=========================================================
//=========================================================
BOOL CHalfLifeMultiplay : : IsDeathmatch ( void )
{
return TRUE ;
}
//=========================================================
//=========================================================
BOOL CHalfLifeMultiplay : : IsCoOp ( void )
{
return gpGlobals - > coop ? TRUE : FALSE ;
}
//=========================================================
//=========================================================
BOOL CHalfLifeMultiplay : : 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_iAutoWepSwitch )
{
return FALSE ;
}
if ( pPlayer - > m_iAutoWepSwitch = = 2
& & pPlayer - > m_afButtonLast & ( IN_ATTACK | IN_ATTACK2 ) )
{
return FALSE ;
}
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 CHalfLifeMultiplay : : 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 CHalfLifeMultiplay : : ClientConnected ( edict_t * pEntity , const char * pszName , const char * pszAddress , char szRejectReason [ 128 ] )
{
# if !NO_VOICEGAMEMGR
g_VoiceGameMgr . ClientConnected ( pEntity ) ;
# endif
return TRUE ;
}
extern int gmsgSayText ;
extern int gmsgGameMode ;
void CHalfLifeMultiplay : : UpdateGameMode ( CBasePlayer * pPlayer )
{
MESSAGE_BEGIN ( MSG_ONE , gmsgGameMode , NULL , pPlayer - > edict ( ) ) ;
WRITE_BYTE ( 0 ) ; // game mode none
MESSAGE_END ( ) ;
}
void CHalfLifeMultiplay : : 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 " ) ) ;
// team match?
if ( g_teamplay )
{
UTIL_LogPrintf ( " \" %s<%i><%s><%s> \" entered the game \n " ,
STRING ( pl - > pev - > netname ) ,
GETPLAYERUSERID ( pl - > edict ( ) ) ,
GETPLAYERAUTHID ( pl - > edict ( ) ) ,
g_engfuncs . pfnInfoKeyValue ( g_engfuncs . pfnGetInfoKeyBuffer ( pl - > edict ( ) ) , " model " ) ) ;
}
else
{
UTIL_LogPrintf ( " \" %s<%i><%s><%i> \" entered the game \n " ,
STRING ( pl - > pev - > netname ) ,
GETPLAYERUSERID ( pl - > edict ( ) ) ,
GETPLAYERAUTHID ( pl - > edict ( ) ) ,
GETPLAYERUSERID ( pl - > edict ( ) ) ) ;
}
UpdateGameMode ( pl ) ;
// sending just one score makes the hud scoreboard active; otherwise
// it is just disabled for single play
//fix a bug in the information about the player's score when he left the server, so that his score would not be transferred to another player(seems to work)
MESSAGE_BEGIN ( MSG_ALL , gmsgScoreInfo ) ;
WRITE_BYTE ( ENTINDEX ( pl - > edict ( ) ) ) ;
WRITE_SHORT ( ( int ) pl - > pev - > frags ) ;
WRITE_SHORT ( pl - > m_iDeaths ) ;
WRITE_SHORT ( 0 ) ;
WRITE_SHORT ( GetTeamIndex ( pl - > m_szTeamName ) + 1 ) ;
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 ( ( int ) plr - > pev - > frags ) ;
WRITE_SHORT ( plr - > m_iDeaths ) ;
WRITE_SHORT ( 0 ) ;
WRITE_SHORT ( GetTeamIndex ( plr - > m_szTeamName ) + 1 ) ;
MESSAGE_END ( ) ;
}
}
if ( g_fGameOver )
{
MESSAGE_BEGIN ( MSG_ONE , SVC_INTERMISSION , NULL , pl - > edict ( ) ) ;
MESSAGE_END ( ) ;
}
}
//=========================================================
//=========================================================
void CHalfLifeMultiplay : : ClientDisconnected ( edict_t * pClient )
{
if ( pClient )
{
CBasePlayer * pPlayer = ( CBasePlayer * ) CBaseEntity : : Instance ( pClient ) ;
if ( pPlayer )
{
FireTargets ( " game_playerleave " , pPlayer , pPlayer , USE_TOGGLE , 0 ) ;
// team match?
if ( g_teamplay )
{
UTIL_LogPrintf ( " \" %s<%i><%s><%s> \" disconnected \n " ,
STRING ( pPlayer - > pev - > netname ) ,
GETPLAYERUSERID ( pPlayer - > edict ( ) ) ,
GETPLAYERAUTHID ( pPlayer - > edict ( ) ) ,
g_engfuncs . pfnInfoKeyValue ( g_engfuncs . pfnGetInfoKeyBuffer ( pPlayer - > edict ( ) ) , " model " ) ) ;
}
else
{
UTIL_LogPrintf ( " \" %s<%i><%s><%i> \" disconnected \n " ,
STRING ( pPlayer - > pev - > netname ) ,
GETPLAYERUSERID ( pPlayer - > edict ( ) ) ,
GETPLAYERAUTHID ( pPlayer - > edict ( ) ) ,
GETPLAYERUSERID ( pPlayer - > edict ( ) ) ) ;
}
pPlayer - > RemoveAllItems ( TRUE ) ; // destroy all of the players weapons and items
}
}
}
//=========================================================
//=========================================================
float CHalfLifeMultiplay : : FlPlayerFallDamage ( CBasePlayer * pPlayer )
{
int iFallDamage = ( int ) falldamage . value ;
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 CHalfLifeMultiplay : : FPlayerCanTakeDamage ( CBasePlayer * pPlayer , CBaseEntity * pAttacker )
{
return TRUE ;
}
//=========================================================
//=========================================================
void CHalfLifeMultiplay : : 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 ;
}
}
//=========================================================
//=========================================================
void CHalfLifeMultiplay : : PlayerSpawn ( CBasePlayer * pPlayer )
{
BOOL addDefault ;
CBaseEntity * pWeaponEntity = NULL ;
int iOldAutoWepSwitch ;
iOldAutoWepSwitch = pPlayer - > m_iAutoWepSwitch ;
pPlayer - > m_iAutoWepSwitch = 1 ;
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_crowbar " ) ;
pPlayer - > GiveNamedItem ( " weapon_9mmhandgun " ) ;
pPlayer - > GiveAmmo ( 68 , " 9mm " , _9MM_MAX_CARRY ) ; // 4 full reloads
}
pPlayer - > m_iAutoWepSwitch = iOldAutoWepSwitch ;
}
//=========================================================
//=========================================================
BOOL CHalfLifeMultiplay : : FPlayerCanRespawn ( CBasePlayer * pPlayer )
{
return TRUE ;
}
//=========================================================
//=========================================================
float CHalfLifeMultiplay : : FlPlayerSpawnTime ( CBasePlayer * pPlayer )
{
return gpGlobals - > time ; //now!
}
BOOL CHalfLifeMultiplay : : AllowAutoTargetCrosshair ( void )
{
return ( aimcrosshair . value ! = 0 ) ;
}
//=========================================================
// IPointsForKill - how many points awarded to anyone
// that kills this player?
//=========================================================
int CHalfLifeMultiplay : : IPointsForKill ( CBasePlayer * pAttacker , CBasePlayer * pKilled )
{
return 1 ;
}
//=========================================================
// PlayerKilled - someone/something killed this player
//=========================================================
void CHalfLifeMultiplay : : 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 ( ( int ) pVictim - > pev - > frags ) ;
WRITE_SHORT ( pVictim - > m_iDeaths ) ;
WRITE_SHORT ( 0 ) ;
WRITE_SHORT ( GetTeamIndex ( pVictim - > m_szTeamName ) + 1 ) ;
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 ( ( int ) PK - > pev - > frags ) ;
WRITE_SHORT ( PK - > m_iDeaths ) ;
WRITE_SHORT ( 0 ) ;
WRITE_SHORT ( GetTeamIndex ( PK - > m_szTeamName ) + 1 ) ;
MESSAGE_END ( ) ;
// let the killer paint another decal as soon as he'd like.
PK - > m_flNextDecalTime = gpGlobals - > time ;
}
}
//=========================================================
// Deathnotice.
//=========================================================
void CHalfLifeMultiplay : : 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 : : Instance ( pKiller ) ;
const char * killer_weapon_name = " world " ; // by default, the player is killed by the world
int killer_index = 0 ;
// Hack to fix name change
const char * tau = " tau_cannon " ;
const char * gluon = " gluon gun " ;
if ( pevInflictor )
{
if ( pKiller - > flags & FL_CLIENT )
{
killer_index = ENTINDEX ( ENT ( pKiller ) ) ;
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 ( ) ;
// replace the code names with the 'real' names
if ( ! strcmp ( killer_weapon_name , " egon " ) )
killer_weapon_name = gluon ;
else if ( ! strcmp ( killer_weapon_name , " gauss " ) )
killer_weapon_name = tau ;
if ( pVictim - > pev = = pKiller )
{
// killed self
// team match?
if ( g_teamplay )
{
UTIL_LogPrintf ( " \" %s<%i><%s><%s> \" committed suicide with \" %s \" \n " ,
STRING ( pVictim - > pev - > netname ) ,
GETPLAYERUSERID ( pVictim - > edict ( ) ) ,
GETPLAYERAUTHID ( pVictim - > edict ( ) ) ,
g_engfuncs . pfnInfoKeyValue ( g_engfuncs . pfnGetInfoKeyBuffer ( pVictim - > edict ( ) ) , " model " ) ,
killer_weapon_name ) ;
}
else
{
UTIL_LogPrintf ( " \" %s<%i><%s><%i> \" committed suicide with \" %s \" \n " ,
STRING ( pVictim - > pev - > netname ) ,
GETPLAYERUSERID ( pVictim - > edict ( ) ) ,
GETPLAYERAUTHID ( pVictim - > edict ( ) ) ,
GETPLAYERUSERID ( pVictim - > edict ( ) ) ,
killer_weapon_name ) ;
}
}
else if ( pKiller - > flags & FL_CLIENT )
{
// team match?
if ( g_teamplay )
{
UTIL_LogPrintf ( " \" %s<%i><%s><%s> \" killed \" %s<%i><%s><%s> \" with \" %s \" \n " ,
STRING ( pKiller - > netname ) ,
GETPLAYERUSERID ( ENT ( pKiller ) ) ,
GETPLAYERAUTHID ( ENT ( pKiller ) ) ,
g_engfuncs . pfnInfoKeyValue ( g_engfuncs . pfnGetInfoKeyBuffer ( ENT ( pKiller ) ) , " model " ) ,
STRING ( pVictim - > pev - > netname ) ,
GETPLAYERUSERID ( pVictim - > edict ( ) ) ,
GETPLAYERAUTHID ( pVictim - > edict ( ) ) ,
g_engfuncs . pfnInfoKeyValue ( g_engfuncs . pfnGetInfoKeyBuffer ( pVictim - > edict ( ) ) , " model " ) ,
killer_weapon_name ) ;
}
else
{
UTIL_LogPrintf ( " \" %s<%i><%s><%i> \" killed \" %s<%i><%s><%i> \" with \" %s \" \n " ,
STRING ( pKiller - > netname ) ,
GETPLAYERUSERID ( ENT ( pKiller ) ) ,
GETPLAYERAUTHID ( ENT ( pKiller ) ) ,
GETPLAYERUSERID ( ENT ( pKiller ) ) ,
STRING ( pVictim - > pev - > netname ) ,
GETPLAYERUSERID ( pVictim - > edict ( ) ) ,
GETPLAYERAUTHID ( pVictim - > edict ( ) ) ,
GETPLAYERUSERID ( pVictim - > edict ( ) ) ,
killer_weapon_name ) ;
}
}
else
{
// killed by the world
// team match?
if ( g_teamplay )
{
UTIL_LogPrintf ( " \" %s<%i><%s><%s> \" committed suicide with \" %s \" (world) \n " ,
STRING ( pVictim - > pev - > netname ) ,
GETPLAYERUSERID ( pVictim - > edict ( ) ) ,
GETPLAYERAUTHID ( pVictim - > edict ( ) ) ,
g_engfuncs . pfnInfoKeyValue ( g_engfuncs . pfnGetInfoKeyBuffer ( pVictim - > edict ( ) ) , " model " ) ,
killer_weapon_name ) ;
}
else
{
UTIL_LogPrintf ( " \" %s<%i><%s><%i> \" committed suicide with \" %s \" (world) \n " ,
STRING ( pVictim - > pev - > netname ) ,
GETPLAYERUSERID ( pVictim - > edict ( ) ) ,
GETPLAYERAUTHID ( pVictim - > edict ( ) ) ,
GETPLAYERUSERID ( pVictim - > edict ( ) ) ,
killer_weapon_name ) ;
}
}
MESSAGE_BEGIN ( MSG_SPEC , SVC_DIRECTOR ) ;
WRITE_BYTE ( 9 ) ; // command length in bytes
WRITE_BYTE ( DRC_CMD_EVENT ) ; // player killed
WRITE_SHORT ( ENTINDEX ( pVictim - > edict ( ) ) ) ; // index number of primary entity
if ( pevInflictor )
WRITE_SHORT ( ENTINDEX ( ENT ( pevInflictor ) ) ) ; // index number of secondary entity
else
WRITE_SHORT ( ENTINDEX ( ENT ( pKiller ) ) ) ; // index number of secondary entity
WRITE_LONG ( 7 | DRC_FLAG_DRAMATIC ) ; // eventflags (priority and flags)
MESSAGE_END ( ) ;
// Print a standard message
// TODO: make this go direct to console
return ; // just remove for now
/*
char szText [ 128 ] ;
if ( pKiller - > flags & FL_MONSTER )
{
// killed by a monster
strcpy ( szText , STRING ( pVictim - > pev - > netname ) ) ;
strcat ( szText , " was killed by a monster. \n " ) ;
return ;
}
if ( pKiller = = pVictim - > pev )
{
strcpy ( szText , STRING ( pVictim - > pev - > netname ) ) ;
strcat ( szText , " commited suicide. \n " ) ;
}
else if ( pKiller - > flags & FL_CLIENT )
{
strcpy ( szText , STRING ( pKiller - > netname ) ) ;
strcat ( szText , " : " ) ;
strcat ( szText , killer_weapon_name ) ;
strcat ( szText , " : " ) ;
strcat ( szText , STRING ( pVictim - > pev - > netname ) ) ;
strcat ( szText , " \n " ) ;
}
else if ( FClassnameIs ( pKiller , " worldspawn " ) )
{
strcpy ( szText , STRING ( pVictim - > pev - > netname ) ) ;
strcat ( szText , " fell or drowned or something. \n " ) ;
}
else if ( pKiller - > solid = = SOLID_BSP )
{
strcpy ( szText , STRING ( pVictim - > pev - > netname ) ) ;
strcat ( szText , " was mooshed. \n " ) ;
}
else
{
strcpy ( szText , STRING ( pVictim - > pev - > netname ) ) ;
strcat ( szText , " died mysteriously. \n " ) ;
}
UTIL_ClientPrintAll ( szText ) ;
*/
}
//=========================================================
// PlayerGotWeapon - player has grabbed a weapon that was
// sitting in the world
//=========================================================
void CHalfLifeMultiplay : : PlayerGotWeapon ( CBasePlayer * pPlayer , CBasePlayerItem * pWeapon )
{
}
//=========================================================
// FlWeaponRespawnTime - what is the time in the future
// at which this weapon may spawn?
//=========================================================
float CHalfLifeMultiplay : : FlWeaponRespawnTime ( CBasePlayerItem * pWeapon )
{
if ( weaponstay . value > 0 )
{
// make sure it's only certain weapons
if ( ! ( pWeapon - > iFlags ( ) & ITEM_FLAG_LIMITINWORLD ) )
{
return gpGlobals - > time + 0 ; // weapon respawns almost instantly
}
}
return gpGlobals - > time + WEAPON_RESPAWN_TIME ;
}
// 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 CHalfLifeMultiplay : : FlWeaponTryRespawn ( CBasePlayerItem * pWeapon )
{
if ( pWeapon & & pWeapon - > m_iId & & ( pWeapon - > iFlags ( ) & ITEM_FLAG_LIMITINWORLD ) )
{
if ( NUMBER_OF_ENTITIES ( ) < ( gpGlobals - > maxEntities - ENTITY_INTOLERANCE ) )
return 0 ;
// we're past the entity tolerance level, so delay the respawn
return FlWeaponRespawnTime ( pWeapon ) ;
}
return 0 ;
}
//=========================================================
// VecWeaponRespawnSpot - where should this weapon spawn?
// Some game variations may choose to randomize spawn locations
//=========================================================
Vector CHalfLifeMultiplay : : VecWeaponRespawnSpot ( CBasePlayerItem * pWeapon )
{
return pWeapon - > pev - > origin ;
}
//=========================================================
// WeaponShouldRespawn - any conditions inhibiting the
// respawning of this weapon?
//=========================================================
int CHalfLifeMultiplay : : WeaponShouldRespawn ( CBasePlayerItem * pWeapon )
{
if ( pWeapon - > pev - > spawnflags & SF_NORESPAWN )
{
return GR_WEAPON_RESPAWN_NO ;
}
return GR_WEAPON_RESPAWN_YES ;
}
//=========================================================
// CanHaveWeapon - returns FALSE if the player is not allowed
// to pick up this weapon
//=========================================================
BOOL CHalfLifeMultiplay : : CanHavePlayerItem ( CBasePlayer * pPlayer , CBasePlayerItem * pItem )
{
if ( weaponstay . value > 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 CHalfLifeMultiplay : : CanHaveItem ( CBasePlayer * pPlayer , CItem * pItem )
{
return TRUE ;
}
//=========================================================
//=========================================================
void CHalfLifeMultiplay : : PlayerGotItem ( CBasePlayer * pPlayer , CItem * pItem )
{
}
//=========================================================
//=========================================================
int CHalfLifeMultiplay : : ItemShouldRespawn ( CItem * pItem )
{
if ( pItem - > pev - > spawnflags & SF_NORESPAWN )
{
return GR_ITEM_RESPAWN_NO ;
}
return GR_ITEM_RESPAWN_YES ;
}
//=========================================================
// At what time in the future may this Item respawn?
//=========================================================
float CHalfLifeMultiplay : : FlItemRespawnTime ( CItem * pItem )
{
return gpGlobals - > time + ITEM_RESPAWN_TIME ;
}
//=========================================================
// Where should this item respawn?
// Some game variations may choose to randomize spawn locations
//=========================================================
Vector CHalfLifeMultiplay : : VecItemRespawnSpot ( CItem * pItem )
{
return pItem - > pev - > origin ;
}
//=========================================================
//=========================================================
void CHalfLifeMultiplay : : PlayerGotAmmo ( CBasePlayer * pPlayer , char * szName , int iCount )
{
}
//=========================================================
//=========================================================
BOOL CHalfLifeMultiplay : : IsAllowedToSpawn ( CBaseEntity * pEntity )
{
// if( pEntity->pev->flags & FL_MONSTER )
// return FALSE;
return TRUE ;
}
//=========================================================
//=========================================================
int CHalfLifeMultiplay : : AmmoShouldRespawn ( CBasePlayerAmmo * pAmmo )
{
if ( pAmmo - > pev - > spawnflags & SF_NORESPAWN )
{
return GR_AMMO_RESPAWN_NO ;
}
return GR_AMMO_RESPAWN_YES ;
}
//=========================================================
//=========================================================
float CHalfLifeMultiplay : : FlAmmoRespawnTime ( CBasePlayerAmmo * pAmmo )
{
return gpGlobals - > time + AMMO_RESPAWN_TIME ;
}
//=========================================================
//=========================================================
Vector CHalfLifeMultiplay : : VecAmmoRespawnSpot ( CBasePlayerAmmo * pAmmo )
{
return pAmmo - > pev - > origin ;
}
//=========================================================
//=========================================================
float CHalfLifeMultiplay : : FlHealthChargerRechargeTime ( void )
{
return 60 ;
}
float CHalfLifeMultiplay : : FlHEVChargerRechargeTime ( void )
{
return 30 ;
}
//=========================================================
//=========================================================
int CHalfLifeMultiplay : : DeadPlayerWeapons ( CBasePlayer * pPlayer )
{
return GR_PLR_DROP_GUN_ACTIVE ;
}
//=========================================================
//=========================================================
int CHalfLifeMultiplay : : DeadPlayerAmmo ( CBasePlayer * pPlayer )
{
return GR_PLR_DROP_AMMO_ACTIVE ;
}
edict_t * CHalfLifeMultiplay : : 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 CHalfLifeMultiplay : : PlayerRelationship ( CBaseEntity * pPlayer , CBaseEntity * pTarget )
{
// half life deathmatch has only enemies
return GR_NOTTEAMMATE ;
}
BOOL CHalfLifeMultiplay : : PlayFootstepSounds ( CBasePlayer * pl , float fvol )
{
if ( g_footsteps & & g_footsteps - > value = = 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 CHalfLifeMultiplay : : FAllowFlashlight ( void )
{
return flashlight . value ! = 0 ;
}
//=========================================================
//=========================================================
BOOL CHalfLifeMultiplay : : FAllowMonsters ( void )
{
return ( allowmonsters . value ! = 0 ) ;
}
//=========================================================
//======== CHalfLifeMultiplay private functions ===========
# define INTERMISSION_TIME 6
void CHalfLifeMultiplay : : GoToIntermission ( void )
{
if ( g_fGameOver )
return ; // intermission has already been triggered, so ignore.
MESSAGE_BEGIN ( MSG_ALL , SVC_INTERMISSION ) ;
MESSAGE_END ( ) ;
// bounds check
int time = ( int ) CVAR_GET_FLOAT ( " mp_chattime " ) ;
if ( time < 1 )
CVAR_SET_STRING ( " mp_chattime " , " 1 " ) ;
else if ( time > MAX_INTERMISSION_TIME )
CVAR_SET_STRING ( " mp_chattime " , UTIL_dtos1 ( MAX_INTERMISSION_TIME ) ) ;
m_flIntermissionEndTime = gpGlobals - > time + ( ( int ) mp_chattime . value ) ;
g_flIntermissionStartTime = gpGlobals - > time ;
g_fGameOver = TRUE ;
m_iEndIntermissionButtonHit = FALSE ;
}
# define MAX_RULE_BUFFER 1024
typedef struct mapcycle_item_s
{
struct mapcycle_item_s * next ;
char mapname [ 32 ] ;
int minplayers , maxplayers ;
char rulebuffer [ MAX_RULE_BUFFER ] ;
} mapcycle_item_t ;
typedef struct mapcycle_s
{
struct mapcycle_item_s * items ;
struct mapcycle_item_s * next_item ;
} mapcycle_t ;
/*
= = = = = = = = = = = = = =
DestroyMapCycle
Clean up memory used by mapcycle when switching it
= = = = = = = = = = = = = =
*/
void DestroyMapCycle ( mapcycle_t * cycle )
{
mapcycle_item_t * p , * n , * start ;
p = cycle - > items ;
if ( p )
{
start = p ;
p = p - > next ;
while ( p ! = start )
{
n = p - > next ;
delete p ;
p = n ;
}
delete cycle - > items ;
}
cycle - > items = NULL ;
cycle - > next_item = NULL ;
}
static char com_token [ 1500 ] ;
/*
= = = = = = = = = = = = = =
COM_Parse
Parse a token out of a string
= = = = = = = = = = = = = =
*/
const char * COM_Parse ( const char * data )
{
int c ;
int len ;
len = 0 ;
com_token [ 0 ] = 0 ;
if ( ! data )
return NULL ;
// skip whitespace
skipwhite :
while ( ( c = * data ) < = ' ' )
{
if ( c = = 0 )
return NULL ; // end of file;
data + + ;
}
// skip // comments
if ( c = = ' / ' & & data [ 1 ] = = ' / ' )
{
while ( * data & & * data ! = ' \n ' )
data + + ;
goto skipwhite ;
}
// handle quoted strings specially
if ( c = = ' \" ' )
{
data + + ;
while ( 1 )
{
c = * data + + ;
if ( c = = ' \" ' | | ! c )
{
com_token [ len ] = 0 ;
return data ;
}
com_token [ len ] = c ;
len + + ;
}
}
// parse single characters
if ( c = = ' { ' | | c = = ' } ' | | c = = ' ) ' | | c = = ' ( ' | | c = = ' \' ' | | c = = ' , ' )
{
com_token [ len ] = c ;
len + + ;
com_token [ len ] = 0 ;
return data + 1 ;
}
// parse a regular word
do
{
com_token [ len ] = c ;
data + + ;
len + + ;
c = * data ;
if ( c = = ' { ' | | c = = ' } ' | | c = = ' ) ' | | c = = ' ( ' | | c = = ' \' ' | | c = = ' , ' )
break ;
} while ( c > 32 ) ;
com_token [ len ] = 0 ;
return data ;
}
/*
= = = = = = = = = = = = = =
COM_TokenWaiting
Returns 1 if additional data is waiting to be processed on this line
= = = = = = = = = = = = = =
*/
int COM_TokenWaiting ( const char * buffer )
{
const char * p ;
p = buffer ;
while ( * p & & * p ! = ' \n ' )
{
if ( ! isspace ( * p ) | | isalnum ( * p ) )
return 1 ;
p + + ;
}
return 0 ;
}
/*
= = = = = = = = = = = = = =
ReloadMapCycleFile
Parses mapcycle . txt file into mapcycle_t structure
= = = = = = = = = = = = = =
*/
int ReloadMapCycleFile ( const char * filename , mapcycle_t * cycle )
{
char szMap [ 32 ] ;
int length ;
const char * pFileList ;
const char * aFileList = pFileList = ( const char * ) LOAD_FILE_FOR_ME ( filename , & length ) ;
int hasbuffer ;
mapcycle_item_s * item , * newlist = NULL , * next ;
if ( pFileList & & length )
{
// the first map name in the file becomes the default
while ( 1 )
{
char szBuffer [ MAX_RULE_BUFFER ] = { 0 } ;
hasbuffer = 0 ;
pFileList = COM_Parse ( pFileList ) ;
if ( com_token [ 0 ] = = ' \0 ' )
break ;
strcpy ( szMap , com_token ) ;
// Any more tokens on this line?
if ( COM_TokenWaiting ( pFileList ) )
{
pFileList = COM_Parse ( pFileList ) ;
if ( com_token [ 0 ] ! = ' \0 ' )
{
hasbuffer = 1 ;
strcpy ( szBuffer , com_token ) ;
}
}
// Check map
if ( IS_MAP_VALID ( szMap ) )
{
// Create entry
char * s ;
item = new mapcycle_item_s ;
strcpy ( item - > mapname , szMap ) ;
item - > minplayers = 0 ;
item - > maxplayers = 0 ;
memset ( item - > rulebuffer , 0 , MAX_RULE_BUFFER ) ;
if ( hasbuffer )
{
s = g_engfuncs . pfnInfoKeyValue ( szBuffer , " minplayers " ) ;
if ( s & & s [ 0 ] )
{
item - > minplayers = atoi ( s ) ;
item - > minplayers = Q_max ( item - > minplayers , 0 ) ;
item - > minplayers = Q_min ( item - > minplayers , gpGlobals - > maxClients ) ;
}
s = g_engfuncs . pfnInfoKeyValue ( szBuffer , " maxplayers " ) ;
if ( s & & s [ 0 ] )
{
item - > maxplayers = atoi ( s ) ;
item - > maxplayers = Q_max ( item - > maxplayers , 0 ) ;
item - > maxplayers = Q_min ( item - > maxplayers , gpGlobals - > maxClients ) ;
}
// Remove keys
//
g_engfuncs . pfnInfo_RemoveKey ( szBuffer , " minplayers " ) ;
g_engfuncs . pfnInfo_RemoveKey ( szBuffer , " maxplayers " ) ;
strcpy ( item - > rulebuffer , szBuffer ) ;
}
item - > next = cycle - > items ;
cycle - > items = item ;
}
else
{
ALERT ( at_console , " Skipping %s from mapcycle, not a valid map \n " , szMap ) ;
}
}
FREE_FILE ( ( void * ) aFileList ) ;
}
// Fixup circular list pointer
item = cycle - > items ;
// Reverse it to get original order
while ( item )
{
next = item - > next ;
item - > next = newlist ;
newlist = item ;
item = next ;
}
cycle - > items = newlist ;
item = cycle - > items ;
// Didn't parse anything
if ( ! item )
{
return 0 ;
}
while ( item - > next )
{
item = item - > next ;
}
item - > next = cycle - > items ;
cycle - > next_item = item - > next ;
return 1 ;
}
/*
= = = = = = = = = = = = = =
CountPlayers
Determine the current # of active players on the server for map cycling logic
= = = = = = = = = = = = = =
*/
int CountPlayers ( void )
{
int num = 0 ;
for ( int i = 1 ; i < = gpGlobals - > maxClients ; i + + )
{
CBaseEntity * pEnt = UTIL_PlayerByIndex ( i ) ;
if ( pEnt )
{
num = num + 1 ;
}
}
return num ;
}
/*
= = = = = = = = = = = = = =
ExtractCommandString
Parse commands / key value pairs to issue right after map xxx command is issued on server
level transition
= = = = = = = = = = = = = =
*/
void ExtractCommandString ( char * s , char * szCommand )
{
// Now make rules happen
char pkey [ 512 ] ;
char value [ 512 ] ; // use two buffers so compares
// work without stomping on each other
char * o ;
if ( * s = = ' \\ ' )
s + + ;
while ( 1 )
{
o = pkey ;
while ( * s ! = ' \\ ' )
{
if ( ! * s )
return ;
* o + + = * s + + ;
}
* o = 0 ;
s + + ;
o = value ;
while ( * s ! = ' \\ ' & & * s )
{
if ( ! * s )
return ;
* o + + = * s + + ;
}
* o = 0 ;
strcat ( szCommand , pkey ) ;
if ( value [ 0 ] ! = ' \0 ' )
{
strcat ( szCommand , " " ) ;
strcat ( szCommand , value ) ;
}
strcat ( szCommand , " \n " ) ;
if ( ! * s )
return ;
s + + ;
}
}
/*
= = = = = = = = = = = = = =
ChangeLevel
Server is changing to a new level , check mapcycle . txt for map name and setup info
= = = = = = = = = = = = = =
*/
void CHalfLifeMultiplay : : ChangeLevel ( void )
{
static char szPreviousMapCycleFile [ 256 ] ;
static mapcycle_t mapcycle ;
char szNextMap [ 32 ] ;
char szFirstMapInList [ 32 ] ;
char szCommands [ 1500 ] ;
char szRules [ 1500 ] ;
int minplayers = 0 , maxplayers = 0 ;
strcpy ( szFirstMapInList , " hldm1 " ) ; // the absolute default level is hldm1
int curplayers ;
BOOL do_cycle = TRUE ;
// find the map to change to
const char * mapcfile = CVAR_GET_STRING ( " mapcyclefile " ) ;
ASSERT ( mapcfile ! = NULL ) ;
szCommands [ 0 ] = ' \0 ' ;
szRules [ 0 ] = ' \0 ' ;
curplayers = CountPlayers ( ) ;
// Has the map cycle filename changed?
if ( stricmp ( mapcfile , szPreviousMapCycleFile ) )
{
strcpy ( szPreviousMapCycleFile , mapcfile ) ;
DestroyMapCycle ( & mapcycle ) ;
if ( ! ReloadMapCycleFile ( mapcfile , & mapcycle ) | | ( ! mapcycle . items ) )
{
ALERT ( at_console , " Unable to load map cycle file %s \n " , mapcfile ) ;
do_cycle = FALSE ;
}
}
if ( do_cycle & & mapcycle . items )
{
BOOL keeplooking = FALSE ;
BOOL found = FALSE ;
mapcycle_item_s * item ;
// Assume current map
strcpy ( szNextMap , STRING ( gpGlobals - > mapname ) ) ;
strcpy ( szFirstMapInList , STRING ( gpGlobals - > mapname ) ) ;
// Traverse list
for ( item = mapcycle . next_item ; item - > next ! = mapcycle . next_item ; item = item - > next )
{
keeplooking = FALSE ;
ASSERT ( item ! = NULL ) ;
if ( item - > minplayers ! = 0 )
{
if ( curplayers > = item - > minplayers )
{
found = TRUE ;
minplayers = item - > minplayers ;
}
else
{
keeplooking = TRUE ;
}
}
if ( item - > maxplayers ! = 0 )
{
if ( curplayers < = item - > maxplayers )
{
found = TRUE ;
maxplayers = item - > maxplayers ;
}
else
{
keeplooking = TRUE ;
}
}
if ( keeplooking )
continue ;
found = TRUE ;
break ;
}
if ( ! found )
{
item = mapcycle . next_item ;
}
// Increment next item pointer
mapcycle . next_item = item - > next ;
// Perform logic on current item
strcpy ( szNextMap , item - > mapname ) ;
ExtractCommandString ( item - > rulebuffer , szCommands ) ;
strcpy ( szRules , item - > rulebuffer ) ;
}
if ( ! IS_MAP_VALID ( szNextMap ) )
{
strcpy ( szNextMap , szFirstMapInList ) ;
}
g_fGameOver = TRUE ;
ALERT ( at_console , " CHANGE LEVEL: %s \n " , szNextMap ) ;
if ( minplayers | | maxplayers )
{
ALERT ( at_console , " PLAYER COUNT: min %i max %i current %i \n " , minplayers , maxplayers , curplayers ) ;
}
if ( szRules [ 0 ] ! = ' \0 ' )
{
ALERT ( at_console , " RULES: %s \n " , szRules ) ;
}
CHANGE_LEVEL ( szNextMap , NULL ) ;
if ( szCommands [ 0 ] ! = ' \0 ' )
{
SERVER_COMMAND ( szCommands ) ;
}
}
# define MAX_MOTD_CHUNK 60
# define MAX_MOTD_LENGTH 1536 // (MAX_MOTD_CHUNK * 4)
void CHalfLifeMultiplay : : 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 ( CVAR_GET_STRING ( " motdfile " ) , & length ) ;
// send the server name
MESSAGE_BEGIN ( MSG_ONE , gmsgServerName , NULL , client ) ;
WRITE_STRING ( CVAR_GET_STRING ( " hostname " ) ) ;
MESSAGE_END ( ) ;
// 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 ( ( void * ) aFileList ) ;
}