mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-04-29 14:17:57 +03:00
2117 lines
38 KiB
C++
2117 lines
38 KiB
C++
/*
|
|
===========================================================================
|
|
Copyright (C) 2015 the OpenMoHAA team
|
|
|
|
This file is part of OpenMoHAA source code.
|
|
|
|
OpenMoHAA source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
OpenMoHAA source code is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with OpenMoHAA source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
// level.cpp : Global Level Info.
|
|
|
|
#include "g_local.h"
|
|
#include "g_spawn.h"
|
|
#include "level.h"
|
|
#include "parm.h"
|
|
#include "navigate.h"
|
|
#include "gravpath.h"
|
|
#include "entity.h"
|
|
#include "dm_manager.h"
|
|
#include "player.h"
|
|
|
|
#include "scriptmaster.h"
|
|
#include "scriptthread.h"
|
|
#include "scriptvariable.h"
|
|
#include "scriptexception.h"
|
|
|
|
Level level;
|
|
|
|
gclient_t *spawn_client = NULL;
|
|
|
|
Event EV_Level_GetTime
|
|
(
|
|
"time",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"current level time",
|
|
EV_GETTER
|
|
);
|
|
|
|
Event EV_Level_GetTotalSecrets
|
|
(
|
|
"total_secrets",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"count of total secrets",
|
|
EV_GETTER
|
|
);
|
|
|
|
Event EV_Level_GetFoundSecrets
|
|
(
|
|
"found_secrets",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"count of found secrets",
|
|
EV_GETTER
|
|
);
|
|
|
|
Event EV_Level_PreSpawnSentient
|
|
(
|
|
"prespawnsentient",
|
|
EV_CODEONLY,
|
|
NULL,
|
|
NULL,
|
|
"internal usage."
|
|
);
|
|
|
|
Event EV_Level_GetAlarm
|
|
(
|
|
"alarm",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"zero = global level alarm off, non-zero = alarm on",
|
|
EV_GETTER
|
|
);
|
|
|
|
Event EV_Level_SetAlarm
|
|
(
|
|
"alarm",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"alarm_status",
|
|
"zero = global level alarm off, non-zero = alarm on",
|
|
EV_SETTER
|
|
);
|
|
|
|
Event EV_Level_SetNoDropHealth
|
|
(
|
|
"nodrophealth",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"alarm_status",
|
|
"zero = automatically drop health according to cvars, non-zero = don't autodrop health (like hard mode)",
|
|
EV_SETTER
|
|
);
|
|
|
|
Event EV_Level_GetLoopProtection
|
|
(
|
|
"loop_protection",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"says if infinite loop protection is enabled",
|
|
EV_GETTER
|
|
);
|
|
|
|
Event EV_Level_SetLoopProtection
|
|
(
|
|
"loop_protection",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"loop_protection",
|
|
"specify if infinite loop protection is enabled",
|
|
EV_SETTER
|
|
);
|
|
|
|
Event EV_Level_GetPapersLevel
|
|
(
|
|
"papers",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"the level of papers the player currently has",
|
|
EV_GETTER
|
|
);
|
|
|
|
Event EV_Level_SetPapersLevel
|
|
(
|
|
"papers",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"the level of papers the player currently has",
|
|
EV_SETTER
|
|
);
|
|
|
|
Event EV_Level_GetDMRespawning
|
|
(
|
|
"dmrespawning",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"returns 1 if wave-based DM, 0 if respawns are disabled within a round",
|
|
EV_GETTER
|
|
);
|
|
|
|
Event EV_Level_SetDMRespawning
|
|
(
|
|
"dmrespawning",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"allow_respawn",
|
|
"set to 1 to turn on wave-based DM, to 0 to disable respawns within a round",
|
|
EV_SETTER
|
|
);
|
|
|
|
Event EV_Level_SetDMRespawning2
|
|
(
|
|
"dmrespawning",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"allow_respawn",
|
|
"set to 1 to turn on wave-based DM, to 0 to disable respawns within a round"
|
|
);
|
|
|
|
Event EV_Level_GetDMRoundLimit
|
|
(
|
|
"dmroundlimit",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"gets the actual roundlimit, in minutes; may be 'roundlimit' cvar or the default round limit",
|
|
EV_GETTER
|
|
);
|
|
|
|
Event EV_Level_SetDMRoundLimit
|
|
(
|
|
"dmroundlimit",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"roundlimit",
|
|
"sets the default roundlimit, in minutes; can be overridden by 'roundlimit' cvar",
|
|
EV_SETTER
|
|
);
|
|
|
|
Event EV_Level_SetDMRoundLimit2
|
|
(
|
|
"dmroundlimit",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"roundlimit",
|
|
"sets the default roundlimit, in minutes; can be overridden by 'roundlimit' cvar"
|
|
);
|
|
|
|
Event EV_Level_GetClockSide
|
|
(
|
|
"clockside",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Gets which side the clock is on... 'axis' or 'allies' win when time is up",
|
|
EV_GETTER
|
|
);
|
|
|
|
Event EV_Level_SetClockSide
|
|
(
|
|
"clockside",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"axis_or_allies",
|
|
"Sets which side the clock is on... 'axis' or 'allies' win when time is up",
|
|
EV_SETTER
|
|
);
|
|
|
|
Event EV_Level_SetClockSide2
|
|
(
|
|
"clockside",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"axis_allies_draw_kills",
|
|
"Sets which side the clock is on... 'axis' or 'allies' win when time is up, 'kills' gives the win to the team with more live members, 'draw' no one wins"
|
|
);
|
|
|
|
Event EV_Level_GetBombPlantTeam
|
|
(
|
|
"planting_team",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Gets which is planting the bomb, 'axis' or 'allies'",
|
|
EV_GETTER
|
|
);
|
|
|
|
Event EV_Level_SetBombPlantTeam
|
|
(
|
|
"planting_team",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"axis_or_allies",
|
|
"Sets which is planting the bomb, 'axis' or 'allies'",
|
|
EV_SETTER
|
|
);
|
|
|
|
Event EV_Level_SetBombPlantTeam2
|
|
(
|
|
"planting_team",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"axis_allies_draw_kills",
|
|
"which is planting the bomb, 'axis' or 'allies'"
|
|
);
|
|
|
|
Event EV_Level_GetTargetsToDestroy
|
|
(
|
|
"targets_to_destroy",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Gets the number of bomb targets that must be destroyed",
|
|
EV_GETTER
|
|
);
|
|
|
|
Event EV_Level_SetTargetsToDestroy
|
|
(
|
|
"targets_to_destroy",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"num",
|
|
"Sets the number of bomb targets that must be destroyed",
|
|
EV_SETTER
|
|
);
|
|
|
|
Event EV_Level_SetTargetsToDestroy2
|
|
(
|
|
"targets_to_destroy",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"num",
|
|
"the number of bomb targets that must be destroyed"
|
|
);
|
|
|
|
Event EV_Level_GetTargetsDestroyed
|
|
(
|
|
"targets_destroyed",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Gets the number of bomb targets that have been destroyed",
|
|
EV_GETTER
|
|
);
|
|
|
|
Event EV_Level_SetTargetsDestroyed
|
|
(
|
|
"targets_destroyed",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"num",
|
|
"Sets the number of bomb targets that have been destroyed",
|
|
EV_SETTER
|
|
);
|
|
|
|
Event EV_Level_SetTargetsDestroyed2
|
|
(
|
|
"targets_destroyed",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"num",
|
|
"the number of bomb targets that have been destroyed"
|
|
);
|
|
|
|
Event EV_Level_GetBombsPlanted
|
|
(
|
|
"bombs_planted",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Gets the number of bombs that are set",
|
|
EV_GETTER
|
|
);
|
|
|
|
Event EV_Level_SetBombsPlanted
|
|
(
|
|
"bombs_planted",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"num",
|
|
"Sets the number of bombs that are set",
|
|
EV_SETTER
|
|
);
|
|
|
|
Event EV_Level_SetBombsPlanted2
|
|
(
|
|
"bombs_planted",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"num",
|
|
"the number of bombs that are set"
|
|
);
|
|
|
|
Event EV_Level_GetRoundBased
|
|
(
|
|
"roundbased",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Gets wether or not the game is currently round based or not",
|
|
EV_GETTER
|
|
);
|
|
|
|
Event EV_Level_Rain_Density_Set
|
|
(
|
|
"rain_density",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets the rain density",
|
|
EV_SETTER
|
|
);
|
|
|
|
Event EV_Level_Rain_Density_Get
|
|
(
|
|
"rain_density",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets the rain density",
|
|
EV_GETTER
|
|
);
|
|
|
|
Event EV_Level_Rain_Speed_Set
|
|
(
|
|
"rain_speed",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets the rain speed",
|
|
3
|
|
);
|
|
|
|
Event EV_Level_Rain_Speed_Get
|
|
(
|
|
"rain_speed",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets the rain speed",
|
|
2
|
|
);
|
|
|
|
Event EV_Level_Rain_Speed_Vary_Set
|
|
(
|
|
"rain_speed_vary",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets the rain speed variance",
|
|
EV_SETTER
|
|
);
|
|
|
|
Event EV_Level_Rain_Speed_Vary_Get
|
|
(
|
|
"rain_speed_vary",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets the rain speed variance",
|
|
EV_GETTER
|
|
);
|
|
|
|
Event EV_Level_Rain_Slant_Set
|
|
(
|
|
"rain_slant",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets the rain slant",
|
|
EV_SETTER
|
|
);
|
|
|
|
Event EV_Level_Rain_Slant_Get
|
|
(
|
|
"rain_slant",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets the rain slant",
|
|
EV_GETTER
|
|
);
|
|
|
|
Event EV_Level_Rain_Length_Set
|
|
(
|
|
"rain_length",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets the rain length",
|
|
EV_SETTER
|
|
);
|
|
|
|
Event EV_Level_Rain_Length_Get
|
|
(
|
|
"rain_length",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets the rain length",
|
|
EV_GETTER
|
|
);
|
|
|
|
Event EV_Level_Rain_Min_Dist_Set
|
|
(
|
|
"rain_min_dist",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets the rain min_dist",
|
|
EV_SETTER
|
|
);
|
|
|
|
Event EV_Level_Rain_Min_Dist_Get
|
|
(
|
|
"rain_min_dist",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets the rain min_dist",
|
|
EV_GETTER
|
|
);
|
|
|
|
Event EV_Level_Rain_Width_Set
|
|
(
|
|
"rain_width",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets the rain width",
|
|
EV_SETTER
|
|
);
|
|
|
|
Event EV_Level_Rain_Width_Get
|
|
(
|
|
"rain_width",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets the rain width",
|
|
EV_GETTER
|
|
);
|
|
|
|
Event EV_Level_Rain_Shader_Set
|
|
(
|
|
"rain_shader",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets the rain ",
|
|
EV_SETTER
|
|
);
|
|
|
|
Event EV_Level_Rain_Shader_Get
|
|
(
|
|
"rain_shader",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets the rain ",
|
|
EV_GETTER
|
|
);
|
|
|
|
Event EV_Level_Rain_NumShaders_Set
|
|
(
|
|
"rain_numshaders",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets the rain numshaders",
|
|
EV_SETTER
|
|
);
|
|
|
|
Event EV_Level_Rain_NumShaders_Get
|
|
(
|
|
"rain_numshaders",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets the rain numshaders",
|
|
EV_GETTER
|
|
);
|
|
|
|
Event EV_Level_GetFlags
|
|
(
|
|
"flag",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Initialized flag list",
|
|
EV_GETTER
|
|
);
|
|
|
|
extern Event EV_Entity_Start;
|
|
|
|
Level::Level()
|
|
{
|
|
Init();
|
|
}
|
|
|
|
Level::~Level()
|
|
{
|
|
|
|
}
|
|
|
|
void Level::Archive( Archiver& arc )
|
|
{
|
|
bool prespawn;
|
|
bool spawn;
|
|
|
|
Listener::Archive( arc );
|
|
|
|
if( arc.Saving() )
|
|
{
|
|
prespawn = classinfo()->WaitTillDefined( "prespawn" );
|
|
spawn = classinfo()->WaitTillDefined( "spawn" );
|
|
}
|
|
|
|
arc.ArchiveBool( &prespawn );
|
|
arc.ArchiveBool( &spawn );
|
|
|
|
if( arc.Loading() )
|
|
{
|
|
if( prespawn ) {
|
|
AddWaitTill( STRING_PRESPAWN );
|
|
}
|
|
|
|
if( spawn ) {
|
|
AddWaitTill( STRING_SPAWN );
|
|
}
|
|
}
|
|
|
|
arc.ArchiveInteger( &framenum );
|
|
|
|
arc.ArchiveString( &level_name );
|
|
arc.ArchiveString( &mapname );
|
|
arc.ArchiveString( &spawnpoint );
|
|
arc.ArchiveString( &nextmap );
|
|
|
|
arc.ArchiveBoolean( &playerfrozen );
|
|
|
|
arc.ArchiveFloat( &intermissiontime );
|
|
ArchiveEnum( intermissiontype, INTTYPE_e );
|
|
arc.ArchiveInteger( &exitintermission );
|
|
|
|
arc.ArchiveInteger( &total_secrets );
|
|
arc.ArchiveInteger( &found_secrets );
|
|
|
|
arc.ArchiveFloat( &earthquake_magnitude );
|
|
arc.ArchiveInteger( &num_earthquakes );
|
|
|
|
for( int i = 0; i < num_earthquakes; i++ )
|
|
{
|
|
arc.ArchiveInteger( &earthquakes[ i ].duration );
|
|
arc.ArchiveFloat( &earthquakes[ i ].magnitude );
|
|
arc.ArchiveBool( &earthquakes[ i ].no_rampup );
|
|
arc.ArchiveBool( &earthquakes[ i ].no_rampdown );
|
|
|
|
arc.ArchiveInteger( &earthquakes[ i ].starttime );
|
|
arc.ArchiveInteger( &earthquakes[ i ].endtime );
|
|
|
|
arc.ArchiveSafePointer( &earthquakes[ i ].m_Thread );
|
|
}
|
|
|
|
arc.ArchiveBoolean( &cinematic );
|
|
arc.ArchiveBoolean( &ai_on );
|
|
arc.ArchiveBoolean( &mission_failed );
|
|
arc.ArchiveBoolean( &died_already );
|
|
|
|
arc.ArchiveVector( &water_color );
|
|
arc.ArchiveVector( &lava_color );
|
|
|
|
arc.ArchiveFloat( &water_alpha );
|
|
arc.ArchiveFloat( &lava_alpha );
|
|
|
|
arc.ArchiveString( ¤t_soundtrack );
|
|
arc.ArchiveString( &saved_soundtrack );
|
|
|
|
arc.ArchiveVector( &m_fade_color );
|
|
arc.ArchiveFloat( &m_fade_alpha );
|
|
arc.ArchiveFloat( &m_fade_time );
|
|
arc.ArchiveFloat( &m_fade_time_start );
|
|
ArchiveEnum( m_fade_style, fadestyle_t );
|
|
ArchiveEnum( m_fade_type, fadetype_t );
|
|
|
|
arc.ArchiveFloat( &m_letterbox_fraction );
|
|
arc.ArchiveFloat( &m_letterbox_time );
|
|
arc.ArchiveFloat( &m_letterbox_time_start );
|
|
|
|
ArchiveEnum( m_letterbox_dir, letterboxdir_t );
|
|
arc.ArchiveInteger( &m_iCuriousVoiceTime );
|
|
arc.ArchiveInteger( &m_iAttackEntryAnimTime );
|
|
arc.ArchiveInteger( &mHealthPopCount );
|
|
|
|
arc.ArchiveBoolean( &m_bAlarm );
|
|
arc.ArchiveBoolean( &mbNoDropHealth );
|
|
arc.ArchiveInteger( &m_iPapersLevel );
|
|
arc.ArchiveInteger( &m_LoopProtection );
|
|
|
|
memset( skel_index, 255, sizeof( skel_index ) );
|
|
|
|
if( arc.Loading() )
|
|
{
|
|
str saved = saved_soundtrack;
|
|
ChangeSoundtrack( current_soundtrack );
|
|
saved_soundtrack = saved;
|
|
|
|
memset( &impact_trace, 0, sizeof( trace_t ) );
|
|
}
|
|
|
|
for( int i = 0; i < MAX_HEAD_SENTIENTS; i++ )
|
|
{
|
|
arc.ArchiveObjectPointer( ( Class ** )&m_HeadSentient[ i ] );
|
|
}
|
|
|
|
// FIXME: Archive Actor::mBodyQueue
|
|
// FIXME: Archive Actor::mCurBody
|
|
|
|
arc.ArchiveConfigString( CS_CURRENT_OBJECTIVE );
|
|
|
|
for( int i = CS_OBJECTIVES; i < CS_OBJECTIVES + MAX_OBJECTIVES; i++ )
|
|
{
|
|
arc.ArchiveConfigString( i );
|
|
}
|
|
|
|
arc.ArchiveConfigString( CS_RAIN_DENSITY );
|
|
arc.ArchiveConfigString( CS_RAIN_SPEED );
|
|
arc.ArchiveConfigString( CS_RAIN_SPEEDVARY );
|
|
arc.ArchiveConfigString( CS_RAIN_SLANT );
|
|
arc.ArchiveConfigString( CS_RAIN_LENGTH );
|
|
arc.ArchiveConfigString( CS_RAIN_MINDIST );
|
|
arc.ArchiveConfigString( CS_RAIN_WIDTH );
|
|
arc.ArchiveConfigString( CS_RAIN_SHADER );
|
|
arc.ArchiveConfigString( CS_RAIN_NUMSHADERS );
|
|
}
|
|
|
|
void Level::Init( void )
|
|
{
|
|
m_HeadSentient[ 1 ] = NULL;
|
|
m_HeadSentient[ 0 ] = NULL;
|
|
|
|
spawn_entnum = -1;
|
|
|
|
current_map = NULL;
|
|
|
|
framenum = 0;
|
|
time = 0;
|
|
frametime = 0;
|
|
|
|
level_name = "";
|
|
mapname = "";
|
|
spawnpoint = "";
|
|
nextmap = "";
|
|
|
|
total_secrets = 0;
|
|
found_secrets = 0;
|
|
|
|
m_iCuriousVoiceTime = 0;
|
|
m_iAttackEntryAnimTime = 0;
|
|
|
|
playerfrozen = false;
|
|
|
|
intermissiontime = 0.0f;
|
|
exitintermission = 0;
|
|
|
|
memset( &impact_trace, 0, sizeof( trace_t ) );
|
|
|
|
cinematic = false;
|
|
ai_on = true;
|
|
|
|
near_exit = false;
|
|
mission_failed = false;
|
|
|
|
m_bAlarm = false;
|
|
m_iPapersLevel = 0;
|
|
|
|
died_already = false;
|
|
|
|
water_color = vec_zero;
|
|
lava_color = vec_zero;
|
|
|
|
lava_alpha = 0.0f;
|
|
water_alpha = 0.0f;
|
|
|
|
saved_soundtrack = "";
|
|
current_soundtrack = "";
|
|
|
|
automatic_cameras.ClearObjectList();
|
|
|
|
m_fade_time_start = 0.0f;
|
|
m_fade_time = -1.0f;
|
|
m_fade_color = vec_zero;
|
|
m_fade_style = additive;
|
|
|
|
m_letterbox_dir = letterbox_out;
|
|
m_numArenas = 1;
|
|
|
|
m_fade_alpha = 0;
|
|
|
|
m_letterbox_fraction = 0;
|
|
m_letterbox_time_start = 0;
|
|
|
|
m_voteTime = 0;
|
|
m_numVoters = 0;
|
|
|
|
m_LoopProtection = true;
|
|
m_LoopDrop = true;
|
|
|
|
m_letterbox_time = -1.0f;
|
|
|
|
m_voteYes = 0;
|
|
m_voteNo = 0;
|
|
|
|
m_vObjectiveLocation = vec_zero;
|
|
|
|
svsEndTime = 0;
|
|
|
|
earthquake_magnitude = 0;
|
|
|
|
mHealthPopCount = 0;
|
|
mbNoDropHealth = false;
|
|
|
|
spawning = false;
|
|
|
|
m_bSpawnBot = false;
|
|
m_bScriptSpawn = false;
|
|
m_bRejectSpawn = false;
|
|
}
|
|
|
|
gentity_t *Level::AllocEdict( Entity *entity )
|
|
{
|
|
int i;
|
|
gentity_t *edict;
|
|
|
|
if( spawn_entnum >= 0 )
|
|
{
|
|
edict = &g_entities[ spawn_entnum ];
|
|
spawn_entnum = -1;
|
|
|
|
assert( !edict->inuse && !edict->entity );
|
|
|
|
// free up the entity pointer in case we took one that still exists
|
|
if( edict->inuse && edict->entity )
|
|
{
|
|
delete edict->entity;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
edict = &g_entities[ game.maxclients ];
|
|
|
|
for( i = game.maxclients; i < globals.num_entities; i++, edict++ )
|
|
{
|
|
// the first couple seconds of server time can involve a lot of
|
|
// freeing and allocating, so relax the replacement policy
|
|
if(
|
|
!edict->inuse &&
|
|
(
|
|
( edict->freetime < 2.0f ) ||
|
|
( time - edict->freetime > 0.5f )
|
|
)
|
|
)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// allow two spots for none and world
|
|
if( i == globals.max_entities - 2.0f )
|
|
{
|
|
// Try one more time before failing, relax timing completely
|
|
|
|
edict = &g_entities[ game.maxclients ];
|
|
|
|
for( i = game.maxclients; i < globals.num_entities; i++, edict++ )
|
|
{
|
|
if( !edict->inuse )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( i == globals.max_entities - 2.0f )
|
|
{
|
|
gi.Error( ERR_DROP, "Level::AllocEdict: no free edicts" );
|
|
}
|
|
}
|
|
}
|
|
|
|
LL_Remove( edict, next, prev );
|
|
|
|
InitEdict( edict );
|
|
|
|
LL_Add( &active_edicts, edict, next, prev );
|
|
|
|
// Tell the server about our data since we just spawned something
|
|
if( ( edict->s.number < ENTITYNUM_WORLD ) && ( globals.num_entities <= edict->s.number ) )
|
|
{
|
|
globals.num_entities = edict->s.number + 1;
|
|
gi.LocateGameData( g_entities, globals.num_entities, sizeof( gentity_t ), &game.clients[ 0 ].ps, sizeof( game.clients[ 0 ] ) );
|
|
}
|
|
|
|
edict->entity = entity;
|
|
|
|
return edict;
|
|
}
|
|
|
|
void Level::FreeEdict( gentity_t *ed )
|
|
{
|
|
gclient_t *client;
|
|
|
|
// unlink from world
|
|
gi.UnlinkEntity( ed );
|
|
|
|
LL_Remove( ed, next, prev );
|
|
|
|
client = ed->client;
|
|
|
|
memset( ed, 0, sizeof( *ed ) );
|
|
|
|
ed->client = client;
|
|
ed->freetime = time;
|
|
ed->inuse = false;
|
|
ed->s.number = ed - g_entities;
|
|
|
|
LL_Add( &free_edicts, ed, next, prev );
|
|
}
|
|
|
|
void Level::InitEdict( gentity_t *e )
|
|
{
|
|
int i;
|
|
|
|
e->inuse = true;
|
|
|
|
e->s.renderfx |= RF_FRAMELERP;
|
|
e->s.number = e - g_entities;
|
|
|
|
// make sure a default scale gets set
|
|
e->s.scale = 1.0f;
|
|
// make sure the default constantlight gets set, initalize to r 1.0, g 1.0, b 1.0, r 0
|
|
e->s.constantLight = 0xffffff;
|
|
|
|
e->s.wasframe = 0;
|
|
e->spawntime = level.time;
|
|
|
|
for( i = 0; i < NUM_BONE_CONTROLLERS; i++ )
|
|
{
|
|
e->s.bone_tag[ i ] = -1;
|
|
VectorClear( e->s.bone_angles[ i ] );
|
|
EulerToQuat( e->s.bone_angles[ i ], e->s.bone_quat[ i ] );
|
|
}
|
|
}
|
|
|
|
void Level::AddAutomaticCamera( Camera *cam )
|
|
{
|
|
automatic_cameras.AddUniqueObject( cam );
|
|
}
|
|
|
|
bool Level::PreSpawned( void )
|
|
{
|
|
return WaitTillDefined( "prespawn" );
|
|
}
|
|
|
|
bool Level::Spawned( void )
|
|
{
|
|
return WaitTillDefined( "spawn" );
|
|
}
|
|
|
|
void Level::AddEarthquake( earthquake_t *e )
|
|
{
|
|
if( num_earthquakes == MAX_EARTHQUAKES )
|
|
{
|
|
if( earthquakes[ 0 ].m_Thread ) {
|
|
earthquakes[ 0 ].m_Thread->Wait( ( float )( e->endtime - inttime ) / 1000.0f - 0.5f );
|
|
}
|
|
|
|
num_earthquakes--;
|
|
|
|
for( int i = 0; i < num_earthquakes; i++ )
|
|
{
|
|
earthquakes[ i ] = earthquakes[ i + 1 ];
|
|
}
|
|
}
|
|
|
|
earthquakes[ num_earthquakes ] = *e;
|
|
num_earthquakes++;
|
|
|
|
e->m_Thread->Pause();
|
|
}
|
|
|
|
void Level::DoEarthquakes( void )
|
|
{
|
|
int i, j;
|
|
earthquake_t *e;
|
|
int timedelta;
|
|
int rampuptime;
|
|
int rampdowntime;
|
|
float test_magnitude;
|
|
|
|
if( num_earthquakes <= 0 ) {
|
|
return;
|
|
}
|
|
|
|
earthquake_magnitude = 0.0f;
|
|
|
|
for( i = num_earthquakes; i > 0; i-- )
|
|
{
|
|
e = &earthquakes[ i - 1 ];
|
|
|
|
if( inttime >= e->endtime || !e->m_Thread )
|
|
{
|
|
if( e->m_Thread ) {
|
|
e->m_Thread->Wait( 0 );
|
|
}
|
|
|
|
num_earthquakes--;
|
|
|
|
for( j = 0; j < num_earthquakes; j++ )
|
|
{
|
|
earthquakes[ j ] = earthquakes[ j + 1 ];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
test_magnitude = e->magnitude;
|
|
|
|
timedelta = inttime - e->starttime;
|
|
|
|
if( timedelta >= e->duration / 2 )
|
|
{
|
|
rampdowntime = 2 * e->duration / 3 + e->starttime;
|
|
|
|
if( !e->no_rampdown && inttime > rampdowntime )
|
|
{
|
|
test_magnitude *= 1.0f - ( inttime - rampdowntime ) * 3.0f / e->duration;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rampuptime = e->duration / 3 + e->starttime;
|
|
|
|
if( !e->no_rampup && inttime < rampuptime )
|
|
{
|
|
test_magnitude *= ( inttime - e->starttime ) * 3.0f / e->duration;
|
|
}
|
|
}
|
|
|
|
if( test_magnitude > earthquake_magnitude ) {
|
|
earthquake_magnitude = test_magnitude;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Level::CleanUp( qboolean samemap, qboolean resetConfigStrings )
|
|
{
|
|
DisableListenerNotify++;
|
|
|
|
if( g_gametype->integer ) {
|
|
dmManager.Reset();
|
|
}
|
|
|
|
Director.Reset( samemap );
|
|
|
|
ClearCachedStatemaps();
|
|
|
|
assert( active_edicts.next );
|
|
assert( active_edicts.next->prev == &active_edicts );
|
|
assert( active_edicts.prev );
|
|
assert( active_edicts.prev->next == &active_edicts );
|
|
assert( free_edicts.next );
|
|
assert( free_edicts.next->prev == &free_edicts );
|
|
assert( free_edicts.prev );
|
|
assert( free_edicts.prev->next == &free_edicts );
|
|
|
|
while( active_edicts.next != &active_edicts )
|
|
{
|
|
assert( active_edicts.next != &free_edicts );
|
|
assert( active_edicts.prev != &free_edicts );
|
|
|
|
assert( active_edicts.next );
|
|
assert( active_edicts.next->prev == &active_edicts );
|
|
assert( active_edicts.prev );
|
|
assert( active_edicts.prev->next == &active_edicts );
|
|
assert( free_edicts.next );
|
|
assert( free_edicts.next->prev == &free_edicts );
|
|
assert( free_edicts.prev );
|
|
assert( free_edicts.prev->next == &free_edicts );
|
|
|
|
if( active_edicts.next->entity )
|
|
{
|
|
delete active_edicts.next->entity;
|
|
}
|
|
else
|
|
{
|
|
FreeEdict( active_edicts.next );
|
|
}
|
|
}
|
|
|
|
cinematic = false;
|
|
ai_on = true;
|
|
near_exit = false;
|
|
|
|
mission_failed = false;
|
|
died_already = false;
|
|
|
|
globals.num_entities = game.maxclients + 1;
|
|
gi.LocateGameData( g_entities, game.maxclients + 1, sizeof( gentity_t ), &game.clients[ 0 ].ps, sizeof( gclient_t ) );
|
|
|
|
// clear up all AI node information
|
|
PathManager.ResetNodes();
|
|
|
|
// clear out automatic cameras
|
|
automatic_cameras.ClearObjectList();
|
|
|
|
// clear out level script variables
|
|
level.Vars()->ClearList();
|
|
|
|
// Clear out parm vars
|
|
parm.Vars()->ClearList();
|
|
|
|
// initialize the game variables
|
|
// these get restored by the persistant data, so we can safely clear them here
|
|
game.Vars()->ClearList();
|
|
|
|
// clearout any waiting events
|
|
L_ClearEventList();
|
|
|
|
// reset all edicts
|
|
ResetEdicts();
|
|
|
|
// reset all grenade hints
|
|
GrenadeHint::ResetHints();
|
|
|
|
// Reset the boss health cvar
|
|
gi.Cvar_Set( "bosshealth", "0" );
|
|
|
|
Actor::ResetBodyQueue();
|
|
|
|
if( world ) {
|
|
world->FreeTargetList();
|
|
}
|
|
|
|
num_earthquakes = 0;
|
|
|
|
AddWaitTill(STRING_PRESPAWN);
|
|
AddWaitTill(STRING_SPAWN);
|
|
AddWaitTill(STRING_PLAYERSPAWN);
|
|
AddWaitTill(STRING_SKIP);
|
|
AddWaitTill(STRING_POSTTHINK);
|
|
|
|
if (g_gametype->integer <= GT_OBJECTIVE)
|
|
{
|
|
AddWaitTill(STRING_ROUNDSTART);
|
|
}
|
|
|
|
if (g_gametype->integer > GT_FFA)
|
|
{
|
|
AddWaitTill(STRING_ALLIESWIN);
|
|
AddWaitTill(STRING_AXISWIN);
|
|
AddWaitTill(STRING_DRAW);
|
|
}
|
|
|
|
classinfo()->RemoveWaitTill( "prespawn" );
|
|
classinfo()->RemoveWaitTill( "spawn" );
|
|
classinfo()->RemoveWaitTill( "skip" );
|
|
classinfo()->RemoveWaitTill( "postthink" );
|
|
|
|
if( g_gametype->integer >= GT_TEAM_ROUNDS )
|
|
{
|
|
classinfo()->RemoveWaitTill( "roundstart" );
|
|
classinfo()->RemoveWaitTill( "allieswin" );
|
|
classinfo()->RemoveWaitTill( "axiswin" );
|
|
classinfo()->RemoveWaitTill( "draw" );
|
|
}
|
|
|
|
if( resetConfigStrings )
|
|
{
|
|
gi.SetConfigstring( CS_RAIN_DENSITY, "0" );
|
|
gi.SetConfigstring( CS_RAIN_SPEED, "2048" );
|
|
gi.SetConfigstring( CS_RAIN_SPEEDVARY, "512" );
|
|
gi.SetConfigstring( CS_RAIN_SLANT, "50" );
|
|
gi.SetConfigstring( CS_RAIN_LENGTH, "90" );
|
|
gi.SetConfigstring( CS_RAIN_MINDIST, "512" );
|
|
gi.SetConfigstring( CS_RAIN_WIDTH, "1" );
|
|
gi.SetConfigstring( CS_RAIN_SHADER, "textures/rain" );
|
|
gi.SetConfigstring( CS_RAIN_NUMSHADERS, "0" );
|
|
gi.SetConfigstring( CS_CURRENT_OBJECTIVE, "" );
|
|
|
|
for( int i = CS_OBJECTIVES; i < CS_OBJECTIVES + MAX_OBJECTIVES; i++ )
|
|
{
|
|
gi.SetConfigstring( i, "" );
|
|
}
|
|
}
|
|
|
|
DisableListenerNotify--;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
ResetEdicts
|
|
==============
|
|
*/
|
|
void Level::ResetEdicts( void )
|
|
{
|
|
int i;
|
|
|
|
memset( g_entities, 0, game.maxentities * sizeof( g_entities[ 0 ] ) );
|
|
|
|
// Add all the edicts to the free list
|
|
LL_Reset( &free_edicts, next, prev );
|
|
LL_Reset( &active_edicts, next, prev );
|
|
for( i = 0; i < game.maxentities; i++ )
|
|
{
|
|
LL_Add( &free_edicts, &g_entities[ i ], next, prev );
|
|
}
|
|
|
|
for( i = 0; i < game.maxclients; i++ )
|
|
{
|
|
// set client fields on player ents
|
|
g_entities[ i ].client = game.clients + i;
|
|
|
|
G_InitClientPersistant( &game.clients[ i ] );
|
|
}
|
|
|
|
globals.num_entities = game.maxclients;
|
|
}
|
|
|
|
qboolean Level::inhibitEntity( int spawnflags )
|
|
|
|
{
|
|
if( !developer->integer && ( spawnflags & SPAWNFLAG_DEVELOPMENT ) )
|
|
{
|
|
return qtrue;
|
|
}
|
|
|
|
if( !detail->integer && ( spawnflags & SPAWNFLAG_DETAIL ) )
|
|
{
|
|
return qtrue;
|
|
}
|
|
|
|
#ifdef _CONSOLE
|
|
if( spawnflags & SPAWNFLAG_NOCONSOLE )
|
|
#else
|
|
if( spawnflags & SPAWNFLAG_NOPC )
|
|
#endif
|
|
{
|
|
return qtrue;
|
|
}
|
|
|
|
if( g_gametype->integer )
|
|
{
|
|
if( spawnflags & SPAWNFLAG_NOT_DEATHMATCH )
|
|
{
|
|
return qtrue;
|
|
}
|
|
|
|
return qfalse;
|
|
}
|
|
|
|
switch( skill->integer )
|
|
{
|
|
case 0:
|
|
return ( spawnflags & SPAWNFLAG_NOT_EASY ) != 0;
|
|
break;
|
|
|
|
case 1:
|
|
return ( spawnflags & SPAWNFLAG_NOT_MEDIUM ) != 0;
|
|
break;
|
|
|
|
case 2:
|
|
case 3:
|
|
return ( spawnflags & SPAWNFLAG_NOT_HARD );
|
|
break;
|
|
}
|
|
|
|
return qfalse;
|
|
}
|
|
|
|
void Level::setSkill( int value )
|
|
{
|
|
int skill_level;
|
|
|
|
skill_level = floor( ( float )value );
|
|
skill_level = bound( skill_level, 0, 3 );
|
|
|
|
gi.Cvar_Set( "skill", va( "%d", skill_level ) );
|
|
}
|
|
|
|
void Level::setTime( int levelTime )
|
|
{
|
|
svsTime = levelTime;
|
|
inttime = levelTime - svsStartTime;
|
|
svsFloatTime = levelTime / 1000.0f;
|
|
time = inttime / 1000.0f;
|
|
}
|
|
|
|
void Level::setFrametime( int frametime )
|
|
{
|
|
intframetime = frametime;
|
|
this->frametime = frametime / 1000.0f;
|
|
}
|
|
|
|
void Level::SetMap( const char *themapname )
|
|
{
|
|
char *spawnpos;
|
|
int i;
|
|
str text;
|
|
|
|
Init();
|
|
|
|
spawning = true;
|
|
|
|
// set a specific spawnpoint if the map was started with a $
|
|
spawnpos = strchr( ( char * )themapname, '$' );
|
|
if( spawnpos )
|
|
{
|
|
mapname = ( const char * )( spawnpos - themapname );
|
|
spawnpoint = mapname;
|
|
}
|
|
else
|
|
{
|
|
mapname = themapname;
|
|
spawnpoint = "";
|
|
}
|
|
|
|
current_map = ( char * )themapname;
|
|
|
|
level_name = mapname;
|
|
for( i = 0; i < level_name.length(); i++ )
|
|
{
|
|
if( level_name[ i ] == '.' )
|
|
{
|
|
level_name[ i ] = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_mapscript = "maps/" + level_name + ".scr";
|
|
m_precachescript = "maps/" + level_name + "_precache.scr";
|
|
m_pathfile = "maps/" + level_name + ".pth";
|
|
m_mapfile = "maps/" + level_name + ".bsp";
|
|
}
|
|
|
|
void Level::LoadAllScripts( const char *name, const char *extension )
|
|
{
|
|
char **scriptFiles;
|
|
char filename[ MAX_QPATH ];
|
|
int numScripts;
|
|
|
|
scriptFiles = gi.FS_ListFiles( name, extension, qfalse, &numScripts );
|
|
|
|
if( !scriptFiles || !numScripts ) {
|
|
return;
|
|
}
|
|
|
|
for( int i = 0; i < numScripts; i++ )
|
|
{
|
|
Com_sprintf( filename, sizeof( filename ), "%s/%s", name, scriptFiles[ i ] );
|
|
|
|
// Compile the script
|
|
Director.GetScript( filename );
|
|
}
|
|
|
|
gi.FS_FreeFileList( scriptFiles );
|
|
}
|
|
|
|
void Level::Precache( void )
|
|
{
|
|
setTime( svsStartTime );
|
|
setFrametime( 50 );
|
|
|
|
if( gi.FS_ReadFile( m_precachescript, NULL, qtrue ) != -1 )
|
|
{
|
|
gi.DPrintf( "Adding script: '%s'\n", m_precachescript.c_str() );
|
|
|
|
// temporarily disable the loop protection
|
|
// because caching models require time
|
|
m_LoopProtection = false;
|
|
Director.ExecuteThread( m_precachescript );
|
|
m_LoopProtection = true;
|
|
}
|
|
|
|
if( !g_gametype->integer ) {
|
|
LoadAllScripts( "anim", ".scr" );
|
|
}
|
|
|
|
LoadAllScripts( "global", ".scr" );
|
|
}
|
|
|
|
void Level::SpawnEntities( char *entities, int svsTime )
|
|
{
|
|
int inhibit, radnum = 0, count = 0;
|
|
int enttime;
|
|
const char *value;
|
|
char name[128];
|
|
SpawnArgs args;
|
|
Listener *listener;
|
|
Entity *ent;
|
|
|
|
// Create the game variable list
|
|
game.CreateVars();
|
|
|
|
// Create the parm variable list
|
|
parm.CreateVars();
|
|
|
|
// Create the level variable list
|
|
CreateVars();
|
|
|
|
// set up time so functions still have valid times
|
|
setTime( svsTime );
|
|
setFrametime( 50 );
|
|
|
|
setSkill( skill->integer );
|
|
|
|
// reset out count of the number of game traces
|
|
sv_numtraces = 0;
|
|
|
|
// parse world
|
|
entities = args.Parse( entities );
|
|
spawn_entnum = ENTITYNUM_WORLD;
|
|
args.SpawnInternal();
|
|
|
|
// parse ents
|
|
inhibit = 0;
|
|
enttime = gi.Milliseconds();
|
|
|
|
// Set up for a new map
|
|
PathManager.LoadNodes();
|
|
|
|
Com_Printf( "-------------------- Actual Spawning Entities -----------------------\n" );
|
|
|
|
for( entities = args.Parse( entities ); entities != NULL; entities = args.Parse( entities ) )
|
|
{
|
|
// remove things (except the world) from different skill levels or deathmatch
|
|
spawnflags = 0;
|
|
value = args.getArg( "spawnflags" );
|
|
|
|
if( value )
|
|
{
|
|
spawnflags = atoi( value );
|
|
|
|
if( inhibitEntity( spawnflags ) )
|
|
{
|
|
inhibit++;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
listener = args.SpawnInternal();
|
|
|
|
if( listener )
|
|
{
|
|
radnum++;
|
|
|
|
if( listener->isSubclassOf( Entity ) )
|
|
{
|
|
count++;
|
|
|
|
ent = ( Entity * )listener;
|
|
ent->radnum = radnum;
|
|
|
|
Q_strncpyz( ent->edict->entname, ent->getClassID(), sizeof( ent->edict->entname ) );
|
|
|
|
ent->PostEvent(EV_Entity_Start, -1.0, 0);
|
|
sprintf(name, "i%d", radnum);
|
|
gi.LoadResource(name);
|
|
}
|
|
}
|
|
}
|
|
|
|
Com_Printf( "-------------------- Actual Spawning Entities Done ------------------ %i ms\n", gi.Milliseconds() - enttime );
|
|
|
|
world->UpdateConfigStrings();
|
|
|
|
Event *ev = new Event( EV_Level_PreSpawnSentient );
|
|
PostEvent( ev, EV_SPAWNENTITIES );
|
|
|
|
L_ProcessPendingEvents();
|
|
|
|
if( g_gametype->integer ) {
|
|
dmManager.InitGame();
|
|
}
|
|
|
|
if( game.maxclients == 1 )
|
|
{
|
|
spawn_entnum = 0;
|
|
new Player;
|
|
}
|
|
|
|
m_LoopProtection = false;
|
|
AddWaitTill( STRING_PRESPAWN );
|
|
Unregister( STRING_PRESPAWN );
|
|
m_LoopProtection = true;
|
|
|
|
Com_Printf( "%i entities spawned\n", count );
|
|
Com_Printf( "%i simple entities spawned\n", radnum );
|
|
Com_Printf( "%i entities inhibited\n", inhibit );
|
|
|
|
Com_Printf( "-------------------- Spawning Entities Done ------------------ %i ms\n", gi.Milliseconds() - enttime );
|
|
}
|
|
|
|
void Level::PreSpawnSentient( Event *ev )
|
|
{
|
|
GameScript *script;
|
|
|
|
// general initialization
|
|
FindTeams();
|
|
|
|
script = Director.GetScript( m_mapscript );
|
|
if( script )
|
|
{
|
|
gi.DPrintf( "Adding script: '%s'\n", m_mapscript.c_str() );
|
|
|
|
m_LoopProtection = false;
|
|
Director.ExecuteThread( m_mapscript );
|
|
m_LoopProtection = true;
|
|
}
|
|
|
|
PathManager.CreatePaths();
|
|
|
|
}
|
|
|
|
/*
|
|
================
|
|
FindTeams
|
|
|
|
Chain together all entities with a matching team field.
|
|
Entity teams are used for item groups and multi-entity mover groups.
|
|
|
|
All but the first will have the FL_TEAMSLAVE flag set and teammaster field set
|
|
All but the last will have the teamchain field set to the next one
|
|
================
|
|
*/
|
|
void Level::FindTeams()
|
|
{
|
|
gentity_t *ent, *ent2;
|
|
Entity *e, *e2;
|
|
int i, j;
|
|
int c, c2;
|
|
|
|
c = 0;
|
|
c2 = 0;
|
|
for( i = 1, ent = g_entities + i; i < globals.num_entities; i++, ent++ )
|
|
{
|
|
if( !ent->inuse )
|
|
continue;
|
|
|
|
e = ent->entity;
|
|
|
|
if( !e->moveteam.length() )
|
|
continue;
|
|
|
|
if( e->flags & FL_TEAMSLAVE )
|
|
continue;
|
|
|
|
e->teammaster = e;
|
|
c++;
|
|
c2++;
|
|
|
|
for( j = i + 1, ent2 = ent + 1; j < globals.num_entities; j++, ent2++ )
|
|
{
|
|
if( !ent2->inuse )
|
|
continue;
|
|
|
|
e2 = ent->entity;
|
|
|
|
if( !e2->moveteam.length() )
|
|
continue;
|
|
|
|
if( e2->flags & FL_TEAMSLAVE )
|
|
continue;
|
|
|
|
if( !strcmp( e->moveteam, e2->moveteam ) )
|
|
{
|
|
c2++;
|
|
e2->teamchain = e->teamchain;
|
|
e->teamchain = e2;
|
|
e2->teammaster = e;
|
|
e2->flags |= FL_TEAMSLAVE;
|
|
|
|
// make sure that targets only point at the master
|
|
if( e2->targetname ) {
|
|
e->targetname = e2->targetname;
|
|
e2->targetname = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
G_Printf( "%i teams with %i entities\n", c, c2 );
|
|
}
|
|
|
|
void Level::ServerSpawned( void )
|
|
{
|
|
int i;
|
|
gclient_t *client;
|
|
gentity_t *ent;
|
|
|
|
for( i = 0, client = game.clients; i < game.maxclients; i++, client++ )
|
|
{
|
|
client->ps.commandTime = svsTime;
|
|
}
|
|
|
|
if( !WaitTillDisabled( STRING_SPAWN ) )
|
|
{
|
|
AddWaitTill( STRING_SPAWN );
|
|
|
|
Director.iPaused++;
|
|
|
|
for( ent = active_edicts.next; ent != &active_edicts; ent = ent->next )
|
|
{
|
|
ent->entity->Unregister( STRING_SPAWN );
|
|
}
|
|
|
|
if( Director.iPaused-- == 1 ) {
|
|
Director.ExecuteRunning();
|
|
}
|
|
|
|
Unregister( STRING_SPAWN );
|
|
}
|
|
else
|
|
{
|
|
Director.LoadMenus();
|
|
}
|
|
|
|
spawning = false;
|
|
}
|
|
|
|
void Level::CheckVote
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
// FIXME: show the vote HUD like in SH and BT
|
|
|
|
if( !m_voteTime )
|
|
return;
|
|
|
|
if( time - m_voteTime >= 30000.0f )
|
|
{
|
|
dmManager.PrintAllClients( "Vote failed.\n" );
|
|
m_voteTime = 0;
|
|
return;
|
|
}
|
|
|
|
if( m_voteYes > m_numVoters / 2 )
|
|
{
|
|
dmManager.PrintAllClients( "Vote passed.\n" );
|
|
|
|
// Pass arguments to console
|
|
gi.SendConsoleCommand( va( "%s", level.m_voteString.c_str() ) );
|
|
m_voteTime = 0;
|
|
return;
|
|
}
|
|
|
|
if( m_voteNo >= m_numVoters / 2 )
|
|
{
|
|
dmManager.PrintAllClients( "Vote failed.\n" );
|
|
m_voteTime = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void Level::GetTime
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->AddFloat( level.time );
|
|
}
|
|
|
|
void Level::GetTotalSecrets
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->AddInteger( total_secrets );
|
|
}
|
|
|
|
void Level::GetFoundSecrets
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->AddInteger( found_secrets );
|
|
}
|
|
|
|
void Level::GetAlarm
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->AddInteger( m_bAlarm );
|
|
}
|
|
|
|
void Level::SetAlarm
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
m_bAlarm = ev->GetInteger( 1 );
|
|
}
|
|
|
|
void Level::SetNoDropHealth
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
mbNoDropHealth = ev->GetInteger( 1 );
|
|
}
|
|
|
|
void Level::GetLoopProtection
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->AddInteger( m_LoopProtection );
|
|
}
|
|
|
|
void Level::SetLoopProtection
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
m_LoopProtection = ev->GetInteger( 1 );
|
|
}
|
|
|
|
void Level::GetPapersLevel
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->AddInteger( m_iPapersLevel );
|
|
}
|
|
|
|
void Level::SetPapersLevel
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
m_iPapersLevel = ev->GetInteger( 1 );
|
|
}
|
|
|
|
void Level::EventGetDMRespawning
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->AddInteger( dmManager.GameAllowsRespawns() );
|
|
}
|
|
|
|
void Level::EventSetDMRespawning
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
dmManager.SetGameAllowsRespawns( ev->GetBoolean( 1 ) );
|
|
}
|
|
|
|
void Level::EventGetDMRoundLimit
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->AddInteger( dmManager.GetRoundLimit() );
|
|
}
|
|
|
|
void Level::EventSetDMRoundLimit
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
int round_limit = ev->GetInteger( 1 );
|
|
|
|
if( round_limit < 0 )
|
|
{
|
|
ScriptError( "round limit must be greater than 0" );
|
|
}
|
|
|
|
dmManager.SetDefaultRoundLimit( round_limit );
|
|
}
|
|
|
|
void Level::EventGetClockSide
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->AddConstString( dmManager.GetClockSide() );
|
|
}
|
|
|
|
void Level::EventSetClockSide
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
const_str clockside = ev->GetConstString( 1 );
|
|
|
|
if( clockside < STRING_ALLIES || clockside > STRING_KILLS)
|
|
{
|
|
ScriptError( "clockside must be 'axis', 'allies', 'kills', or 'draw'" );
|
|
}
|
|
|
|
dmManager.SetClockSide( clockside );
|
|
}
|
|
|
|
void Level::EventGetBombPlantTeam
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->AddConstString( dmManager.GetBombPlantTeam() );
|
|
}
|
|
|
|
void Level::EventSetBombPlantTeam
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
const_str plant_team = ev->GetConstString( 1 );
|
|
|
|
if( plant_team < STRING_ALLIES || plant_team > STRING_AXIS )
|
|
{
|
|
ScriptError( "bombplantteam must be 'axis' or 'allies'" );
|
|
}
|
|
|
|
dmManager.SetBombPlantTeam( plant_team );
|
|
}
|
|
|
|
void Level::EventGetTargetsToDestroy
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->AddInteger( dmManager.GetTargetsToDestroy() );
|
|
}
|
|
|
|
void Level::EventSetTargetsToDestroy
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
dmManager.SetTargetsToDestroy( ev->GetInteger( 1 ) );
|
|
}
|
|
|
|
void Level::EventGetTargetsDestroyed
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->AddInteger( dmManager.GetTargetsDestroyed() );
|
|
}
|
|
|
|
void Level::EventSetTargetsDestroyed
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
dmManager.SetTargetsDestroyed( ev->GetInteger( 1 ) );
|
|
}
|
|
|
|
void Level::EventGetBombsPlanted
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->AddInteger( dmManager.GetBombsPlanted() );
|
|
}
|
|
|
|
void Level::EventSetBombsPlanted
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
dmManager.SetBombsPlanted( ev->GetInteger( 1 ) );
|
|
}
|
|
|
|
void Level::EventGetRoundBased
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->AddInteger( g_gametype->integer >= GT_TEAM_ROUNDS );
|
|
}
|
|
|
|
void Level::EventRainDensitySet
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
gi.SetConfigstring( CS_RAIN_DENSITY, ev->GetString( 1 ) );
|
|
}
|
|
|
|
void Level::EventRainDensityGet
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->AddString( gi.GetConfigstring( CS_RAIN_DENSITY ) );
|
|
}
|
|
|
|
void Level::EventRainSpeedSet
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
gi.SetConfigstring( CS_RAIN_SPEED, ev->GetString( 1 ) );
|
|
}
|
|
|
|
void Level::EventRainSpeedGet
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->AddString( gi.GetConfigstring( CS_RAIN_SPEED ) );
|
|
}
|
|
|
|
void Level::EventRainSpeedVarySet
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
gi.SetConfigstring( CS_RAIN_SPEEDVARY, ev->GetString( 1 ) );
|
|
}
|
|
|
|
void Level::EventRainSpeedVaryGet
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->AddString( gi.GetConfigstring( CS_RAIN_SPEEDVARY ) );
|
|
}
|
|
|
|
void Level::EventRainSlantSet
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
gi.SetConfigstring( CS_RAIN_SLANT, ev->GetString( 1 ) );
|
|
}
|
|
|
|
void Level::EventRainSlantGet
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->AddString( gi.GetConfigstring( CS_RAIN_SLANT ) );
|
|
}
|
|
|
|
void Level::EventRainLengthSet
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
gi.SetConfigstring( CS_RAIN_LENGTH, ev->GetString( 1 ) );
|
|
}
|
|
|
|
void Level::EventRainLengthGet
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->AddString( gi.GetConfigstring( CS_RAIN_LENGTH ) );
|
|
}
|
|
|
|
void Level::EventRainMin_DistSet
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
gi.SetConfigstring( CS_RAIN_MINDIST, ev->GetString( 1 ) );
|
|
}
|
|
|
|
void Level::EventRainMin_DistGet
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->AddString( gi.GetConfigstring( CS_RAIN_MINDIST ) );
|
|
}
|
|
|
|
void Level::EventRainWidthSet
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
gi.SetConfigstring( CS_RAIN_WIDTH, ev->GetString( 1 ) );
|
|
}
|
|
|
|
void Level::EventRainWidthGet
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->AddString( gi.GetConfigstring( CS_RAIN_WIDTH ) );
|
|
}
|
|
|
|
void Level::EventRainShaderSet
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
gi.SetConfigstring( CS_RAIN_SHADER, ev->GetString( 1 ) );
|
|
}
|
|
|
|
void Level::EventRainShaderGet
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->AddString( gi.GetConfigstring( CS_RAIN_SHADER ) );
|
|
}
|
|
|
|
void Level::EventRainNumShadersSet
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
gi.SetConfigstring( CS_RAIN_NUMSHADERS, ev->GetString( 1 ) );
|
|
}
|
|
|
|
void Level::EventRainNumShadersGet
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ev->AddString( gi.GetConfigstring( CS_RAIN_NUMSHADERS ) );
|
|
}
|
|
|
|
void Level::EventGetFlags
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
}
|
|
|
|
str Level::GetRandomHeadModel(const char *model)
|
|
{
|
|
//FIXME: macros
|
|
char s[1024];
|
|
int num = random2() * gi.NumHeadModels(model);
|
|
gi.GetHeadModel(model, num, s);
|
|
return s;
|
|
}
|
|
|
|
str Level::GetRandomHeadSkin(const char *model)
|
|
{
|
|
//FIXME: macros
|
|
char s[1024];
|
|
int num = random2() * gi.NumHeadSkins(model);
|
|
gi.GetHeadSkin(model, num, s);
|
|
return s;
|
|
}
|
|
|
|
CLASS_DECLARATION( Listener, Level, NULL )
|
|
{
|
|
{ &EV_Level_GetTime, &Level::GetTime },
|
|
{ &EV_Level_GetTotalSecrets, &Level::GetTotalSecrets },
|
|
{ &EV_Level_GetFoundSecrets, &Level::GetFoundSecrets },
|
|
{ &EV_Level_PreSpawnSentient, &Level::PreSpawnSentient },
|
|
{ &EV_Level_GetAlarm, &Level::GetAlarm },
|
|
{ &EV_Level_SetAlarm, &Level::SetAlarm },
|
|
{ &EV_Level_SetNoDropHealth, &Level::SetNoDropHealth },
|
|
{ &EV_Level_GetLoopProtection, &Level::GetLoopProtection },
|
|
{ &EV_Level_SetLoopProtection, &Level::SetLoopProtection },
|
|
{ &EV_Level_GetPapersLevel, &Level::GetPapersLevel },
|
|
{ &EV_Level_SetPapersLevel, &Level::SetPapersLevel },
|
|
{ &EV_Level_GetDMRespawning, &Level::EventGetDMRespawning },
|
|
{ &EV_Level_SetDMRespawning, &Level::EventSetDMRespawning },
|
|
{ &EV_Level_SetDMRespawning2, &Level::EventSetDMRespawning },
|
|
{ &EV_Level_GetDMRoundLimit, &Level::EventGetDMRoundLimit },
|
|
{ &EV_Level_SetDMRoundLimit, &Level::EventSetDMRoundLimit },
|
|
{ &EV_Level_SetDMRoundLimit2, &Level::EventSetDMRoundLimit },
|
|
{ &EV_Level_GetClockSide, &Level::EventGetClockSide },
|
|
{ &EV_Level_SetClockSide, &Level::EventSetClockSide },
|
|
{ &EV_Level_SetClockSide2, &Level::EventSetClockSide },
|
|
{ &EV_Level_GetBombPlantTeam, &Level::EventGetBombPlantTeam },
|
|
{ &EV_Level_SetBombPlantTeam, &Level::EventSetBombPlantTeam },
|
|
{ &EV_Level_SetBombPlantTeam2, &Level::EventSetBombPlantTeam },
|
|
{ &EV_Level_GetTargetsToDestroy, &Level::EventGetTargetsToDestroy },
|
|
{ &EV_Level_SetTargetsToDestroy, &Level::EventSetTargetsToDestroy },
|
|
{ &EV_Level_SetTargetsToDestroy2, &Level::EventSetTargetsToDestroy },
|
|
{ &EV_Level_GetTargetsDestroyed, &Level::EventGetTargetsDestroyed },
|
|
{ &EV_Level_SetTargetsDestroyed, &Level::EventSetTargetsDestroyed },
|
|
{ &EV_Level_SetTargetsDestroyed2, &Level::EventSetTargetsDestroyed },
|
|
{ &EV_Level_GetBombsPlanted, &Level::EventGetBombsPlanted },
|
|
{ &EV_Level_SetBombsPlanted, &Level::EventSetBombsPlanted },
|
|
{ &EV_Level_SetBombsPlanted2, &Level::EventSetBombsPlanted },
|
|
{ &EV_Level_GetRoundBased, &Level::EventGetRoundBased },
|
|
{ &EV_Level_Rain_Density_Set, &Level::EventRainDensitySet },
|
|
{ &EV_Level_Rain_Density_Get, &Level::EventRainDensityGet },
|
|
{ &EV_Level_Rain_Speed_Set, &Level::EventRainSpeedSet },
|
|
{ &EV_Level_Rain_Speed_Get, &Level::EventRainSpeedGet },
|
|
{ &EV_Level_Rain_Speed_Vary_Set, &Level::EventRainSpeedVarySet },
|
|
{ &EV_Level_Rain_Speed_Vary_Get, &Level::EventRainSpeedVaryGet },
|
|
{ &EV_Level_Rain_Slant_Set, &Level::EventRainSlantSet },
|
|
{ &EV_Level_Rain_Slant_Get, &Level::EventRainSlantGet },
|
|
{ &EV_Level_Rain_Length_Set, &Level::EventRainLengthSet },
|
|
{ &EV_Level_Rain_Length_Get, &Level::EventRainLengthGet },
|
|
{ &EV_Level_Rain_Min_Dist_Set, &Level::EventRainMin_DistSet },
|
|
{ &EV_Level_Rain_Min_Dist_Get, &Level::EventRainMin_DistGet },
|
|
{ &EV_Level_Rain_Width_Set, &Level::EventRainWidthSet },
|
|
{ &EV_Level_Rain_Width_Get, &Level::EventRainWidthGet },
|
|
{ &EV_Level_Rain_Shader_Set, &Level::EventRainShaderSet },
|
|
{ &EV_Level_Rain_Shader_Get, &Level::EventRainShaderGet },
|
|
{ &EV_Level_Rain_NumShaders_Set, &Level::EventRainShaderSet },
|
|
{ &EV_Level_Rain_NumShaders_Get, &Level::EventRainShaderGet },
|
|
{ &EV_Level_GetFlags, &Level::EventGetFlags },
|
|
{ NULL, NULL }
|
|
};
|