halflife-thewastes-sdk/dlls/game_deathmatch.cpp

1938 lines
49 KiB
C++
Raw Normal View History

/***
*
* Copyright (c) 1996-2001, 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.
*
****/
//
// multiplay_gamerules.cpp
//
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "shake.h"
#include "player.h"
#include "weapons.h"
#include "gamerules.h"
#include "client.h"
#include "game.h"
#include "items.h"
#include "voice_gamemgr.h"
#include "thewastes.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 gmsgBriefing;
extern int gmsgServerName;
extern int gmsgSpectator;
extern int g_gametype;
#define ITEM_RESPAWN_TIME 30
#define WEAPON_RESPAWN_TIME 35
#define UNIQUE_RESPAWN_TIME 60
#define AMMO_RESPAWN_TIME 20
float g_flIntermissionStartTime = 0;
CVoiceGameMgr g_VoiceGameMgr;
class CMultiplayGameMgrHelper : public IVoiceGameMgrHelper
{
public:
virtual bool CanPlayerHearPlayer(CBasePlayer *pListener, CBasePlayer *pTalker)
{
if ( g_gametype == 2 )
{
if ( g_pGameRules->PlayerRelationship( pListener, pTalker ) != GR_TEAMMATE )
{
return false;
}
}
return true;
}
};
static CMultiplayGameMgrHelper g_GameMgrHelper;
//*********************************************************
// Rules for the half-life multiplayer game.
//*********************************************************
CHalfLifeMultiplay :: CHalfLifeMultiplay()
{
g_VoiceGameMgr.Init(&g_GameMgrHelper, gpGlobals->maxClients);
RefreshSkillData();
m_flIntermissionEndTime = 0;
g_flIntermissionStartTime = 0;
m_flWeaponAllowCheck = 0;
m_iLaserOn = m_iLaserOff = 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
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 );
}
}
}
BOOL CHalfLifeMultiplay::ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
{
if(g_VoiceGameMgr.ClientCommand(pPlayer, pcmd))
return TRUE;
if(FStrEq(pcmd,"join_game"))
{
// Client wants to be put in the game (used for intro stuff)
if( !pPlayer->m_bCanSpawn )
{
pPlayer->m_bCanSpawn = TRUE;
pPlayer->m_bObserver = FALSE;
respawn(pPlayer->pev,FALSE);
}
return TRUE;
}
else if(FStrEq(pcmd,"spectate"))
{
// Client wants to spectate
if(pPlayer->m_bCanSpawn)
{
pPlayer->m_bCanSpawn = FALSE;
pPlayer->Killed(pPlayer->pev,FALSE);
}
else
{
pPlayer->m_bObserver = TRUE;
pPlayer->Spawn();
}
return TRUE;
}
else if(FStrEq(pcmd,"setrandom"))
{
// Client wants random weapons
pPlayer->m_Inventory.SetRandom( TRUE );
return TRUE;
}
else if(FStrEq(pcmd,"unsetrandom"))
{
// Client doesnt want random weapons
pPlayer->m_Inventory.SetRandom( FALSE );
return TRUE;
}
else if(FStrEq(pcmd,"setweapons"))
{
pPlayer->m_Inventory.SetInventory( 0, atoi( CMD_ARGV(1) ) );
pPlayer->m_Inventory.SetInventory( 1, atoi( CMD_ARGV(2) ) );
pPlayer->m_Inventory.SetInventory( 2, atoi( CMD_ARGV(3) ) );
pPlayer->m_Inventory.SetInventory( 3, atoi( CMD_ARGV(4) ) );
pPlayer->m_Inventory.SetInventory( 4, atoi( CMD_ARGV(5) ) );
return TRUE;
}
return CGameRules::ClientCommand(pPlayer, pcmd);
}
//=========================================================
//=========================================================
void CHalfLifeMultiplay::RefreshSkillData( void )
{
// load all default values
CGameRules::RefreshSkillData();
}
// 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 )
{
g_VoiceGameMgr.Update(gpGlobals->frametime);
///// 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 < 10 )
CVAR_SET_STRING( "mp_chattime", "10" );
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 = 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;
SendLaserInfo();
}
//=========================================================
//=========================================================
BOOL CHalfLifeMultiplay::IsMultiplayer( void )
{
return TRUE;
}
//=========================================================
//=========================================================
BOOL CHalfLifeMultiplay::IsDeathmatch( void )
{
return TRUE;
}
//=========================================================
//=========================================================
BOOL CHalfLifeMultiplay::IsCoOp( void )
{
return gpGlobals->coop;
}
//=========================================================
//=========================================================
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_pActiveItem->CanHolster() )
{
// can't put away the active item.
return FALSE;
}
CBasePlayerWeapon *pCastWeap = (CBasePlayerWeapon*)pWeapon;
if( pCastWeap != NULL )
{
if( !pCastWeap->IsUseable() )
return FALSE;
if( pCastWeap->m_iClip == 0 &&
pPlayer->m_rgAmmo[ pCastWeap->m_iPrimaryAmmoType ] < 1 )
{
// This gun has no ammo
return FALSE;
}
}
pCastWeap = (CBasePlayerWeapon*)pPlayer->m_pActiveItem;
if( pCastWeap != NULL && pCastWeap->m_iClip == 0 &&
pPlayer->m_rgAmmo[ pCastWeap->m_iPrimaryAmmoType ] < 1 )
{
// Current gun has no ammo
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 )
{
pCurrentWeapon->Holster(); // We add this because some weapons remove themselves (like molotovs and grenades)
return FALSE;
}
pPlayer->SwitchWeapon( pBest );
return TRUE;
}
//=========================================================
//=========================================================
BOOL CHalfLifeMultiplay :: ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ] )
{
g_VoiceGameMgr.ClientConnected(pEntity);
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_gametype == 2)
{
UTIL_LogPrintf( "\"%s<%i><%u><%s>\" entered the game\n",
STRING( pl->pev->netname ),
GETPLAYERUSERID( pl->edict() ),
GETPLAYERWONID( pl->edict() ),
g_engfuncs.pfnInfoKeyValue( g_engfuncs.pfnGetInfoKeyBuffer( pl->edict() ), "model" ) );
}
else
{
UTIL_LogPrintf( "\"%s<%i><%u><%i>\" entered the game\n",
STRING( pl->pev->netname ),
GETPLAYERUSERID( pl->edict() ),
GETPLAYERWONID( pl->edict() ),
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 );
WRITE_SHORT( 0 );
WRITE_SHORT( 0 );
MESSAGE_END();
// Send their spectator state
MESSAGE_BEGIN( MSG_ONE, gmsgSpectator, NULL, pl->edict() );
WRITE_BYTE( pl->entindex() );
WRITE_BYTE( (pl->pev->iuser1 != 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 );
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_gametype == 2 )
{
UTIL_LogPrintf( "\"%s<%i><%u><%s>\" disconnected\n",
STRING( pPlayer->pev->netname ),
GETPLAYERUSERID( pPlayer->edict() ),
GETPLAYERWONID( pPlayer->edict() ),
g_engfuncs.pfnInfoKeyValue( g_engfuncs.pfnGetInfoKeyBuffer( pPlayer->edict() ), "model" ) );
}
else
{
UTIL_LogPrintf( "\"%s<%i><%u><%i>\" disconnected\n",
STRING( pPlayer->pev->netname ),
GETPLAYERUSERID( pPlayer->edict() ),
GETPLAYERWONID( pPlayer->edict() ),
GETPLAYERUSERID( pPlayer->edict() ) );
}
pPlayer->RemoveAllItems();// destroy all of the players weapons and items
// Tell all clients this player isn't a spectator anymore
MESSAGE_BEGIN( MSG_ALL, gmsgSpectator );
WRITE_BYTE( ENTINDEX(pClient) );
WRITE_BYTE( 0 );
MESSAGE_END();
CBasePlayer *client = NULL;
while ( ((client = (CBasePlayer*)UTIL_FindEntityByClassname( client, "player" )) != NULL) && (!FNullEnt(client->edict())) )
{
if ( !client->pev )
continue;
if ( client == pPlayer )
continue;
// If a spectator was chasing this player, move him/her onto the next player
if ( client->m_hObserverTarget == pPlayer )
{
int iMode = client->pev->iuser1;
client->pev->iuser1 = 0;
client->m_hObserverTarget = NULL;
client->Observer_SetMode( iMode );
}
}
}
}
}
//=========================================================
//=========================================================
float CHalfLifeMultiplay :: FlPlayerFallDamage( CBasePlayer *pPlayer )
{
pPlayer->m_flFallVelocity -= PLAYER_MAX_SAFE_FALL_SPEED;
if(pPlayer->m_flFallVelocity * DAMAGE_FOR_FALL_SPEED >= 10)
{
// Fade if the player isnt gonna die from it
if(pPlayer->m_flFallVelocity*DAMAGE_FOR_FALL_SPEED < pPlayer->pev->health)
{
UTIL_ScreenFade(pPlayer,RGB_FALL,TIME_FALL,HOLD_FALL,ALPHA_FALL,FLAGS_FALL);
// Only cripple if player fell far enough
if(pPlayer->m_flFallVelocity*DAMAGE_FOR_FALL_SPEED > 40)
CRIPPLE_PLAYER(pPlayer,pPlayer->m_flFallVelocity*DAMAGE_FOR_FALL_SPEED);
}
return pPlayer->m_flFallVelocity * DAMAGE_FOR_FALL_SPEED;
}
return 0;
}
//=========================================================
// ModifyTiltedDamage
//
// Calculate damage tilt based on modifiers such as distance.
//=========================================================
float CHalfLifeMultiplay::ModifyTiltedDamage(CBaseEntity *pAttacker,CBaseEntity *pVictim,float flCurDamage,int iDamageType)
{
float fTiltedDamage = flCurDamage;
Vector vecDistance;
vecDistance = pAttacker->pev->origin - pVictim->pev->origin;
if( strcmp( STRING(pVictim->pev->classname), "player" ) != 0 )
return flCurDamage;
// Damage reduction range
if( iDamageType == DMG_BULLET )
fTiltedDamage = max( 0, fTiltedDamage - (vecDistance.Length()/30.0f) );
else if( iDamageType == DMG_BUCKSHOT )
fTiltedDamage = max( 0, fTiltedDamage - (vecDistance.Length()/25.0f) );
// Each person incurs a 5% damage penalty
// if not on the ground
if(!(pAttacker->pev->flags & FL_ONGROUND))
fTiltedDamage *= 0.95f;
if(!(pVictim->pev->flags & FL_ONGROUND))
fTiltedDamage *= 0.95f;
// Each person incurs a 5% damage INCREASE
// if crouching
if(pAttacker->pev->flags & FL_DUCKING)
fTiltedDamage *= 1.05f;
if(pVictim->pev->flags & FL_DUCKING)
fTiltedDamage *= 1.05f;
#if 0
char dmg[1024];
sprintf( dmg, "Damage: %f Distance: %f", fTiltedDamage, vecDistance.Length() );
UTIL_SayTextAll( dmg, UTIL_FindEntityByClassname( NULL, "player" ) );
#endif
return fTiltedDamage;
}
//=========================================================
//=========================================================
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;
addDefault = TRUE;
while ( pWeaponEntity = UTIL_FindEntityByClassname( pWeaponEntity, "game_player_equip" ))
{
pWeaponEntity->Touch( pPlayer );
addDefault = FALSE;
}
if ( addDefault )
{
pPlayer->m_Inventory.SpawnInventory(pPlayer);
}
// Clear any screen fades
UTIL_ScreenFade(pPlayer,Vector(0,0,0),0,0,0,FFADE_OUT);
CRIPPLE_PLAYER(pPlayer,0);
}
//=========================================================
//=========================================================
BOOL CHalfLifeMultiplay :: FPlayerCanRespawn( CBasePlayer *pPlayer )
{
return TRUE;
}
//=========================================================
//=========================================================
float CHalfLifeMultiplay :: FlPlayerSpawnTime( CBasePlayer *pPlayer )
{
return gpGlobals->time;//now!
}
//=========================================================
// 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() )
{
int Frags = IPointsForKill( peKiller, pVictim );
// if a player dies in a deathmatch game and the killer is a client, award the killer some points
pKiller->frags += Frags;
if(peKiller != NULL)
peKiller->m_iTempFrags += Frags;
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 );
WRITE_SHORT( 1 );
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( 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 *Killer = CBaseEntity::Instance( pKiller );
CBasePlayer *pKPlayer = NULL;
CWasteWeapon *pWeapon = NULL;
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;
// If player was attacked within a second of dying,
// dont count it as bleeding
if( pVictim->pev->vuser2.y == 2 )
{
if( gpGlobals->time < pVictim->m_flLastAttack + 1.0f )
pVictim->pev->vuser2.y = 0;
}
MESSAGE_BEGIN( MSG_ALL, gmsgDeathMsg );
WRITE_BYTE( killer_index ); // the killer
WRITE_BYTE( ENTINDEX(pVictim->edict()) ); // the victim
WRITE_BYTE( pVictim->pev->vuser2.y ); // special kill ?
WRITE_STRING( killer_weapon_name ); // what they were killed by (should this be a string?)
if(Killer->IsPlayer())
{
pKPlayer = (CBasePlayer*)Killer;
pWeapon = (CWasteWeapon*)pKPlayer->m_pActiveItem;
}
if(pKPlayer != NULL)
{
if(pVictim->pev->vuser2.y == 2)
{
char szBledString[128];
switch(RANDOM_LONG(0,1))
{
case 0:
strcpy(szBledString, DEATH_BLEED_1);
break;
case 1:
strcpy(szBledString, DEATH_BLEED_2);
break;
}
WRITE_STRING( szBledString );
}
else if(pKPlayer->HasWeapons() && pWeapon != NULL)
WRITE_STRING( pWeapon->szGetDeathNoticeString() );
else
WRITE_STRING( "%s killed %s... this message shouldnt exist" ); // If this happens, oops!
}
else
WRITE_STRING(""); // client.dll populates this
MESSAGE_END();
if ( pVictim->pev == pKiller )
{
// killed self
// team match?
if ( g_gametype == 2 )
{
UTIL_LogPrintf( "\"%s<%i><%u><%s>\" committed suicide with \"%s\"\n",
STRING( pVictim->pev->netname ),
GETPLAYERUSERID( pVictim->edict() ),
GETPLAYERWONID( pVictim->edict() ),
g_engfuncs.pfnInfoKeyValue( g_engfuncs.pfnGetInfoKeyBuffer( pVictim->edict() ), "model" ),
killer_weapon_name );
}
else
{
UTIL_LogPrintf( "\"%s<%i><%u><%i>\" committed suicide with \"%s\"\n",
STRING( pVictim->pev->netname ),
GETPLAYERUSERID( pVictim->edict() ),
GETPLAYERWONID( pVictim->edict() ),
GETPLAYERUSERID( pVictim->edict() ),
killer_weapon_name );
}
}
else if ( pKiller->flags & FL_CLIENT )
{
// team match?
if ( g_gametype == 2 )
{
UTIL_LogPrintf( "\"%s<%i><%u><%s>\" killed \"%s<%i><%u><%s>\" with \"%s\"\n",
STRING( pKiller->netname ),
GETPLAYERUSERID( ENT(pKiller) ),
GETPLAYERWONID( ENT(pKiller) ),
g_engfuncs.pfnInfoKeyValue( g_engfuncs.pfnGetInfoKeyBuffer( ENT(pKiller) ), "model" ),
STRING( pVictim->pev->netname ),
GETPLAYERUSERID( pVictim->edict() ),
GETPLAYERWONID( pVictim->edict() ),
g_engfuncs.pfnInfoKeyValue( g_engfuncs.pfnGetInfoKeyBuffer( pVictim->edict() ), "model" ),
killer_weapon_name );
}
else
{
UTIL_LogPrintf( "\"%s<%i><%u><%i>\" killed \"%s<%i><%u><%i>\" with \"%s\"\n",
STRING( pKiller->netname ),
GETPLAYERUSERID( ENT(pKiller) ),
GETPLAYERWONID( ENT(pKiller) ),
GETPLAYERUSERID( ENT(pKiller) ),
STRING( pVictim->pev->netname ),
GETPLAYERUSERID( pVictim->edict() ),
GETPLAYERWONID( pVictim->edict() ),
GETPLAYERUSERID( pVictim->edict() ),
killer_weapon_name );
}
}
else
{
// killed by the world
// team match?
if ( g_gametype == 2 )
{
UTIL_LogPrintf( "\"%s<%i><%u><%s>\" committed suicide with \"%s\" (world)\n",
STRING( pVictim->pev->netname ),
GETPLAYERUSERID( pVictim->edict() ),
GETPLAYERWONID( pVictim->edict() ),
g_engfuncs.pfnInfoKeyValue( g_engfuncs.pfnGetInfoKeyBuffer( pVictim->edict() ), "model" ),
killer_weapon_name );
}
else
{
UTIL_LogPrintf( "\"%s<%i><%u><%i>\" committed suicide with \"%s\" (world)\n",
STRING( pVictim->pev->netname ),
GETPLAYERUSERID( pVictim->edict() ),
GETPLAYERWONID( pVictim->edict() ),
GETPLAYERUSERID( pVictim->edict() ),
killer_weapon_name );
}
}
MESSAGE_BEGIN ( MSG_SPEC, SVC_DIRECTOR );
WRITE_BYTE ( 9 ); //command length in bytes
WRITE_BYTE ( DRC_EVENT );
WRITE_SHORT( ENTINDEX(pVictim->edict()) );
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 );
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
}
}
CBasePlayerWeapon *pWeap = (CBasePlayerWeapon*)pWeapon;
if( pWeap != NULL )
if( pWeap->iWeight() == WEIGHT_UNIQUE )
return gpGlobals->time + UNIQUE_RESPAWN_TIME;
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;
if( !CVAR_GET_FLOAT( "mp_allowsidearms" ) )
{
playeritemlist_t *pCurList = &g_SidearmItems;
for( int i = 0;i < pCurList->size; i++ )
{
playeritem_t *pItem = &pCurList->array[i];
if( FStrEq( STRING( pEntity->pev->classname ), pItem->weapon_classname ) )
return FALSE;
}
}
if( !CVAR_GET_FLOAT( "mp_allowprimary" ) )
{
playeritemlist_t *pCurList = &g_PrimaryItems;
for( int i = 0;i < pCurList->size; i++ )
{
playeritem_t *pItem = &pCurList->array[i];
if( FStrEq( STRING( pEntity->pev->classname ), pItem->weapon_classname ) )
return FALSE;
}
}
if( !CVAR_GET_FLOAT( "mp_allowunique" ) )
{
playeritemlist_t *pCurList = &g_UniqueItems;
for( int i = 0;i < pCurList->size; i++ )
{
playeritem_t *pItem = &pCurList->array[i];
if( FStrEq( STRING( pEntity->pev->classname ), pItem->weapon_classname ) )
return FALSE;
}
}
if( !CVAR_GET_FLOAT( "mp_allowexplosives" ) )
{
playeritemlist_t *pCurList = &g_OtherItems;
for( int i = 0;i < pCurList->size; i++ )
{
playeritem_t *pItem = &pCurList->array[i];
if( FStrEq( STRING( pEntity->pev->classname ), pItem->weapon_classname ) )
return FALSE;
}
}
// TODO: Remove when molotovs are put back in :)
if( FStrEq( STRING( pEntity->pev->classname ), "weapon_molotov" ) )
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_NO;
}
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 < 10 )
CVAR_SET_STRING( "mp_chattime", "10" );
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
==============
*/
char *COM_Parse (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( char *buffer )
{
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( char *filename, mapcycle_t *cycle )
{
char szBuffer[ MAX_RULE_BUFFER ];
char szMap[ 32 ];
int length;
char *pFileList;
char *aFileList = pFileList = (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 )
{
hasbuffer = 0;
memset( szBuffer, 0, MAX_RULE_BUFFER );
pFileList = COM_Parse( pFileList );
if ( strlen( com_token ) <= 0 )
break;
strcpy( szMap, com_token );
// Any more tokens on this line?
if ( COM_TokenWaiting( pFileList ) )
{
pFileList = COM_Parse( pFileList );
if ( strlen( com_token ) > 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 = max( item->minplayers, 0 );
item->minplayers = min( item->minplayers, gpGlobals->maxClients );
}
s = g_engfuncs.pfnInfoKeyValue( szBuffer, "maxplayers" );
if ( s && s[0] )
{
item->maxplayers = atoi( s );
item->maxplayers = max( item->maxplayers, 0 );
item->maxplayers = 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( 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 ( strlen( value ) > 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
char *mapcfile = (char*)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 ( strlen( szRules ) > 0 )
{
ALERT( at_console, "RULES: %s\n", szRules );
}
CHANGE_LEVEL( szNextMap, NULL );
if ( strlen( szCommands ) > 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 need_briefing = 1;
int length, char_count = 0;
char *pFileList;
char *aFileList = pFileList = (char*)LOAD_FILE_FOR_ME( (char *)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];
need_briefing = 0;
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();
}
if(need_briefing)
{
MESSAGE_BEGIN(MSG_ONE,gmsgBriefing,NULL,client);
WRITE_BYTE(1);
MESSAGE_END();
}
FREE_FILE( aFileList );
}
//=========================================================
//=========================================================
void CHalfLifeMultiplay::ClearLevel( void )
{
// TODO: Remove guns, reset glass, etc
}
#define SET_LASER( var, idx, val ) \
var |= val << idx
void CHalfLifeMultiplay::SetLaserBeam( int entindex, BOOL value )
{
if( value == TRUE )
{
SET_LASER( m_iLaserOn, entindex, 1 );
SET_LASER( m_iLaserOff, entindex, 0 );
}
else
{
SET_LASER( m_iLaserOff, entindex, 1 );
SET_LASER( m_iLaserOn, entindex, 0 );
}
/* char sz[128];
sprintf( sz, "Lasers: On: %i Off: %i\n", m_iLaserOn, m_iLaserOff );
UTIL_SayTextAll( sz, UTIL_FindEntityByClassname( NULL, "player" ) );*/
}
void CHalfLifeMultiplay::SendLaserInfo()
{
}