2019-08-12 14:16:28 +03:00
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
2019-09-21 23:20:33 +03:00
// Copyright (c) Yet Another POD-Bot Contributors <yapb@entix.io>.
2019-08-12 14:16:28 +03:00
//
2019-09-21 23:20:33 +03:00
// This software is licensed under the MIT license.
// Additional exceptions apply. For full license details, see LICENSE.txt
2019-08-12 14:16:28 +03:00
//
# include <yapb.h>
void MessageDispatcher : : netMsgTextMsg ( ) {
enum args { msg = 1 , min = 2 } ;
// check the minimum states
if ( m_args . length ( ) < min ) {
return ;
}
// lookup cached message
auto cached = m_textMsgCache [ m_args [ msg ] . chars_ ] ;
// check if we're need to handle message
if ( ! ( cached & TextMsgCache : : NeedHandle ) ) {
return ;
}
2019-08-24 12:43:42 +03:00
// reset bomb position for all the bots
const auto resetBombPosition = [ ] ( ) - > void {
if ( game . mapIs ( MapFlags : : Demolition ) ) {
graph . setBombOrigin ( true ) ;
}
} ;
2019-08-12 14:16:28 +03:00
if ( cached & TextMsgCache : : Commencing ) {
util . setNeedForWelcome ( true ) ;
}
else if ( cached & TextMsgCache : : CounterWin ) {
bots . setLastWinner ( Team : : CT ) ; // update last winner for economics
2019-08-24 12:43:42 +03:00
resetBombPosition ( ) ;
2019-08-12 14:16:28 +03:00
}
else if ( cached & TextMsgCache : : RestartRound ) {
bots . updateTeamEconomics ( Team : : CT , true ) ;
bots . updateTeamEconomics ( Team : : Terrorist , true ) ;
2019-08-18 21:00:00 +03:00
extern ConVar mp_startmoney ;
// set balance for all players
bots . forEach ( [ ] ( Bot * bot ) {
bot - > m_moneyAmount = mp_startmoney . int_ ( ) ;
return false ;
} ) ;
2019-08-24 12:43:42 +03:00
resetBombPosition ( ) ;
2019-08-12 14:16:28 +03:00
}
else if ( cached & TextMsgCache : : TerroristWin ) {
bots . setLastWinner ( Team : : Terrorist ) ; // update last winner for economics
2019-08-24 12:43:42 +03:00
resetBombPosition ( ) ;
2019-08-12 14:16:28 +03:00
}
else if ( ( cached & TextMsgCache : : BombPlanted ) & & ! bots . isBombPlanted ( ) ) {
bots . setBombPlanted ( true ) ;
for ( const auto & notify : bots ) {
if ( notify - > m_notKilled ) {
notify - > clearSearchNodes ( ) ;
notify - > clearTasks ( ) ;
if ( yb_radio_mode . int_ ( ) = = 2 & & rg . chance ( 55 ) & & notify - > m_team = = Team : : CT ) {
notify - > pushChatterMessage ( Chatter : : WhereIsTheC4 ) ;
}
}
}
2019-08-24 12:43:42 +03:00
graph . setBombOrigin ( ) ;
2019-08-12 14:16:28 +03:00
}
// check for burst fire message
if ( m_bot ) {
if ( cached & TextMsgCache : : BurstOn ) {
m_bot - > m_weaponBurstMode = BurstMode : : On ;
}
else if ( cached & TextMsgCache : : BurstOff ) {
m_bot - > m_weaponBurstMode = BurstMode : : Off ;
}
}
}
void MessageDispatcher : : netMsgVGUIMenu ( ) {
// this message is sent when a VGUI menu is displayed.
enum args { menu = 0 , min = 1 } ;
// check the minimum states or existance of bot
if ( m_args . length ( ) < min | | ! m_bot ) {
return ;
}
switch ( m_args [ menu ] . long_ ) {
case GuiMenu : : TeamSelect :
m_bot - > m_startAction = BotMsg : : TeamSelect ;
break ;
case GuiMenu : : TerroristSelect :
case GuiMenu : : CTSelect :
m_bot - > m_startAction = BotMsg : : ClassSelect ;
break ;
}
}
void MessageDispatcher : : netMsgShowMenu ( ) {
// this message is sent when a text menu is displayed.
enum args { menu = 3 , min = 4 } ;
// check the minimum states or existance of bot
if ( m_args . length ( ) < min | | ! m_bot ) {
return ;
}
m_bot - > m_startAction = m_showMenuCache [ m_args [ menu ] . chars_ ] ;
}
void MessageDispatcher : : netMsgWeaponList ( ) {
// 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.
enum args { classname = 0 , ammo_index_1 = 1 , max_ammo_1 = 2 , slot = 5 , slot_pos = 6 , id = 7 , flags = 8 , min = 9 } ;
// check the minimum states
if ( m_args . length ( ) < min ) {
return ;
}
2019-08-12 22:51:26 +03:00
// store away this weapon with it's ammo information...
conf . getWeaponProp ( m_args [ id ] . long_ ) = {
2019-08-12 14:16:28 +03:00
m_args [ classname ] . chars_ ,
m_args [ ammo_index_1 ] . long_ ,
m_args [ max_ammo_1 ] . long_ ,
m_args [ slot ] . long_ ,
m_args [ slot_pos ] . long_ ,
m_args [ id ] . long_ ,
m_args [ flags ] . long_
} ;
}
void MessageDispatcher : : netMsgCurWeapon ( ) {
// 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
enum args { state = 0 , id = 1 , clip = 2 , min = 3 } ;
// check the minimum states
if ( m_args . length ( ) < min | | ! m_bot ) {
return ;
}
if ( m_args [ id ] . long_ < kMaxWeapons ) {
if ( m_args [ state ] . long_ ! = 0 ) {
m_bot - > m_currentWeapon = m_args [ id ] . long_ ;
}
// ammo amount decreased ? must have fired a bullet...
if ( m_args [ id ] . long_ = = m_bot - > m_currentWeapon & & m_bot - > m_ammoInClip [ m_args [ id ] . long_ ] > m_args [ clip ] . long_ ) {
2019-08-18 21:00:00 +03:00
m_bot - > m_timeLastFired = game . time ( ) ; // remember the last bullet time
2019-08-12 14:16:28 +03:00
}
m_bot - > m_ammoInClip [ m_args [ id ] . long_ ] = m_args [ clip ] . long_ ;
}
}
void MessageDispatcher : : netMsgAmmoX ( ) {
// this message is sent whenever ammo amounts are adjusted (up or down). NOTE: Logging reveals that CS uses it very unreliable!
enum args { index = 0 , value = 1 , min = 2 } ;
// check the minimum states
if ( m_args . length ( ) < min | | ! m_bot ) {
return ;
}
m_bot - > m_ammo [ m_args [ index ] . long_ ] = m_args [ value ] . long_ ; // store it away
}
void MessageDispatcher : : netMsgAmmoPickup ( ) {
// 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.
enum args { index = 0 , value = 1 , min = 2 } ;
// check the minimum states
if ( m_args . length ( ) < min | | ! m_bot ) {
return ;
}
m_bot - > m_ammo [ m_args [ index ] . long_ ] = m_args [ value ] . long_ ; // store it away
}
void MessageDispatcher : : netMsgDamage ( ) {
// this message gets sent when the bots are getting damaged.
enum args { armor = 0 , health = 1 , bits = 2 , min = 3 } ;
// check the minimum states
if ( m_args . length ( ) < min | | ! m_bot ) {
return ;
}
// handle damage if any
if ( m_args [ armor ] . long_ > 0 | | m_args [ health ] . long_ ) {
2019-08-18 21:00:00 +03:00
m_bot - > takeDamage ( m_bot - > pev - > dmg_inflictor , m_args [ health ] . long_ , m_args [ armor ] . long_ , m_args [ bits ] . long_ ) ;
2019-08-12 14:16:28 +03:00
}
}
void MessageDispatcher : : netMsgMoney ( ) {
// this message gets sent when the bots money amount changes
enum args { money = 0 , min = 1 } ;
// check the minimum states
if ( m_args . length ( ) < min | | ! m_bot ) {
return ;
}
m_bot - > m_moneyAmount = m_args [ money ] . long_ ;
}
void MessageDispatcher : : netMsgStatusIcon ( ) {
enum args { enabled = 0 , icon = 1 , min = 2 } ;
// check the minimum states
if ( m_args . length ( ) < min | | ! m_bot ) {
return ;
}
// lookup cached icon
auto cached = m_statusIconCache [ m_args [ icon ] . chars_ ] ;
// check if we're need to handle message
if ( ! ( cached & TextMsgCache : : NeedHandle ) ) {
return ;
}
// handle cases
if ( cached & StatusIconCache : : BuyZone ) {
m_bot - > m_inBuyZone = ( m_args [ enabled ] . long_ ! = 0 ) ;
// try to equip in buyzone
2019-08-18 21:00:00 +03:00
m_bot - > enteredBuyZone ( BuyState : : PrimaryWeapon ) ;
2019-08-12 14:16:28 +03:00
}
else if ( cached & StatusIconCache : : VipSafety ) {
m_bot - > m_inVIPZone = ( m_args [ enabled ] . long_ ! = 0 ) ;
}
else if ( cached & StatusIconCache : : C4 ) {
m_bot - > m_inBombZone = ( m_args [ enabled ] . long_ = = 2 ) ;
}
}
void MessageDispatcher : : netMsgDeathMsg ( ) {
// this message gets sent when player kills player
enum args { killer = 0 , victim = 1 , min = 2 } ;
// check the minimum states
if ( m_args . length ( ) < min ) {
return ;
}
auto killerEntity = game . entityOfIndex ( m_args [ killer ] . long_ ) ;
auto victimEntity = game . entityOfIndex ( m_args [ victim ] . long_ ) ;
if ( game . isNullEntity ( killerEntity ) | | game . isNullEntity ( victimEntity ) | | victimEntity = = killerEntity ) {
return ;
}
bots . handleDeath ( killerEntity , victimEntity ) ;
}
void MessageDispatcher : : netMsgScreenFade ( ) {
// this message gets sent when the screen fades (flashbang)
enum args { r = 3 , g = 4 , b = 5 , alpha = 6 , min = 7 } ;
// check the minimum states
if ( m_args . length ( ) < min | | ! m_bot ) {
return ;
}
// screen completely faded ?
if ( m_args [ r ] . long_ > = 255 & & m_args [ g ] . long_ > = 255 & & m_args [ b ] . long_ > = 255 & & m_args [ alpha ] . long_ > 170 ) {
2019-08-18 21:00:00 +03:00
m_bot - > takeBlind ( m_args [ alpha ] . long_ ) ;
2019-08-12 14:16:28 +03:00
}
}
void MessageDispatcher : : netMsgHLTV ( ) {
// this message gets sent when new round is started in modern cs versions
enum args { players = 0 , fov = 1 , min = 2 } ;
// check the minimum states
if ( m_args . length ( ) < min ) {
return ;
}
// need to start new round ? (we're tracking FOV reset message)
if ( m_args [ players ] . long_ = = 0 & & m_args [ fov ] . long_ = = 0 ) {
bots . initRound ( ) ;
}
}
void MessageDispatcher : : netMsgTeamInfo ( ) {
// this message gets sent when player team index is changed
enum args { index = 0 , team = 1 , min = 2 } ;
// check the minimum states
if ( m_args . length ( ) < min ) {
return ;
}
auto & client = util . getClient ( m_args [ index ] . long_ - 1 ) ;
// update player team
client . team2 = m_teamInfoCache [ m_args [ team ] . chars_ ] ; // update real team
client . team = game . is ( GameFlags : : FreeForAll ) ? m_args [ index ] . long_ : client . team2 ;
}
void MessageDispatcher : : netMsgBarTime ( ) {
enum args { enabled = 0 , min = 1 } ;
// check the minimum states
if ( m_args . length ( ) < min | | ! m_bot ) {
return ;
}
// check if has progress bar
if ( m_args [ enabled ] . long_ > 0 ) {
m_bot - > m_hasProgressBar = true ; // the progress bar on a hud
// notify bots about defusing has started
if ( game . mapIs ( MapFlags : : Demolition ) & & bots . isBombPlanted ( ) & & m_bot - > m_team = = Team : : CT ) {
bots . notifyBombDefuse ( ) ;
}
}
else {
m_bot - > m_hasProgressBar = false ; // no progress bar or disappeared
}
}
void MessageDispatcher : : netMsgItemStatus ( ) {
enum args { value = 0 , min = 1 } ;
// check the minimum states
if ( m_args . length ( ) < min | | ! m_bot ) {
return ;
}
auto mask = m_args [ value ] . long_ ;
m_bot - > m_hasNVG = ! ! ( mask & ItemStatus : : Nightvision ) ;
m_bot - > m_hasDefuser = ! ! ( mask & ItemStatus : : DefusalKit ) ;
}
void MessageDispatcher : : netMsgNVGToggle ( ) {
enum args { value = 0 , min = 1 } ;
// check the minimum states
if ( m_args . length ( ) < min | | ! m_bot ) {
return ;
}
m_bot - > m_usesNVG = m_args [ value ] . long_ > 0 ;
}
void MessageDispatcher : : netMsgFlashBat ( ) {
enum args { value = 0 , min = 1 } ;
// check the minimum states
if ( m_args . length ( ) < min | | ! m_bot ) {
return ;
}
m_bot - > m_flashLevel = m_args [ value ] . float_ ;
}
MessageDispatcher : : MessageDispatcher ( ) {
// register wanted message
auto pushWanted = [ & ] ( const String & name , NetMsg id , MsgFunc handler ) - > void {
m_wanted [ name ] = id ;
m_handlers [ id ] = handler ;
} ;
2019-08-12 16:58:00 +03:00
reset ( ) ;
2019-08-12 14:16:28 +03:00
// we want to handle next messages
pushWanted ( " TextMsg " , NetMsg : : TextMsg , & MessageDispatcher : : netMsgTextMsg ) ;
pushWanted ( " VGUIMenu " , NetMsg : : VGUIMenu , & MessageDispatcher : : netMsgVGUIMenu ) ;
pushWanted ( " ShowMenu " , NetMsg : : ShowMenu , & MessageDispatcher : : netMsgShowMenu ) ;
pushWanted ( " WeaponList " , NetMsg : : WeaponList , & MessageDispatcher : : netMsgWeaponList ) ;
pushWanted ( " CurWeapon " , NetMsg : : CurWeapon , & MessageDispatcher : : netMsgCurWeapon ) ;
pushWanted ( " AmmoX " , NetMsg : : AmmoX , & MessageDispatcher : : netMsgAmmoX ) ;
pushWanted ( " AmmoPickup " , NetMsg : : AmmoPickup , & MessageDispatcher : : netMsgAmmoPickup ) ;
pushWanted ( " Damage " , NetMsg : : Damage , & MessageDispatcher : : netMsgDamage ) ;
pushWanted ( " Money " , NetMsg : : Money , & MessageDispatcher : : netMsgMoney ) ;
pushWanted ( " StatusIcon " , NetMsg : : StatusIcon , & MessageDispatcher : : netMsgStatusIcon ) ;
pushWanted ( " DeathMsg " , NetMsg : : DeathMsg , & MessageDispatcher : : netMsgDeathMsg ) ;
pushWanted ( " ScreenFade " , NetMsg : : ScreenFade , & MessageDispatcher : : netMsgScreenFade ) ;
pushWanted ( " HLTV " , NetMsg : : HLTV , & MessageDispatcher : : netMsgHLTV ) ;
pushWanted ( " TeamInfo " , NetMsg : : TeamInfo , & MessageDispatcher : : netMsgTeamInfo ) ;
pushWanted ( " BarTime " , NetMsg : : BarTime , & MessageDispatcher : : netMsgBarTime ) ;
pushWanted ( " ItemStatus " , NetMsg : : ItemStatus , & MessageDispatcher : : netMsgItemStatus ) ;
pushWanted ( " NVGToggle " , NetMsg : : NVGToggle , & MessageDispatcher : : netMsgNVGToggle ) ;
pushWanted ( " FlashBat " , NetMsg : : FlashBat , & MessageDispatcher : : netMsgFlashBat ) ;
// we're need next messages IDs but we're won't handle them, so they will be removed from wanted list as soon as they get engine IDs
pushWanted ( " BotVoice " , NetMsg : : BotVoice , nullptr ) ;
pushWanted ( " SendAudio " , NetMsg : : SendAudio , nullptr ) ;
// register text msg cache
m_textMsgCache [ " #CTs_Win " ] = TextMsgCache : : NeedHandle | TextMsgCache : : CounterWin ;
m_textMsgCache [ " #Bomb_Defused " ] = TextMsgCache : : NeedHandle ;
m_textMsgCache [ " #Bomb_Planted " ] = TextMsgCache : : NeedHandle | TextMsgCache : : BombPlanted ;
m_textMsgCache [ " #Terrorists_Win " ] = TextMsgCache : : NeedHandle | TextMsgCache : : TerroristWin ;
m_textMsgCache [ " #Round_Draw " ] = TextMsgCache : : NeedHandle ;
m_textMsgCache [ " #All_Hostages_Rescued " ] = TextMsgCache : : NeedHandle ;
m_textMsgCache [ " #Target_Saved " ] = TextMsgCache : : NeedHandle ;
m_textMsgCache [ " #Hostages_Not_Rescued " ] = TextMsgCache : : NeedHandle ;
m_textMsgCache [ " #Terrorists_Not_Escaped " ] = TextMsgCache : : NeedHandle ;
m_textMsgCache [ " #VIP_Not_Escaped " ] = TextMsgCache : : NeedHandle ;
m_textMsgCache [ " #Escaping_Terrorists_Neutralized " ] = TextMsgCache : : NeedHandle ;
m_textMsgCache [ " #VIP_Assassinated " ] = TextMsgCache : : NeedHandle ;
m_textMsgCache [ " #VIP_Escaped " ] = TextMsgCache : : NeedHandle ;
m_textMsgCache [ " #Terrorists_Escaped " ] = TextMsgCache : : NeedHandle ;
m_textMsgCache [ " #CTs_PreventEscape " ] = TextMsgCache : : NeedHandle ;
m_textMsgCache [ " #Target_Bombed " ] = TextMsgCache : : NeedHandle ;
m_textMsgCache [ " #Game_Commencing " ] = TextMsgCache : : NeedHandle | TextMsgCache : : Commencing ;
m_textMsgCache [ " #Game_will_restart_in " ] = TextMsgCache : : NeedHandle | TextMsgCache : : RestartRound ;
m_textMsgCache [ " #Switch_To_BurstFire " ] = TextMsgCache : : NeedHandle | TextMsgCache : : BurstOn ;
m_textMsgCache [ " #Switch_To_SemiAuto " ] = TextMsgCache : : NeedHandle | TextMsgCache : : BurstOff ;
// register show menu cache
m_showMenuCache [ " #Team_Select " ] = BotMsg : : TeamSelect ;
m_showMenuCache [ " #Team_Select_Spect " ] = BotMsg : : TeamSelect ;
m_showMenuCache [ " #IG_Team_Select_Spect " ] = BotMsg : : TeamSelect ;
m_showMenuCache [ " #IG_Team_Select " ] = BotMsg : : TeamSelect ;
m_showMenuCache [ " #IG_VIP_Team_Select " ] = BotMsg : : TeamSelect ;
m_showMenuCache [ " #IG_VIP_Team_Select_Spect " ] = BotMsg : : TeamSelect ;
m_showMenuCache [ " #Terrorist_Select " ] = BotMsg : : ClassSelect ;
m_showMenuCache [ " #CT_Select " ] = BotMsg : : ClassSelect ;
// register status icon cache
m_statusIconCache [ " buyzone " ] = StatusIconCache : : NeedHandle | StatusIconCache : : BuyZone ;
m_statusIconCache [ " vipsafety " ] = StatusIconCache : : NeedHandle | StatusIconCache : : VipSafety ;
m_statusIconCache [ " c4 " ] = StatusIconCache : : NeedHandle | StatusIconCache : : C4 ;
// register team info cache
m_teamInfoCache [ " TERRORIST " ] = Team : : Terrorist ;
m_teamInfoCache [ " UNASSIGNED " ] = Team : : Unassigned ;
m_teamInfoCache [ " SPECTATOR " ] = Team : : Spectator ;
m_teamInfoCache [ " CT " ] = Team : : CT ;
}
2019-08-29 16:24:24 +03:00
int32 MessageDispatcher : : add ( const String & name , int32 id ) {
2019-08-12 14:16:28 +03:00
if ( ! m_wanted . exists ( name ) ) {
2019-08-29 16:24:24 +03:00
return id ;
2019-08-12 14:16:28 +03:00
}
2019-08-29 16:24:24 +03:00
m_maps [ m_wanted [ name ] ] = id ; // add message from engine regusermsg
return id ;
2019-08-12 14:16:28 +03:00
}
2019-08-12 22:51:26 +03:00
void MessageDispatcher : : start ( edict_t * ent , int32 type ) {
2019-08-12 16:58:00 +03:00
reset ( ) ;
2019-08-12 14:16:28 +03:00
// search if we need to handle this message
for ( const auto & msg : m_maps ) {
if ( msg . value = = type & & m_handlers [ msg . key ] ) {
m_current = msg . key ;
break ;
}
}
// no messagem no processing
if ( m_current = = NetMsg : : None ) {
return ;
}
// message for bot bot?
2019-08-12 22:51:26 +03:00
if ( ! game . isNullEntity ( ent ) & & ! ( ent - > v . flags & FL_DORMANT ) ) {
2019-08-12 14:16:28 +03:00
m_bot = bots [ ent ] ;
if ( ! m_bot ) {
m_current = NetMsg : : None ;
return ;
}
}
m_args . clear ( ) ; // clear previous args
}
void MessageDispatcher : : stop ( ) {
if ( m_current = = NetMsg : : None ) {
return ;
}
( this - > * m_handlers [ m_current ] ) ( ) ;
m_current = NetMsg : : None ;
}
2019-09-14 23:13:55 +03:00
void MessageDispatcher : : ensureMessages ( ) {
// we're getting messages ids in regusermsg for metamod, but when we're unloaded, we're lost our ids on next 'meta load'.
// this function tries to associate appropriate message ids.
// check if we're have one
if ( m_maps . exists ( NetMsg : : Money ) ) {
return ;
}
// re-register our message
for ( const auto & msg : m_wanted ) {
add ( msg . key , GET_USER_MSG_ID ( PLID , msg . key . chars ( ) , nullptr ) ) ;
}
}
int32 MessageDispatcher : : id ( NetMsg msg ) {
if ( game . is ( GameFlags : : Metamod ) ) {
ensureMessages ( ) ;
}
return m_maps [ msg ] ;
}