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.
985 lines
27 KiB
985 lines
27 KiB
8 years ago
|
|
||
|
#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<<WEAPON_SUIT);
|
||
|
|
||
|
addDefault = TRUE;
|
||
|
|
||
|
while ( pWeaponEntity = UTIL_FindEntityByClassname( pWeaponEntity, "game_player_equip" ))
|
||
|
{
|
||
|
pWeaponEntity->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 );
|
||
|
}
|
||
|
|
||
|
|