|
|
|
#include "extdll.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "cbase.h"
|
|
|
|
#include "player.h"
|
|
|
|
#include "coop_util.h"
|
|
|
|
#include "gravgunmod.h"
|
|
|
|
|
|
|
|
bool g_fPause;
|
|
|
|
|
|
|
|
// offset for all maps relative to current map
|
|
|
|
struct COOPMapState
|
|
|
|
{
|
|
|
|
struct COOPMapPersist
|
|
|
|
{
|
|
|
|
char szMapName[32];
|
|
|
|
Vector vecOffset;
|
|
|
|
struct COOPCheckpoint {
|
|
|
|
char szDisplayName[32];
|
|
|
|
float flTime;
|
|
|
|
struct GGMPosition pos;
|
|
|
|
} rgCheckpoints[5];
|
|
|
|
} p;
|
|
|
|
|
|
|
|
struct COOPMapState *pNext;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct COOPLandmarkTransition {
|
|
|
|
char szSourceMap[32];
|
|
|
|
char szTargetMap[32];
|
|
|
|
char szLandmarkName[32];
|
|
|
|
Vector vecLandmark;
|
|
|
|
bool fTriggerUsed;
|
|
|
|
bool fSavedPos;
|
|
|
|
struct GGMPosition pos;
|
|
|
|
bool fLoading;
|
|
|
|
};
|
|
|
|
|
|
|
|
enum COOPSaveSlot
|
|
|
|
{
|
|
|
|
COOP_SAVE_START1 = 0,
|
|
|
|
COOP_SAVE_START2,
|
|
|
|
COOP_SAVE_AUTO1,
|
|
|
|
COOP_SAVE_AUTO2,
|
|
|
|
COOP_SAVE_COUNT
|
|
|
|
};
|
|
|
|
|
|
|
|
struct COOPState
|
|
|
|
{
|
|
|
|
// will be saved
|
|
|
|
struct COOPPersist
|
|
|
|
{
|
|
|
|
// weapon list
|
|
|
|
char rgszWeapons[64][32];
|
|
|
|
int iWeaponCount;
|
|
|
|
|
|
|
|
// data for spawnpoint
|
|
|
|
struct GGMPosition savedPos;
|
|
|
|
bool fSaved;
|
|
|
|
char rgszSaveSlots[COOP_SAVE_COUNT][32];
|
|
|
|
char iLastAutoSave;
|
|
|
|
} p;
|
|
|
|
|
|
|
|
// runtime state
|
|
|
|
struct COOPMapState *pMapStates;
|
|
|
|
struct COOPMapState *pCurrentMap;
|
|
|
|
|
|
|
|
// translate GGMMapState during changelevel
|
|
|
|
struct COOPLandmarkTransition landmarkTransition;
|
|
|
|
|
|
|
|
int iVote;
|
|
|
|
float flMsgLimit1, flMsgLimit2;
|
|
|
|
|
|
|
|
} g_CoopState;
|
|
|
|
|
|
|
|
cvar_t mp_coop = { "mp_coop", "0", FCVAR_SERVER };
|
|
|
|
cvar_t mp_coop_nofriendlyfire = { "mp_coop_nofriendlyfire", "0", FCVAR_SERVER };
|
|
|
|
cvar_t mp_coop_reconnect_hack = { "mp_coop_reconnect_hack", "0", FCVAR_SERVER };
|
|
|
|
cvar_t mp_coop_noangry = { "mp_coop_noangry", "0", FCVAR_SERVER };
|
|
|
|
cvar_t mp_coop_checkpoints = { "mp_coop_checkpoints", "1", FCVAR_SERVER };
|
|
|
|
cvar_t mp_coop_strongcheckpoints = { "mp_coop_strongcheckpoints", "0", FCVAR_SERVER };
|
|
|
|
cvar_t mp_semclip = { "mp_semclip", "0", FCVAR_SERVER };
|
|
|
|
|
|
|
|
|
|
|
|
cvar_t materials_txt = { "materials_txt", "sound/materials.txt", FCVAR_SERVER };
|
|
|
|
cvar_t sentences_txt = { "sentences_txt", "sound/sentences.txt", FCVAR_SERVER };
|
|
|
|
void COOP_CheckpointMenu( CBasePlayer *pPlayer );
|
|
|
|
|
|
|
|
|
|
|
|
void COOP_WriteState( const char *path )
|
|
|
|
{
|
|
|
|
FILE *f = fopen( path, "wb" );
|
|
|
|
unsigned int tsize = sizeof( g_CoopState.p );
|
|
|
|
struct COOPMapState *pState = g_CoopState.pMapStates;
|
|
|
|
|
|
|
|
if( !f )
|
|
|
|
return;
|
|
|
|
|
|
|
|
// keep size to allow extent struct
|
|
|
|
fwrite( &tsize, 4, 1, f );
|
|
|
|
fwrite( &g_CoopState.p, tsize, 1, f );
|
|
|
|
|
|
|
|
tsize = sizeof( pState->p );
|
|
|
|
fwrite( &tsize, 4, 1, f );
|
|
|
|
|
|
|
|
while( pState )
|
|
|
|
{
|
|
|
|
fwrite( &pState->p, tsize, 1, f );
|
|
|
|
pState = pState->pNext;
|
|
|
|
}
|
|
|
|
fclose( f );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool COOP_ReadState( const char *path )
|
|
|
|
{
|
|
|
|
FILE *f = fopen( path, "rb" );
|
|
|
|
unsigned int tsize;
|
|
|
|
|
|
|
|
if( !f )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
g_CoopState.landmarkTransition.fLoading = true;
|
|
|
|
|
|
|
|
if( !fread( &tsize, 4, 1, f ) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// do not allow shrink structure
|
|
|
|
if( tsize > sizeof( g_CoopState.p ) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if( !fread( &g_CoopState.p, tsize, 1, f ) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if( !fread( &tsize, 4, 1, f ) )
|
|
|
|
{
|
|
|
|
ALERT( at_error, "Could not read map states from %s\n", path );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
while( true )
|
|
|
|
{
|
|
|
|
struct COOPMapState *pState = (struct COOPMapState *)calloc( 1, sizeof( struct COOPMapState ) );
|
|
|
|
|
|
|
|
memset( pState, 0, sizeof( struct COOPMapState ) );
|
|
|
|
|
|
|
|
fread( &pState->p, tsize, 1, f );
|
|
|
|
|
|
|
|
if( feof(f) || ferror( f ) )
|
|
|
|
{
|
|
|
|
free( pState );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
pState->pNext = g_CoopState.pMapStates;
|
|
|
|
g_CoopState.pMapStates = pState;
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose( f );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void COOP_AutoSave( void )
|
|
|
|
{
|
|
|
|
strncpy( g_CoopState.p.rgszSaveSlots[COOP_SAVE_AUTO2], g_CoopState.p.rgszSaveSlots[COOP_SAVE_AUTO1], 31 );
|
|
|
|
g_CoopState.p.iLastAutoSave ^= 1;
|
|
|
|
snprintf( g_CoopState.p.rgszSaveSlots[COOP_SAVE_AUTO1], 31, "%s-auto%d", STRING( gpGlobals->mapname ), g_CoopState.p.iLastAutoSave );
|
|
|
|
GGM_Save( g_CoopState.p.rgszSaveSlots[COOP_SAVE_AUTO1] );
|
|
|
|
}
|
|
|
|
|
|
|
|
void COOP_MapStartSave( void )
|
|
|
|
{
|
|
|
|
char szSavename[32] = "";
|
|
|
|
|
|
|
|
snprintf( szSavename, 31, "%s-start", STRING( gpGlobals->mapname ) );
|
|
|
|
|
|
|
|
// moving to previous map and returning back should not trigger save
|
|
|
|
if( !strcmp( g_CoopState.p.rgszSaveSlots[COOP_SAVE_START1], szSavename ) || !strcmp( g_CoopState.p.rgszSaveSlots[COOP_SAVE_START2], szSavename ) )
|
|
|
|
return;
|
|
|
|
|
|
|
|
strncpy( g_CoopState.p.rgszSaveSlots[COOP_SAVE_START2], g_CoopState.p.rgszSaveSlots[COOP_SAVE_START1], 31 );
|
|
|
|
strncpy( g_CoopState.p.rgszSaveSlots[COOP_SAVE_START1], szSavename, 31 );
|
|
|
|
SERVER_COMMAND( UTIL_VarArgs( "wait;wait;ggm_save %s\n", g_CoopState.p.rgszSaveSlots[COOP_SAVE_START1] ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
edict_t *COOP_FindLandmark( const char *pLandmarkName )
|
|
|
|
{
|
|
|
|
edict_t *pentLandmark;
|
|
|
|
|
|
|
|
pentLandmark = FIND_ENTITY_BY_STRING( NULL, "targetname", pLandmarkName );
|
|
|
|
while( !FNullEnt( pentLandmark ) )
|
|
|
|
{
|
|
|
|
// Found the landmark
|
|
|
|
if( FClassnameIs( pentLandmark, "info_landmark" ) )
|
|
|
|
return pentLandmark;
|
|
|
|
else
|
|
|
|
pentLandmark = FIND_ENTITY_BY_STRING( pentLandmark, "targetname", pLandmarkName );
|
|
|
|
}
|
|
|
|
ALERT( at_error, "Can't find landmark %s\n", pLandmarkName );
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void UTIL_CoopPlayerMessage( CBaseEntity *pPlayer, int channel, float time, unsigned int color1, unsigned int color2, float x, float y, const char *format, ... )
|
|
|
|
{
|
|
|
|
if( !pPlayer )
|
|
|
|
return;
|
|
|
|
|
|
|
|
hudtextparms_t params;
|
|
|
|
params.x = x, params.y = y;
|
|
|
|
params.fadeinTime = params.fadeoutTime = .5f;
|
|
|
|
params.holdTime = time;
|
|
|
|
params.r1 = (color1 >> 24) & 0xFF, params.g1 = (color1 >> 16) & 0xFF, params.b1 = (color1 >> 8) & 0xFF, params.a1 = color1 & 0xFF;
|
|
|
|
params.r2 = (color2 >> 24) & 0xFF, params.g2 = (color2 >> 16) & 0xFF, params.b2 = (color2 >> 8) & 0xFF, params.a2 = color2 & 0xFF;
|
|
|
|
params.channel = channel;
|
|
|
|
va_list argptr;
|
|
|
|
char string[256];
|
|
|
|
|
|
|
|
va_start( argptr, format );
|
|
|
|
int len = vsnprintf( string, 256, format, argptr );
|
|
|
|
va_end( argptr );
|
|
|
|
string[len] = 0;
|
|
|
|
char *pstr = string;
|
|
|
|
|
|
|
|
// set line breaks
|
|
|
|
for( int i = 0; *pstr; pstr++,i++ )
|
|
|
|
{
|
|
|
|
if( *pstr == '\n' )
|
|
|
|
i = 0;
|
|
|
|
if( i >= 79 )
|
|
|
|
*pstr = '\n', i = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
UTIL_HudMessage( pPlayer, params, string );
|
|
|
|
}
|
|
|
|
|
|
|
|
void UTIL_CoopHudMessage( int channel, float time, unsigned int color1, unsigned int color2, float x, float y, const char *format, ... )
|
|
|
|
{
|
|
|
|
if( gpGlobals->time < g_CoopState.flMsgLimit1 )
|
|
|
|
return;
|
|
|
|
g_CoopState.flMsgLimit1 = gpGlobals->time + 0.4;
|
|
|
|
|
|
|
|
hudtextparms_t params;
|
|
|
|
params.x = x, params.y = y;
|
|
|
|
params.fadeinTime = params.fadeoutTime = .5f;
|
|
|
|
params.holdTime = time;
|
|
|
|
params.r1 = (color1 >> 24) & 0xFF, params.g1 = (color1 >> 16) & 0xFF, params.b1 = (color1 >> 8) & 0xFF, params.a1 = color1 & 0xFF;
|
|
|
|
params.r2 = (color2 >> 24) & 0xFF, params.g2 = (color2 >> 16) & 0xFF, params.b2 = (color2 >> 8) & 0xFF, params.a2 = color2 & 0xFF;
|
|
|
|
params.channel = channel;
|
|
|
|
va_list argptr;
|
|
|
|
char string[256];
|
|
|
|
|
|
|
|
va_start( argptr, format );
|
|
|
|
int len = vsnprintf( string, 256, format, argptr );
|
|
|
|
va_end( argptr );
|
|
|
|
string[len] = 0;
|
|
|
|
char *pstr = string;
|
|
|
|
|
|
|
|
// set line breaks
|
|
|
|
for( int i = 0; *pstr; pstr++,i++ )
|
|
|
|
{
|
|
|
|
if( *pstr == '\n' )
|
|
|
|
i = 0;
|
|
|
|
if( i >= 79 )
|
|
|
|
*pstr = '\n', i = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
UTIL_HudMessageAll( params, string );
|
|
|
|
}
|
|
|
|
|
|
|
|
void UTIL_CoopPrintMessage( const char *format, ... )
|
|
|
|
{
|
|
|
|
if( gpGlobals->time < g_CoopState.flMsgLimit2 )
|
|
|
|
return;
|
|
|
|
g_CoopState.flMsgLimit2 = gpGlobals->time + 0.4;
|
|
|
|
|
|
|
|
va_list argptr;
|
|
|
|
char string[256] = "^2";
|
|
|
|
|
|
|
|
va_start( argptr, format );
|
|
|
|
int len = vsnprintf( string + 2, 253, format, argptr );
|
|
|
|
va_end( argptr );
|
|
|
|
string[len+2] = 0;
|
|
|
|
UTIL_ClientPrintAll( HUD_PRINTTALK, string );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void UTIL_CleanSpawnPoint( Vector origin, float dist )
|
|
|
|
{
|
|
|
|
CBaseEntity *ent = NULL;
|
|
|
|
while( ( ent = UTIL_FindEntityInSphere( ent, origin, dist ) ) != NULL )
|
|
|
|
{
|
|
|
|
if( ent->IsPlayer() )
|
|
|
|
{
|
|
|
|
TraceResult tr;
|
|
|
|
UTIL_TraceHull( ent->pev->origin + Vector( 0, 0, 36), ent->pev->origin + Vector( RANDOM_FLOAT( -150, 150 ), RANDOM_FLOAT( -150, 150 ), 0 ), dont_ignore_monsters, human_hull, ent->edict(), &tr);
|
|
|
|
//UTIL_TraceModel( ent->pev->origin + Vector( 0, 0, 36), ent->pev->origin + Vector( RANDOM_FLOAT( -150, 150 ), RANDOM_FLOAT( -150, 150 ), 0 ), 0, ent->edict(), &tr);
|
|
|
|
if( !tr.fAllSolid )
|
|
|
|
UTIL_SetOrigin(ent->pev, tr.vecEndPos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Vector COOP_FixupSpawnPoint( Vector vecOrigin, bool fDuck )
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
// predict that spawn point is almost correct
|
|
|
|
while( i < 2 ) // 2 player heights
|
|
|
|
{
|
|
|
|
Vector point = vecOrigin + Vector( 0, 0, 36 * i );
|
|
|
|
TraceResult tr;
|
|
|
|
UTIL_TraceHull( point, point, ignore_monsters, (mp_unduck.value&&fDuck)?head_hull:human_hull, NULL, &tr );
|
|
|
|
if( !tr.fStartSolid && !tr.fAllSolid )
|
|
|
|
return point;
|
|
|
|
i = -i;
|
|
|
|
if( i >= 0 )
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
return vecOrigin;
|
|
|
|
}
|
|
|
|
|
|
|
|
void UTIL_BecomeSpectator( CBasePlayer *pPlayer )
|
|
|
|
{
|
|
|
|
//pPlayer->m_bDoneFirstSpawn = true;
|
|
|
|
pPlayer->pev->takedamage = DAMAGE_NO;
|
|
|
|
pPlayer->pev->flags |= FL_SPECTATOR;
|
|
|
|
pPlayer->pev->flags |= FL_NOTARGET;
|
|
|
|
pPlayer->pev->effects |= EF_NODRAW;
|
|
|
|
pPlayer->pev->solid = SOLID_NOT;
|
|
|
|
pPlayer->pev->movetype = MOVETYPE_NOCLIP;
|
|
|
|
pPlayer->pev->modelindex = 0;
|
|
|
|
pPlayer->pev->health = 1;
|
|
|
|
pPlayer->m_pGoalEnt = NULL;
|
|
|
|
//pPlayer->StopObserver();
|
|
|
|
//while( !pPlayer->IsObserver() )
|
|
|
|
//pPlayer->StartObserver(pPlayer->pev->origin, pPlayer->pev->angles);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void UTIL_SpawnPlayer( CBasePlayer *pPlayer )
|
|
|
|
{
|
|
|
|
//pPlayer->StopObserver();
|
|
|
|
if( pPlayer->m_ggm.iState == STATE_SPECTATOR )
|
|
|
|
pPlayer->m_ggm.iState = STATE_SPAWNED;
|
|
|
|
pPlayer->m_iRespawnFrames = 0;
|
|
|
|
pPlayer->pev->effects &= ~EF_NODRAW;
|
|
|
|
|
|
|
|
pPlayer->pev->takedamage = DAMAGE_YES;
|
|
|
|
pPlayer->pev->flags &= ~FL_SPECTATOR;
|
|
|
|
pPlayer->pev->movetype = MOVETYPE_WALK;
|
|
|
|
pPlayer->Spawn();
|
|
|
|
//pPlayer->StopObserver();
|
|
|
|
if( mp_coop.value )
|
|
|
|
CLIENT_COMMAND( pPlayer->edict(), "touch_show _coopm*\n" );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void COOP_GiveDefaultWeapons(CBasePlayer *pPlayer)
|
|
|
|
{
|
|
|
|
for(int i = 0; i < g_CoopState.p.iWeaponCount;i++)
|
|
|
|
pPlayer->GiveNamedItem(g_CoopState.p.rgszWeapons[i]);
|
|
|
|
}
|
|
|
|
void COOP_AddDefaultWeapon( const char *classname )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
//if( !strcmp( classname, "item_suit") )
|
|
|
|
//return;
|
|
|
|
|
|
|
|
if( !strcmp( classname, "item_healthkit") )
|
|
|
|
return;
|
|
|
|
|
|
|
|
for(i = 0; i < g_CoopState.p.iWeaponCount;i++)
|
|
|
|
if(!strcmp(g_CoopState.p.rgszWeapons[i], classname))
|
|
|
|
return;
|
|
|
|
strcpy(g_CoopState.p.rgszWeapons[g_CoopState.p.iWeaponCount++], classname);
|
|
|
|
for( int i = 1; i <= gpGlobals->maxClients; i++ )
|
|
|
|
{
|
|
|
|
CBasePlayer *plr = (CBasePlayer*)UTIL_PlayerByIndex( i );
|
|
|
|
|
|
|
|
// broadcast to active players
|
|
|
|
if( plr && plr->pev->modelindex )
|
|
|
|
plr->GiveNamedItem( classname );
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void COOP_MarkTriggers( void )
|
|
|
|
{
|
|
|
|
CBaseEntity *pTrigger = NULL;
|
|
|
|
|
|
|
|
while( pTrigger = UTIL_FindEntityByClassname( pTrigger, "trigger_changelevel" ) )
|
|
|
|
{
|
|
|
|
struct COOPChangelevelData *pData = COOP_GetTriggerData( pTrigger );
|
|
|
|
//pData->fIsBack = !strcmp( pData->pszMapName, g_CoopState.landmarkTransition.szSourceMap );
|
|
|
|
pData->fIsBack = false;
|
|
|
|
if( !strcmp(pData->pszLandmarkName, g_CoopState.landmarkTransition.szLandmarkName) )
|
|
|
|
pData->fIsBack = true;
|
|
|
|
|
|
|
|
pTrigger->pev->renderamt = 127;
|
|
|
|
pTrigger->pev->effects &= ~EF_NODRAW;
|
|
|
|
pTrigger->pev->rendermode = kRenderTransColor;
|
|
|
|
pTrigger->pev->rendercolor = g_vecZero;
|
|
|
|
if( pData->fIsBack )
|
|
|
|
pTrigger->pev->rendercolor.z = 255;
|
|
|
|
else
|
|
|
|
pTrigger->pev->rendercolor.y = 255;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool COOP_ProcessTransition( void )
|
|
|
|
{
|
|
|
|
bool fAddCurrent = true;
|
|
|
|
edict_t *landmark;
|
|
|
|
|
|
|
|
// only set pCurrentMap when loading
|
|
|
|
if( g_CoopState.landmarkTransition.fLoading )
|
|
|
|
{
|
|
|
|
for( struct COOPMapState *pMapState = g_CoopState.pMapStates; pMapState; pMapState = pMapState->pNext )
|
|
|
|
{
|
|
|
|
if( !strcmp( pMapState->p.szMapName, STRING(gpGlobals->mapname) ) )
|
|
|
|
{
|
|
|
|
g_CoopState.pCurrentMap = pMapState;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset( &g_CoopState.p.savedPos, 0, sizeof( struct GGMPosition ) );
|
|
|
|
g_CoopState.p.fSaved = false;
|
|
|
|
|
|
|
|
COOP_MarkTriggers();
|
|
|
|
|
|
|
|
g_CoopState.p.savedPos = g_CoopState.landmarkTransition.pos;
|
|
|
|
g_CoopState.p.fSaved = g_CoopState.landmarkTransition.fSavedPos;
|
|
|
|
|
|
|
|
if( !mp_coop.value )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if( !g_CoopState.landmarkTransition.szLandmarkName[0] )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if( strcmp( g_CoopState.landmarkTransition.szTargetMap, STRING(gpGlobals->mapname) ) )
|
|
|
|
return false;
|
|
|
|
landmark = COOP_FindLandmark( g_CoopState.landmarkTransition.szLandmarkName );
|
|
|
|
if( !landmark )
|
|
|
|
return false;
|
|
|
|
Vector &lm = landmark->v.origin;
|
|
|
|
|
|
|
|
for( struct COOPMapState *pMapState = g_CoopState.pMapStates; pMapState; pMapState = pMapState->pNext )
|
|
|
|
{
|
|
|
|
if( !strcmp( pMapState->p.szMapName, STRING(gpGlobals->mapname) ) )
|
|
|
|
{
|
|
|
|
pMapState->p.vecOffset = Vector( 0, 0, 0 );
|
|
|
|
fAddCurrent = false;
|
|
|
|
g_CoopState.pCurrentMap = pMapState;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
pMapState->p.vecOffset = pMapState->p.vecOffset - g_CoopState.landmarkTransition.vecLandmark + lm;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( fAddCurrent )
|
|
|
|
{
|
|
|
|
COOPMapState *pNewState = (COOPMapState *)calloc(1, sizeof( struct COOPMapState ) );
|
|
|
|
|
|
|
|
pNewState->pNext = g_CoopState.pMapStates;
|
|
|
|
pNewState->p.vecOffset = Vector(0, 0, 0);
|
|
|
|
strncpy(pNewState->p.szMapName, STRING(gpGlobals->mapname), 31);
|
|
|
|
g_CoopState.pMapStates = g_CoopState.pCurrentMap = pNewState;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void COOP_SetupLandmarkTransition( const char *szNextMap, const char *szNextSpot, Vector vecLandmarkOffset, struct GGMPosition *pPos )
|
|
|
|
{
|
|
|
|
g_CoopState.landmarkTransition.fLoading = false;
|
|
|
|
strncpy(g_CoopState.landmarkTransition.szSourceMap, STRING(gpGlobals->mapname), 31 );
|
|
|
|
strncpy(g_CoopState.landmarkTransition.szTargetMap, szNextMap, 31 );
|
|
|
|
strncpy(g_CoopState.landmarkTransition.szLandmarkName, szNextSpot, 31 );
|
|
|
|
g_CoopState.landmarkTransition.vecLandmark = vecLandmarkOffset;
|
|
|
|
if( pPos )
|
|
|
|
{
|
|
|
|
g_CoopState.landmarkTransition.pos = *pPos;
|
|
|
|
g_CoopState.landmarkTransition.fSavedPos = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void COOP_ServerActivate( void )
|
|
|
|
{
|
|
|
|
static float st_DupCheck;
|
|
|
|
if( g_CoopState.landmarkTransition.fLoading )
|
|
|
|
{
|
|
|
|
st_DupCheck = gpGlobals->time;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( st_DupCheck && gpGlobals->time && st_DupCheck == gpGlobals->time)
|
|
|
|
{
|
|
|
|
st_DupCheck = 0.0f;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !mp_coop.value )
|
|
|
|
return;
|
|
|
|
|
|
|
|
GGM_ConnectSaveBot();
|
|
|
|
|
|
|
|
if( !COOP_ProcessTransition() )
|
|
|
|
{
|
|
|
|
ALERT( at_console, "Transition failed, new game started\n");
|
|
|
|
while( g_CoopState.pMapStates )
|
|
|
|
{
|
|
|
|
COOPMapState *pMapState = g_CoopState.pMapStates;
|
|
|
|
g_CoopState.pMapStates = pMapState->pNext;
|
|
|
|
free( pMapState );
|
|
|
|
}
|
|
|
|
COOPMapState *pNewState = (COOPMapState *)calloc(1, sizeof( struct COOPMapState ) );
|
|
|
|
|
|
|
|
memset( &g_CoopState.p, 0, sizeof( g_CoopState.p ) );
|
|
|
|
pNewState->pNext = g_CoopState.pMapStates;
|
|
|
|
pNewState->p.vecOffset = Vector(0, 0, 0);
|
|
|
|
strncpy(pNewState->p.szMapName, STRING(gpGlobals->mapname), 31);
|
|
|
|
g_CoopState.pMapStates = g_CoopState.pCurrentMap = pNewState;
|
|
|
|
GGM_ClearLists();
|
|
|
|
}
|
|
|
|
|
|
|
|
g_fPause = false;
|
|
|
|
g_CoopState.flMsgLimit1 = g_CoopState.flMsgLimit2 = 0;
|
|
|
|
|
|
|
|
|
|
|
|
for( int i = 1; i <= gpGlobals->maxClients; i++ )
|
|
|
|
{
|
|
|
|
CBasePlayer *plr = (CBasePlayer*)UTIL_PlayerByIndex( i );
|
|
|
|
|
|
|
|
// reset all players state
|
|
|
|
if( plr )
|
|
|
|
{
|
|
|
|
plr->m_ggm.iState = STATE_UNINITIALIZED;
|
|
|
|
plr->RemoveAllItems( TRUE );
|
|
|
|
UTIL_BecomeSpectator( plr );
|
|
|
|
//plr->Spawn();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( !g_CoopState.landmarkTransition.fLoading && g_CoopState.p.fSaved && mp_coop_checkpoints.value )
|
|
|
|
{
|
|
|
|
memmove( &g_CoopState.pCurrentMap->p.rgCheckpoints[1], &g_CoopState.pCurrentMap->p.rgCheckpoints[0], sizeof ( g_CoopState.pCurrentMap->p.rgCheckpoints[0] ) * 3 );
|
|
|
|
g_CoopState.pCurrentMap->p.rgCheckpoints[0].flTime = gpGlobals->time;
|
|
|
|
snprintf( g_CoopState.pCurrentMap->p.rgCheckpoints[0].szDisplayName, 31, "From %s", g_CoopState.landmarkTransition.szSourceMap );
|
|
|
|
g_CoopState.pCurrentMap->p.rgCheckpoints[0].pos = g_CoopState.p.savedPos;
|
|
|
|
}
|
|
|
|
if( !g_CoopState.landmarkTransition.fLoading )
|
|
|
|
COOP_MapStartSave();
|
|
|
|
memset( &g_CoopState.landmarkTransition, 0, sizeof( struct COOPLandmarkTransition ) );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool COOP_GetOrigin( Vector *pvecNewOrigin, const Vector &vecOrigin, const char *pszMapName )
|
|
|
|
{
|
|
|
|
if( !mp_coop.value )
|
|
|
|
{
|
|
|
|
if( !strcmp( STRING( gpGlobals->mapname ), pszMapName ) )
|
|
|
|
{
|
|
|
|
*pvecNewOrigin = vecOrigin;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
for( COOPMapState *pOffset = g_CoopState.pMapStates; pOffset; pOffset = pOffset->pNext )
|
|
|
|
{
|
|
|
|
if( !strcmp( pOffset->p.szMapName, pszMapName ) )
|
|
|
|
{
|
|
|
|
*pvecNewOrigin = vecOrigin + pOffset->p.vecOffset;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void COOP_NewCheckpoint( entvars_t *pevPlayer )
|
|
|
|
{
|
|
|
|
if( !pevPlayer->netname || pevPlayer->health <= 0 )
|
|
|
|
return;
|
|
|
|
memmove( &g_CoopState.pCurrentMap->p.rgCheckpoints[1], &g_CoopState.pCurrentMap->p.rgCheckpoints[0], sizeof ( g_CoopState.pCurrentMap->p.rgCheckpoints[0] ) * 3 );
|
|
|
|
g_CoopState.pCurrentMap->p.rgCheckpoints[0].flTime = gpGlobals->time;
|
|
|
|
snprintf( g_CoopState.pCurrentMap->p.rgCheckpoints[0].szDisplayName, 31, "%5s %d", STRING( pevPlayer->netname ), (int)( gpGlobals->time / 60 ) );
|
|
|
|
GGM_SavePosition( (CBasePlayer*)CBaseEntity::Instance( pevPlayer ), &g_CoopState.pCurrentMap->p.rgCheckpoints[0].pos );
|
|
|
|
UTIL_CoopPrintMessage("New checkpoint by %s!\n", STRING( pevPlayer->netname ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool COOP_PlayerDeath( CBasePlayer *pPlayer )
|
|
|
|
{
|
|
|
|
static bool st_fSkipNext;
|
|
|
|
if( st_fSkipNext )
|
|
|
|
{
|
|
|
|
st_fSkipNext = false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if( pPlayer->pev->health > 0 || pPlayer->m_ggm.iState != STATE_SPAWNED )
|
|
|
|
return true;
|
|
|
|
// if( pPlayer->gravgunmod_data.m_iMenuState == MENUSTATE_CHECKPOINT )
|
|
|
|
// return true;
|
|
|
|
|
|
|
|
if( g_CoopState.pCurrentMap->p.rgCheckpoints[0].flTime )
|
|
|
|
{
|
|
|
|
COOP_CheckpointMenu( pPlayer );
|
|
|
|
st_fSkipNext = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool COOP_SetDefaultSpawnPosition( CBasePlayer *pPlayer )
|
|
|
|
{
|
|
|
|
if( !g_CoopState.p.fSaved )
|
|
|
|
return false;
|
|
|
|
return GGM_RestorePosition( pPlayer, &g_CoopState.p.savedPos );
|
|
|
|
}
|
|
|
|
|
|
|
|
CBaseEntity *UTIL_CoopGetPlayerTrain( CBaseEntity *pPlayer)
|
|
|
|
{
|
|
|
|
CBaseEntity *train = NULL;
|
|
|
|
|
|
|
|
if( !pPlayer)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if( !pPlayer->IsPlayer() )
|
|
|
|
{
|
|
|
|
// activated by path track
|
|
|
|
train = pPlayer;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( FNullEnt(pPlayer->pev->groundentity))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
train = CBaseEntity::Instance(pPlayer->pev->groundentity);
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !train )
|
|
|
|
return NULL;
|
|
|
|
if( !train->pev->globalname ||!STRING(train->pev->globalname) || !STRING(train->pev->globalname)[0] )
|
|
|
|
return NULL;
|
|
|
|
// doors are elevators
|
|
|
|
if( strcmp( STRING( train->pev->classname ), "func_train") && strcmp( STRING( train->pev->classname ), "func_tracktrain") && strcmp( STRING( train->pev->classname ), "func_door") )
|
|
|
|
return NULL;
|
|
|
|
return train;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool COOP_ConfirmMenu(CBaseEntity *pTrigger, CBaseEntity *pActivator, int count2, char *mapname )
|
|
|
|
{
|
|
|
|
if( mp_coop_strongcheckpoints.value )
|
|
|
|
{
|
|
|
|
// do not allow go back if there are checkpoints, but not near changelevel
|
|
|
|
if( g_CoopState.pCurrentMap->p.rgCheckpoints[0].flTime && (g_CoopState.pCurrentMap->p.rgCheckpoints[0].pos.vecOrigin - VecBModelOrigin(pTrigger->pev)).Length() > 150 )
|
|
|
|
{
|
|
|
|
ClientPrint( pActivator->pev, HUD_PRINTCENTER, "Changelevel back locked by checkpoint\nCheckpoint here to activate trigger!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( GGM_ChangelevelVote((CBasePlayer*)pActivator, pTrigger->edict(), mapname ) < count2 )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void COOP_CheckpointMenu( CBasePlayer *pPlayer )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if( !mp_coop_checkpoints.value )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if( !g_CoopState.pCurrentMap )
|
|
|
|
return;
|
|
|
|
|
|
|
|
GGM_PlayerMenu &m = pPlayer->m_ggm.menu.New("Select checkpoint", false);
|
|
|
|
|
|
|
|
if( pPlayer->m_ggm.iState == STATE_SPECTATOR || pPlayer->m_ggm.iState == STATE_SPECTATOR_BEGIN || pPlayer->pev->health <= 0 )
|
|
|
|
m.Add("Default", "respawn");
|
|
|
|
else
|
|
|
|
m.Add("New checkpoint", "newcheckpoint");
|
|
|
|
|
|
|
|
for( i = 1; g_CoopState.pCurrentMap->p.rgCheckpoints[i-1].flTime; i++ )
|
|
|
|
{
|
|
|
|
char cmd[32];
|
|
|
|
sprintf(cmd, "loadcheckpoint %d", i-1 );
|
|
|
|
m.Add(g_CoopState.pCurrentMap->p.rgCheckpoints[i-1].szDisplayName, cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
m.Show();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool COOP_ClientCommand( edict_t *pEntity )
|
|
|
|
{
|
|
|
|
const char *pcmd = CMD_ARGV(0);
|
|
|
|
CBasePlayer *pPlayer = (CBasePlayer*)GET_PRIVATE(pEntity);
|
|
|
|
entvars_t *pev = &pEntity->v;
|
|
|
|
if( !mp_coop.value )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
else if( FStrEq(pcmd, "unblock") )
|
|
|
|
{
|
|
|
|
if( pPlayer->m_ggm.iState != STATE_SPAWNED )
|
|
|
|
return false;
|
|
|
|
UTIL_CleanSpawnPoint( pev->origin, 150 );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if( FStrEq( pcmd, "joincoop" ) )
|
|
|
|
{
|
|
|
|
if( pPlayer->m_ggm.iState == STATE_SPAWNED )
|
|
|
|
return false;
|
|
|
|
if( mp_coop_checkpoints.value && g_CoopState.pCurrentMap && g_CoopState.pCurrentMap->p.rgCheckpoints[0].szDisplayName[0] )
|
|
|
|
COOP_CheckpointMenu( pPlayer );
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pPlayer->RemoveAllItems( TRUE );
|
|
|
|
UTIL_SpawnPlayer( pPlayer );
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if( FStrEq( pcmd, "coopmenu" ) )
|
|
|
|
{
|
|
|
|
//UTIL_CoopMenu( pPlayer );
|
|
|
|
if( pPlayer->m_ggm.iState == STATE_SPAWNED )
|
|
|
|
{
|
|
|
|
GGM_PlayerMenu &m = pPlayer->m_ggm.menu.New( "COOP MENU" )
|
|
|
|
.Add( "Unblock", "unblock" )
|
|
|
|
/// TODO: statistics button here
|
|
|
|
.Add( "Respawn", "respawn" )
|
|
|
|
.Add( "Other", "coopmenu1");
|
|
|
|
if( mp_coop_checkpoints.value )
|
|
|
|
m.Add( "Checkpoints", "checkpointmenu" );
|
|
|
|
m.Add( "Cancel", "" );
|
|
|
|
m.Show();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if( pPlayer->m_ggm.iState == STATE_SPECTATOR )
|
|
|
|
{
|
|
|
|
pPlayer->m_ggm.menu.New( "COOP MENU" )
|
|
|
|
.Add( "Join game", "joincoop" )
|
|
|
|
.Add( "Cancel", "" )
|
|
|
|
.Show();
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( FStrEq( pcmd, "coopmenu1" ) )
|
|
|
|
{
|
|
|
|
//UTIL_CoopMenu( pPlayer );
|
|
|
|
if( pPlayer->m_ggm.iState == STATE_SPAWNED )
|
|
|
|
{
|
|
|
|
pPlayer->m_ggm.menu.New( "COOP MENU" )
|
|
|
|
.Add( "Force respawn", "respawn" )
|
|
|
|
.Add( "Spectate", "spectate" )
|
|
|
|
.Add( "Vote load", "voteload")
|
|
|
|
.Add( "Cancel", "" )
|
|
|
|
.Show();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( FStrEq( pcmd, "respawn" ) )
|
|
|
|
{
|
|
|
|
pPlayer->RemoveAllItems( TRUE );
|
|
|
|
UTIL_SpawnPlayer( pPlayer );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if( FStrEq( pcmd, "checkpointmenu") )
|
|
|
|
{
|
|
|
|
if( !mp_coop_checkpoints.value )
|
|
|
|
return false;
|
|
|
|
COOP_CheckpointMenu( pPlayer );
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if( FStrEq( pcmd, "loadcheckpoint") )
|
|
|
|
{
|
|
|
|
int i = atoi(CMD_ARGV(1));
|
|
|
|
if( i > 4 || i < 0 )
|
|
|
|
return false;
|
|
|
|
if( pPlayer->m_ggm.iState != STATE_SPAWNED || pPlayer->pev->health < 1 )
|
|
|
|
UTIL_SpawnPlayer( pPlayer );
|
|
|
|
GGM_RestorePosition( pPlayer, &g_CoopState.pCurrentMap->p.rgCheckpoints[i].pos );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if( FStrEq( pcmd, "newcheckpoint") )
|
|
|
|
{
|
|
|
|
if( !mp_coop_checkpoints.value )
|
|
|
|
return false;
|
|
|
|
if( pPlayer->m_ggm.iState != STATE_SPAWNED )
|
|
|
|
return false;
|
|
|
|
if( !GGM_IsTempBanned( pPlayer ) )
|
|
|
|
COOP_NewCheckpoint( pPlayer->pev );
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if( FStrEq( pcmd, "voteload") )
|
|
|
|
{
|
|
|
|
if( !mp_coop_checkpoints.value )
|
|
|
|
return false;
|
|
|
|
if( pPlayer->m_ggm.iState != STATE_SPAWNED )
|
|
|
|
return false;
|
|
|
|
if( !GGM_IsTempBanned( pPlayer ) )
|
|
|
|
{
|
|
|
|
if( CMD_ARGC() == 1)
|
|
|
|
{
|
|
|
|
GGM_PlayerMenu &menu = pPlayer->m_ggm.menu.New("Select save", false );
|
|
|
|
|
|
|
|
for( int i = 0; i < 4; i++ )
|
|
|
|
if( g_CoopState.p.rgszSaveSlots[i][0] )
|
|
|
|
menu.Add( g_CoopState.p.rgszSaveSlots[i], UTIL_VarArgs("voteload %s",g_CoopState.p.rgszSaveSlots[i]) );
|
|
|
|
|
|
|
|
menu.Add( "Cancel", "" ).Show();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char szCommand[32];
|
|
|
|
snprintf( szCommand, 31, "ggm_load %s", CMD_ARGV(1) );
|
|
|
|
|
|
|
|
GGM_StartVoteCommand( pPlayer, szCommand, UTIL_VarArgs("Load save %s", CMD_ARGV(1) ));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if( FStrEq( pcmd, "confirmchangelevel" ) )
|
|
|
|
{
|
|
|
|
if( pPlayer->m_ggm.iLocalConfirm )
|
|
|
|
{
|
|
|
|
pPlayer->m_ggm.iLocalConfirm++;
|
|
|
|
if( pPlayer->m_ggm.pChangeLevel )
|
|
|
|
{
|
|
|
|
edict_t *pChangeLevel = pPlayer->m_ggm.pChangeLevel;
|
|
|
|
pPlayer->m_ggm.pChangeLevel = NULL;
|
|
|
|
DispatchTouch( pChangeLevel, pPlayer->edict() );
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void COOP_RegisterCVars()
|
|
|
|
{
|
|
|
|
CVAR_REGISTER( &mp_coop );
|
|
|
|
CVAR_REGISTER( &mp_coop_nofriendlyfire );
|
|
|
|
|
|
|
|
CVAR_REGISTER( &mp_semclip );
|
|
|
|
CVAR_REGISTER( &mp_coop_reconnect_hack );
|
|
|
|
CVAR_REGISTER( &mp_coop_noangry );
|
|
|
|
CVAR_REGISTER( &mp_coop_checkpoints );
|
|
|
|
CVAR_REGISTER( &mp_coop_strongcheckpoints );
|
|
|
|
|
|
|
|
CVAR_REGISTER( &sentences_txt );
|
|
|
|
CVAR_REGISTER( &materials_txt );
|
|
|
|
}
|