2012-12-30 16:37:54 +00:00
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// $Logfile:: /EF2/Code/DLLs/game/mp_modeBase.cpp $
|
|
|
|
// $Revision:: 91 $
|
|
|
|
// $Author:: Singlis $
|
|
|
|
// $Date:: 9/26/03 2:36p $
|
|
|
|
//
|
|
|
|
// Copyright (C) 2002 by Ritual Entertainment, Inc.
|
|
|
|
// All rights reserved.
|
|
|
|
//
|
|
|
|
// This source may not be distributed and/or modified without
|
|
|
|
// expressly written permission by Ritual Entertainment, Inc.
|
|
|
|
//
|
|
|
|
// Description:
|
|
|
|
//
|
|
|
|
|
|
|
|
#include "_pch_cpp.h"
|
|
|
|
|
|
|
|
#include "mp_manager.hpp"
|
|
|
|
#include "mp_modeBase.hpp"
|
|
|
|
#include "equipment.h"
|
|
|
|
#include "powerups.h"
|
|
|
|
#include "weaputils.h"
|
|
|
|
|
|
|
|
// Setup constants
|
|
|
|
const float MultiplayerModeBase::_defaultStartinghealth = 100.0f;
|
|
|
|
const int MultiplayerModeBase::_defaultPointsPerKill = 1;
|
|
|
|
const int MultiplayerModeBase::_defaultPointsPerTakenAwayForSuicide = 1;
|
|
|
|
const float MultiplayerModeBase::_spectatorMoveSpeedModifier = 1.5f;
|
|
|
|
|
|
|
|
MultiplayerPlayerGameData::MultiplayerPlayerGameData()
|
|
|
|
{
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MultiplayerPlayerGameData::init( void )
|
|
|
|
{
|
|
|
|
_numDeaths = 0;
|
|
|
|
_numKills = 0;
|
|
|
|
_points = 0;
|
|
|
|
|
|
|
|
_entnum = 0;
|
|
|
|
|
|
|
|
_playing = false;
|
|
|
|
|
|
|
|
_nextHitSoundTime = 0.0f;
|
|
|
|
|
|
|
|
_currentTeam = NULL;
|
|
|
|
|
|
|
|
_lastKilledByPlayer = -1;
|
|
|
|
_lastKillerOfPlayer = -1;
|
|
|
|
|
|
|
|
_lastKilledByPlayerMOD = MOD_NONE;
|
|
|
|
_lastKillerOfPlayerMOD = MOD_NONE;
|
|
|
|
|
|
|
|
_lastPlace = -1;
|
|
|
|
_lastTied = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MultiplayerPlayerGameData::reset( void )
|
|
|
|
{
|
|
|
|
_numDeaths = 0;
|
|
|
|
_numKills = 0;
|
|
|
|
_points = 0;
|
|
|
|
|
|
|
|
_lastKilledByPlayer = -1;
|
|
|
|
_lastKillerOfPlayer = -1;
|
|
|
|
|
|
|
|
_lastKilledByPlayerMOD = MOD_NONE;
|
|
|
|
_lastKillerOfPlayerMOD = MOD_NONE;
|
|
|
|
|
|
|
|
_nextHitSoundTime = 0.0f;
|
|
|
|
|
|
|
|
_lastPlace = -1;
|
|
|
|
_lastTied = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
CLASS_DECLARATION( Class, MultiplayerModeBase, NULL )
|
|
|
|
{
|
|
|
|
{ NULL, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//================================================================
|
|
|
|
// Name: MultiplayerModeBase
|
|
|
|
// Class: MultiplayerModeBase
|
|
|
|
//
|
|
|
|
// Description: Constructor
|
|
|
|
//
|
|
|
|
// Parameters: const str& -- name of the arena
|
|
|
|
//
|
|
|
|
// Returns: None
|
|
|
|
//
|
|
|
|
//================================================================
|
|
|
|
MultiplayerModeBase::MultiplayerModeBase()
|
|
|
|
{
|
|
|
|
_maxPlayers = 20;
|
|
|
|
_startingHealth = (unsigned int) _defaultStartinghealth;
|
|
|
|
|
|
|
|
_fightInProgress = false;
|
|
|
|
|
|
|
|
_activePlayers = 0;
|
|
|
|
_spawncounter = 0;
|
|
|
|
|
|
|
|
_pointLimit = 0;
|
|
|
|
_timeLimit = 0.0f;
|
|
|
|
|
|
|
|
_playerGameData = NULL;
|
|
|
|
|
|
|
|
_spectatorIconIndex = gi.imageindex( "sysimg/icons/mp/spectator" );
|
|
|
|
|
|
|
|
_warmUpTextIndex = G_FindConfigstringIndex( "$$WarmUp$$", CS_GENERAL_STRINGS, MAX_GENERAL_STRINGS, true ) + CS_GENERAL_STRINGS;;
|
|
|
|
_waitingForMinPlayersTextIndex = G_FindConfigstringIndex( "$$WaitMinPlayers$$", CS_GENERAL_STRINGS, MAX_GENERAL_STRINGS, true ) + CS_GENERAL_STRINGS;;
|
|
|
|
_playingTextIndex = G_FindConfigstringIndex( "$$Playing$$", CS_GENERAL_STRINGS, MAX_GENERAL_STRINGS, true ) + CS_GENERAL_STRINGS;;
|
|
|
|
|
|
|
|
_gameStarted = false;
|
|
|
|
_lastTimeRemaining = 0;
|
|
|
|
|
|
|
|
_lastHighestPoints = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//================================================================
|
|
|
|
// Name: ~MultiplayerModeBase
|
|
|
|
// Class: MultiplayerModeBase
|
|
|
|
//
|
|
|
|
// Description: Destructor
|
|
|
|
//
|
|
|
|
// Parameters: None
|
|
|
|
//
|
|
|
|
// Returns: None
|
|
|
|
//
|
|
|
|
//================================================================
|
|
|
|
MultiplayerModeBase::~MultiplayerModeBase()
|
|
|
|
{
|
|
|
|
int idx = 0 ;
|
|
|
|
|
|
|
|
// Free up all of the ammos
|
|
|
|
|
|
|
|
for (idx = 1; idx <= _ammoList.NumObjects(); idx++)
|
|
|
|
{
|
|
|
|
SimpleAmmoType* ammo = _ammoList.ObjectAt(idx);
|
|
|
|
delete ammo;
|
|
|
|
ammo = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Free up all of the player data
|
|
|
|
|
|
|
|
delete [] _playerGameData;
|
|
|
|
_playerGameData = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MultiplayerModeBase::init( int maxPlayers )
|
|
|
|
{
|
|
|
|
_maxPlayers = maxPlayers;
|
|
|
|
_playerGameData = new MultiplayerPlayerGameData[ _maxPlayers ];
|
|
|
|
|
|
|
|
multiplayerManager.cacheMultiplayerFiles( "mp_general" );
|
|
|
|
}
|
|
|
|
|
|
|
|
void MultiplayerModeBase::initItems( void )
|
|
|
|
{
|
|
|
|
// Setup spawn points
|
|
|
|
|
|
|
|
getSpawnpoints();
|
|
|
|
|
|
|
|
resetSpawnpoints();
|
|
|
|
|
|
|
|
// Setup the start time
|
|
|
|
|
|
|
|
_matchStartTime = multiplayerManager.getTime();
|
|
|
|
_gameStartTime = multiplayerManager.getTime();
|
|
|
|
|
|
|
|
_played5MinWarning = false;
|
|
|
|
_played2MinWarning = false;
|
|
|
|
_played1MinWarning = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int MultiplayerModeBase::findPlayer( const Player *player )
|
|
|
|
{
|
2012-12-31 12:37:16 +00:00
|
|
|
for ( unsigned i = 0 ; i < _maxPlayers ; i++ )
|
2012-12-30 16:37:54 +00:00
|
|
|
{
|
|
|
|
if ( _playerGameData[ i ]._playing && _playerGameData[ i ]._entnum == player->entnum )
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int MultiplayerModeBase::getPoints( Player *player )
|
|
|
|
{
|
|
|
|
int index;
|
|
|
|
|
|
|
|
index = findPlayer( player );
|
|
|
|
|
|
|
|
if ( index >= 0 )
|
|
|
|
{
|
|
|
|
return _playerGameData[ index ]._points;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int MultiplayerModeBase::getKills( Player *player )
|
|
|
|
{
|
|
|
|
int index;
|
|
|
|
|
|
|
|
index = findPlayer( player );
|
|
|
|
|
|
|
|
if ( index >= 0 )
|
|
|
|
{
|
|
|
|
return _playerGameData[ index ]._numKills;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int MultiplayerModeBase::getDeaths( Player *player )
|
|
|
|
{
|
|
|
|
int index;
|
|
|
|
|
|
|
|
index = findPlayer( player );
|
|
|
|
|
|
|
|
if ( index >= 0 )
|
|
|
|
{
|
|
|
|
return _playerGameData[ index ]._numDeaths;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Team* MultiplayerModeBase::getPlayersTeam( const Player *player )
|
|
|
|
{
|
|
|
|
int index;
|
|
|
|
|
|
|
|
index = findPlayer( player );
|
|
|
|
|
|
|
|
if ( index >= 0 )
|
|
|
|
{
|
|
|
|
return _playerGameData[ index ]._currentTeam;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MultiplayerModeBase::update( float frameTime )
|
|
|
|
{
|
2012-12-31 15:19:55 +00:00
|
|
|
Q_UNUSED(frameTime);
|
|
|
|
|
2012-12-30 16:37:54 +00:00
|
|
|
if ( !_gameStarted )
|
|
|
|
{
|
|
|
|
if ( shouldStartMatch() )
|
|
|
|
{
|
|
|
|
multiplayerManager.startMatch();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tell players if timelimit is nearing
|
|
|
|
|
|
|
|
if ( getTimeLimit() > 0.0f )
|
|
|
|
{
|
|
|
|
float timeLimit;
|
|
|
|
float timeRemaining;
|
|
|
|
float fiveMinutes = 5 * 60;
|
|
|
|
float twoMinutes = 2 * 60;
|
|
|
|
float oneMinute = 1 * 60;
|
|
|
|
|
|
|
|
timeLimit = getTimeLimit();
|
|
|
|
timeRemaining = getTimeLimit() - ( multiplayerManager.getTime() - _gameStartTime );
|
|
|
|
|
|
|
|
if ( ( timeLimit > fiveMinutes ) && ( timeRemaining < fiveMinutes ) && ( !_played5MinWarning ) )
|
|
|
|
{
|
|
|
|
multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_5mins.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, NULL, 1.5f );
|
|
|
|
_played5MinWarning = true;
|
|
|
|
}
|
|
|
|
else if ( ( timeLimit > twoMinutes ) && ( timeRemaining < twoMinutes ) && ( !_played2MinWarning ) )
|
|
|
|
{
|
|
|
|
multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_2mins.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, NULL, 1.5f );
|
|
|
|
_played2MinWarning = true;
|
|
|
|
}
|
|
|
|
else if ( ( timeLimit > oneMinute ) && ( timeRemaining < oneMinute ) && ( !_played1MinWarning ) )
|
|
|
|
{
|
|
|
|
multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_1mins.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, NULL, 1.5f );
|
|
|
|
_played1MinWarning = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tell players if pointlimit is nearing
|
|
|
|
|
|
|
|
if ( ( getPointLimit() > 0 ) && ( !isEndOfMatch() ) )
|
|
|
|
{
|
|
|
|
int pointLimit;
|
|
|
|
int pointsRemaining;
|
|
|
|
int highestPoints;
|
|
|
|
int lastPointsRemaining;
|
|
|
|
|
|
|
|
highestPoints = getHighestPoints();
|
|
|
|
|
|
|
|
if ( highestPoints > _lastHighestPoints )
|
|
|
|
{
|
|
|
|
pointLimit = getPointLimit();
|
|
|
|
pointsRemaining = pointLimit - highestPoints;
|
|
|
|
lastPointsRemaining = pointLimit - _lastHighestPoints;
|
|
|
|
|
|
|
|
if ( ( pointLimit > 1 ) && ( pointsRemaining <= 1 ) && ( lastPointsRemaining > 1 ) )
|
|
|
|
multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_1pointsleft.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, NULL, 1.5f );
|
|
|
|
else if ( ( pointLimit > 2 ) && ( pointsRemaining <= 2 ) && ( lastPointsRemaining > 2 ) )
|
|
|
|
multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_2pointsleft.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, NULL, 1.5f );
|
|
|
|
else if ( ( pointLimit > 3 ) && ( pointsRemaining <= 3 ) && ( lastPointsRemaining > 3 ) )
|
|
|
|
multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_3pointsleft.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, NULL, 1.5f );
|
|
|
|
else if ( ( pointLimit > 4 ) && ( pointsRemaining <= 4 ) && ( lastPointsRemaining > 4 ) )
|
|
|
|
multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_4pointsleft.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, NULL, 1.5f );
|
|
|
|
else if ( ( pointLimit > 5 ) && ( pointsRemaining <= 5 ) && ( lastPointsRemaining > 5 ) )
|
|
|
|
multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_5pointsleft.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, NULL, 1.5f );
|
|
|
|
else if ( ( pointLimit > 10 ) && ( pointsRemaining <= 10 ) && ( lastPointsRemaining > 10 ) )
|
|
|
|
multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_10pointsleft.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, NULL, 1.5f );
|
|
|
|
else if ( ( pointLimit > 25 ) && ( pointsRemaining <= 25 ) && ( lastPointsRemaining > 25 ) )
|
|
|
|
multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_25pointsleft.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, NULL, 1.5f );
|
|
|
|
else if ( ( pointLimit > 100 ) && ( pointsRemaining <= 100 ) && ( lastPointsRemaining > 100 ) )
|
|
|
|
multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_100pointsleft.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, NULL, 1.5f );
|
|
|
|
else if ( ( pointLimit > 500 ) && ( pointsRemaining <= 500 ) && ( lastPointsRemaining > 500 ) )
|
|
|
|
multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_500pointsleft.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, NULL, 1.5f );
|
|
|
|
}
|
|
|
|
|
|
|
|
_lastHighestPoints = highestPoints;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//================================================================
|
|
|
|
// Name: isEndOfMatch
|
|
|
|
// Class: MultiplayerModeBase
|
|
|
|
//
|
|
|
|
// Description: Checks to see if the end of match conditions have been
|
|
|
|
// met. Only condition in the base class is a single
|
|
|
|
// player exceeding the frag limit (if set).
|
|
|
|
//
|
|
|
|
// Parameters: None
|
|
|
|
//
|
|
|
|
// Returns: bool -- true if the match should now end
|
|
|
|
//
|
|
|
|
//================================================================
|
|
|
|
bool MultiplayerModeBase::isEndOfMatch( void )
|
|
|
|
{
|
|
|
|
// See if we have a gone over the point limit
|
|
|
|
|
|
|
|
if ( getPointLimit() > 0 )
|
|
|
|
{
|
2012-12-31 12:37:16 +00:00
|
|
|
for ( unsigned i = 0 ; i < _maxPlayers ; i++ )
|
2012-12-30 16:37:54 +00:00
|
|
|
{
|
|
|
|
if ( _playerGameData[ i ]._playing )
|
|
|
|
{
|
|
|
|
if ( _playerGameData[ i ]._points >= getPointLimit() )
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// See if we have a gone over the time limit
|
|
|
|
|
|
|
|
if ( ( getTimeLimit() > 0.0f ) && ( multiplayerManager.getTime() - _gameStartTime > getTimeLimit() ) )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MultiplayerModeBase::playerKilled( Player *killedPlayer, Player *attackingPlayer, Entity *inflictor, int meansOfDeath )
|
|
|
|
{
|
|
|
|
bool goodKill;
|
|
|
|
|
|
|
|
if ( attackingPlayer && ( killedPlayer != attackingPlayer ) )
|
|
|
|
goodKill = true;
|
|
|
|
else
|
|
|
|
goodKill = false;
|
|
|
|
|
|
|
|
handleKill( killedPlayer, attackingPlayer, inflictor, meansOfDeath, goodKill );
|
|
|
|
}
|
|
|
|
|
|
|
|
void MultiplayerModeBase::handleKill( Player *killedPlayer, Player *attackingPlayer, Entity *inflictor, int meansOfDeath, bool goodKill )
|
|
|
|
{
|
|
|
|
// Record the death
|
|
|
|
|
|
|
|
_playerGameData[ killedPlayer->entnum ]._numDeaths++;
|
|
|
|
_playerGameData[ killedPlayer->entnum ]._lastKillerOfPlayerMOD = meansOfDeath;
|
|
|
|
|
|
|
|
// Modify the points and kills
|
|
|
|
|
|
|
|
if ( goodKill )
|
|
|
|
{
|
|
|
|
// Player killed someone, so increment kills and points
|
|
|
|
|
|
|
|
_playerGameData[ attackingPlayer->entnum ]._numKills++;
|
|
|
|
|
|
|
|
addPoints( attackingPlayer->entnum, multiplayerManager.getPointsForKill( killedPlayer, attackingPlayer,
|
|
|
|
inflictor, meansOfDeath, _defaultPointsPerKill ) );
|
|
|
|
//_playerGameData[ attackingPlayer->entnum ]._points += multiplayerManager.getPointsForKill( killedPlayer,
|
|
|
|
// attackingPlayer, inflictor, meansOfDeath, _defaultPointsPerKill );
|
|
|
|
|
|
|
|
_playerGameData[ killedPlayer->entnum ]._lastKillerOfPlayer = attackingPlayer->entnum;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Player killed himself or a teammate, so decrement kills and points
|
|
|
|
|
|
|
|
if ( attackingPlayer )
|
|
|
|
{
|
|
|
|
//_playerGameData[ attackingPlayer->entnum ]._numKills--;
|
|
|
|
|
|
|
|
addPoints( attackingPlayer->entnum, -_defaultPointsPerTakenAwayForSuicide );
|
|
|
|
//_playerGameData[ attackingPlayer->entnum ]._points -= _defaultPointsPerTakenAwayForSuicide;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//_playerGameData[ killedPlayer->entnum ]._numKills--;
|
|
|
|
|
|
|
|
addPoints( killedPlayer->entnum, -_defaultPointsPerTakenAwayForSuicide );
|
|
|
|
//_playerGameData[ killedPlayer->entnum ]._points -= _defaultPointsPerTakenAwayForSuicide;
|
|
|
|
}
|
|
|
|
|
|
|
|
_playerGameData[ killedPlayer->entnum ]._lastKillerOfPlayer = killedPlayer->entnum;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Print out an obituary
|
|
|
|
|
|
|
|
obituary( killedPlayer, attackingPlayer, meansOfDeath );
|
|
|
|
|
|
|
|
// Save off some important info
|
|
|
|
|
|
|
|
if ( attackingPlayer )
|
|
|
|
{
|
|
|
|
_playerGameData[ attackingPlayer->entnum ]._lastKilledByPlayer = killedPlayer->entnum;
|
|
|
|
_playerGameData[ attackingPlayer->entnum ]._lastKilledByPlayerMOD = meansOfDeath;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MultiplayerModeBase::addPoints( int entnum, int points )
|
|
|
|
{
|
|
|
|
_playerGameData[ entnum ]._points += points;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MultiplayerModeBase::obituary( Player *killedPlayer, Player *attackingPlayer, int meansOfDeath )
|
|
|
|
{
|
|
|
|
const char *s1=NULL, *s2=NULL;
|
|
|
|
str printString;
|
|
|
|
bool suicide;
|
|
|
|
bool printSomething;
|
|
|
|
char color;
|
|
|
|
bool sameTeam;
|
|
|
|
|
|
|
|
// Client killed himself
|
|
|
|
|
|
|
|
suicide = false;
|
|
|
|
printSomething = false;
|
|
|
|
|
|
|
|
sameTeam = false;
|
|
|
|
|
|
|
|
if ( attackingPlayer && ( killedPlayer != attackingPlayer ) )
|
|
|
|
{
|
|
|
|
Team *killedPlayersTeam;
|
|
|
|
Team *attackingPlayersTeam;
|
|
|
|
|
|
|
|
killedPlayersTeam = multiplayerManager.getPlayersTeam( killedPlayer );
|
|
|
|
|
|
|
|
attackingPlayersTeam = multiplayerManager.getPlayersTeam( attackingPlayer );
|
|
|
|
|
|
|
|
if ( killedPlayersTeam && attackingPlayersTeam && ( killedPlayersTeam == attackingPlayersTeam ) )
|
|
|
|
{
|
|
|
|
sameTeam = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( killedPlayer == attackingPlayer || !attackingPlayer )
|
|
|
|
{
|
|
|
|
suicide = true;
|
|
|
|
printSomething = true;
|
|
|
|
|
|
|
|
switch( meansOfDeath )
|
|
|
|
{
|
|
|
|
case MOD_SUICIDE:
|
|
|
|
s1 = "$$MOD_SUICIDE$$";
|
|
|
|
break;
|
|
|
|
case MOD_DROWN:
|
|
|
|
s1 = "$$MOD_DROWN$$";
|
|
|
|
break;
|
|
|
|
case MOD_LAVA:
|
|
|
|
s1 = "$$MOD_LAVA$$";
|
|
|
|
break;
|
|
|
|
case MOD_SLIME:
|
|
|
|
s1 = "$$MOD_SLIME$$";
|
|
|
|
break;
|
|
|
|
case MOD_FALLING:
|
|
|
|
s1 = "$$MOD_FALLING$$";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
s1 = "$$MOD_SUICIDE$$";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Killed by another player
|
|
|
|
|
|
|
|
if ( attackingPlayer && attackingPlayer->isClient() && ( killedPlayer != attackingPlayer ) )
|
|
|
|
{
|
|
|
|
printSomething = true;
|
|
|
|
|
|
|
|
switch( meansOfDeath )
|
|
|
|
{
|
|
|
|
case MOD_CRUSH:
|
|
|
|
case MOD_CRUSH_EVERY_FRAME:
|
|
|
|
s1 = "$$MOD_CRUSH$$";
|
|
|
|
break;
|
|
|
|
case MOD_TELEFRAG:
|
|
|
|
s1 = "$$MOD_TELEFRAG$$";
|
|
|
|
break;
|
|
|
|
case MOD_EXPLODEWALL:
|
|
|
|
case MOD_EXPLOSION:
|
|
|
|
case MOD_POO_EXPLOSION:
|
|
|
|
s1 = "$$MOD_EXPLOSION$$";
|
|
|
|
break;
|
|
|
|
case MOD_ELECTRICWATER:
|
|
|
|
case MOD_ELECTRIC:
|
|
|
|
case MOD_CIRCLEOFPROTECTION:
|
|
|
|
s1 = "$$MOD_ELECTRIC$$";
|
|
|
|
break;
|
|
|
|
case MOD_IMPACT:
|
|
|
|
case MOD_THROWNOBJECT:
|
|
|
|
s1 = "$$MOD_IMPACT$$";
|
|
|
|
s2 = "$$MOD_IMPACT2$$";
|
|
|
|
break;
|
|
|
|
case MOD_BEAM:
|
|
|
|
s1 = "$$MOD_BEAM$$";
|
|
|
|
break;
|
|
|
|
case MOD_ROCKET:
|
|
|
|
s1 = "$$MOD_ROCKET$$";
|
|
|
|
s2 = "$$MOD_ROCKET2$$";
|
|
|
|
break;
|
|
|
|
case MOD_GAS_BLOCKABLE:
|
|
|
|
case MOD_GAS:
|
|
|
|
s1 = "$$MOD_GAS$$";
|
|
|
|
break;
|
|
|
|
case MOD_ACID:
|
|
|
|
s1 = "$$MOD_ACID$$";
|
|
|
|
break;
|
|
|
|
case MOD_SWORD:
|
|
|
|
s1 = "$$MOD_SWORD$$";
|
|
|
|
break;
|
|
|
|
case MOD_PLASMA:
|
|
|
|
case MOD_PLASMABEAM:
|
|
|
|
case MOD_PLASMASHOTGUN:
|
|
|
|
s1 = "$$MOD_ASSULT_RIFLE$$";
|
|
|
|
break;
|
|
|
|
case MOD_RADIATION:
|
|
|
|
s1 = "$$MOD_PLASMA$$";
|
|
|
|
break;
|
|
|
|
case MOD_STING:
|
|
|
|
case MOD_STING2:
|
|
|
|
s1 = "$$MOD_STING$$";
|
|
|
|
break;
|
|
|
|
case MOD_BULLET:
|
|
|
|
case MOD_FAST_BULLET:
|
|
|
|
s1 = "$$MOD_BULLET$$";
|
|
|
|
break;
|
|
|
|
case MOD_VEHICLE:
|
|
|
|
s1 = "$$MOD_VEHICLE$$";
|
|
|
|
break;
|
|
|
|
case MOD_FIRE:
|
|
|
|
case MOD_FIRE_BLOCKABLE:
|
|
|
|
case MOD_ON_FIRE:
|
|
|
|
s1 = "$$MOD_FIRE$$";
|
|
|
|
break;
|
|
|
|
case MOD_LIFEDRAIN:
|
|
|
|
s1 = "$$MOD_LIFEDRAIN$$";
|
|
|
|
break;
|
|
|
|
case MOD_FLASHBANG:
|
|
|
|
s1 = "$$MOD_FLASHBANG$$";
|
|
|
|
break;
|
|
|
|
case MOD_AXE:
|
|
|
|
s1 = "$$MOD_AXE$$";
|
|
|
|
s2 = "$$MOD_AXE2$$";
|
|
|
|
break;
|
|
|
|
case MOD_CHAINSWORD:
|
|
|
|
s1 = "$$MOD_CHAINSWORD$$";
|
|
|
|
break;
|
|
|
|
case MOD_FIRESWORD:
|
|
|
|
s1 = "$$MOD_FIRESWORD$$";
|
|
|
|
break;
|
|
|
|
case MOD_ELECTRICSWORD:
|
|
|
|
s1 = "$$MOD_ELECTRICSWORD$$";
|
|
|
|
s2 = "$$MOD_ELECTRICSWORD2$$";
|
|
|
|
break;
|
|
|
|
case MOD_LIGHTSWORD:
|
|
|
|
s1 = "$$MOD_LIGHTSWORD$$";
|
|
|
|
s2 = "$$MOD_LIGHTSWORD2$$";
|
|
|
|
break;
|
|
|
|
case MOD_IMPALE:
|
|
|
|
s1 = "$$MOD_IMPALE$$";
|
|
|
|
break;
|
|
|
|
case MOD_UPPERCUT:
|
|
|
|
s1 = "$$MOD_UPPERCUT$$";
|
|
|
|
break;
|
|
|
|
case MOD_POISON:
|
|
|
|
s1 = "$$MOD_POISON$$";
|
|
|
|
break;
|
|
|
|
case MOD_PHASER:
|
|
|
|
s1 = "$$MOD_PHASER$$";
|
|
|
|
break;
|
|
|
|
case MOD_COMP_RIFLE:
|
|
|
|
s1 = "$$MOD_COMP_RIFLE$$";
|
|
|
|
break;
|
|
|
|
//case MOD_ASSULT_RIFLE:
|
|
|
|
|
|
|
|
//case MOD_IMOD:
|
|
|
|
// s1 = "$$MOD_IMOD$$";
|
|
|
|
// break;
|
|
|
|
|
|
|
|
case MOD_VAPORIZE:
|
|
|
|
case MOD_VAPORIZE_COMP:
|
|
|
|
case MOD_VAPORIZE_DISRUPTOR:
|
|
|
|
case MOD_VAPORIZE_PHOTON:
|
|
|
|
s1 = "$$MOD_VAPORIZE$$";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
s1 = "$$MOD_DEFAULT$$";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( printSomething )
|
|
|
|
{
|
|
|
|
Player *currentPlayer;
|
|
|
|
|
|
|
|
// Print to the dedicated console
|
|
|
|
|
|
|
|
if ( dedicated->integer )
|
|
|
|
{
|
|
|
|
if ( suicide )
|
|
|
|
{
|
|
|
|
printString = va( "%s %s", killedPlayer->client->pers.netname, s1 );
|
|
|
|
}
|
|
|
|
else if ( s2 )
|
|
|
|
{
|
|
|
|
printString = va( "%s %s %s %s", killedPlayer->client->pers.netname, s1, attackingPlayer->client->pers.netname, s2 );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
printString = va( "%s %s %s", killedPlayer->client->pers.netname, s1, attackingPlayer->client->pers.netname );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( sameTeam )
|
|
|
|
{
|
|
|
|
printString += " ($$SameTeam$$)";
|
|
|
|
}
|
|
|
|
|
|
|
|
printString += "\n";
|
|
|
|
|
|
|
|
gi.Printf( printString.c_str() );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Print to all of the players
|
|
|
|
|
2012-12-31 12:37:16 +00:00
|
|
|
for ( unsigned i = 0 ; i < _maxPlayers ; i++ )
|
2012-12-30 16:37:54 +00:00
|
|
|
{
|
|
|
|
currentPlayer = multiplayerManager.getPlayer( i );
|
|
|
|
|
|
|
|
if ( !currentPlayer )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Figure out which color to use
|
|
|
|
|
|
|
|
if ( killedPlayer && killedPlayer->entnum == i )
|
|
|
|
color = COLOR_RED;
|
|
|
|
else if ( attackingPlayer && attackingPlayer->entnum == i )
|
|
|
|
color = COLOR_GREEN;
|
|
|
|
else
|
|
|
|
color = COLOR_NONE;
|
|
|
|
|
|
|
|
// Build the death string
|
|
|
|
|
|
|
|
if ( suicide )
|
|
|
|
{
|
|
|
|
printString = va( "%s ^%c%s^8", killedPlayer->client->pers.netname, color, s1 );
|
|
|
|
}
|
|
|
|
else if ( s2 )
|
|
|
|
{
|
|
|
|
printString = va( "%s ^%c%s^8 %s ^%c%s^8", killedPlayer->client->pers.netname, color, s1, attackingPlayer->client->pers.netname, color, s2 );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
printString = va( "%s ^%c%s^8 %s", killedPlayer->client->pers.netname, color, s1, attackingPlayer->client->pers.netname );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( sameTeam )
|
|
|
|
{
|
|
|
|
printString += " (^";
|
|
|
|
printString += COLOR_RED;
|
|
|
|
printString += "$$SameTeam$$^8)";
|
|
|
|
}
|
|
|
|
|
|
|
|
printString += "\n";
|
|
|
|
|
|
|
|
// Print out the death string
|
|
|
|
|
|
|
|
if ( gi.GetNumFreeReliableServerCommands( currentPlayer->edict - g_entities ) > 32 )
|
|
|
|
{
|
|
|
|
multiplayerManager.HUDPrint( currentPlayer->entnum, printString.c_str() );
|
|
|
|
}
|
|
|
|
|
|
|
|
//multiplayerManager.HUDPrintAllClients( printString.c_str() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MultiplayerModeBase::needToAddPlayer( Player *player )
|
|
|
|
{
|
|
|
|
MultiplayerPlayerGameData *playerGameData;
|
|
|
|
|
|
|
|
playerGameData = &_playerGameData[ player->entnum ];
|
|
|
|
|
|
|
|
if ( playerGameData->_playing )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//================================================================
|
|
|
|
// Name: AddPlayer
|
|
|
|
// Class: MultiplayerModeBase
|
|
|
|
//
|
|
|
|
// Description: Adds the specified player to the list of players
|
|
|
|
//
|
|
|
|
// Parameters: Player* -- player to add
|
|
|
|
//
|
|
|
|
// Returns: None
|
|
|
|
//
|
|
|
|
//================================================================
|
|
|
|
void MultiplayerModeBase::AddPlayer( Player *player )
|
|
|
|
{
|
|
|
|
MultiplayerPlayerGameData *playerGameData;
|
|
|
|
|
|
|
|
// Make sure player is not already added
|
|
|
|
|
|
|
|
if ( !needToAddPlayer( player ) )
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Setup the player's data
|
|
|
|
|
|
|
|
playerGameData = &_playerGameData[ player->entnum ];
|
|
|
|
|
|
|
|
playerGameData->reset();
|
|
|
|
playerGameData->_entnum = player->entnum;
|
|
|
|
playerGameData->_playing = true;
|
|
|
|
playerGameData->_startTime = multiplayerManager.getTime();
|
|
|
|
|
|
|
|
// Setup the correct ui on the client
|
|
|
|
|
|
|
|
setupMultiplayerUI( player );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MultiplayerModeBase::canJoinTeam( Player *player, const str &teamName )
|
|
|
|
{
|
|
|
|
if ( teamName == "normal" && multiplayerManager.isPlayerSpectator( player ) )
|
|
|
|
return true;
|
|
|
|
//else if ( teamName == "spectator" && !multiplayerManager.isPlayerSpectator( player ) )
|
|
|
|
else if ( teamName == "spectator" )
|
|
|
|
return true;
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MultiplayerModeBase::joinTeam( Player *player, const str &teamName )
|
|
|
|
{
|
|
|
|
if ( teamName == "normal" )
|
|
|
|
{
|
|
|
|
multiplayerManager.setTeamHud( player, "" );
|
|
|
|
RemovePlayer( player );
|
|
|
|
AddPlayer( player );
|
|
|
|
}
|
|
|
|
else if ( teamName == "spectator" )
|
|
|
|
{
|
|
|
|
multiplayerManager.makePlayerSpectator( player, SPECTATOR_TYPE_FOLLOW, true );
|
|
|
|
//multiplayerManager.setTeamHud( player, "mp_teamspec" );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MultiplayerModeBase::setupMultiplayerUI( Player *player )
|
|
|
|
{
|
|
|
|
gi.SendServerCommand( player->entnum, "stufftext \"ui_removehuds all\"\n" );
|
|
|
|
gi.SendServerCommand( player->entnum, "stufftext \"ui_addhud mp_console\"\n" );
|
|
|
|
|
|
|
|
|
|
|
|
if(mp_timelimit->integer)
|
|
|
|
{
|
|
|
|
gi.SendServerCommand( player->entnum, "stufftext \"globalwidgetcommand dmTimer enable\"\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gi.SendServerCommand( player->entnum, "stufftext \"globalwidgetcommand dmTimer disable\"\n");
|
|
|
|
}
|
|
|
|
//gi.SendServerCommand( player->entnum, "stufftext \"ui_addhud mp_dmhud\"\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
//================================================================
|
|
|
|
// Name: RemovePlayer
|
|
|
|
// Class: MultiplayerModeBase
|
|
|
|
//
|
|
|
|
// Description: Removes a player from this arena.
|
|
|
|
//
|
|
|
|
// Parameters: Player* -- player to remove
|
|
|
|
//
|
|
|
|
// Returns: None
|
|
|
|
//
|
|
|
|
//================================================================
|
|
|
|
void MultiplayerModeBase::RemovePlayer( Player *player )
|
|
|
|
{
|
|
|
|
_playerGameData[ player->entnum ]._playing = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
float MultiplayerModeBase::playerDamaged( Player *damagedPlayer, Player *attackingPlayer, float damage, int meansOfDeath )
|
|
|
|
{
|
2012-12-31 15:19:55 +00:00
|
|
|
Q_UNUSED(meansOfDeath);
|
|
|
|
Q_UNUSED(attackingPlayer);
|
|
|
|
Q_UNUSED(damagedPlayer);
|
|
|
|
|
2012-12-30 16:37:54 +00:00
|
|
|
return damage;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MultiplayerModeBase::playerTookDamage( Player *damagedPlayer, Player *attackingPlayer, float damage, int meansOfDeath )
|
|
|
|
{
|
2012-12-31 15:19:55 +00:00
|
|
|
Q_UNUSED(meansOfDeath);
|
|
|
|
Q_UNUSED(damage);
|
|
|
|
|
2012-12-30 16:37:54 +00:00
|
|
|
if ( attackingPlayer && ( attackingPlayer != damagedPlayer ) )
|
|
|
|
{
|
|
|
|
// Play the hurt someone sound
|
|
|
|
|
|
|
|
if ( multiplayerManager.getTime() >= _playerGameData[ attackingPlayer->entnum ]._nextHitSoundTime && damagedPlayer->health > 0.0f )
|
|
|
|
{
|
|
|
|
multiplayerManager.instantPlayerSound( attackingPlayer->entnum, "snd_mp_hurtsomeone", CHAN_COMBAT4 );
|
|
|
|
|
|
|
|
_playerGameData[ attackingPlayer->entnum ]._nextHitSoundTime = level.time + 0.25;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MultiplayerModeBase::readMultiplayerConfig( const char *configName )
|
|
|
|
{
|
|
|
|
Script buffer;
|
|
|
|
const char *token;
|
|
|
|
|
|
|
|
// Make sure thee file exists
|
|
|
|
|
|
|
|
if ( !gi.FS_Exists( configName ) )
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Load the file
|
|
|
|
|
|
|
|
buffer.LoadFile( configName );
|
|
|
|
|
|
|
|
// Parse the file
|
|
|
|
|
|
|
|
while ( buffer.TokenAvailable( true ) )
|
|
|
|
{
|
|
|
|
token = buffer.GetToken( true );
|
|
|
|
|
|
|
|
// Parse the current token (this should be parsed by all child classes also)
|
|
|
|
|
|
|
|
if ( !parseConfigToken( token, &buffer ) )
|
|
|
|
{
|
|
|
|
gi.DPrintf( "Token %s from %s not handled by anyone\n", token, configName );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MultiplayerModeBase::parseConfigToken( const char *key, Script *buffer )
|
|
|
|
{
|
|
|
|
const char *token;
|
|
|
|
|
|
|
|
if ( stricmp( key, "giveWeapon" ) == 0 )
|
|
|
|
{
|
|
|
|
if ( buffer->TokenAvailable( false ) )
|
|
|
|
{
|
|
|
|
token = buffer->GetToken( false );
|
|
|
|
|
|
|
|
AddStartingWeapon( token );
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ( stricmp( key, "startingWeapon" ) == 0 )
|
|
|
|
{
|
|
|
|
if ( buffer->TokenAvailable( false ) )
|
|
|
|
{
|
|
|
|
token = buffer->GetToken( false );
|
|
|
|
|
|
|
|
SetStartingWeapon( token );
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int MultiplayerModeBase::getIcon( Player *player, int statNum, int value )
|
|
|
|
{
|
2012-12-31 15:19:55 +00:00
|
|
|
Q_UNUSED(statNum);
|
|
|
|
Q_UNUSED(player);
|
|
|
|
|
2012-12-30 16:37:54 +00:00
|
|
|
/* if ( statNum == STAT_MP_TEAMHUD_ICON && multiplayerManager.isPlayerSpectator( player ) )
|
|
|
|
return _spectatorIconIndex;
|
|
|
|
else */
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MultiplayerModeBase::shouldStartMatch( void )
|
|
|
|
{
|
|
|
|
int timeRemaining;
|
|
|
|
int numPlayers;
|
|
|
|
|
|
|
|
if ( _gameStarted )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if ( isEndOfMatch() )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Print time remaining (if changed)
|
|
|
|
|
|
|
|
timeRemaining = (int)(_matchStartTime + mp_warmUpTime->value - multiplayerManager.getTime() + 1.0f);
|
|
|
|
|
|
|
|
if ( ( timeRemaining > 0 ) && ( timeRemaining < 6 ) && ( timeRemaining != _lastTimeRemaining ) )
|
|
|
|
{
|
|
|
|
_lastTimeRemaining = timeRemaining;
|
|
|
|
|
|
|
|
multiplayerManager.centerPrintAllClients( va( "%d", _lastTimeRemaining ), CENTERPRINT_IMPORTANCE_NORMAL );
|
|
|
|
|
|
|
|
switch( timeRemaining )
|
|
|
|
{
|
|
|
|
case 1 :
|
|
|
|
multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_1.mp3", CHAN_AUTO, DEFAULT_VOL,
|
|
|
|
DEFAULT_MIN_DIST, NULL, 0.5f );
|
|
|
|
break;
|
|
|
|
case 2 :
|
|
|
|
multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_2.mp3", CHAN_AUTO, DEFAULT_VOL,
|
|
|
|
DEFAULT_MIN_DIST, NULL, 0.5f );
|
|
|
|
break;
|
|
|
|
case 3 :
|
|
|
|
multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_3.mp3", CHAN_AUTO, DEFAULT_VOL,
|
|
|
|
DEFAULT_MIN_DIST, NULL, 0.5f );
|
|
|
|
break;
|
|
|
|
case 4 :
|
|
|
|
multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_4.mp3", CHAN_AUTO, DEFAULT_VOL,
|
|
|
|
DEFAULT_MIN_DIST, NULL, 0.5f );
|
|
|
|
break;
|
|
|
|
case 5 :
|
|
|
|
multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_5.mp3", CHAN_AUTO, DEFAULT_VOL,
|
|
|
|
DEFAULT_MIN_DIST, NULL, 0.5f );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure we have done our warm up already
|
|
|
|
|
|
|
|
if ( multiplayerManager.getTime() < _matchStartTime + mp_warmUpTime->value )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Make sure we have enough players
|
|
|
|
|
|
|
|
numPlayers = 0;
|
|
|
|
|
2012-12-31 12:37:16 +00:00
|
|
|
for ( unsigned i = 0 ; i < _maxPlayers ; i++ )
|
2012-12-30 16:37:54 +00:00
|
|
|
{
|
|
|
|
if ( _playerGameData[ i ]._playing )
|
|
|
|
{
|
|
|
|
numPlayers++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( numPlayers < mp_minPlayers->integer )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int MultiplayerModeBase::getStat( Player *player, int statNum, int value )
|
|
|
|
{
|
2012-12-31 15:19:55 +00:00
|
|
|
Q_UNUSED(player);
|
|
|
|
|
2012-12-30 16:37:54 +00:00
|
|
|
if ( statNum == STAT_MP_STATE )
|
|
|
|
{
|
|
|
|
int numPlayers;
|
|
|
|
Player *player;
|
|
|
|
|
|
|
|
if ( _gameStarted )
|
|
|
|
{
|
|
|
|
if ( !value )
|
|
|
|
return _playingTextIndex;
|
|
|
|
else
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( multiplayerManager.getTime() < _matchStartTime + mp_warmUpTime->value )
|
|
|
|
return _warmUpTextIndex;
|
|
|
|
|
|
|
|
// Make sure we have enough players
|
|
|
|
|
|
|
|
numPlayers = 0;
|
|
|
|
|
2012-12-31 12:37:16 +00:00
|
|
|
for ( unsigned i = 0 ; i < _maxPlayers ; i++ )
|
2012-12-30 16:37:54 +00:00
|
|
|
{
|
|
|
|
player = multiplayerManager.getPlayer( i );
|
|
|
|
|
|
|
|
if ( player && !multiplayerManager.isPlayerSpectatorByChoice( player ) )
|
|
|
|
{
|
|
|
|
numPlayers++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( numPlayers < mp_minPlayers->integer )
|
|
|
|
return _waitingForMinPlayersTextIndex;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MultiplayerModeBase::startMatch( void )
|
|
|
|
{
|
|
|
|
Player *player;
|
|
|
|
|
|
|
|
_gameStarted = true;
|
|
|
|
multiplayerManager.allowFighting( true );
|
|
|
|
|
|
|
|
// Make everyone not a spectator and spawn them into the world
|
|
|
|
|
2012-12-31 12:37:16 +00:00
|
|
|
for ( unsigned i = 0 ; i < _maxPlayers ; i++ )
|
2012-12-30 16:37:54 +00:00
|
|
|
{
|
|
|
|
player = getPlayer( i );
|
|
|
|
|
|
|
|
if ( player && ( ( player->edict->svflags & SVF_BOT ) || !multiplayerManager.isPlayerSpectatorByChoice( player ) ) )
|
|
|
|
{
|
|
|
|
multiplayerManager.playerEnterArena( player->entnum, player->health );
|
|
|
|
|
|
|
|
respawnPlayer( player );
|
|
|
|
|
|
|
|
multiplayerManager.playerSpawned( player );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tell everyone the match started
|
|
|
|
|
|
|
|
multiplayerManager.centerPrintAllClients( "$$MatchStarted$$", CENTERPRINT_IMPORTANCE_HIGH );
|
|
|
|
multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_mats.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, NULL, 1.5f );
|
|
|
|
}
|
|
|
|
|
|
|
|
void MultiplayerModeBase::restartMatch( void )
|
|
|
|
{
|
|
|
|
_gameStarted = false;
|
|
|
|
|
|
|
|
_matchStartTime = multiplayerManager.getTime();
|
|
|
|
_lastTimeRemaining = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MultiplayerModeBase::endMatch( void )
|
|
|
|
{
|
|
|
|
Player *player;
|
|
|
|
|
|
|
|
_gameStarted = false;
|
|
|
|
|
|
|
|
// Make everyone a spectator
|
|
|
|
|
2012-12-31 12:37:16 +00:00
|
|
|
for ( unsigned i = 0 ; i < _maxPlayers ; i++ )
|
2012-12-30 16:37:54 +00:00
|
|
|
{
|
|
|
|
if ( _playerGameData[ i ]._playing )
|
|
|
|
{
|
|
|
|
player = getPlayer( i );
|
|
|
|
|
|
|
|
if ( player )
|
|
|
|
{
|
|
|
|
multiplayerManager.makePlayerSpectator( player );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MultiplayerModeBase::inMatch( void )
|
|
|
|
{
|
|
|
|
return _gameStarted;
|
|
|
|
}
|
|
|
|
|
2012-12-31 12:37:16 +00:00
|
|
|
Player *MultiplayerModeBase::getPlayer( unsigned entnum )
|
2012-12-30 16:37:54 +00:00
|
|
|
{
|
|
|
|
// Make sure everything is ok
|
|
|
|
|
|
|
|
if ( ( entnum < 0 ) || ( entnum >= _maxPlayers ) )
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if ( !g_entities[ entnum ].inuse || !g_entities[ entnum ].entity )
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if ( !g_entities[ entnum ].entity->isSubclassOf( Player ) )
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
// Return the referenced player
|
|
|
|
|
|
|
|
return (Player *)g_entities[ entnum ].entity;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//================================================================
|
|
|
|
// Name: score
|
|
|
|
// Class: MultiplayerModeBase
|
|
|
|
//
|
|
|
|
// Description: Sends the current score to the specified player.
|
|
|
|
//
|
|
|
|
// Parameters: Player* -- player to send score to.
|
|
|
|
//
|
|
|
|
// Returns: None
|
|
|
|
//
|
|
|
|
//================================================================
|
|
|
|
void MultiplayerModeBase::score( const Player *player )
|
|
|
|
{
|
|
|
|
char string[1400];
|
|
|
|
char entry[1024];
|
|
|
|
int tempStringlength;
|
|
|
|
int count = 0;
|
|
|
|
int stringlength = 0;
|
|
|
|
Player *currentPlayer;
|
|
|
|
int spectator;
|
|
|
|
|
|
|
|
assert( player );
|
|
|
|
if ( !player )
|
|
|
|
{
|
|
|
|
warning( "MultiplayerModeBase::score", "Null Player specified.\n" );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
string[0] = 0;
|
|
|
|
entry[0] = 0;
|
|
|
|
|
|
|
|
// This for loop builds a string containing all the players scores.
|
|
|
|
|
2012-12-31 12:37:16 +00:00
|
|
|
for ( unsigned i = 0 ; i < _maxPlayers ; i++ )
|
2012-12-30 16:37:54 +00:00
|
|
|
{
|
|
|
|
currentPlayer = multiplayerManager.getPlayer( i );
|
|
|
|
|
|
|
|
if ( !currentPlayer )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if ( multiplayerManager.isPlayerSpectator( currentPlayer ) && multiplayerManager.isPlayerSpectatorByChoice( currentPlayer ) )
|
|
|
|
spectator = true;
|
|
|
|
else
|
|
|
|
spectator = false;
|
|
|
|
|
|
|
|
Com_sprintf( entry, sizeof( entry ), "%i %i %i %i %i %i %i %d %d %d %d %d %d ",
|
|
|
|
multiplayerManager.getClientNum( _playerGameData[ i ]._entnum ),
|
|
|
|
_playerGameData[ i ]._points,
|
|
|
|
_playerGameData[ i ]._numKills,
|
|
|
|
_playerGameData[ i ]._numDeaths,
|
|
|
|
spectator,
|
|
|
|
//0 /*pl->GetMatchesWon() */,
|
|
|
|
//0 /*pl->GetMatchesLost()*/,
|
|
|
|
(int)(multiplayerManager.getTime() - _playerGameData[ i ]._startTime ),
|
|
|
|
multiplayerManager.getClientPing( _playerGameData[ i ]._entnum ),
|
|
|
|
multiplayerManager.getScoreIcon( currentPlayer, SCOREICON1 ),
|
|
|
|
multiplayerManager.getScoreIcon( currentPlayer, SCOREICON2 ),
|
|
|
|
multiplayerManager.getScoreIcon( currentPlayer, SCOREICON3 ),
|
|
|
|
multiplayerManager.getScoreIcon( currentPlayer, SCOREICON4 ),
|
|
|
|
multiplayerManager.getScoreIcon( currentPlayer, SCOREICON5 ),
|
|
|
|
multiplayerManager.getScoreIcon( currentPlayer, SCOREICON6 ) );
|
|
|
|
|
|
|
|
tempStringlength = strlen( entry );
|
|
|
|
|
|
|
|
// Make sure the string is not too big (take into account other stuff that gets prepended below also)
|
|
|
|
|
|
|
|
if ( stringlength + tempStringlength > 1000 )
|
|
|
|
break;
|
|
|
|
|
|
|
|
strcpy( string + stringlength, entry );
|
|
|
|
|
|
|
|
stringlength += tempStringlength;
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
gi.SendServerCommand( player->edict-g_entities, "scores 0 %i %s", count, string );
|
|
|
|
}
|
|
|
|
|
|
|
|
//================================================================
|
|
|
|
// Name: playerDead
|
|
|
|
// Class: MultiplayerModeBase
|
|
|
|
//
|
|
|
|
// Description: Do appropriate thing when player has been killed.
|
|
|
|
// Base class puts up a dead body and hides the player
|
|
|
|
// model.
|
|
|
|
//
|
|
|
|
// Parameters: Player* -- player that was killed.
|
|
|
|
//
|
|
|
|
// Returns: None
|
|
|
|
//
|
|
|
|
//================================================================
|
|
|
|
void MultiplayerModeBase::playerDead( Player *player )
|
|
|
|
{
|
|
|
|
assert(player);
|
|
|
|
|
|
|
|
if (!player)
|
|
|
|
{
|
|
|
|
warning("MultiplayerModeBase::playerDead", "NULL Player\n");
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
|
|
|
|
player->ProcessEvent( EV_Player_DeadBody );
|
|
|
|
player->hideModel();
|
|
|
|
}
|
|
|
|
|
|
|
|
//================================================================
|
|
|
|
// Name: SetStartingWeapon
|
|
|
|
// Class: MultiplayerModeBase
|
|
|
|
//
|
|
|
|
// Description: Sets the name of the weapon the players start with out
|
|
|
|
//
|
|
|
|
// Parameters: const str& -- weaponName
|
|
|
|
//
|
|
|
|
// Returns: None
|
|
|
|
//
|
|
|
|
//================================================================
|
|
|
|
void MultiplayerModeBase::SetStartingWeapon( const str &weaponName )
|
|
|
|
{
|
|
|
|
_startingWeaponName = weaponName;
|
|
|
|
}
|
|
|
|
|
|
|
|
//================================================================
|
|
|
|
// Name: AddStartingWeapon
|
|
|
|
// Class: MultiplayerModeBase
|
|
|
|
//
|
|
|
|
// Description: Adds the specified weapon to the list of weapons
|
|
|
|
// the player starts with. The string must be a .tik
|
|
|
|
// file.
|
|
|
|
//
|
|
|
|
// Parameters: const str& -- viewmodel name (eg. viewmodel_peacemaker.tik);
|
|
|
|
//
|
|
|
|
// Returns: None
|
|
|
|
//
|
|
|
|
//================================================================
|
|
|
|
void MultiplayerModeBase::AddStartingWeapon( const str& weaponViewmodel )
|
|
|
|
{
|
|
|
|
_weaponList.AddObject(weaponViewmodel);
|
|
|
|
}
|
|
|
|
|
|
|
|
//================================================================
|
|
|
|
// Name: ActivatePlayer
|
|
|
|
// Class: MultiplayerModeBase
|
|
|
|
//
|
|
|
|
// Description: Activates the specified player. Activation includes
|
|
|
|
// loading skin and giving appropriate weapons and ammo.
|
|
|
|
//
|
|
|
|
// Parameters: Player* -- player to be activated
|
|
|
|
//
|
|
|
|
// Returns: None
|
|
|
|
//
|
|
|
|
//================================================================
|
|
|
|
void MultiplayerModeBase::ActivatePlayer( Player* player )
|
|
|
|
{
|
|
|
|
assert( player );
|
|
|
|
|
|
|
|
if ( !player )
|
|
|
|
{
|
|
|
|
warning("MultiplayerModeBase::ActivatePlayer", "NULL Player\n");
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make the player enter the game
|
|
|
|
|
|
|
|
if ( !_gameStarted )
|
|
|
|
{
|
|
|
|
multiplayerManager.makePlayerSpectator( player );
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
multiplayerManager.playerEnterArena( player->entnum, _startingHealth );
|
|
|
|
|
|
|
|
multiplayerManager.changePlayerModel( player, player->client->pers.mp_playermodel );
|
|
|
|
_giveInitialConditions( player );
|
|
|
|
|
|
|
|
// Make player invunerable for a little bit
|
|
|
|
|
|
|
|
if ( mp_respawnInvincibilityTime->value > 0.0f )
|
|
|
|
{
|
|
|
|
Powerup *powerup;
|
|
|
|
Event *event;
|
|
|
|
|
|
|
|
powerup = Powerup::CreatePowerup( "ProtectionTemp", "models/item/powerup_protection.tik", player );
|
|
|
|
|
|
|
|
if ( powerup )
|
|
|
|
{
|
|
|
|
powerup->CancelEventsOfType( EV_ProcessInitCommands );
|
|
|
|
powerup->ProcessInitCommands( powerup->edict->s.modelindex );
|
|
|
|
|
|
|
|
event = new Event( EV_Item_SetAmount );
|
|
|
|
event->AddFloat( mp_respawnInvincibilityTime->value );
|
|
|
|
powerup->ProcessEvent( event );
|
|
|
|
|
|
|
|
player->setPowerup( powerup );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
multiplayerManager.allowFighting( true );
|
|
|
|
player->takedamage = DAMAGE_YES;
|
|
|
|
|
|
|
|
multiplayerManager.playerSpawned( player );
|
|
|
|
}
|
|
|
|
|
|
|
|
//================================================================
|
|
|
|
// Name: BeginMatch
|
|
|
|
// Class: MultiplayerModeBase
|
|
|
|
//
|
|
|
|
// Description: Begins the match. Resets the spawn points and calls
|
|
|
|
// _beginMatch so subclasses can do their special thing.
|
|
|
|
//
|
|
|
|
// Parameters: None
|
|
|
|
//
|
|
|
|
// Returns: None
|
|
|
|
//
|
|
|
|
//================================================================
|
|
|
|
void MultiplayerModeBase::BeginMatch( void )
|
|
|
|
{
|
|
|
|
_fightInProgress = true ;
|
|
|
|
resetSpawnpoints();
|
|
|
|
_beginMatch();
|
|
|
|
}
|
|
|
|
|
|
|
|
//================================================================
|
|
|
|
// Name: EndMatch
|
|
|
|
// Class: MultiplayerModeBase
|
|
|
|
//
|
|
|
|
// Description: Ends the match. Resets the spawn points and calls
|
|
|
|
// _endMatch so subclasses can do their special thing.
|
|
|
|
//
|
|
|
|
// Parameters: None
|
|
|
|
//
|
|
|
|
// Returns: None
|
|
|
|
//
|
|
|
|
//================================================================
|
|
|
|
void MultiplayerModeBase::EndMatch( void )
|
|
|
|
{
|
|
|
|
_fightInProgress = false ;
|
|
|
|
resetSpawnpoints();
|
|
|
|
_endMatch();
|
|
|
|
}
|
|
|
|
|
|
|
|
//================================================================
|
|
|
|
// Name: resetSpawnpoints
|
|
|
|
// Class: MultiplayerModeBase
|
|
|
|
//
|
|
|
|
// Description: Resets the spawnpoints in the arena. This list
|
|
|
|
// of spawnpoints is doled out as people enter the arena.
|
|
|
|
//
|
|
|
|
// Parameters: None
|
|
|
|
//
|
|
|
|
// Returns: None
|
|
|
|
//
|
|
|
|
//================================================================
|
|
|
|
void MultiplayerModeBase::resetSpawnpoints( void )
|
|
|
|
{
|
|
|
|
// This builds a list of all the spawnpoints in the arena, which will be
|
|
|
|
// removed as each one is used at start
|
|
|
|
_unusedSpawnpointList.ClearObjectList();
|
|
|
|
|
|
|
|
for( int idx = 1; idx <= _spawnpointList.NumObjects(); idx++ )
|
|
|
|
{
|
|
|
|
_unusedSpawnpointList.AddObject( _spawnpointList.ObjectAt( idx ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MultiplayerModeBase::getSpawnpoints( void )
|
|
|
|
{
|
|
|
|
PlayerDeathmatchStart *deathmatchStart;
|
|
|
|
|
|
|
|
deathmatchStart = ( PlayerDeathmatchStart * )G_FindClass( NULL, "info_player_deathmatch" );
|
|
|
|
|
|
|
|
while ( deathmatchStart )
|
|
|
|
{
|
|
|
|
_spawnpointList.AddObject( deathmatchStart );
|
|
|
|
|
|
|
|
deathmatchStart = ( PlayerDeathmatchStart * )G_FindClass( deathmatchStart, "info_player_deathmatch" );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------
|
|
|
|
// P R O T E C T E D M E T H O D S
|
|
|
|
//----------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
//================================================================
|
|
|
|
// Name: _beginMatch
|
|
|
|
// Class: MultiplayerModeBase
|
|
|
|
//
|
|
|
|
// Description: Begins the match. All players in the arena have
|
|
|
|
// BeginFight() called on them to enable fighting.
|
|
|
|
//
|
|
|
|
// Parameters: None
|
|
|
|
//
|
|
|
|
// Returns: None
|
|
|
|
//
|
|
|
|
//================================================================
|
|
|
|
void MultiplayerModeBase::_beginMatch()
|
|
|
|
{
|
|
|
|
for(int idx=1; idx <= _playerList.NumObjects(); idx++ )
|
|
|
|
{
|
|
|
|
Player *player = _playerList.ObjectAt( idx );
|
|
|
|
|
|
|
|
assert( player );
|
|
|
|
if ( !player )
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
multiplayerManager.allowFighting( true );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//================================================================
|
|
|
|
// Name: _endMatch
|
|
|
|
// Class: MultiplayerModeBase
|
|
|
|
//
|
|
|
|
// Description: Ends the match. All players in the arena have
|
|
|
|
// EndFight() called on them to disable fighting.
|
|
|
|
//
|
|
|
|
// Parameters: None
|
|
|
|
//
|
|
|
|
// Returns: None
|
|
|
|
//
|
|
|
|
//================================================================
|
|
|
|
void MultiplayerModeBase::_endMatch()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void MultiplayerModeBase::declareWinner( void )
|
|
|
|
{
|
|
|
|
Player *player;
|
|
|
|
int place;
|
|
|
|
bool tied;
|
|
|
|
|
2012-12-31 12:37:16 +00:00
|
|
|
for ( unsigned i = 0 ; i < _maxPlayers ; i++ )
|
2012-12-30 16:37:54 +00:00
|
|
|
{
|
|
|
|
player = getPlayer( i );
|
|
|
|
|
|
|
|
if ( !player )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
place = getPlace( player, &tied );
|
|
|
|
|
|
|
|
if ( ( place == 1 ) && tied )
|
|
|
|
multiplayerManager.playerSound( player->entnum, "localization/sound/dialog/dm/comp_tiedfirst.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, 1.5f );
|
|
|
|
else if ( place == 1 )
|
|
|
|
multiplayerManager.playerSound( player->entnum, "localization/sound/dialog/dm/comp_winn.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, 1.5f );
|
|
|
|
else if ( place == 2 )
|
|
|
|
multiplayerManager.playerSound( player->entnum, "localization/sound/dialog/dm/comp_second.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, 1.5f );
|
|
|
|
else if ( place == 3 )
|
|
|
|
multiplayerManager.playerSound( player->entnum, "localization/sound/dialog/dm/comp_third.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, 1.5f );
|
|
|
|
else
|
|
|
|
multiplayerManager.playerSound( player->entnum, "localization/sound/dialog/dm/comp_didnotrank.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, 1.5f );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//================================================================
|
|
|
|
// Name: _giveInitialConditions
|
|
|
|
// Class: MultiplayerModeBase
|
|
|
|
//
|
|
|
|
// Description: Gives the specified player the weapons and ammo
|
|
|
|
// designated for this arena.
|
|
|
|
//
|
|
|
|
// Parameters: Player* -- player to receive weapons
|
|
|
|
//
|
|
|
|
// Returns: None
|
|
|
|
//
|
|
|
|
//================================================================
|
|
|
|
void MultiplayerModeBase::_giveInitialConditions( Player *player )
|
|
|
|
{
|
|
|
|
int idx ;
|
|
|
|
|
|
|
|
assert( player );
|
|
|
|
|
|
|
|
if ( !player )
|
|
|
|
{
|
|
|
|
warning( "MultiplayerModeBase::_giveInitialConditions", "NULL player specified.\n" );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Give all the weapons to the player
|
|
|
|
|
|
|
|
for ( idx=1; idx <= _weaponList.NumObjects(); idx++ )
|
|
|
|
{
|
|
|
|
multiplayerManager.givePlayerItem( player->entnum, _weaponList.ObjectAt( idx ) );
|
|
|
|
/* Event *ev = new Event( "weapon" );
|
|
|
|
ev->AddToken( _weaponList.ObjectAt( idx ) );
|
|
|
|
player->ProcessEvent( ev ); // deletes the created Event */
|
|
|
|
}
|
|
|
|
|
|
|
|
// Give all the ammo to the player
|
|
|
|
|
|
|
|
for ( idx=1; idx <= _ammoList.NumObjects(); idx++ )
|
|
|
|
{
|
|
|
|
Event *ev = new Event( "ammo" );
|
|
|
|
ev->AddString( _ammoList.ObjectAt( idx )->type );
|
|
|
|
ev->AddInteger( _ammoList.ObjectAt( idx )->amount );
|
|
|
|
player->ProcessEvent( ev ); // deletes the created Event
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start the player with the appropriate weapon
|
|
|
|
|
|
|
|
if ( _startingWeaponName.length() )
|
|
|
|
{
|
|
|
|
Event *ev = new Event( "use" );
|
|
|
|
ev->AddString( _startingWeaponName );
|
|
|
|
player->ProcessEvent( ev ); // deletes the created Event
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int MultiplayerModeBase::getNumSpawnpoints( void )
|
|
|
|
{
|
|
|
|
return _spawnpointList.NumObjects();
|
|
|
|
}
|
|
|
|
|
|
|
|
Entity* MultiplayerModeBase::getSpawnpointbyIndex( int index )
|
|
|
|
{
|
|
|
|
int numPoints;
|
|
|
|
|
|
|
|
// Make sure everything is ok
|
|
|
|
|
|
|
|
numPoints = getNumSpawnpoints();
|
|
|
|
|
|
|
|
if ( !numPoints )
|
|
|
|
{
|
|
|
|
warning( "MultiplayerModeBase::getSpawnpointbyIndex", "No spawnpoints found in arena\n" );
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ( index < 0 ) || ( index >= numPoints ) )
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
// Get the spawn point
|
|
|
|
|
|
|
|
return _spawnpointList.ObjectAt( index + 1 );
|
|
|
|
}
|
|
|
|
|
|
|
|
Entity* MultiplayerModeBase::getRandomSpawnpoint( bool useCounter )
|
|
|
|
{
|
|
|
|
Entity *spot = NULL;
|
|
|
|
|
|
|
|
int numPoints = _spawnpointList.NumObjects();
|
|
|
|
|
|
|
|
// Make sure everything is ok
|
|
|
|
|
|
|
|
if ( !numPoints )
|
|
|
|
{
|
|
|
|
warning( "MultiplayerModeBase::GetRandomSpawnpoint", "No spawnpoints found in arena\n" );
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the selected index (either in order or random)
|
|
|
|
|
|
|
|
if ( useCounter )
|
|
|
|
{
|
2012-12-31 12:37:16 +00:00
|
|
|
if ( (int)_spawncounter > numPoints )
|
2012-12-30 16:37:54 +00:00
|
|
|
{
|
|
|
|
_spawncounter = 1; // reuse spawn points
|
|
|
|
}
|
|
|
|
|
|
|
|
spot = ( Entity * )_spawnpointList.ObjectAt( _spawncounter );
|
|
|
|
_spawncounter++;
|
|
|
|
return spot;
|
|
|
|
}
|
|
|
|
|
|
|
|
int selection = ( (int)( G_Random() * numPoints ) );
|
|
|
|
|
|
|
|
// Get the spawn point
|
|
|
|
|
|
|
|
return getSpawnpointbyIndex( selection );
|
|
|
|
}
|
|
|
|
|
|
|
|
int MultiplayerModeBase::getNumNamedSpawnpoints( const str &spawnpointType )
|
|
|
|
{
|
|
|
|
int numPoints;
|
|
|
|
int numNamedPoints;
|
|
|
|
int i;
|
|
|
|
PlayerDeathmatchStart *spawnpoint;
|
|
|
|
|
|
|
|
numPoints = _spawnpointList.NumObjects();
|
|
|
|
|
|
|
|
if ( !numPoints )
|
|
|
|
{
|
|
|
|
warning( "MultiplayerModeBase::getNumNamedSpawnpoint", "No spawnpoints found in map\n" );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find out how many spawnpoints match this type
|
|
|
|
|
|
|
|
numNamedPoints = 0;
|
|
|
|
|
|
|
|
for ( i = 1 ; i <= numPoints ; i++ )
|
|
|
|
{
|
|
|
|
spawnpoint = _spawnpointList.ObjectAt( i );
|
|
|
|
|
|
|
|
if ( spawnpoint->_type == spawnpointType )
|
|
|
|
{
|
|
|
|
numNamedPoints++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return numNamedPoints;
|
|
|
|
}
|
|
|
|
|
|
|
|
Entity* MultiplayerModeBase::getNamedSpawnpointbyIndex( const str &spawnpointType, int index )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int numNamedPoints;
|
|
|
|
int numPoints;
|
|
|
|
PlayerDeathmatchStart *spawnpoint;
|
|
|
|
|
|
|
|
|
|
|
|
numNamedPoints = getNumNamedSpawnpoints( spawnpointType );
|
|
|
|
|
|
|
|
// Make sure we found at least 1 spawnpoint matching the type
|
|
|
|
|
|
|
|
if ( !numNamedPoints || ( index < 0 ) || ( index >= numNamedPoints ) )
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
numNamedPoints = 0;
|
|
|
|
|
|
|
|
// Find the spawn point picked
|
|
|
|
|
|
|
|
numPoints = getNumSpawnpoints();
|
|
|
|
|
|
|
|
for ( i = 1 ; i <= numPoints ; i++ )
|
|
|
|
{
|
|
|
|
spawnpoint = _spawnpointList.ObjectAt( i );
|
|
|
|
|
|
|
|
if ( spawnpoint->_type == spawnpointType )
|
|
|
|
{
|
|
|
|
if ( numNamedPoints == index )
|
|
|
|
{
|
|
|
|
return spawnpoint;
|
|
|
|
}
|
|
|
|
|
|
|
|
numNamedPoints++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
Entity* MultiplayerModeBase::getRandomNamedSpawnpoint( const str &spawnpointType )
|
|
|
|
{
|
|
|
|
int numNamedPoints;
|
|
|
|
int selection;
|
|
|
|
|
|
|
|
numNamedPoints = getNumNamedSpawnpoints( spawnpointType );
|
|
|
|
|
|
|
|
selection = ( (int)( G_Random() * numNamedPoints ) );
|
|
|
|
|
|
|
|
return getNamedSpawnpointbyIndex( spawnpointType, selection );
|
|
|
|
}
|
|
|
|
|
|
|
|
Entity* MultiplayerModeBase::getFarthestNamedSpawnpoint( const Vector &origin, const str &spawnpointType )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int numNamedPoints;
|
|
|
|
int numPoints;
|
|
|
|
PlayerDeathmatchStart *spawnpoint;
|
|
|
|
PlayerDeathmatchStart *farthestSpawnpoint;
|
|
|
|
float farthestDistance;
|
|
|
|
Vector dir;
|
|
|
|
float dist;
|
|
|
|
|
|
|
|
|
|
|
|
farthestSpawnpoint = NULL;
|
|
|
|
farthestDistance = 0.0f;
|
|
|
|
|
|
|
|
numNamedPoints = getNumNamedSpawnpoints( spawnpointType );
|
|
|
|
|
|
|
|
// Make sure we found at least 1 spawnpoint matching the type
|
|
|
|
|
|
|
|
if ( !numNamedPoints )
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
// Go through all of the spawn points and find the farthest one from the specified point
|
|
|
|
|
|
|
|
numPoints = getNumSpawnpoints();
|
|
|
|
|
|
|
|
for ( i = 1 ; i <= numPoints ; i++ )
|
|
|
|
{
|
|
|
|
spawnpoint = _spawnpointList.ObjectAt( i );
|
|
|
|
|
|
|
|
if ( spawnpoint->_type == spawnpointType )
|
|
|
|
{
|
|
|
|
dir = origin - spawnpoint->origin;
|
|
|
|
dist = dir.length();
|
|
|
|
|
|
|
|
if ( !farthestSpawnpoint || ( dist > farthestDistance ) )
|
|
|
|
{
|
|
|
|
farthestSpawnpoint = spawnpoint;
|
|
|
|
farthestDistance = dist;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return farthestSpawnpoint;
|
|
|
|
}
|
|
|
|
|
|
|
|
Entity *MultiplayerModeBase::getSpawnPoint( Player *player )
|
|
|
|
{
|
|
|
|
Entity *spawnPoint = NULL;
|
|
|
|
int randomStartingSpot;
|
|
|
|
int i;
|
|
|
|
int spawnPointIndex;
|
|
|
|
int numSpawnPoints;
|
|
|
|
bool useAnySpawnPoint;
|
|
|
|
|
|
|
|
numSpawnPoints = getNumNamedSpawnpoints( "" );
|
|
|
|
|
|
|
|
if ( numSpawnPoints == 0 )
|
|
|
|
{
|
|
|
|
useAnySpawnPoint = true;
|
|
|
|
|
|
|
|
numSpawnPoints = getNumSpawnpoints();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
useAnySpawnPoint = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
randomStartingSpot = ( (int)( G_Random() * numSpawnPoints ) );
|
|
|
|
|
|
|
|
for( i = 0 ; i < numSpawnPoints ; i++ )
|
|
|
|
{
|
|
|
|
spawnPointIndex = ( randomStartingSpot + i ) % numSpawnPoints;
|
|
|
|
|
|
|
|
if ( useAnySpawnPoint )
|
|
|
|
spawnPoint = getSpawnpointbyIndex( spawnPointIndex );
|
|
|
|
else
|
|
|
|
spawnPoint = getNamedSpawnpointbyIndex( "", spawnPointIndex );
|
|
|
|
|
|
|
|
if ( spawnPoint )
|
|
|
|
{
|
|
|
|
int j;
|
|
|
|
int num;
|
|
|
|
int touch[ MAX_GENTITIES ];
|
|
|
|
gentity_t *hit;
|
|
|
|
Vector min;
|
|
|
|
Vector max;
|
|
|
|
bool badSpot;
|
|
|
|
|
|
|
|
min = spawnPoint->origin + player->mins + Vector( 0, 0, 1 );
|
|
|
|
max = spawnPoint->origin + player->maxs + Vector( 0, 0, 1 );
|
|
|
|
|
|
|
|
num = gi.AreaEntities( min, max, touch, MAX_GENTITIES, qfalse );
|
|
|
|
|
|
|
|
badSpot = false;
|
|
|
|
|
|
|
|
for( j = 0 ; j < num ; j++ )
|
|
|
|
{
|
|
|
|
hit = &g_entities[ touch[ j ] ];
|
|
|
|
|
|
|
|
if ( !hit->inuse || ( hit->entity == player ) || !hit->entity || ( hit->entity == world ) || ( !hit->entity->edict->solid ) )
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( hit->entity->isSubclassOf( Player ) )
|
|
|
|
{
|
|
|
|
Player *hitPlayer;
|
|
|
|
|
|
|
|
hitPlayer = (Player *)hit->entity;
|
|
|
|
|
|
|
|
badSpot = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !badSpot )
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return spawnPoint;
|
|
|
|
}
|
|
|
|
|
|
|
|
Player *MultiplayerModeBase::getLastKilledByPlayer( Player *player, int *meansOfDeath )
|
|
|
|
{
|
|
|
|
int entnum;
|
|
|
|
|
|
|
|
entnum = _playerGameData[ player->entnum ]._lastKilledByPlayer;
|
|
|
|
|
|
|
|
if ( entnum < 0 )
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if ( meansOfDeath )
|
|
|
|
*meansOfDeath = _playerGameData[ player->entnum ]._lastKilledByPlayerMOD;
|
|
|
|
|
|
|
|
return GetPlayer( entnum );
|
|
|
|
}
|
|
|
|
|
|
|
|
Player *MultiplayerModeBase::getLastKillerOfPlayer( Player *player, int *meansOfDeath )
|
|
|
|
{
|
|
|
|
int entnum;
|
|
|
|
|
|
|
|
entnum = _playerGameData[ player->entnum ]._lastKillerOfPlayer;
|
|
|
|
|
|
|
|
if ( entnum < 0 )
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if ( meansOfDeath )
|
|
|
|
*meansOfDeath = _playerGameData[ player->entnum ]._lastKillerOfPlayerMOD;
|
|
|
|
|
|
|
|
return GetPlayer( entnum );
|
|
|
|
}
|
|
|
|
|
|
|
|
void MultiplayerModeBase::applySpeedModifiers( Player *player, int *moveSpeed )
|
|
|
|
{
|
|
|
|
if ( multiplayerManager.isPlayerSpectator( player ) )
|
|
|
|
{
|
|
|
|
*moveSpeed *= (int)_spectatorMoveSpeedModifier;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int MultiplayerModeBase::comparePlayerScore( MultiplayerPlayerGameData &player1Data, MultiplayerPlayerGameData &player2Data )
|
|
|
|
{
|
|
|
|
// Check points first
|
|
|
|
|
|
|
|
if ( player1Data._points > player2Data._points )
|
|
|
|
return 1;
|
|
|
|
else if ( player1Data._points < player2Data._points )
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
// Check kills second
|
|
|
|
|
|
|
|
if ( player1Data._numKills > player2Data._numKills )
|
|
|
|
return 1;
|
|
|
|
else if ( player1Data._numKills < player2Data._numKills )
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
// Check deaths third
|
|
|
|
|
|
|
|
if ( player1Data._numDeaths > player2Data._numDeaths )
|
|
|
|
return -1;
|
|
|
|
else if ( player1Data._numDeaths < player2Data._numDeaths )
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
// If we get here the player's are tied
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int MultiplayerModeBase::getPlace( Player *player, bool *tied )
|
|
|
|
{
|
|
|
|
int place = 1;
|
|
|
|
bool isTied = false;
|
|
|
|
Player *currentPlayer;
|
|
|
|
int scoreDiff;
|
|
|
|
|
2012-12-31 12:37:16 +00:00
|
|
|
for ( unsigned i = 0 ; i < _maxPlayers ; i++ )
|
2012-12-30 16:37:54 +00:00
|
|
|
{
|
|
|
|
// Get this player and make sure everything is ok
|
|
|
|
|
|
|
|
currentPlayer = getPlayer( i );
|
|
|
|
|
|
|
|
if ( !currentPlayer || ( currentPlayer == player ) )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// See how the player compares with the current player
|
|
|
|
|
|
|
|
scoreDiff = comparePlayerScore( _playerGameData[ player->entnum ], _playerGameData[ currentPlayer->entnum ] );
|
|
|
|
|
|
|
|
if ( scoreDiff < 0 )
|
|
|
|
{
|
|
|
|
// The current player has a higher score so we bump the place one higher
|
|
|
|
|
|
|
|
place++;
|
|
|
|
}
|
|
|
|
else if ( scoreDiff == 0 )
|
|
|
|
{
|
|
|
|
// The current player has a same score so they are tied
|
|
|
|
|
|
|
|
isTied = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( tied )
|
|
|
|
{
|
|
|
|
*tied = isTied;
|
|
|
|
}
|
|
|
|
|
|
|
|
return place;
|
|
|
|
}
|
|
|
|
|
|
|
|
int MultiplayerModeBase::getHighestPoints( void )
|
|
|
|
{
|
|
|
|
int highestPoints = -999999999;
|
|
|
|
|
2012-12-31 12:37:16 +00:00
|
|
|
for ( unsigned i = 0 ; i < _maxPlayers ; i++ )
|
2012-12-30 16:37:54 +00:00
|
|
|
{
|
|
|
|
if ( _playerGameData[ i ]._points > highestPoints )
|
|
|
|
{
|
|
|
|
highestPoints = _playerGameData[ i ]._points;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return highestPoints;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MultiplayerModeBase::shouldKeepNormalItem( Item *item )
|
|
|
|
{
|
|
|
|
if ( multiplayerManager.checkFlag( MP_FLAG_NO_POWERUPS ) && item->isSubclassOf( PowerupBase ) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|