#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 Rocket Arena //********************************************************* CRocketArena :: CRocketArena() { 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 CRocketArena::RefreshSkillData( void ) { CGameRules::RefreshSkillData(); // suitcharger gSkillData.suitchargerCapacity = 30; // Rocket gSkillData.plrDmgRocket = 145; } // longest the intermission can last, in seconds #define MAX_INTERMISSION_TIME 120 //========================================================= //========================================================= void CRocketArena :: 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 CRocketArena::IsMultiplayer( void ) { return TRUE; } //========================================================= //========================================================= BOOL CRocketArena::IsDeathmatch( void ) { return TRUE; } //========================================================= //========================================================= BOOL CRocketArena::IsRocketArena( void ) { return TRUE; } //========================================================= //========================================================= BOOL CRocketArena::IsCoOp( void ) { return gpGlobals->coop; } //========================================================= //========================================================= BOOL CRocketArena::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 CRocketArena :: 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 CRocketArena :: ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ] ) { return TRUE; } extern int gmsgSayText; extern int gmsgGameMode; void CRocketArena :: UpdateGameMode( CBasePlayer *pPlayer ) { MESSAGE_BEGIN( MSG_ONE, gmsgGameMode, NULL, pPlayer->edict() ); WRITE_BYTE( 0 ); // game mode none MESSAGE_END(); } void CRocketArena :: 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 Rocket Arena Mode\n\n1. Join Game\n2. Observe Game"); } //========================================================= //========================================================= void CRocketArena :: 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 Rocket Arena\n", STRING( pPlayer->pev->netname ), GETPLAYERUSERID( pPlayer->edict() ) ); pPlayer->RemoveAllItems( TRUE );// destroy all of the players weapons and items } } } //========================================================= //========================================================= float CRocketArena :: 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 CRocketArena::FPlayerCanTakeDamage( CBasePlayer *pPlayer, CBaseEntity *pAttacker ) { return TRUE; } //========================================================= //========================================================= void CRocketArena :: 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 CRocketArena :: 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 CRocketArena :: PlayerSpawn( CBasePlayer *pPlayer ) { BOOL addDefault; CBaseEntity *pWeaponEntity = NULL; pPlayer->pev->weapons |= (1<Touch( pPlayer ); addDefault = FALSE; } if ( addDefault ) { pPlayer->GiveNamedItem( "weapon_rocketl" ); pPlayer->GiveNamedItem( "ammo_helirockets" ); } } //========================================================= //========================================================= BOOL CRocketArena :: FPlayerCanRespawn( CBasePlayer *pPlayer ) { return pPlayer->m_fWantRespawn; } //========================================================= //========================================================= float CRocketArena :: FlPlayerSpawnTime( CBasePlayer *pPlayer ) { return gpGlobals->time;//now! } BOOL CRocketArena :: AllowAutoTargetCrosshair( void ) { return ( CVAR_GET_FLOAT( "mp_autocrosshair" ) != 0 ); } //========================================================= // IPointsForKill - how many points awarded to anyone // that kills this player? //========================================================= int CRocketArena :: IPointsForKill( CBasePlayer *pAttacker, CBasePlayer *pKilled ) { return 1; } //========================================================= // PlayerKilled - someone/something killed this player //========================================================= void CRocketArena :: 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 CRocketArena::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 CRocketArena :: PlayerGotWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pWeapon ) { } //========================================================= // FlWeaponRespawnTime - what is the time in the future // at which this weapon may spawn? //========================================================= float CRocketArena :: 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 CRocketArena :: 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 CRocketArena :: VecWeaponRespawnSpot( CBasePlayerItem *pWeapon ) { return pWeapon->pev->origin; } //========================================================= // WeaponShouldRespawn - any conditions inhibiting the // respawning of this weapon? //========================================================= int CRocketArena :: WeaponShouldRespawn( CBasePlayerItem *pWeapon ) { return GR_WEAPON_RESPAWN_NO; } //========================================================= // CanHaveWeapon - returns FALSE if the player is not allowed // to pick up this weapon //========================================================= BOOL CRocketArena::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 CRocketArena::CanHaveItem( CBasePlayer *pPlayer, CItem *pItem ) { return TRUE; } //========================================================= //========================================================= void CRocketArena::PlayerGotItem( CBasePlayer *pPlayer, CItem *pItem ) { } //========================================================= //========================================================= int CRocketArena::ItemShouldRespawn( CItem *pItem ) { return GR_ITEM_RESPAWN_NO; } //========================================================= // At what time in the future may this Item respawn? //========================================================= float CRocketArena::FlItemRespawnTime( CItem *pItem ) { return gpGlobals->time + ITEM_RESPAWN_TIME; } //========================================================= // Where should this item respawn? // Some game variations may choose to randomize spawn locations //========================================================= Vector CRocketArena::VecItemRespawnSpot( CItem *pItem ) { return pItem->pev->origin; } //========================================================= //========================================================= void CRocketArena::PlayerGotAmmo( CBasePlayer *pPlayer, char *szName, int iCount ) { } //========================================================= //========================================================= BOOL CRocketArena::IsAllowedToSpawn( CBaseEntity *pEntity ) { // if ( pEntity->pev->flags & FL_MONSTER ) // return FALSE; return TRUE; } //========================================================= //========================================================= int CRocketArena::AmmoShouldRespawn( CBasePlayerAmmo *pAmmo ) { return GR_AMMO_RESPAWN_NO; } //========================================================= //========================================================= float CRocketArena::FlAmmoRespawnTime( CBasePlayerAmmo *pAmmo ) { return gpGlobals->time + AMMO_RESPAWN_TIME; } //========================================================= //========================================================= Vector CRocketArena::VecAmmoRespawnSpot( CBasePlayerAmmo *pAmmo ) { return pAmmo->pev->origin; } //========================================================= //========================================================= float CRocketArena::FlHealthChargerRechargeTime( void ) { return 60; } float CRocketArena::FlHEVChargerRechargeTime( void ) { return 30; } //========================================================= //========================================================= int CRocketArena::DeadPlayerWeapons( CBasePlayer *pPlayer ) { return GR_PLR_DROP_GUN_ACTIVE; } //========================================================= //========================================================= int CRocketArena::DeadPlayerAmmo( CBasePlayer *pPlayer ) { return GR_PLR_DROP_AMMO_ACTIVE; } edict_t *CRocketArena::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 CRocketArena::PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget ) { // half life deathmatch has only enemies return GR_NOTTEAMMATE; } //========================================================= //========================================================= BOOL CRocketArena :: 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 CRocketArena :: FAllowFlashlight( void ) { return CVAR_GET_FLOAT( "mp_flashlight" ) != 0; } //========================================================= //========================================================= BOOL CRocketArena :: FAllowMonsters( void ) { return ( CVAR_GET_FLOAT( "mp_allowmonsters" ) != 0 ); } //========================================================= //======== CRocketArena private functions =========== #define INTERMISSION_TIME 6 void CRocketArena :: 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 CRocketArena :: 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 CRocketArena :: 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( "rocket_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 ); }