2016-03-12 14:35:44 +03:00
//
2014-09-09 18:29:42 +04:00
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
2014-07-30 14:17:46 +04:00
//
2014-09-09 18:29:42 +04:00
// 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
2014-07-30 14:17:46 +04:00
//
# include <core.h>
2014-09-09 18:29:42 +04:00
2016-02-06 23:37:58 +03:00
ConVar yb_display_menu_text ( " yb_display_menu_text " , " 1 " ) ;
2014-07-30 14:17:46 +04:00
2016-09-11 21:01:06 +03:00
ConVar mp_roundtime ( " mp_roundtime " , nullptr , VT_NOREGISTER ) ;
ConVar mp_freezetime ( " mp_freezetime " , nullptr , VT_NOREGISTER , true ) ;
2014-07-30 14:17:46 +04:00
uint16 FixedUnsigned16 ( float value , float scale )
{
int output = ( static_cast < int > ( value * scale ) ) ;
if ( output < 0 )
output = 0 ;
if ( output > 0xffff )
output = 0xffff ;
return static_cast < uint16 > ( output ) ;
}
short FixedSigned16 ( float value , float scale )
{
int output = ( static_cast < int > ( value * scale ) ) ;
if ( output > 32767 )
output = 32767 ;
if ( output < - 32768 )
output = - 32768 ;
return static_cast < short > ( output ) ;
}
2015-06-06 16:19:20 +03:00
const char * FormatBuffer ( const char * format , . . . )
2014-07-30 14:17:46 +04:00
{
2016-03-12 14:35:44 +03:00
static char strBuffer [ 2 ] [ MAX_PRINT_BUFFER ] ;
static int rotator = 0 ;
2016-09-11 21:01:06 +03:00
if ( format = = nullptr )
2016-03-12 14:35:44 +03:00
return strBuffer [ rotator ] ;
static char * ptr = strBuffer [ rotator ^ = 1 ] ;
2014-07-30 14:17:46 +04:00
2016-03-12 14:35:44 +03:00
va_list ap ;
2014-07-30 14:17:46 +04:00
va_start ( ap , format ) ;
2016-03-12 14:35:44 +03:00
vsnprintf ( ptr , MAX_PRINT_BUFFER - 1 , format , ap ) ;
2014-07-30 14:17:46 +04:00
va_end ( ap ) ;
2016-03-12 14:35:44 +03:00
return ptr ;
2014-07-30 14:17:46 +04:00
}
bool IsAlive ( edict_t * ent )
{
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( ent ) )
2015-06-04 11:52:48 +03:00
return false ;
2014-07-30 14:17:46 +04:00
2015-06-04 11:52:48 +03:00
return ent - > v . deadflag = = DEAD_NO & & ent - > v . health > 0 & & ent - > v . movetype ! = MOVETYPE_NOCLIP ;
2014-07-30 14:17:46 +04:00
}
float GetShootingConeDeviation ( edict_t * ent , Vector * position )
{
const Vector & dir = ( * position - ( ent - > v . origin + ent - > v . view_ofs ) ) . Normalize ( ) ;
MakeVectors ( ent - > v . v_angle ) ;
// he's facing it, he meant it
return g_pGlobals - > v_forward | dir ;
}
2015-06-24 15:38:48 +03:00
bool IsInViewCone ( const Vector & origin , edict_t * ent )
2014-07-30 14:17:46 +04:00
{
MakeVectors ( ent - > v . v_angle ) ;
2015-06-24 15:38:48 +03:00
if ( ( ( origin - ( ent - > v . origin + ent - > v . view_ofs ) ) . Normalize ( ) | g_pGlobals - > v_forward ) > = cosf ( ( ( ent - > v . fov > 0 ? ent - > v . fov : 90.0f ) * 0.5f ) * MATH_D2R ) )
2014-07-30 14:17:46 +04:00
return true ;
return false ;
}
bool IsVisible ( const Vector & origin , edict_t * ent )
{
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( ent ) )
2014-07-30 14:17:46 +04:00
return false ;
TraceResult tr ;
2016-03-01 13:37:10 +03:00
engine . TestLine ( ent - > v . origin + ent - > v . view_ofs , origin , TRACE_IGNORE_EVERYTHING , ent , & tr ) ;
2014-07-30 14:17:46 +04:00
2016-01-04 18:26:06 +03:00
if ( tr . flFraction ! = 1.0f )
2016-02-11 21:50:05 +03:00
return false ;
2014-07-30 14:17:46 +04:00
2016-02-11 21:50:05 +03:00
return true ;
2014-07-30 14:17:46 +04:00
}
2016-09-13 19:09:20 +03:00
void DisplayMenuToClient ( edict_t * ent , MenuId menu )
2014-07-30 14:17:46 +04:00
{
2016-09-13 19:09:20 +03:00
static bool s_menusParsed = false ;
2014-07-30 14:17:46 +04:00
2016-09-13 19:09:20 +03:00
// make menus looks like we need only once
if ( ! s_menusParsed )
2014-07-30 14:17:46 +04:00
{
2016-09-13 19:09:20 +03:00
for ( int i = 0 ; i < ARRAYSIZE_HLSDK ( g_menus ) ; i + + )
2014-07-30 14:17:46 +04:00
{
2016-09-13 19:09:20 +03:00
auto parsed = & g_menus [ i ] ;
2014-07-30 14:17:46 +04:00
2016-09-13 19:09:20 +03:00
// translate all the things
parsed - > text . Replace ( " \v " , " \n " ) ;
parsed - > text . Assign ( engine . TraslateMessage ( parsed - > text . GetBuffer ( ) ) ) ;
2014-07-30 14:17:46 +04:00
2016-09-13 19:09:20 +03:00
// make menu looks best
2016-09-14 11:51:58 +03:00
if ( ! ( g_gameFlags & GAME_LEGACY ) )
{
for ( int j = 0 ; j < 10 ; j + + )
parsed - > text . Replace ( FormatBuffer ( " %d. " , j ) , FormatBuffer ( " \\ r%d. \\ w " , j ) ) ;
}
2014-07-30 14:17:46 +04:00
}
2016-09-13 19:09:20 +03:00
s_menusParsed = true ;
}
2014-07-30 14:17:46 +04:00
2016-09-13 19:09:20 +03:00
if ( ! IsValidPlayer ( ent ) )
return ;
2014-07-30 14:17:46 +04:00
2016-09-13 19:09:20 +03:00
Client & client = g_clients [ engine . IndexOfEntity ( ent ) - 1 ] ;
if ( menu = = BOT_MENU_IVALID )
2014-07-30 14:17:46 +04:00
{
2016-09-11 21:01:06 +03:00
MESSAGE_BEGIN ( MSG_ONE_UNRELIABLE , engine . FindMessageId ( NETMSG_SHOWMENU ) , nullptr , ent ) ;
2014-07-30 14:17:46 +04:00
WRITE_SHORT ( 0 ) ;
WRITE_CHAR ( 0 ) ;
WRITE_BYTE ( 0 ) ;
WRITE_STRING ( " " ) ;
2016-09-13 19:09:20 +03:00
MESSAGE_END ( ) ;
client . menu = BOT_MENU_IVALID ;
return ;
}
2014-07-30 14:17:46 +04:00
2016-09-13 19:09:20 +03:00
MenuText * menuPtr = nullptr ;
for ( int i = 0 ; i < ARRAYSIZE_HLSDK ( g_menus ) ; i + + )
{
if ( g_menus [ i ] . id = = menu )
{
menuPtr = & g_menus [ i ] ;
break ;
}
2014-07-30 14:17:46 +04:00
}
2016-09-14 11:51:58 +03:00
// worst case
if ( ! menuPtr )
{
client . menu = BOT_MENU_IVALID ;
return ;
}
2016-09-14 09:20:33 +03:00
const char * displayText = ( ( g_gameFlags & ( GAME_XASH_ENGINE | GAME_MOBILITY ) ) & & ! yb_display_menu_text . GetBool ( ) ) ? " " : menuPtr - > text . GetBuffer ( ) ;
2016-09-13 19:09:20 +03:00
while ( strlen ( displayText ) > = 64 )
{
MESSAGE_BEGIN ( MSG_ONE_UNRELIABLE , engine . FindMessageId ( NETMSG_SHOWMENU ) , nullptr , ent ) ;
WRITE_SHORT ( menuPtr - > slots ) ;
WRITE_CHAR ( - 1 ) ;
WRITE_BYTE ( 1 ) ;
for ( int i = 0 ; i < = 63 ; i + + )
WRITE_CHAR ( displayText [ i ] ) ;
MESSAGE_END ( ) ;
displayText + = 64 ;
}
MESSAGE_BEGIN ( MSG_ONE_UNRELIABLE , engine . FindMessageId ( NETMSG_SHOWMENU ) , nullptr , ent ) ;
WRITE_SHORT ( menuPtr - > slots ) ;
WRITE_CHAR ( - 1 ) ;
WRITE_BYTE ( 0 ) ;
WRITE_STRING ( displayText ) ;
MESSAGE_END ( ) ;
client . menu = menu ;
2014-07-30 14:17:46 +04:00
CLIENT_COMMAND ( ent , " speak \" player/geiger1 \" \n " ) ; // Stops others from hearing menu sounds..
}
void DecalTrace ( entvars_t * pev , TraceResult * trace , int logotypeIndex )
{
// this function draw spraypaint depending on the tracing results.
static Array < String > logotypes ;
if ( logotypes . IsEmpty ( ) )
2015-06-14 23:13:09 +03:00
logotypes = String ( " {biohaz;{graf003;{graf004;{graf005;{lambda06;{target;{hand1;{spit2;{bloodhand6;{foot_l;{foot_r " ) . Split ( " ; " ) ;
2014-07-30 14:17:46 +04:00
int entityIndex = - 1 , message = TE_DECAL ;
2015-06-04 11:52:48 +03:00
int decalIndex = ( * g_engfuncs . pfnDecalIndex ) ( logotypes [ logotypeIndex ] . GetBuffer ( ) ) ;
2014-07-30 14:17:46 +04:00
if ( decalIndex < 0 )
decalIndex = ( * g_engfuncs . pfnDecalIndex ) ( " {lambda06 " ) ;
2016-01-04 18:26:06 +03:00
if ( trace - > flFraction = = 1.0f )
2014-07-30 14:17:46 +04:00
return ;
2016-03-05 23:08:07 +03:00
if ( ! engine . IsNullEntity ( trace - > pHit ) )
2014-07-30 14:17:46 +04:00
{
if ( trace - > pHit - > v . solid = = SOLID_BSP | | trace - > pHit - > v . movetype = = MOVETYPE_PUSHSTEP )
2016-03-05 23:08:07 +03:00
entityIndex = engine . IndexOfEntity ( trace - > pHit ) ;
2014-07-30 14:17:46 +04:00
else
return ;
}
else
entityIndex = 0 ;
if ( entityIndex ! = 0 )
{
if ( decalIndex > 255 )
{
message = TE_DECALHIGH ;
decalIndex - = 256 ;
}
}
else
{
message = TE_WORLDDECAL ;
if ( decalIndex > 255 )
{
message = TE_WORLDDECALHIGH ;
decalIndex - = 256 ;
}
}
if ( logotypes [ logotypeIndex ] . Contains ( " { " ) )
{
MESSAGE_BEGIN ( MSG_BROADCAST , SVC_TEMPENTITY ) ;
WRITE_BYTE ( TE_PLAYERDECAL ) ;
2016-03-12 14:35:44 +03:00
WRITE_BYTE ( engine . IndexOfEntity ( pev - > pContainingEntity ) ) ;
2014-07-30 14:17:46 +04:00
WRITE_COORD ( trace - > vecEndPos . x ) ;
WRITE_COORD ( trace - > vecEndPos . y ) ;
WRITE_COORD ( trace - > vecEndPos . z ) ;
2016-03-05 23:08:07 +03:00
WRITE_SHORT ( static_cast < short > ( engine . IndexOfEntity ( trace - > pHit ) ) ) ;
2014-07-30 14:17:46 +04:00
WRITE_BYTE ( decalIndex ) ;
MESSAGE_END ( ) ;
}
else
{
MESSAGE_BEGIN ( MSG_BROADCAST , SVC_TEMPENTITY ) ;
WRITE_BYTE ( message ) ;
WRITE_COORD ( trace - > vecEndPos . x ) ;
WRITE_COORD ( trace - > vecEndPos . y ) ;
WRITE_COORD ( trace - > vecEndPos . z ) ;
WRITE_BYTE ( decalIndex ) ;
if ( entityIndex )
WRITE_SHORT ( entityIndex ) ;
MESSAGE_END ( ) ;
}
}
void FreeLibraryMemory ( void )
{
// this function free's all allocated memory
2015-07-24 15:10:51 +03:00
waypoints . Init ( ) ; // frees waypoint data
2015-06-18 19:29:40 +03:00
2015-06-04 11:52:48 +03:00
delete [ ] g_experienceData ;
2016-09-11 21:01:06 +03:00
g_experienceData = nullptr ;
2014-07-30 14:17:46 +04:00
}
void UpdateGlobalExperienceData ( void )
{
// this function called after each end of the round to update knowledge about most dangerous waypoints for each team.
// no waypoints, no experience used or waypoints edited or being edited?
2016-09-10 19:31:38 +03:00
if ( g_numWaypoints < 1 | | waypoints . HasChanged ( ) )
2014-07-30 14:17:46 +04:00
return ; // no action
2016-09-11 21:01:06 +03:00
uint16 maxDamage ; // maximum damage
uint16 actDamage ; // actual damage
2014-07-30 14:17:46 +04:00
int bestIndex ; // best index to store
bool recalcKills = false ;
// get the most dangerous waypoint for this position for terrorist team
for ( int i = 0 ; i < g_numWaypoints ; i + + )
{
maxDamage = 0 ;
bestIndex = - 1 ;
for ( int j = 0 ; j < g_numWaypoints ; j + + )
{
if ( i = = j )
continue ;
actDamage = ( g_experienceData + ( i * g_numWaypoints ) + j ) - > team0Damage ;
if ( actDamage > maxDamage )
{
maxDamage = actDamage ;
bestIndex = j ;
}
}
if ( maxDamage > MAX_DAMAGE_VALUE )
recalcKills = true ;
( g_experienceData + ( i * g_numWaypoints ) + i ) - > team0DangerIndex = static_cast < short > ( bestIndex ) ;
}
// get the most dangerous waypoint for this position for counter-terrorist team
for ( int i = 0 ; i < g_numWaypoints ; i + + )
{
maxDamage = 0 ;
bestIndex = - 1 ;
for ( int j = 0 ; j < g_numWaypoints ; j + + )
{
if ( i = = j )
continue ;
actDamage = ( g_experienceData + ( i * g_numWaypoints ) + j ) - > team1Damage ;
if ( actDamage > maxDamage )
{
maxDamage = actDamage ;
bestIndex = j ;
}
}
if ( maxDamage > MAX_DAMAGE_VALUE )
recalcKills = true ;
( g_experienceData + ( i * g_numWaypoints ) + i ) - > team1DangerIndex = static_cast < short > ( bestIndex ) ;
}
// adjust values if overflow is about to happen
if ( recalcKills )
{
for ( int i = 0 ; i < g_numWaypoints ; i + + )
{
for ( int j = 0 ; j < g_numWaypoints ; j + + )
{
if ( i = = j )
continue ;
int clip = ( g_experienceData + ( i * g_numWaypoints ) + j ) - > team0Damage ;
clip - = static_cast < int > ( MAX_DAMAGE_VALUE * 0.5 ) ;
if ( clip < 0 )
clip = 0 ;
2016-09-11 21:01:06 +03:00
( g_experienceData + ( i * g_numWaypoints ) + j ) - > team0Damage = static_cast < uint16 > ( clip ) ;
2014-07-30 14:17:46 +04:00
clip = ( g_experienceData + ( i * g_numWaypoints ) + j ) - > team1Damage ;
clip - = static_cast < int > ( MAX_DAMAGE_VALUE * 0.5 ) ;
if ( clip < 0 )
clip = 0 ;
2016-09-11 21:01:06 +03:00
( g_experienceData + ( i * g_numWaypoints ) + j ) - > team1Damage = static_cast < uint16 > ( clip ) ;
2014-07-30 14:17:46 +04:00
}
}
}
2014-08-05 21:04:43 +04:00
g_highestKills + + ;
2014-07-30 14:17:46 +04:00
2014-08-05 21:04:43 +04:00
int clip = g_highestDamageT - static_cast < int > ( MAX_DAMAGE_VALUE * 0.5 ) ;
if ( clip < 1 )
clip = 1 ;
g_highestDamageT = clip ;
clip = ( int ) g_highestDamageCT - static_cast < int > ( MAX_DAMAGE_VALUE * 0.5 ) ;
if ( clip < 1 )
clip = 1 ;
g_highestDamageCT = clip ;
if ( g_highestKills = = MAX_KILL_HISTORY )
2014-07-30 14:17:46 +04:00
{
for ( int i = 0 ; i < g_numWaypoints ; i + + )
{
2016-09-11 21:01:06 +03:00
( g_experienceData + ( i * g_numWaypoints ) + i ) - > team0Damage / = static_cast < uint16 > ( engine . MaxClients ( ) * 0.5 ) ;
( g_experienceData + ( i * g_numWaypoints ) + i ) - > team1Damage / = static_cast < uint16 > ( engine . MaxClients ( ) * 0.5 ) ;
2014-07-30 14:17:46 +04:00
}
2014-08-05 21:04:43 +04:00
g_highestKills = 1 ;
2014-07-30 14:17:46 +04:00
}
}
void RoundInit ( void )
{
// this is called at the start of each round
g_roundEnded = false ;
2016-09-10 19:31:38 +03:00
g_canSayBombPlanted = true ;
2014-07-30 14:17:46 +04:00
// check team economics
2016-09-10 19:31:38 +03:00
for ( int team = TERRORIST ; team < SPECTATOR ; team + + )
{
bots . CheckTeamEconomics ( team ) ;
bots . SelectLeaderEachTeam ( team , true ) ;
}
2014-07-30 14:17:46 +04:00
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-07-30 14:17:46 +04:00
{
2015-07-24 15:10:51 +03:00
if ( bots . GetBot ( i ) )
bots . GetBot ( i ) - > NewRound ( ) ;
2014-07-30 14:17:46 +04:00
g_radioSelect [ i ] = 0 ;
}
2015-07-24 15:10:51 +03:00
waypoints . SetBombPosition ( true ) ;
2016-01-07 18:49:55 +03:00
waypoints . ClearVisitedGoals ( ) ;
2014-07-30 14:17:46 +04:00
g_bombSayString = false ;
2015-08-15 18:09:15 +03:00
g_timeBombPlanted = 0.0f ;
g_timeNextBombUpdate = 0.0f ;
2014-07-30 14:17:46 +04:00
2015-08-15 18:09:15 +03:00
g_lastRadioTime [ 0 ] = 0.0f ;
g_lastRadioTime [ 1 ] = 0.0f ;
2014-07-30 14:17:46 +04:00
g_botsCanPause = false ;
2015-06-04 11:52:48 +03:00
for ( int i = 0 ; i < TASK_MAX ; i + + )
g_taskFilters [ i ] . time = 0.0f ;
2014-07-30 14:17:46 +04:00
UpdateGlobalExperienceData ( ) ; // update experience data on round start
// calculate the round mid/end in world time
2016-03-01 13:37:10 +03:00
g_timeRoundStart = engine . Time ( ) + mp_freezetime . GetFloat ( ) ;
2015-08-15 18:09:15 +03:00
g_timeRoundMid = g_timeRoundStart + mp_roundtime . GetFloat ( ) * 60.0f * 0.5f ;
g_timeRoundEnd = g_timeRoundStart + mp_roundtime . GetFloat ( ) * 60.0f ;
2014-07-30 14:17:46 +04:00
}
2015-06-04 11:52:48 +03:00
int GetWeaponPenetrationPower ( int id )
2014-07-30 14:17:46 +04:00
{
// returns if weapon can pierce through a wall
int i = 0 ;
while ( g_weaponSelect [ i ] . id )
{
if ( g_weaponSelect [ i ] . id = = id )
2015-06-04 11:52:48 +03:00
return g_weaponSelect [ i ] . penetratePower ;
2014-07-30 14:17:46 +04:00
i + + ;
}
2015-06-04 11:52:48 +03:00
return 0 ;
2014-07-30 14:17:46 +04:00
}
bool IsValidPlayer ( edict_t * ent )
{
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( ent ) )
2014-07-30 14:17:46 +04:00
return false ;
if ( ent - > v . flags & FL_PROXY )
return false ;
2016-09-11 21:01:06 +03:00
if ( ( ent - > v . flags & ( FL_CLIENT | FL_FAKECLIENT ) ) | | bots . GetBot ( ent ) ! = nullptr )
2014-07-30 14:17:46 +04:00
return ! IsNullString ( STRING ( ent - > v . netname ) ) ;
return false ;
}
2015-06-28 19:43:31 +03:00
bool IsPlayerVIP ( edict_t * ent )
{
if ( ! ( g_mapType & MAP_AS ) )
return false ;
if ( ! IsValidPlayer ( ent ) )
return false ;
return * ( INFOKEY_VALUE ( GET_INFOKEYBUFFER ( ent ) , " model " ) ) = = ' v ' ;
}
2014-07-30 14:17:46 +04:00
bool IsValidBot ( edict_t * ent )
{
2016-09-11 21:01:06 +03:00
if ( bots . GetBot ( ent ) ! = nullptr | | ( ! engine . IsNullEntity ( ent ) & & ( ent - > v . flags & FL_FAKECLIENT ) ) )
2014-07-30 14:17:46 +04:00
return true ;
return false ;
}
2016-03-09 19:17:56 +03:00
bool OpenConfig ( const char * fileName , const char * errorIfNotExists , MemoryFile * outFile , bool languageDependant /*= false*/ )
2014-07-30 14:17:46 +04:00
{
if ( outFile - > IsValid ( ) )
outFile - > Close ( ) ;
2016-03-13 21:12:09 +03:00
// save config dir
const char * configDir = " addons/yapb/conf " ;
2014-07-30 14:17:46 +04:00
if ( languageDependant )
{
extern ConVar yb_language ;
if ( strcmp ( fileName , " lang.cfg " ) = = 0 & & strcmp ( yb_language . GetString ( ) , " en " ) = = 0 )
return false ;
2016-03-13 21:12:09 +03:00
const char * langConfig = FormatBuffer ( " %s/lang/%s_%s " , configDir , yb_language . GetString ( ) , fileName ) ;
// check file existence
int size = 0 ;
2016-09-11 21:01:06 +03:00
uint8 * buffer = nullptr ;
2014-07-30 14:17:46 +04:00
// check is file is exists for this language
2016-09-11 21:01:06 +03:00
if ( ( buffer = MemoryFile : : Loader ( langConfig , & size ) ) ! = nullptr )
2016-03-13 21:12:09 +03:00
{
MemoryFile : : Unloader ( buffer ) ;
// unload and reopen file using MemoryFile
outFile - > Open ( langConfig ) ;
}
2014-07-30 14:17:46 +04:00
else
2016-03-13 21:12:09 +03:00
outFile - > Open ( FormatBuffer ( " %s/lang/en_%s " , configDir , fileName ) ) ;
2014-07-30 14:17:46 +04:00
}
else
2016-03-13 21:12:09 +03:00
outFile - > Open ( FormatBuffer ( " %s/%s " , configDir , fileName ) ) ;
2014-07-30 14:17:46 +04:00
if ( ! outFile - > IsValid ( ) )
{
AddLogEntry ( true , LL_ERROR , errorIfNotExists ) ;
return false ;
}
return true ;
}
2016-08-27 23:39:20 +03:00
void CheckWelcomeMessage ( void )
{
// the purpose of this function, is to send quick welcome message, to the listenserver entity.
static bool alreadyReceived = false ;
static float receiveTime = 0.0f ;
if ( alreadyReceived )
return ;
Array < String > sentences ;
2016-09-14 09:20:33 +03:00
if ( ! ( g_gameFlags & ( GAME_MOBILITY | GAME_XASH_ENGINE ) ) )
2016-08-27 23:39:20 +03:00
{
// add default messages
sentences . Push ( " hello user,communication is acquired " ) ;
sentences . Push ( " your presence is acknowledged " ) ;
sentences . Push ( " high man, your in command now " ) ;
sentences . Push ( " blast your hostile for good " ) ;
sentences . Push ( " high man, kill some idiot here " ) ;
sentences . Push ( " is there a doctor in the area " ) ;
sentences . Push ( " warning, experimental materials detected " ) ;
sentences . Push ( " high amigo, shoot some but " ) ;
sentences . Push ( " attention, hours of work software, detected " ) ;
sentences . Push ( " time for some bad ass explosion " ) ;
sentences . Push ( " bad ass son of a breach device activated " ) ;
sentences . Push ( " high, do not question this great service " ) ;
sentences . Push ( " engine is operative, hello and goodbye " ) ;
sentences . Push ( " high amigo, your administration has been great last day " ) ;
sentences . Push ( " attention, expect experimental armed hostile presence " ) ;
sentences . Push ( " warning, medical attention required " ) ;
}
2016-09-13 19:09:20 +03:00
if ( IsAlive ( g_hostEntity ) & & ! alreadyReceived & & receiveTime < 1.0 & & ( g_numWaypoints > 0 ? g_gameWelcomeSent : true ) )
2016-08-27 23:39:20 +03:00
receiveTime = engine . Time ( ) + 4.0f ; // receive welcome message in four seconds after game has commencing
2016-09-13 19:09:20 +03:00
if ( receiveTime > 0.0f & & receiveTime < engine . Time ( ) & & ! alreadyReceived & & ( g_numWaypoints > 0 ? g_gameWelcomeSent : true ) )
2016-08-27 23:39:20 +03:00
{
2016-09-14 09:20:33 +03:00
if ( ! ( g_gameFlags & ( GAME_MOBILITY | GAME_XASH_ENGINE ) ) )
2016-08-27 23:39:20 +03:00
engine . IssueCmd ( " speak \" %s \" " , const_cast < char * > ( sentences . GetRandomElement ( ) . GetBuffer ( ) ) ) ;
engine . ChatPrintf ( " ----- %s v%s (Build: %u), {%s}, (c) 2016, by %s (%s)----- " , PRODUCT_NAME , PRODUCT_VERSION , GenerateBuildNumber ( ) , PRODUCT_DATE , PRODUCT_AUTHOR , PRODUCT_URL ) ;
2016-09-11 21:01:06 +03:00
MESSAGE_BEGIN ( MSG_ONE , SVC_TEMPENTITY , nullptr , g_hostEntity ) ;
2016-08-27 23:39:20 +03:00
WRITE_BYTE ( TE_TEXTMESSAGE ) ;
WRITE_BYTE ( 1 ) ;
WRITE_SHORT ( FixedSigned16 ( - 1 , 1 < < 13 ) ) ;
WRITE_SHORT ( FixedSigned16 ( - 1 , 1 < < 13 ) ) ;
WRITE_BYTE ( 2 ) ;
2016-09-11 21:01:06 +03:00
WRITE_BYTE ( Random . Int ( 33 , 255 ) ) ;
WRITE_BYTE ( Random . Int ( 33 , 255 ) ) ;
WRITE_BYTE ( Random . Int ( 33 , 255 ) ) ;
2016-08-27 23:39:20 +03:00
WRITE_BYTE ( 0 ) ;
2016-09-11 21:01:06 +03:00
WRITE_BYTE ( Random . Int ( 230 , 255 ) ) ;
WRITE_BYTE ( Random . Int ( 230 , 255 ) ) ;
WRITE_BYTE ( Random . Int ( 230 , 255 ) ) ;
2016-08-27 23:39:20 +03:00
WRITE_BYTE ( 200 ) ;
WRITE_SHORT ( FixedUnsigned16 ( 0.0078125f , 1 < < 8 ) ) ;
WRITE_SHORT ( FixedUnsigned16 ( 2.0f , 1 < < 8 ) ) ;
WRITE_SHORT ( FixedUnsigned16 ( 6.0f , 1 < < 8 ) ) ;
WRITE_SHORT ( FixedUnsigned16 ( 0.1f , 1 < < 8 ) ) ;
WRITE_STRING ( FormatBuffer ( " \n Server is running YaPB v%s (Build: %u) \n Developed by %s \n \n %s " , PRODUCT_VERSION , GenerateBuildNumber ( ) , PRODUCT_AUTHOR , waypoints . GetInfo ( ) ) ) ;
MESSAGE_END ( ) ;
receiveTime = 0.0 ;
alreadyReceived = true ;
}
}
2014-07-30 14:17:46 +04:00
void AddLogEntry ( bool outputToConsole , int logLevel , const char * format , . . . )
{
// this function logs a message to the message log file root directory.
va_list ap ;
2016-03-12 14:35:44 +03:00
char buffer [ MAX_PRINT_BUFFER ] = { 0 , } , levelString [ 32 ] = { 0 , } , logLine [ MAX_PRINT_BUFFER ] = { 0 , } ;
2014-07-30 14:17:46 +04:00
va_start ( ap , format ) ;
2016-03-12 14:35:44 +03:00
vsnprintf ( buffer , SIZEOF_CHAR ( buffer ) , format , ap ) ;
2014-07-30 14:17:46 +04:00
va_end ( ap ) ;
switch ( logLevel )
{
case LL_DEFAULT :
2016-03-12 14:35:44 +03:00
strcpy ( levelString , " LOG: " ) ;
2014-07-30 14:17:46 +04:00
break ;
case LL_WARNING :
2016-03-12 14:35:44 +03:00
strcpy ( levelString , " WARN: " ) ;
2014-07-30 14:17:46 +04:00
break ;
case LL_ERROR :
2016-03-12 14:35:44 +03:00
strcpy ( levelString , " ERROR: " ) ;
2014-07-30 14:17:46 +04:00
break ;
case LL_FATAL :
2016-03-12 14:35:44 +03:00
strcpy ( levelString , " FATAL: " ) ;
2014-07-30 14:17:46 +04:00
break ;
}
if ( outputToConsole )
2016-03-01 13:37:10 +03:00
engine . Printf ( " %s%s " , levelString , buffer ) ;
2014-07-30 14:17:46 +04:00
// now check if logging disabled
if ( ! ( logLevel & LL_IGNORE ) )
{
extern ConVar yb_debug ;
if ( logLevel = = LL_DEFAULT & & yb_debug . GetInt ( ) < 3 )
return ; // no log, default logging is disabled
if ( logLevel = = LL_WARNING & & yb_debug . GetInt ( ) < 2 )
return ; // no log, warning logging is disabled
if ( logLevel = = LL_ERROR & & yb_debug . GetInt ( ) < 1 )
return ; // no log, error logging is disabled
}
// open file in a standard stream
File fp ( " yapb.txt " , " at " ) ;
// check if we got a valid handle
if ( ! fp . IsValid ( ) )
return ;
time_t tickTime = time ( & tickTime ) ;
tm * time = localtime ( & tickTime ) ;
sprintf ( logLine , " [%02d:%02d:%02d] %s%s " , time - > tm_hour , time - > tm_min , time - > tm_sec , levelString , buffer ) ;
fp . Printf ( " %s \n " , logLine ) ;
fp . Close ( ) ;
if ( logLevel = = LL_FATAL )
{
2015-08-15 18:09:15 +03:00
bots . RemoveAll ( ) ;
FreeLibraryMemory ( ) ;
2014-07-30 14:17:46 +04:00
# if defined (PLATFORM_WIN32)
2015-08-15 18:09:15 +03:00
DestroyWindow ( GetForegroundWindow ( ) ) ;
2014-07-30 14:17:46 +04:00
MessageBoxA ( GetActiveWindow ( ) , buffer , " YaPB Error " , MB_ICONSTOP ) ;
# else
2015-06-06 16:37:15 +03:00
printf ( " %s " , buffer ) ;
2014-07-30 14:17:46 +04:00
# endif
2015-08-15 18:09:15 +03:00
2014-07-30 14:17:46 +04:00
# if defined (PLATFORM_WIN32)
_exit ( 1 ) ;
# else
exit ( 1 ) ;
# endif
}
}
bool FindNearestPlayer ( void * * pvHolder , edict_t * to , float searchDistance , bool sameTeam , bool needBot , bool isAlive , bool needDrawn )
{
// this function finds nearest to to, player with set of parameters, like his
// team, live status, search distance etc. if needBot is true, then pvHolder, will
// be filled with bot pointer, else with edict pointer(!).
2016-09-11 21:01:06 +03:00
edict_t * survive = nullptr ; // pointer to temporally & survive entity
2015-08-15 18:09:15 +03:00
float nearestPlayer = 4096.0f ; // nearest player
2014-07-30 14:17:46 +04:00
2016-03-05 23:08:07 +03:00
int toTeam = engine . GetTeam ( to ) ;
2015-06-09 15:45:34 +03:00
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-07-30 14:17:46 +04:00
{
2016-09-10 19:31:38 +03:00
const Client & client = g_clients [ i ] ;
2015-06-08 16:18:55 +03:00
2016-09-10 19:31:38 +03:00
if ( ! ( client . flags & CF_USED ) | | client . ent = = to )
2015-06-08 16:18:55 +03:00
continue ;
2014-07-30 14:17:46 +04:00
2016-09-10 19:31:38 +03:00
if ( ( sameTeam & & client . team ! = toTeam ) | | ( isAlive & & ! ( client . flags & CF_ALIVE ) ) | | ( needBot & & ! IsValidBot ( client . ent ) ) | | ( needDrawn & & ( client . ent - > v . effects & EF_NODRAW ) ) )
2014-07-30 14:17:46 +04:00
continue ; // filter players with parameters
2016-09-10 19:31:38 +03:00
float distance = ( client . ent - > v . origin - to - > v . origin ) . GetLength ( ) ;
2014-07-30 14:17:46 +04:00
2015-06-28 19:43:31 +03:00
if ( distance < nearestPlayer & & distance < searchDistance )
2014-07-30 14:17:46 +04:00
{
nearestPlayer = distance ;
2016-09-10 19:31:38 +03:00
survive = client . ent ;
2014-07-30 14:17:46 +04:00
}
}
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( survive ) )
2014-07-30 14:17:46 +04:00
return false ; // nothing found
// fill the holder
if ( needBot )
2015-07-24 15:10:51 +03:00
* pvHolder = reinterpret_cast < void * > ( bots . GetBot ( survive ) ) ;
2014-07-30 14:17:46 +04:00
else
* pvHolder = reinterpret_cast < void * > ( survive ) ;
return true ;
}
2015-07-19 13:39:00 +03:00
void SoundAttachToClients ( edict_t * ent , const char * sample , float volume )
2014-07-30 14:17:46 +04:00
{
// this function called by the sound hooking code (in emit_sound) enters the played sound into
// the array associated with the entity
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( ent ) | | IsNullString ( sample ) )
2016-03-12 19:56:09 +03:00
return ;
2014-07-30 14:17:46 +04:00
2016-03-01 13:37:10 +03:00
const Vector & origin = engine . GetAbsOrigin ( ent ) ;
2016-03-05 23:08:07 +03:00
int index = engine . IndexOfEntity ( ent ) - 1 ;
2014-07-30 14:17:46 +04:00
2016-03-01 13:37:10 +03:00
if ( index < 0 | | index > = engine . MaxClients ( ) )
2014-07-30 14:17:46 +04:00
{
2015-07-12 17:18:20 +03:00
float nearestDistance = 99999.0f ;
2014-07-30 14:17:46 +04:00
// loop through all players
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-07-30 14:17:46 +04:00
{
2016-09-10 19:31:38 +03:00
const Client & client = g_clients [ i ] ;
if ( ! ( client . flags & CF_USED ) | | ! ( client . flags & CF_ALIVE ) )
2014-07-30 14:17:46 +04:00
continue ;
2016-09-10 19:31:38 +03:00
float distance = ( client . origin - origin ) . GetLength ( ) ;
2014-07-30 14:17:46 +04:00
// now find nearest player
if ( distance < nearestDistance )
{
index = i ;
nearestDistance = distance ;
}
}
}
2016-03-12 19:56:09 +03:00
// in case of worst case
if ( index < 0 | | index > = engine . MaxClients ( ) )
index = 0 ;
Client & client = g_clients [ index ] ;
2014-07-30 14:17:46 +04:00
if ( strncmp ( " player/bhit_flesh " , sample , 17 ) = = 0 | | strncmp ( " player/headshot " , sample , 15 ) = = 0 )
{
// hit/fall sound?
2016-03-12 19:56:09 +03:00
client . hearingDistance = 768.0f * volume ;
client . timeSoundLasting = engine . Time ( ) + 0.5f ;
2016-09-14 11:51:58 +03:00
client . soundPos = origin ;
2014-07-30 14:17:46 +04:00
}
else if ( strncmp ( " items/gunpickup " , sample , 15 ) = = 0 )
{
// weapon pickup?
2016-03-12 19:56:09 +03:00
client . hearingDistance = 768.0f * volume ;
client . timeSoundLasting = engine . Time ( ) + 0.5f ;
2016-09-14 11:51:58 +03:00
client . soundPos = origin ;
2014-07-30 14:17:46 +04:00
}
else if ( strncmp ( " weapons/zoom " , sample , 12 ) = = 0 )
{
// sniper zooming?
2016-03-12 19:56:09 +03:00
client . hearingDistance = 512.0f * volume ;
client . timeSoundLasting = engine . Time ( ) + 0.1f ;
2016-09-14 11:51:58 +03:00
client . soundPos = origin ;
2014-07-30 14:17:46 +04:00
}
else if ( strncmp ( " items/9mmclip " , sample , 13 ) = = 0 )
{
// ammo pickup?
2016-03-12 19:56:09 +03:00
client . hearingDistance = 512.0f * volume ;
client . timeSoundLasting = engine . Time ( ) + 0.1f ;
2016-09-14 11:51:58 +03:00
client . soundPos = origin ;
2014-07-30 14:17:46 +04:00
}
else if ( strncmp ( " hostage/hos " , sample , 11 ) = = 0 )
{
// CT used hostage?
2016-03-12 19:56:09 +03:00
client . hearingDistance = 1024.0f * volume ;
client . timeSoundLasting = engine . Time ( ) + 5.0f ;
2016-09-14 11:51:58 +03:00
client . soundPos = origin ;
2014-07-30 14:17:46 +04:00
}
else if ( strncmp ( " debris/bustmetal " , sample , 16 ) = = 0 | | strncmp ( " debris/bustglass " , sample , 16 ) = = 0 )
{
// broke something?
2016-03-12 19:56:09 +03:00
client . hearingDistance = 1024.0f * volume ;
client . timeSoundLasting = engine . Time ( ) + 2.0f ;
2016-09-14 11:51:58 +03:00
client . soundPos = origin ;
2014-07-30 14:17:46 +04:00
}
else if ( strncmp ( " doors/doormove " , sample , 14 ) = = 0 )
{
// someone opened a door
2016-03-12 19:56:09 +03:00
client . hearingDistance = 1024.0f * volume ;
client . timeSoundLasting = engine . Time ( ) + 3.0f ;
2016-09-14 11:51:58 +03:00
client . soundPos = origin ;
2014-07-30 14:17:46 +04:00
}
}
void SoundSimulateUpdate ( int playerIndex )
{
// this function tries to simulate playing of sounds to let the bots hear sounds which aren't
// captured through server sound hooking
2016-03-01 13:37:10 +03:00
if ( playerIndex < 0 | | playerIndex > = engine . MaxClients ( ) )
2014-07-30 14:17:46 +04:00
return ; // reliability check
2016-03-12 19:56:09 +03:00
Client & client = g_clients [ playerIndex ] ;
2014-07-30 14:17:46 +04:00
2015-07-19 13:39:00 +03:00
float hearDistance = 0.0f ;
float timeSound = 0.0f ;
2014-07-30 14:17:46 +04:00
2016-03-12 19:56:09 +03:00
if ( client . ent - > v . oldbuttons & IN_ATTACK ) // pressed attack button?
2014-07-30 14:17:46 +04:00
{
2016-01-30 13:15:50 +03:00
hearDistance = 2048.0f ;
2016-03-01 13:37:10 +03:00
timeSound = engine . Time ( ) + 0.3f ;
2014-07-30 14:17:46 +04:00
}
2016-03-12 19:56:09 +03:00
else if ( client . ent - > v . oldbuttons & IN_USE ) // pressed used button?
2014-07-30 14:17:46 +04:00
{
2016-01-30 13:15:50 +03:00
hearDistance = 512.0f ;
2016-03-01 13:37:10 +03:00
timeSound = engine . Time ( ) + 0.5f ;
2014-07-30 14:17:46 +04:00
}
2016-03-12 19:56:09 +03:00
else if ( client . ent - > v . oldbuttons & IN_RELOAD ) // pressed reload button?
2014-07-30 14:17:46 +04:00
{
2016-01-30 13:15:50 +03:00
hearDistance = 512.0f ;
2016-03-01 13:37:10 +03:00
timeSound = engine . Time ( ) + 0.5f ;
2014-07-30 14:17:46 +04:00
}
2016-03-12 19:56:09 +03:00
else if ( client . ent - > v . movetype = = MOVETYPE_FLY ) // uses ladder?
2014-07-30 14:17:46 +04:00
{
2016-03-12 19:56:09 +03:00
if ( fabsf ( client . ent - > v . velocity . z ) > 50.0f )
2014-07-30 14:17:46 +04:00
{
2015-07-19 13:39:00 +03:00
hearDistance = 1024.0f ;
2016-03-01 13:37:10 +03:00
timeSound = engine . Time ( ) + 0.3f ;
2014-07-30 14:17:46 +04:00
}
}
else
{
extern ConVar mp_footsteps ;
if ( mp_footsteps . GetBool ( ) )
{
// moves fast enough?
2016-03-12 19:56:09 +03:00
hearDistance = 1280.0f * ( client . ent - > v . velocity . GetLength2D ( ) / 260.0f ) ;
2016-03-01 13:37:10 +03:00
timeSound = engine . Time ( ) + 0.3f ;
2014-07-30 14:17:46 +04:00
}
}
if ( hearDistance < = 0.0 )
return ; // didn't issue sound?
// some sound already associated
2016-03-12 19:56:09 +03:00
if ( client . timeSoundLasting > engine . Time ( ) )
2014-07-30 14:17:46 +04:00
{
2016-03-12 19:56:09 +03:00
if ( client . hearingDistance < = hearDistance )
2014-07-30 14:17:46 +04:00
{
// override it with new
2016-03-12 19:56:09 +03:00
client . hearingDistance = hearDistance ;
client . timeSoundLasting = timeSound ;
2016-09-14 11:51:58 +03:00
client . soundPos = client . ent - > v . origin ;
2014-07-30 14:17:46 +04:00
}
}
else
{
// just remember it
2016-03-12 19:56:09 +03:00
client . hearingDistance = hearDistance ;
client . timeSoundLasting = timeSound ;
2016-09-14 11:51:58 +03:00
client . soundPos = client . ent - > v . origin ;
2014-07-30 14:17:46 +04:00
}
}
2016-03-01 22:52:17 +03:00
int GenerateBuildNumber ( void )
2014-07-30 14:17:46 +04:00
{
// this function generates build number from the compiler date macros
2016-03-01 22:52:17 +03:00
static int buildNumber = 0 ;
2015-07-05 21:32:56 +03:00
if ( buildNumber ! = 0 )
return buildNumber ;
2014-07-30 14:17:46 +04:00
// get compiling date using compiler macros
const char * date = __DATE__ ;
// array of the month names
const char * months [ 12 ] = { " Jan " , " Feb " , " Mar " , " Apr " , " May " , " Jun " , " Jul " , " Aug " , " Sep " , " Oct " , " Nov " , " Dec " } ;
// array of the month days
2016-09-11 21:01:06 +03:00
uint8 monthDays [ 12 ] = { 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 } ;
2014-07-30 14:17:46 +04:00
int day = 0 ; // day of the year
int year = 0 ; // year
int i = 0 ;
2015-07-05 21:32:56 +03:00
// go through all months, and calculate, days since year start
2014-07-30 14:17:46 +04:00
for ( i = 0 ; i < 11 ; i + + )
{
if ( strncmp ( & date [ 0 ] , months [ i ] , 3 ) = = 0 )
break ; // found current month break
day + = monthDays [ i ] ; // add month days
}
day + = atoi ( & date [ 4 ] ) - 1 ; // finally calculate day
year = atoi ( & date [ 7 ] ) - 2000 ; // get years since year 2000
2015-07-05 21:32:56 +03:00
buildNumber = day + static_cast < int > ( ( year - 1 ) * 365.25 ) ;
2014-07-30 14:17:46 +04:00
// if the year is a leap year?
if ( ( year % 4 ) = = 0 & & i > 1 )
buildNumber + = 1 ; // add one year more
2015-07-05 21:32:56 +03:00
buildNumber - = 1114 ;
return buildNumber ;
2014-07-30 14:17:46 +04:00
}
int GetWeaponReturn ( bool needString , const char * weaponAlias , int weaponIndex )
{
// this function returning weapon id from the weapon alias and vice versa.
// structure definition for weapon tab
struct WeaponTab_t
{
Weapon weaponIndex ; // weapon id
const char * alias ; // weapon alias
} ;
// weapon enumeration
WeaponTab_t weaponTab [ ] =
{
{ WEAPON_USP , " usp " } , // HK USP .45 Tactical
{ WEAPON_GLOCK , " glock " } , // Glock18 Select Fire
{ WEAPON_DEAGLE , " deagle " } , // Desert Eagle .50AE
{ WEAPON_P228 , " p228 " } , // SIG P228
{ WEAPON_ELITE , " elite " } , // Dual Beretta 96G Elite
{ WEAPON_FIVESEVEN , " fn57 " } , // FN Five-Seven
{ WEAPON_M3 , " m3 " } , // Benelli M3 Super90
{ WEAPON_XM1014 , " xm1014 " } , // Benelli XM1014
{ WEAPON_MP5 , " mp5 " } , // HK MP5-Navy
{ WEAPON_TMP , " tmp " } , // Steyr Tactical Machine Pistol
{ WEAPON_P90 , " p90 " } , // FN P90
{ WEAPON_MAC10 , " mac10 " } , // Ingram MAC-10
{ WEAPON_UMP45 , " ump45 " } , // HK UMP45
{ WEAPON_AK47 , " ak47 " } , // Automat Kalashnikov AK-47
{ WEAPON_GALIL , " galil " } , // IMI Galil
{ WEAPON_FAMAS , " famas " } , // GIAT FAMAS
{ WEAPON_SG552 , " sg552 " } , // Sig SG-552 Commando
{ WEAPON_M4A1 , " m4a1 " } , // Colt M4A1 Carbine
{ WEAPON_AUG , " aug " } , // Steyr Aug
{ WEAPON_SCOUT , " scout " } , // Steyr Scout
{ WEAPON_AWP , " awp " } , // AI Arctic Warfare/Magnum
{ WEAPON_G3SG1 , " g3sg1 " } , // HK G3/SG-1 Sniper Rifle
{ WEAPON_SG550 , " sg550 " } , // Sig SG-550 Sniper
{ WEAPON_M249 , " m249 " } , // FN M249 Para
{ WEAPON_FLASHBANG , " flash " } , // Concussion Grenade
{ WEAPON_EXPLOSIVE , " hegren " } , // High-Explosive Grenade
{ WEAPON_SMOKE , " sgren " } , // Smoke Grenade
{ WEAPON_ARMOR , " vest " } , // Kevlar Vest
{ WEAPON_ARMORHELM , " vesthelm " } , // Kevlar Vest and Helmet
{ WEAPON_DEFUSER , " defuser " } , // Defuser Kit
{ WEAPON_SHIELD , " shield " } , // Tactical Shield
} ;
// if we need to return the string, find by weapon id
if ( needString & & weaponIndex ! = - 1 )
{
for ( int i = 0 ; i < ARRAYSIZE_HLSDK ( weaponTab ) ; i + + )
{
if ( weaponTab [ i ] . weaponIndex = = weaponIndex ) // is weapon id found?
return MAKE_STRING ( weaponTab [ i ] . alias ) ;
}
return MAKE_STRING ( " (none) " ) ; // return none
}
// else search weapon by name and return weapon id
for ( int i = 0 ; i < ARRAYSIZE_HLSDK ( weaponTab ) ; i + + )
{
if ( strncmp ( weaponTab [ i ] . alias , weaponAlias , strlen ( weaponTab [ i ] . alias ) ) = = 0 )
return weaponTab [ i ] . weaponIndex ;
}
return - 1 ; // no weapon was found return -1
2016-01-07 18:49:55 +03:00
}