2016-03-01 13:37:10 +03:00
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
2016-09-11 21:01:06 +03:00
// https://yapb.jeefo.net/license
2016-03-01 13:37:10 +03:00
//
# include <core.h>
2016-03-12 14:35:44 +03:00
Engine : : Engine ( void )
{
2016-09-11 21:01:06 +03:00
m_startEntity = nullptr ;
m_localEntity = nullptr ;
2016-03-12 14:35:44 +03:00
m_language . RemoveAll ( ) ;
ResetMessageCapture ( ) ;
for ( int i = 0 ; i < NETMSG_NUM ; i + + )
m_msgBlock . regMsgs [ i ] = NETMSG_UNDEFINED ;
2016-03-12 19:56:09 +03:00
m_isBotCommand = false ;
m_argumentCount = 0 ;
memset ( m_arguments , 0 , sizeof ( m_arguments ) ) ;
m_cvars . RemoveAll ( ) ;
m_language . RemoveAll ( ) ;
2016-03-12 14:35:44 +03:00
}
Engine : : ~ Engine ( void )
{
TerminateTranslator ( ) ;
ResetMessageCapture ( ) ;
for ( int i = 0 ; i < NETMSG_NUM ; i + + )
m_msgBlock . regMsgs [ i ] = NETMSG_UNDEFINED ;
}
2016-03-05 23:08:07 +03:00
void Engine : : Precache ( edict_t * startEntity )
2016-03-01 13:37:10 +03:00
{
2016-03-01 22:52:17 +03:00
// this function precaches needed models and initialize class variables
2016-03-01 13:37:10 +03:00
m_drawModels [ DRAW_SIMPLE ] = PRECACHE_MODEL ( ENGINE_STR ( " sprites/laserbeam.spr " ) ) ;
m_drawModels [ DRAW_ARROW ] = PRECACHE_MODEL ( ENGINE_STR ( " sprites/arrow1.spr " ) ) ;
2016-03-01 22:52:17 +03:00
2016-09-11 21:01:06 +03:00
m_localEntity = nullptr ;
2016-03-05 23:08:07 +03:00
m_startEntity = startEntity ;
2016-03-01 13:37:10 +03:00
}
void Engine : : Printf ( const char * fmt , . . . )
{
// this function outputs string into server console
va_list ap ;
2016-03-12 14:35:44 +03:00
char string [ MAX_PRINT_BUFFER ] ;
2016-03-01 13:37:10 +03:00
va_start ( ap , fmt ) ;
2016-03-12 14:35:44 +03:00
vsnprintf ( string , SIZEOF_CHAR ( string ) , TraslateMessage ( fmt ) , ap ) ;
2016-03-01 13:37:10 +03:00
va_end ( ap ) ;
2016-03-13 19:20:25 +03:00
strcat ( string , " \n " ) ;
2016-03-01 13:37:10 +03:00
g_engfuncs . pfnServerPrint ( string ) ;
}
void Engine : : ChatPrintf ( const char * fmt , . . . )
{
va_list ap ;
2016-03-12 14:35:44 +03:00
char string [ MAX_PRINT_BUFFER ] ;
2016-03-01 13:37:10 +03:00
va_start ( ap , fmt ) ;
2016-03-12 14:35:44 +03:00
vsnprintf ( string , SIZEOF_CHAR ( string ) , TraslateMessage ( fmt ) , ap ) ;
2016-03-01 13:37:10 +03:00
va_end ( ap ) ;
if ( IsDedicatedServer ( ) )
{
2016-03-12 14:35:44 +03:00
Printf ( string ) ;
2016-03-01 13:37:10 +03:00
return ;
}
strcat ( string , " \n " ) ;
2016-03-12 14:35:44 +03:00
MESSAGE_BEGIN ( MSG_BROADCAST , FindMessageId ( NETMSG_TEXTMSG ) ) ;
2016-03-01 13:37:10 +03:00
WRITE_BYTE ( HUD_PRINTTALK ) ;
WRITE_STRING ( string ) ;
MESSAGE_END ( ) ;
}
void Engine : : CenterPrintf ( const char * fmt , . . . )
{
va_list ap ;
2016-03-12 14:35:44 +03:00
char string [ MAX_PRINT_BUFFER ] ;
2016-03-01 13:37:10 +03:00
va_start ( ap , fmt ) ;
2016-03-12 14:35:44 +03:00
vsnprintf ( string , SIZEOF_CHAR ( string ) , TraslateMessage ( fmt ) , ap ) ;
2016-03-01 13:37:10 +03:00
va_end ( ap ) ;
if ( IsDedicatedServer ( ) )
{
2016-03-12 14:35:44 +03:00
Printf ( string ) ;
2016-03-01 13:37:10 +03:00
return ;
}
strcat ( string , " \n " ) ;
2016-03-12 14:35:44 +03:00
MESSAGE_BEGIN ( MSG_BROADCAST , FindMessageId ( NETMSG_TEXTMSG ) ) ;
2016-03-01 13:37:10 +03:00
WRITE_BYTE ( HUD_PRINTCENTER ) ;
WRITE_STRING ( string ) ;
MESSAGE_END ( ) ;
}
void Engine : : ClientPrintf ( edict_t * ent , const char * fmt , . . . )
{
va_list ap ;
2016-03-12 14:35:44 +03:00
char string [ MAX_PRINT_BUFFER ] ;
2016-03-01 13:37:10 +03:00
va_start ( ap , fmt ) ;
2016-03-12 14:35:44 +03:00
vsnprintf ( string , SIZEOF_CHAR ( string ) , TraslateMessage ( fmt ) , ap ) ;
2016-03-01 13:37:10 +03:00
va_end ( ap ) ;
2016-03-13 21:12:09 +03:00
if ( IsDedicatedServer ( ) | | IsNullEntity ( ent ) | | ent = = g_hostEntity )
2016-03-01 13:37:10 +03:00
{
2016-03-12 14:35:44 +03:00
Printf ( string ) ;
2016-03-01 13:37:10 +03:00
return ;
}
strcat ( string , " \n " ) ;
g_engfuncs . pfnClientPrintf ( ent , print_console , string ) ;
}
void Engine : : DrawLine ( edict_t * ent , const Vector & start , const Vector & end , int width , int noise , int red , int green , int blue , int brightness , int speed , int life , DrawLineType type )
{
// this function draws a arrow visible from the client side of the player whose player entity
// is pointed to by ent, from the vector location start to the vector location end,
// which is supposed to last life tenths seconds, and having the color defined by RGB.
if ( ! IsValidPlayer ( ent ) )
return ; // reliability check
2016-09-11 21:01:06 +03:00
MESSAGE_BEGIN ( MSG_ONE_UNRELIABLE , SVC_TEMPENTITY , nullptr , ent ) ;
2016-03-01 13:37:10 +03:00
WRITE_BYTE ( TE_BEAMPOINTS ) ;
WRITE_COORD ( end . x ) ;
WRITE_COORD ( end . y ) ;
WRITE_COORD ( end . z ) ;
WRITE_COORD ( start . x ) ;
WRITE_COORD ( start . y ) ;
WRITE_COORD ( start . z ) ;
WRITE_SHORT ( m_drawModels [ type ] ) ;
WRITE_BYTE ( 0 ) ; // framestart
WRITE_BYTE ( 10 ) ; // framerate
WRITE_BYTE ( life ) ; // life in 0.1's
WRITE_BYTE ( width ) ; // width
WRITE_BYTE ( noise ) ; // noise
WRITE_BYTE ( red ) ; // r, g, b
WRITE_BYTE ( green ) ; // r, g, b
WRITE_BYTE ( blue ) ; // r, g, b
WRITE_BYTE ( brightness ) ; // brightness
WRITE_BYTE ( speed ) ; // speed
MESSAGE_END ( ) ;
}
void Engine : : TestLine ( const Vector & start , const Vector & end , int ignoreFlags , edict_t * ignoreEntity , TraceResult * ptr )
{
// this function traces a line dot by dot, starting from vecStart in the direction of vecEnd,
// ignoring or not monsters (depending on the value of IGNORE_MONSTERS, true or false), and stops
// at the first obstacle encountered, returning the results of the trace in the TraceResult structure
// ptr. Such results are (amongst others) the distance traced, the hit surface, the hit plane
// vector normal, etc. See the TraceResult structure for details. This function allows to specify
// whether the trace starts "inside" an entity's polygonal model, and if so, to specify that entity
// in ignoreEntity in order to ignore it as a possible obstacle.
int engineFlags = 0 ;
if ( ignoreFlags & TRACE_IGNORE_MONSTERS )
engineFlags = 1 ;
if ( ignoreFlags & TRACE_IGNORE_GLASS )
engineFlags | = 0x100 ;
g_engfuncs . pfnTraceLine ( start , end , engineFlags , ignoreEntity , ptr ) ;
}
void Engine : : TestHull ( const Vector & start , const Vector & end , int ignoreFlags , int hullNumber , edict_t * ignoreEntity , TraceResult * ptr )
{
// this function traces a hull dot by dot, starting from vecStart in the direction of vecEnd,
// ignoring or not monsters (depending on the value of IGNORE_MONSTERS, true or
// false), and stops at the first obstacle encountered, returning the results
// of the trace in the TraceResult structure ptr, just like TraceLine. Hulls that can be traced
// (by parameter hull_type) are point_hull (a line), head_hull (size of a crouching player),
// human_hull (a normal body size) and large_hull (for monsters?). Not all the hulls in the
// game can be traced here, this function is just useful to give a relative idea of spatial
// reachability (i.e. can a hostage pass through that tiny hole ?) Also like TraceLine, this
// function allows to specify whether the trace starts "inside" an entity's polygonal model,
// and if so, to specify that entity in ignoreEntity in order to ignore it as an obstacle.
( * g_engfuncs . pfnTraceHull ) ( start , end , ( ignoreFlags & TRACE_IGNORE_MONSTERS ) , hullNumber , ignoreEntity , ptr ) ;
}
float Engine : : GetWaveLength ( const char * fileName )
{
extern ConVar yb_chatter_path ;
2016-03-01 22:52:17 +03:00
const char * filePath = FormatBuffer ( " %s/%s/%s.wav " , GetModName ( ) , yb_chatter_path . GetString ( ) , fileName ) ;
File fp ( filePath , " rb " ) ;
2016-03-01 13:37:10 +03:00
// we're got valid handle?
if ( ! fp . IsValid ( ) )
return 0.0f ;
// check if we have engine function for this
2016-09-14 11:51:58 +03:00
if ( ! ( g_gameFlags & GAME_XASH_ENGINE ) & & g_engfuncs . pfnGetApproxWavePlayLen ! = nullptr )
2016-03-01 13:37:10 +03:00
{
fp . Close ( ) ;
2016-03-01 22:52:17 +03:00
return g_engfuncs . pfnGetApproxWavePlayLen ( filePath ) / 1000.0f ;
2016-03-01 13:37:10 +03:00
}
// else fuck with manual search
struct WavHeader
{
char riffChunkId [ 4 ] ;
unsigned long packageSize ;
char chunkID [ 4 ] ;
char formatChunkId [ 4 ] ;
unsigned long formatChunkLength ;
2016-09-11 21:01:06 +03:00
uint16 dummy ;
uint16 channels ;
2016-03-01 13:37:10 +03:00
unsigned long sampleRate ;
unsigned long bytesPerSecond ;
2016-09-11 21:01:06 +03:00
uint16 bytesPerSample ;
uint16 bitsPerSample ;
2016-03-01 13:37:10 +03:00
char dataChunkId [ 4 ] ;
unsigned long dataChunkLength ;
} waveHdr ;
memset ( & waveHdr , 0 , sizeof ( waveHdr ) ) ;
if ( fp . Read ( & waveHdr , sizeof ( WavHeader ) ) = = 0 )
{
2016-03-01 22:52:17 +03:00
AddLogEntry ( true , LL_ERROR , " Wave File %s - has wrong or unsupported format " , filePath ) ;
2016-03-01 13:37:10 +03:00
return 0.0f ;
}
if ( strncmp ( waveHdr . chunkID , " WAVE " , 4 ) ! = 0 )
{
2016-03-01 22:52:17 +03:00
AddLogEntry ( true , LL_ERROR , " Wave File %s - has wrong wave chunk id " , filePath ) ;
2016-03-01 13:37:10 +03:00
return 0.0f ;
}
fp . Close ( ) ;
if ( waveHdr . dataChunkLength = = 0 )
{
2016-03-01 22:52:17 +03:00
AddLogEntry ( true , LL_ERROR , " Wave File %s - has zero length! " , filePath ) ;
2016-03-01 13:37:10 +03:00
return 0.0f ;
}
return static_cast < float > ( waveHdr . dataChunkLength ) / static_cast < float > ( waveHdr . bytesPerSecond ) ;
}
bool Engine : : IsDedicatedServer ( void )
{
// return true if server is dedicated server, false otherwise
2016-03-12 19:56:09 +03:00
static bool dedicated = g_engfuncs . pfnIsDedicatedServer ( ) > 0 ;
return dedicated ;
2016-03-01 13:37:10 +03:00
}
const char * Engine : : GetModName ( void )
{
// this function returns mod name without path
2016-03-03 15:36:16 +03:00
static char engineModName [ 256 ] ;
2016-03-01 13:37:10 +03:00
2016-03-08 17:11:40 +03:00
g_engfuncs . pfnGetGameDir ( engineModName ) ;
int length = strlen ( engineModName ) ;
2016-03-01 13:37:10 +03:00
2016-03-08 17:11:40 +03:00
int stop = length - 1 ;
while ( ( engineModName [ stop ] = = ' \\ ' | | engineModName [ stop ] = = ' / ' ) & & stop > 0 )
stop - - ;
2016-03-01 13:37:10 +03:00
2016-03-08 17:11:40 +03:00
int start = stop ;
while ( engineModName [ start ] ! = ' \\ ' & & engineModName [ start ] ! = ' / ' & & start > 0 )
start - - ;
2016-03-01 13:37:10 +03:00
2016-03-08 17:11:40 +03:00
if ( engineModName [ start ] = = ' \\ ' | | engineModName [ start ] = = ' / ' )
start + + ;
for ( length = start ; length < = stop ; length + + )
engineModName [ length - start ] = engineModName [ length ] ;
engineModName [ length - start ] = 0 ; // terminate the string
return & engineModName [ 0 ] ;
2016-03-01 13:37:10 +03:00
}
const char * Engine : : GetMapName ( void )
{
// this function gets the map name and store it in the map_name global string variable.
static char engineMap [ 256 ] ;
strncpy ( engineMap , const_cast < const char * > ( g_pGlobals - > pStringBase + static_cast < int > ( g_pGlobals - > mapname ) ) , SIZEOF_CHAR ( engineMap ) ) ;
return & engineMap [ 0 ] ;
}
Vector Engine : : GetAbsOrigin ( edict_t * ent )
{
// this expanded function returns the vector origin of a bounded entity, assuming that any
// entity that has a bounding box has its center at the center of the bounding box itself.
2016-03-12 14:35:44 +03:00
if ( IsNullEntity ( ent ) )
2016-03-01 13:37:10 +03:00
return Vector : : GetZero ( ) ;
if ( ent - > v . origin . IsZero ( ) )
return ent - > v . absmin + ent - > v . size * 0.5f ;
return ent - > v . origin ;
}
void Engine : : RegisterCmd ( const char * command , void func ( void ) )
{
// this function tells the engine that a new server command is being declared, in addition
// to the standard ones, whose name is command_name. The engine is thus supposed to be aware
// that for every "command_name" server command it receives, it should call the function
// pointed to by "function" in order to handle it.
g_engfuncs . pfnAddServerCommand ( const_cast < char * > ( command ) , func ) ;
}
void Engine : : EmitSound ( edict_t * ent , const char * sound )
{
2016-09-11 21:01:06 +03:00
g_engfuncs . pfnEmitSound ( ent , CHAN_WEAPON , sound , 1.0f , ATTN_NORM , 0 , 100 ) ;
2016-03-01 13:37:10 +03:00
}
2016-03-01 22:52:17 +03:00
void Engine : : IssueBotCommand ( edict_t * ent , const char * fmt , . . . )
{
// the purpose of this function is to provide fakeclients (bots) with the same client
// command-scripting advantages (putting multiple commands in one line between semicolons)
// as real players. It is an improved version of botman's FakeClientCommand, in which you
// supply directly the whole string as if you were typing it in the bot's "console". It
// is supposed to work exactly like the pfnClientCommand (server-sided client command).
2016-03-12 14:35:44 +03:00
if ( IsNullEntity ( ent ) )
2016-03-01 22:52:17 +03:00
return ;
va_list ap ;
static char string [ 256 ] ;
va_start ( ap , fmt ) ;
vsnprintf ( string , SIZEOF_CHAR ( string ) , fmt , ap ) ;
va_end ( ap ) ;
if ( IsNullString ( string ) )
return ;
2016-03-09 23:32:45 +03:00
m_arguments [ 0 ] = 0x0 ;
2016-03-01 22:52:17 +03:00
m_argumentCount = 0 ;
m_isBotCommand = true ;
int i , pos = 0 ;
int length = strlen ( string ) ;
while ( pos < length )
{
int start = pos ;
int stop = pos ;
while ( pos < length & & string [ pos ] ! = ' ; ' )
pos + + ;
if ( string [ pos - 1 ] = = ' \n ' )
stop = pos - 2 ;
else
stop = pos - 1 ;
for ( i = start ; i < = stop ; i + + )
m_arguments [ i - start ] = string [ i ] ;
m_arguments [ i - start ] = 0 ;
pos + + ;
int index = 0 ;
m_argumentCount = 0 ;
while ( index < i - start )
{
while ( index < i - start & & m_arguments [ index ] = = ' ' )
index + + ;
if ( m_arguments [ index ] = = ' " ' )
{
index + + ;
while ( index < i - start & & m_arguments [ index ] ! = ' " ' )
index + + ;
index + + ;
}
else
while ( index < i - start & & m_arguments [ index ] ! = ' ' )
index + + ;
m_argumentCount + + ;
}
MDLL_ClientCommand ( ent ) ;
}
m_isBotCommand = false ;
2016-03-09 23:32:45 +03:00
m_arguments [ 0 ] = 0x0 ;
2016-03-01 22:52:17 +03:00
m_argumentCount = 0 ;
}
const char * Engine : : ExtractSingleField ( const char * string , int id , bool terminate )
{
2016-03-08 17:11:40 +03:00
// this function gets and returns a particular field in a string where several strings are concatenated
2016-03-01 22:52:17 +03:00
static char field [ 256 ] ;
2016-03-09 23:32:45 +03:00
field [ 0 ] = 0x0 ;
2016-03-01 22:52:17 +03:00
int pos = 0 , count = 0 , start = 0 , stop = 0 ;
int length = strlen ( string ) ;
while ( pos < length & & count < = id )
{
while ( pos < length & & ( string [ pos ] = = ' ' | | string [ pos ] = = ' \t ' ) )
pos + + ;
if ( string [ pos ] = = ' " ' )
{
pos + + ;
start = pos ;
while ( pos < length & & string [ pos ] ! = ' " ' )
pos + + ;
stop = pos - 1 ;
pos + + ;
}
else
{
start = pos ;
while ( pos < length & & string [ pos ] ! = ' ' & & string [ pos ] ! = ' \t ' )
pos + + ;
stop = pos - 1 ;
}
if ( count = = id )
{
int i = start ;
for ( ; i < = stop ; i + + )
field [ i - start ] = string [ i ] ;
field [ i - start ] = 0 ;
break ;
}
count + + ; // we have parsed one field more
}
if ( terminate )
field [ strlen ( field ) - 1 ] = 0 ;
String : : TrimExternalBuffer ( field ) ;
return field ;
}
2016-03-01 13:37:10 +03:00
void Engine : : IssueCmd ( const char * fmt , . . . )
{
// this function asks the engine to execute a server command
va_list ap ;
2016-03-12 14:35:44 +03:00
char string [ MAX_PRINT_BUFFER ] ;
2016-03-01 13:37:10 +03:00
// concatenate all the arguments in one string
va_start ( ap , fmt ) ;
2016-03-01 22:52:17 +03:00
vsnprintf ( string , SIZEOF_CHAR ( string ) , fmt , ap ) ;
2016-03-01 13:37:10 +03:00
va_end ( ap ) ;
strcat ( string , " \n " ) ;
g_engfuncs . pfnServerCommand ( string ) ;
}
2016-03-12 14:35:44 +03:00
void Engine : : PushVariableToStack ( const char * variable , const char * value , VarType varType , bool regMissing , ConVar * self )
2016-03-01 22:52:17 +03:00
{
// this function adds globally defined variable to registration stack
2016-03-05 21:04:46 +03:00
VarPair pair ;
memset ( & pair , 0 , sizeof ( VarPair ) ) ;
2016-03-01 13:37:10 +03:00
2016-03-05 21:04:46 +03:00
pair . reg . name = const_cast < char * > ( variable ) ;
pair . reg . string = const_cast < char * > ( value ) ;
2016-03-08 17:22:44 +03:00
pair . regMissing = regMissing ;
2016-03-01 22:52:17 +03:00
int engineFlags = FCVAR_EXTDLL ;
if ( varType = = VT_NORMAL )
engineFlags | = FCVAR_SERVER ;
else if ( varType = = VT_READONLY )
engineFlags | = FCVAR_SERVER | FCVAR_SPONLY | FCVAR_PRINTABLEONLY ;
else if ( varType = = VT_PASSWORD )
engineFlags | = FCVAR_PROTECTED ;
2016-03-05 21:04:46 +03:00
pair . reg . flags = engineFlags ;
pair . self = self ;
pair . type = varType ;
2016-03-01 22:52:17 +03:00
2016-03-12 14:35:44 +03:00
m_cvars . Push ( pair ) ;
2016-03-01 22:52:17 +03:00
}
2016-03-12 14:35:44 +03:00
void Engine : : PushRegisteredConVarsToEngine ( bool gameVars )
2016-03-01 22:52:17 +03:00
{
// this function pushes all added global variables to engine registration
2016-03-12 14:35:44 +03:00
FOR_EACH_AE ( m_cvars , i )
2016-03-01 22:52:17 +03:00
{
2016-03-12 14:35:44 +03:00
VarPair * ptr = & m_cvars [ i ] ;
2016-03-01 22:52:17 +03:00
if ( ptr - > type ! = VT_NOREGISTER )
{
ptr - > self - > m_eptr = g_engfuncs . pfnCVarGetPointer ( ptr - > reg . name ) ;
2016-09-11 21:01:06 +03:00
if ( ptr - > self - > m_eptr = = nullptr )
2016-03-01 22:52:17 +03:00
{
g_engfuncs . pfnCVarRegister ( & ptr - > reg ) ;
ptr - > self - > m_eptr = g_engfuncs . pfnCVarGetPointer ( ptr - > reg . name ) ;
}
}
else if ( gameVars & & ptr - > type = = VT_NOREGISTER )
{
ptr - > self - > m_eptr = g_engfuncs . pfnCVarGetPointer ( ptr - > reg . name ) ;
2016-09-11 21:01:06 +03:00
if ( ptr - > regMissing & & ptr - > self - > m_eptr = = nullptr )
2016-03-08 17:22:44 +03:00
{
g_engfuncs . pfnCVarRegister ( & ptr - > reg ) ;
ptr - > self - > m_eptr = g_engfuncs . pfnCVarGetPointer ( ptr - > reg . name ) ;
}
2016-09-11 21:01:06 +03:00
InternalAssert ( ptr - > self - > m_eptr ! = nullptr ) ; // ensure game var exists
2016-03-01 22:52:17 +03:00
}
}
}
2016-03-12 14:35:44 +03:00
char * Engine : : TraslateMessage ( const char * input )
{
// this function translate input string into needed language
if ( IsDedicatedServer ( ) )
return const_cast < char * > ( & input [ 0 ] ) ;
2016-09-14 11:51:58 +03:00
static char string [ MAX_PRINT_BUFFER ] = { 0 , } ;
2016-03-12 14:35:44 +03:00
const char * ptr = input + strlen ( input ) - 1 ;
while ( ptr > input & & * ptr = = ' \n ' )
ptr - - ;
if ( ptr ! = input )
ptr + + ;
strncpy ( string , input , SIZEOF_CHAR ( string ) ) ;
String : : TrimExternalBuffer ( string ) ;
FOR_EACH_AE ( m_language , i )
{
if ( strcmp ( string , m_language [ i ] . original ) = = 0 )
{
strncpy ( string , m_language [ i ] . translated , SIZEOF_CHAR ( string ) ) ;
if ( ptr ! = input )
strncat ( string , ptr , MAX_PRINT_BUFFER - 1 - strlen ( string ) ) ;
return & string [ 0 ] ;
}
}
return const_cast < char * > ( & input [ 0 ] ) ; // nothing found
}
void Engine : : TerminateTranslator ( void )
{
// this function terminates language translator and frees all memory
FOR_EACH_AE ( m_language , it )
{
delete [ ] m_language [ it ] . original ;
delete [ ] m_language [ it ] . translated ;
}
m_language . RemoveAll ( ) ;
}
2016-03-13 11:58:35 +03:00
void Engine : : ProcessMessageCapture ( void * ptr )
2016-03-12 14:35:44 +03:00
{
if ( m_msgBlock . msg = = NETMSG_UNDEFINED )
return ;
// some needed variables
2016-09-11 21:01:06 +03:00
static uint8 r , g , b ;
static uint8 enabled ;
2016-03-12 14:35:44 +03:00
static int damageArmor , damageTaken , damageBits ;
static int killerIndex , victimIndex , playerIndex ;
static int index , numPlayers ;
static int state , id , clip ;
static Vector damageOrigin ;
static WeaponProperty weaponProp ;
// some widely used stuff
Bot * bot = bots . GetBot ( m_msgBlock . bot ) ;
char * strVal = reinterpret_cast < char * > ( ptr ) ;
int intVal = * reinterpret_cast < int * > ( ptr ) ;
2016-09-11 21:01:06 +03:00
uint8 byteVal = * reinterpret_cast < uint8 * > ( ptr ) ;
2016-03-12 14:35:44 +03:00
// now starts of network message execution
switch ( m_msgBlock . msg )
{
case NETMSG_VGUI :
// this message is sent when a VGUI menu is displayed.
if ( m_msgBlock . state = = 0 )
{
switch ( intVal )
{
case VMS_TEAM :
bot - > m_startAction = GSM_TEAM_SELECT ;
break ;
case VMS_TF :
case VMS_CT :
bot - > m_startAction = GSM_CLASS_SELECT ;
break ;
}
}
break ;
case NETMSG_SHOWMENU :
// this message is sent when a text menu is displayed.
if ( m_msgBlock . state < 3 ) // ignore first 3 fields of message
break ;
if ( strcmp ( strVal , " #Team_Select " ) = = 0 ) // team select menu?
bot - > m_startAction = GSM_TEAM_SELECT ;
else if ( strcmp ( strVal , " #Team_Select_Spect " ) = = 0 ) // team select menu?
bot - > m_startAction = GSM_TEAM_SELECT ;
else if ( strcmp ( strVal , " #IG_Team_Select_Spect " ) = = 0 ) // team select menu?
bot - > m_startAction = GSM_TEAM_SELECT ;
else if ( strcmp ( strVal , " #IG_Team_Select " ) = = 0 ) // team select menu?
bot - > m_startAction = GSM_TEAM_SELECT ;
else if ( strcmp ( strVal , " #IG_VIP_Team_Select " ) = = 0 ) // team select menu?
bot - > m_startAction = GSM_TEAM_SELECT ;
else if ( strcmp ( strVal , " #IG_VIP_Team_Select_Spect " ) = = 0 ) // team select menu?
bot - > m_startAction = GSM_TEAM_SELECT ;
else if ( strcmp ( strVal , " #Terrorist_Select " ) = = 0 ) // T model select?
bot - > m_startAction = GSM_CLASS_SELECT ;
else if ( strcmp ( strVal , " #CT_Select " ) = = 0 ) // CT model select menu?
bot - > m_startAction = GSM_CLASS_SELECT ;
break ;
case NETMSG_WEAPONLIST :
// this message is sent when a client joins the game. All of the weapons are sent with the weapon ID and information about what ammo is used.
switch ( m_msgBlock . state )
{
case 0 :
strncpy ( weaponProp . className , strVal , SIZEOF_CHAR ( weaponProp . className ) ) ;
break ;
case 1 :
weaponProp . ammo1 = intVal ; // ammo index 1
break ;
case 2 :
weaponProp . ammo1Max = intVal ; // max ammo 1
break ;
case 5 :
weaponProp . slotID = intVal ; // slot for this weapon
break ;
case 6 :
weaponProp . position = intVal ; // position in slot
break ;
case 7 :
weaponProp . id = intVal ; // weapon ID
break ;
case 8 :
weaponProp . flags = intVal ; // flags for weapon (WTF???)
g_weaponDefs [ weaponProp . id ] = weaponProp ; // store away this weapon with it's ammo information...
break ;
}
break ;
case NETMSG_CURWEAPON :
// this message is sent when a weapon is selected (either by the bot chosing a weapon or by the server auto assigning the bot a weapon). In CS it's also called when Ammo is increased/decreased
switch ( m_msgBlock . state )
{
case 0 :
state = intVal ; // state of the current weapon (WTF???)
break ;
case 1 :
id = intVal ; // weapon ID of current weapon
break ;
case 2 :
clip = intVal ; // ammo currently in the clip for this weapon
if ( id < = 31 )
{
if ( state ! = 0 )
bot - > m_currentWeapon = id ;
// ammo amount decreased ? must have fired a bullet...
if ( id = = bot - > m_currentWeapon & & bot - > m_ammoInClip [ id ] > clip )
bot - > m_timeLastFired = Time ( ) ; // remember the last bullet time
bot - > m_ammoInClip [ id ] = clip ;
}
break ;
}
break ;
case NETMSG_AMMOX :
// this message is sent whenever ammo amounts are adjusted (up or down). NOTE: Logging reveals that CS uses it very unreliable!
switch ( m_msgBlock . state )
{
case 0 :
index = intVal ; // ammo index (for type of ammo)
break ;
case 1 :
bot - > m_ammo [ index ] = intVal ; // store it away
break ;
}
break ;
case NETMSG_AMMOPICKUP :
// this message is sent when the bot picks up some ammo (AmmoX messages are also sent so this message is probably
// not really necessary except it allows the HUD to draw pictures of ammo that have been picked up. The bots
// don't really need pictures since they don't have any eyes anyway.
switch ( m_msgBlock . state )
{
case 0 :
index = intVal ;
break ;
case 1 :
bot - > m_ammo [ index ] = intVal ;
break ;
}
break ;
case NETMSG_DAMAGE :
// this message gets sent when the bots are getting damaged.
switch ( m_msgBlock . state )
{
case 0 :
damageArmor = intVal ;
break ;
case 1 :
damageTaken = intVal ;
break ;
case 2 :
damageBits = intVal ;
2016-09-11 21:01:06 +03:00
if ( bot ! = nullptr & & ( damageArmor > 0 | | damageTaken > 0 ) )
2016-03-12 14:35:44 +03:00
bot - > TakeDamage ( bot - > pev - > dmg_inflictor , damageTaken , damageArmor , damageBits ) ;
break ;
}
break ;
case NETMSG_MONEY :
// this message gets sent when the bots money amount changes
if ( m_msgBlock . state = = 0 )
bot - > m_moneyAmount = intVal ; // amount of money
break ;
case NETMSG_STATUSICON :
switch ( m_msgBlock . state )
{
case 0 :
enabled = byteVal ;
break ;
case 1 :
if ( strcmp ( strVal , " defuser " ) = = 0 )
bot - > m_hasDefuser = ( enabled ! = 0 ) ;
else if ( strcmp ( strVal , " buyzone " ) = = 0 )
{
bot - > m_inBuyZone = ( enabled ! = 0 ) ;
// try to equip in buyzone
bot - > EquipInBuyzone ( BUYSTATE_PRIMARY_WEAPON ) ;
}
else if ( strcmp ( strVal , " vipsafety " ) = = 0 )
bot - > m_inVIPZone = ( enabled ! = 0 ) ;
else if ( strcmp ( strVal , " c4 " ) = = 0 )
bot - > m_inBombZone = ( enabled = = 2 ) ;
break ;
}
break ;
case NETMSG_DEATH : // this message sends on death
switch ( m_msgBlock . state )
{
case 0 :
killerIndex = intVal ;
break ;
case 1 :
victimIndex = intVal ;
break ;
case 2 :
bots . SetDeathMsgState ( true ) ;
if ( killerIndex ! = 0 & & killerIndex ! = victimIndex )
{
edict_t * killer = EntityOfIndex ( killerIndex ) ;
edict_t * victim = EntityOfIndex ( victimIndex ) ;
if ( IsNullEntity ( killer ) | | IsNullEntity ( victim ) )
break ;
if ( yb_communication_type . GetInt ( ) = = 2 )
{
// need to send congrats on well placed shot
for ( int i = 0 ; i < MaxClients ( ) ; i + + )
{
2016-03-25 14:56:40 +03:00
Bot * notify = bots . GetBot ( i ) ;
2016-03-12 14:35:44 +03:00
2016-09-11 21:01:06 +03:00
if ( notify ! = nullptr & & notify - > m_notKilled & & killer ! = notify - > GetEntity ( ) & & notify - > EntityIsVisible ( victim - > v . origin ) & & GetTeam ( killer ) = = notify - > m_team & & GetTeam ( killer ) ! = GetTeam ( victim ) )
2016-03-12 14:35:44 +03:00
{
if ( killer = = g_hostEntity )
2016-03-25 14:56:40 +03:00
notify - > HandleChatterMessage ( " #Bot_NiceShotCommander " ) ;
2016-03-12 14:35:44 +03:00
else
2016-03-25 14:56:40 +03:00
notify - > HandleChatterMessage ( " #Bot_NiceShotPall " ) ;
2016-03-12 14:35:44 +03:00
break ;
}
}
}
// notice nearby to victim teammates, that attacker is near
for ( int i = 0 ; i < MaxClients ( ) ; i + + )
{
2016-03-25 14:56:40 +03:00
Bot * notify = bots . GetBot ( i ) ;
2016-03-12 14:35:44 +03:00
2016-09-11 21:01:06 +03:00
if ( notify ! = nullptr & & notify - > m_seeEnemyTime + 2.0f < Time ( ) & & notify - > m_notKilled & & notify - > m_team = = GetTeam ( victim ) & & IsVisible ( killer - > v . origin , notify - > GetEntity ( ) ) & & IsNullEntity ( notify - > m_enemy ) & & GetTeam ( killer ) ! = GetTeam ( victim ) )
2016-03-12 14:35:44 +03:00
{
2016-03-25 14:56:40 +03:00
notify - > m_actualReactionTime = 0.0f ;
notify - > m_seeEnemyTime = Time ( ) ;
notify - > m_enemy = killer ;
notify - > m_lastEnemy = killer ;
notify - > m_lastEnemyOrigin = killer - > v . origin ;
2016-03-12 14:35:44 +03:00
}
}
2016-03-25 14:56:40 +03:00
Bot * notify = bots . GetBot ( killer ) ;
2016-03-12 14:35:44 +03:00
// is this message about a bot who killed somebody?
2016-09-11 21:01:06 +03:00
if ( notify ! = nullptr )
2016-03-25 14:56:40 +03:00
notify - > m_lastVictim = victim ;
2016-03-12 14:35:44 +03:00
else // did a human kill a bot on his team?
{
Bot * target = bots . GetBot ( victim ) ;
2016-09-11 21:01:06 +03:00
if ( target ! = nullptr )
2016-03-12 14:35:44 +03:00
{
if ( GetTeam ( killer ) = = GetTeam ( victim ) )
target - > m_voteKickIndex = killerIndex ;
target - > m_notKilled = false ;
}
}
}
break ;
}
break ;
case NETMSG_SCREENFADE : // this message gets sent when the screen fades (flashbang)
switch ( m_msgBlock . state )
{
case 3 :
r = byteVal ;
break ;
case 4 :
g = byteVal ;
break ;
case 5 :
b = byteVal ;
break ;
case 6 :
bot - > TakeBlinded ( r , g , b , byteVal ) ;
break ;
}
break ;
case NETMSG_HLTV : // round restart in steam cs
switch ( m_msgBlock . state )
{
case 0 :
numPlayers = intVal ;
break ;
case 1 :
if ( numPlayers = = 0 & & intVal = = 0 )
RoundInit ( ) ;
break ;
}
break ;
case NETMSG_TEXTMSG :
if ( m_msgBlock . state = = 1 )
{
if ( FStrEq ( strVal , " #CTs_Win " ) | |
FStrEq ( strVal , " #Bomb_Defused " ) | |
FStrEq ( strVal , " #Terrorists_Win " ) | |
FStrEq ( strVal , " #Round_Draw " ) | |
FStrEq ( strVal , " #All_Hostages_Rescued " ) | |
FStrEq ( strVal , " #Target_Saved " ) | |
FStrEq ( strVal , " #Hostages_Not_Rescued " ) | |
FStrEq ( strVal , " #Terrorists_Not_Escaped " ) | |
FStrEq ( strVal , " #VIP_Not_Escaped " ) | |
FStrEq ( strVal , " #Escaping_Terrorists_Neutralized " ) | |
FStrEq ( strVal , " #VIP_Assassinated " ) | |
FStrEq ( strVal , " #VIP_Escaped " ) | |
FStrEq ( strVal , " #Terrorists_Escaped " ) | |
FStrEq ( strVal , " #CTs_PreventEscape " ) | |
FStrEq ( strVal , " #Target_Bombed " ) | |
FStrEq ( strVal , " #Game_Commencing " ) | |
FStrEq ( strVal , " #Game_will_restart_in " ) )
{
g_roundEnded = true ;
2016-08-27 23:39:20 +03:00
if ( FStrEq ( strVal , " #Game_Commencing " ) )
2016-09-13 19:09:20 +03:00
g_gameWelcomeSent = true ;
2016-03-12 14:35:44 +03:00
if ( FStrEq ( strVal , " #CTs_Win " ) )
{
bots . SetLastWinner ( CT ) ; // update last winner for economics
if ( yb_communication_type . GetInt ( ) = = 2 )
{
2016-03-25 14:56:40 +03:00
Bot * notify = bots . FindOneValidAliveBot ( ) ;
2016-03-12 14:35:44 +03:00
2016-09-11 21:01:06 +03:00
if ( notify ! = nullptr & & notify - > m_notKilled )
2016-03-25 14:56:40 +03:00
notify - > HandleChatterMessage ( strVal ) ;
2016-03-12 14:35:44 +03:00
}
}
if ( FStrEq ( strVal , " #Game_will_restart_in " ) )
{
bots . CheckTeamEconomics ( CT , true ) ;
bots . CheckTeamEconomics ( TERRORIST , true ) ;
}
if ( FStrEq ( strVal , " #Terrorists_Win " ) )
{
bots . SetLastWinner ( TERRORIST ) ; // update last winner for economics
if ( yb_communication_type . GetInt ( ) = = 2 )
{
2016-03-25 14:56:40 +03:00
Bot * notify = bots . FindOneValidAliveBot ( ) ;
2016-03-12 14:35:44 +03:00
2016-09-11 21:01:06 +03:00
if ( notify ! = nullptr & & notify - > m_notKilled )
2016-03-25 14:56:40 +03:00
notify - > HandleChatterMessage ( strVal ) ;
2016-03-12 14:35:44 +03:00
}
}
waypoints . SetBombPosition ( true ) ;
}
else if ( ! g_bombPlanted & & FStrEq ( strVal , " #Bomb_Planted " ) )
{
g_bombPlanted = g_bombSayString = true ;
g_timeBombPlanted = Time ( ) ;
for ( int i = 0 ; i < MaxClients ( ) ; i + + )
{
2016-03-25 14:56:40 +03:00
Bot * notify = bots . GetBot ( i ) ;
2016-03-12 14:35:44 +03:00
2016-09-11 21:01:06 +03:00
if ( notify ! = nullptr & & notify - > m_notKilled )
2016-03-12 14:35:44 +03:00
{
2016-03-25 14:56:40 +03:00
notify - > DeleteSearchNodes ( ) ;
notify - > ResetTasks ( ) ;
2016-03-12 14:35:44 +03:00
2016-09-11 21:01:06 +03:00
if ( yb_communication_type . GetInt ( ) = = 2 & & Random . Int ( 0 , 100 ) < 75 & & notify - > m_team = = CT )
2016-03-25 14:56:40 +03:00
notify - > ChatterMessage ( Chatter_WhereIsTheBomb ) ;
2016-03-12 14:35:44 +03:00
}
}
2016-03-12 19:56:09 +03:00
waypoints . SetBombPosition ( ) ;
2016-03-12 14:35:44 +03:00
}
2016-09-11 21:01:06 +03:00
else if ( bot ! = nullptr & & FStrEq ( strVal , " #Switch_To_BurstFire " ) )
2016-03-12 14:35:44 +03:00
bot - > m_weaponBurstMode = BM_ON ;
2016-09-11 21:01:06 +03:00
else if ( bot ! = nullptr & & FStrEq ( strVal , " #Switch_To_SemiAuto " ) )
2016-03-12 14:35:44 +03:00
bot - > m_weaponBurstMode = BM_OFF ;
}
break ;
case NETMSG_SCOREINFO :
switch ( m_msgBlock . state )
{
case 0 :
playerIndex = intVal ;
break ;
case 4 :
if ( playerIndex > = 0 & & playerIndex < = MaxClients ( ) )
{
# ifndef XASH_CSDM
2016-03-12 19:56:09 +03:00
Client & client = g_clients [ playerIndex - 1 ] ;
2016-03-12 14:35:44 +03:00
if ( intVal = = 1 )
2016-03-12 19:56:09 +03:00
client . team2 = TERRORIST ;
2016-03-12 14:35:44 +03:00
else if ( intVal = = 2 )
2016-03-12 19:56:09 +03:00
client . team2 = CT ;
2016-03-12 14:35:44 +03:00
else
2016-03-12 19:56:09 +03:00
client . team2 = SPECTATOR ;
2016-03-12 14:35:44 +03:00
if ( yb_csdm_mode . GetInt ( ) = = 2 )
2016-03-12 19:56:09 +03:00
client . team = playerIndex ;
2016-03-12 14:35:44 +03:00
else
2016-03-12 19:56:09 +03:00
client . team = client . team2 ;
2016-03-12 14:35:44 +03:00
# endif
}
break ;
}
break ;
case NETMSG_BARTIME :
if ( m_msgBlock . state = = 0 )
{
if ( intVal > 0 )
bot - > m_hasProgressBar = true ; // the progress bar on a hud
else if ( intVal = = 0 )
bot - > m_hasProgressBar = false ; // no progress bar or disappeared
}
break ;
default :
AddLogEntry ( true , LL_FATAL , " Network message handler error. Call to unrecognized message id (%d). \n " , m_msgBlock . msg ) ;
}
m_msgBlock . state + + ; // and finally update network message state
}
2016-09-11 21:01:06 +03:00
ConVar : : ConVar ( const char * name , const char * initval , VarType type , bool regMissing ) : m_eptr ( nullptr )
2016-03-01 22:52:17 +03:00
{
2016-03-12 14:35:44 +03:00
engine . PushVariableToStack ( name , initval , type , regMissing , this ) ;
2016-03-01 22:52:17 +03:00
}