#include "CustomFiberThread.h"
#include "Scripting.h"
#include "../ScriptHook/Log.h"
#include <windows.h>
#include <stdio.h>
using namespace Scripting;
// ASSEMBLY FUNCTIONS, MAYBE CAN NOT BE A CLASS FUNCTION
__declspec( noinline ) u32 GTA4_GetHandleFromPed( CPool< void* >* pPool, void *Ped )
{
_asm mov ecx, pPool;
_asm mov eax, Ped;
_asm sub eax, [ecx];
_asm cdq;
_asm idiv dword ptr [ecx+12];
_asm mov edx, eax;
_asm mov eax, [ecx+4];
_asm movzx eax, byte ptr [eax+edx];
_asm shl edx, 8;
_asm add eax, edx;
}
// ASSEMBLY FUNCTIONS, MAYBE CAN NOT BE A CLASS FUNCTION
RiotThread::RiotThread()
{
SetName( "RiotIV_050" );
}
CPool< void* >* RiotThread::GetPedPoolNative()
{
// HACKHACK: Static address for GTAIV 1.0.6.0; DEPRECIATED!!!
// return reinterpret_cast< CPool< void* >* >( *reinterpret_cast< DWORD* >( Game::GetBase() + 0x18A72BC ) );
return reinterpret_cast< CPool< void* >* >( *reinterpret_cast< DWORD* >( m_ulPedPoolBase ) );
}
u32 RiotThread::GetPedCount()
{
if( GetPedPoolNative() == NULL ) return 0;
return GetPedPoolNative()->Count();
}
b8 RiotThread::GetPedByIndex( int idx, Ped *Out )
{
if( !Out )
return false;
Out->Set( 0 );
if( GetPedCount() == 0 )
return false;
void *CurrentPedIdx = GetPedPoolNative()->at( idx );
if( CurrentPedIdx == NULL )
return false;
Out->Set( GTA4_GetHandleFromPed( GetPedPoolNative(), CurrentPedIdx ) );
return ( Out->IsValid() && DoesCharExist( *Out ) );
}
Scripting::Vector3 RiotThread::GetPedVector( Scripting::Ped *In )
{
Vector3 Ret(0,0,0);
Scripting::GetCharCoordinates( *In, &Ret.X, &Ret.Y, &Ret.Z );
return Ret;
}
Player RiotThread::GetPlayer()
{
Player playerIndex = ConvertIntToPlayerIndex(GetPlayerId());
return playerIndex;
}
Scripting::Ped RiotThread::GetPlayerPed()
{
Ped ped;
GetPlayerChar( GetPlayer(), &ped );
return ped;
}
Scripting::eWeapon RiotThread::GetRandomWeapon()
{
srand( GetTickCount() );
return static_cast< eWeapon >( m_UsableWeaponry[ rand() % m_UsableWeaponry.size() ] );
}
b8 RiotThread::IsPedPolice( Scripting::Ped ped )
{
eModel CurrentModel;
Scripting::GetCharModel( ped, &CurrentModel );
return (
( CurrentModel == MODEL_M_Y_COP ) ||
( CurrentModel == MODEL_M_Y_COP_TRAFFIC ) ||
( CurrentModel == MODEL_M_M_FATCOP_01 ) ||
( CurrentModel == MODEL_CS_MITCHCOP ) ||
( CurrentModel == MODEL_M_Y_SWAT ) ||
( CurrentModel == MODEL_M_M_FBI ) ||
( CurrentModel == MODEL_M_Y_STROOPER ) );
}
b8 RiotThread::ShouldRiot( Scripting::Ped ped )
{
if( m_DontRiotHashes.empty() )
{
return true;
}
for( size_t i = 0; i < m_DontRiotHashes.size(); i++ )
{
if( IsCharModel( ped, static_cast< Scripting::eModel >( m_DontRiotHashes[ i ] ) ) )
{
return false;
}
}
return true;
}
b8 RiotThread::IsPedAllowedToRiot( Scripting::Ped ped )
{
if( m_RiotHashes.empty() )
return true;
for( size_t i = 0; i < m_RiotHashes.size(); i++ )
{
if( IsCharModel( ped, static_cast< Scripting::eModel >( m_RiotHashes[ i ] ) ) )
{
return true;
}
}
return false;
}
b8 RiotThread::CausePedToRiot( Scripting::Ped LocalPed, Scripting::Ped RiotPed )
{
// Check if ped exists
if( DoesCharExist( RiotPed ) == false )
{
return false;
}
// Check the model for status
if( ShouldRiot( RiotPed ) == false || IsPedAllowedToRiot( RiotPed ) == false )
{
if( IsPedAMissionPed( RiotPed ) )
{
MarkCharAsNoLongerNeeded( &RiotPed );
}
return false;
}
// If not "visible", delete them, not needed
if( IsCharVisible( RiotPed ) == false )
{
if( IsPedAMissionPed( RiotPed ) )
{
MarkCharAsNoLongerNeeded( &RiotPed );
DeleteChar( &RiotPed );
}
return false;
}
// If dead, fatally injured
// if mission ped, unmark, stop
if( IsCharDead( RiotPed ) || IsCharFatallyInjured( RiotPed ) )
{
if( IsPedAMissionPed( RiotPed ) )
{
MarkCharAsNoLongerNeeded( &RiotPed );
}
return false;
}
// If cops rioting is disabled and ped is police, stop
if( m_bCopsGoCrazy == false && IsPedPolice( RiotPed ) )
{
if( IsPedAMissionPed( RiotPed ) )
{
MarkCharAsNoLongerNeeded( &RiotPed );
}
return false;
}
// If usable weaponry is available
if( m_UsableWeaponry.size() > 0 )
{
//if NOT police (already have weapons)
if( IsPedPolice( RiotPed ) == false )
{
//if have a gun already, and not police, stop
if( Scripting::IsCharArmed( RiotPed, WEAPON_SLOT_MELEE )
|| Scripting::IsCharArmed( RiotPed, WEAPON_SLOT_HANDGUN )
|| Scripting::IsCharArmed( RiotPed, WEAPON_SLOT_HEAVY )
|| Scripting::IsCharArmed( RiotPed, WEAPON_SLOT_RIFLE )
|| Scripting::IsCharArmed( RiotPed, WEAPON_SLOT_SHOTGUN )
|| Scripting::IsCharArmed( RiotPed, WEAPON_SLOT_SMG )
|| Scripting::IsCharArmed( RiotPed, WEAPON_SLOT_SNIPER )
|| Scripting::IsCharArmed( RiotPed, WEAPON_SLOT_THROWN ) )
{
return false;
}
}
}
// Range checking to avoid crashes
Scripting::Vector3 LocalVec = GetPedVector( &LocalPed );
Scripting::Vector3 PedVec = GetPedVector( &RiotPed );
if( PedVec.Distance( &LocalVec ) > m_fMaximumRange )
{
// HACKHACK: If outside of maximum range mark as no longer needed...
if( IsPedAMissionPed( RiotPed ) )
{
MarkCharAsNoLongerNeeded( &RiotPed );
}
return false;
}
if( IsPedAMissionPed( RiotPed ) == false )
{
// Setup violent tendencies
SetCharAsMissionChar( RiotPed );
SetCharAsEnemy( RiotPed, m_bTargetPlayer );
SetPedDiesWhenInjured( RiotPed, true );
AllowTargetWhenInjured( RiotPed, true );
SetCharWillMoveWhenInjured( RiotPed, true );
SetCharWillDoDrivebys( RiotPed, true );
SetCharWillUseCarsInCombat( RiotPed, m_bPedsUseCars );
SetPedWontAttackPlayerWithoutWantedLevel( RiotPed, 0 );
SetSenseRange( RiotPed, m_fMaximumRange );
SetCharAccuracy( RiotPed, 100 );
if( IsPedPolice( RiotPed ) == false )
{
SetCharWantedByPolice( RiotPed, m_bPedsWanted );
SetCharRelationshipGroup( RiotPed, 8 );
// If weaponry available, give random weapon
if( m_UsableWeaponry.size() > 0 )
{
eWeapon RandomWeapon = GetRandomWeapon();
GiveWeaponToChar( RiotPed, RandomWeapon, ( RandomWeapon > 3 ) ? 9999 : 0, 0 );
SetCurrentCharWeapon( RiotPed, RandomWeapon, true );
BlockPedWeaponSwitching( RiotPed, true );
}
else
{
// Melee if no weapons are set
RemoveAllCharWeapons( RiotPed );
}
}
// Set attacking task
TaskCombatHatedTargetsAroundChar( RiotPed, m_fMaximumRange );
SetCharKeepTask( RiotPed, true );
}
return true;
}
void RiotThread::SpawnNewRioter( Ped LocalPed, Vector3 SpawnVector )
{
Scripting::PrintStringWithLiteralStringNow( "STRING", "Spawning new riot ped", 3000, true );
srand( GetTickCount() );
eModel ModelSpawn = static_cast< eModel >( m_SpawnHashes[ rand() % m_SpawnHashes.size() ] );
RequestModel( ModelSpawn );
while( HasModelLoaded( ModelSpawn ) == false )
{
Wait(10);
}
Ped OutPed;
CreateChar( 2, ModelSpawn, SpawnVector.X, SpawnVector.Y, SpawnVector.Z, &OutPed, true );
if( OutPed.IsValid() )
{
while( DoesCharExist( OutPed ) == false )
{
Wait(10);
}
while( IsCharVisible( OutPed ) == false )
{
Wait(10);
}
SetCharHealth( OutPed, 200 );
MarkCharAsNoLongerNeeded( &OutPed );
}
}
void RiotThread::SetupRelationships()
{
SetRelationship( 5, 8, 8 ); // Rioters hate themselves
SetRelationship( 5, 8, 3 ); // Rioters hate police
SetPlayerCanBeHassledByGangs( GetPlayer(), m_bTargetPlayer );
if( m_bTargetPlayer )
{
SetRelationship( 5, 8, 0 ); // Rioters hate player (you)
SetRelationship( 5, 0, 8 ); // Player hate rioters
if( m_bCopsGoCrazy )
{
SetRelationship( 5, 0, 3 ); //Cops hate player (you)
}
}
else
{
SetRelationship( 1, 8, 0 ); // Rioters ignore player (you)
SetRelationship( 1, 0, 8 ); // Player ignore rioters
}
if( m_bCopsGoCrazy )
{
SetRelationship( 5, 3, 8 ); // Cops hate rioters
}
}
std::vector< int > RiotThread::StringToIntList( const char *str )
{
std::vector< int > ReturnVector;
if( str == NULL )
{
ReturnVector.clear();
return ReturnVector;
}
//
char *pch = strtok( const_cast< char* >( str ), "," );
if( pch == NULL )
{
ReturnVector.push_back( atoi( str ) );
}
else
{
while( pch )
{
ReturnVector.push_back( atoi( pch ) );
pch = strtok( 0, "," );
}
}
return ReturnVector;
}
void RiotThread::SetupSettingsFile()
{
Log::Debug( "Loading Settings..." );
m_pConfigService = ScriptHookManager::RequestService< IConfigService >( "Config" );
if( m_pConfigService )
{
IConfig* Configuration = m_pConfigService->Create( IConfigService::ConfigTypeXml );
if( Configuration == NULL )
return;
Configuration->Set( "RiotIV", "Enabled", "1" );
Configuration->Set( "RiotIV", "TargetPlayer", "0" );
Configuration->Set( "RiotIV", "CrazyCops", "1" );
Configuration->Set( "RiotIV", "PedsUseCars", "1" );
Configuration->Set( "RiotIV", "PedsWanted", "1" );
Configuration->Set( "RiotIV", "MadDrivers", "1" );
Configuration->Set( "RiotIV", "EmergencyServicesDisabled", "1" );
Configuration->Set( "RiotIV", "PedMultiplierEnforce", "NONE" );
Configuration->Set( "RiotIV", "UsableWeaponry", "4,5,7,9,10,11,12,13,14,15,16,17,18" );
Configuration->Set( "RiotIV", "DontRiotHashes", "" );
Configuration->Set( "RiotIV", "SpawnHashes", "1794146792" );
Configuration->Set( "RiotIV", "RiotHashes", "ANY" );
Configuration->Set( "RiotIV", "MaximumRange", "100.0" );
char *pszModulePath = GetModulePath();
if( pszModulePath == 0 || strlen( pszModulePath ) == 0 )
return;
Log::Debug( "Module Path [%s]", pszModulePath );
char pszConfigPath[ MAX_PATH ] = { 0 };
strcat_s( pszConfigPath, MAX_PATH, GetModulePath() );
strcat_s( pszConfigPath, MAX_PATH, "Riot.xml" );
Log::Debug( "Loading configuration from [%s]", pszConfigPath );
WIN32_FIND_DATAA wfd;
if ( FindFirstFileA( pszConfigPath, &wfd ) != INVALID_HANDLE_VALUE )
{
Configuration->Load( pszConfigPath );
// HACKHACK: Save after we load to ensure any new values are inserted into the file for future loading
Configuration->Save( pszConfigPath );
}
else
{
Configuration->Save( pszConfigPath );
}
m_bEnabled = ( Configuration->GetInteger( "RiotIV", "Enabled", 1 ) == 1 );
m_bTargetPlayer = ( Configuration->GetInteger( "RiotIV", "TargetPlayer", 0 ) == 1 );
m_bCopsGoCrazy = ( Configuration->GetInteger( "RiotIV", "CrazyCops", 0 ) == 1 );
m_bPedsUseCars = ( Configuration->GetInteger( "RiotIV", "PedsUseCars", 1 ) == 1 );
m_bPedsWanted = ( Configuration->GetInteger( "RiotIV", "PedsWanted", 0 ) == 1 );
m_bMadDrivers = ( Configuration->GetInteger( "RiotIV", "MadDrivers", 0 ) == 1 );
m_bEmergencyServicesDisabled = ( Configuration->GetInteger( "RiotIV", "EmergencyServicesDisabled", 0 ) == 1 );
m_fMaximumRange = Configuration->GetFloat( "RiotIV", "MaximumRange", 100.f );
CONST CHAR* PedMultiEnforce = Configuration->GetString( "RiotIV", "PedMultiplierEnforce" );
CONST CHAR* UsableWeapons = Configuration->GetString( "RiotIV", "UsableWeaponry" );
CONST CHAR* DontRiotHashes = Configuration->GetString( "RiotIV", "DontRiotHashes" );
CONST CHAR* RiotHashes = Configuration->GetString( "RiotIV", "RiotHashes" );
CONST CHAR* SpawnHashes = Configuration->GetString( "RiotIV", "SpawnHashes" );
m_UsableWeaponry = StringToIntList( UsableWeapons );
m_DontRiotHashes = StringToIntList( DontRiotHashes );
m_SpawnHashes = StringToIntList( SpawnHashes );
if( _stricmp( PedMultiEnforce, "NONE" ) == 0 )
{
m_bForcePedMultiplier = false;
}
else
{
m_bForcePedMultiplier = true;
m_fPedMultiplier = static_cast< f32 >( atof( PedMultiEnforce ) );
}
if( _stricmp( RiotHashes, "ANY" ) == 0 )
{
m_RiotHashes.clear();
}
else
{
m_RiotHashes = StringToIntList( RiotHashes );
}
Configuration->Release();
}
else
{
Log::Debug( "Unable to load configuration service.." );
}
}
char* RiotThread::GetModulePath()
{
static char pszPath[ MAX_PATH ] = { 0 };
if( strlen( pszPath ) > 0 )
return pszPath;
GetModuleFileNameA( m_hModule, pszPath, sizeof( pszPath ) );
u32 i = strlen( pszPath );
while( i > 0 && pszPath[i] != '\\' )
{
i--;
}
pszPath[i] = 0;
strcat_s( pszPath, MAX_PATH, "\\" );
return pszPath;
}
void RiotThread::UpdateCode()
{
HMODULE hMainModule = GetModuleHandleW( NULL );
Log::Debug( "Finding signiture..[0x%X][0x%X]", hMainModule, Game::GetBase() );
m_ulPedPoolBase = Code::FindAddress::DetectPattern(
reinterpret_cast< unsigned long >( hMainModule ), 0xFFFFFF,
( BYTE* )"\x55\x8B\xEC\x83\xE4\xF0\x8B\x15\x00\x00\x00\x00\x81\xEC\xEC\x00\x00\x00",
( CHAR* )"xxxxxxxx????xxxxxx" );
if( m_ulPedPoolBase == NULL )
{
Log::Fatal( "PedPoolBase not found...critical error" );
ExitProcess(0);
}
else
{
Log::Debug( "PedPoolBase 001 [0x%X]", m_ulPedPoolBase );
m_ulPedPoolBase += 8;
Log::Debug( "PedPoolBase 002 [0x%X]", m_ulPedPoolBase );
m_ulPedPoolBase = *reinterpret_cast< unsigned long* >( m_ulPedPoolBase );
Log::Debug( "PedPoolBase FIN [0x%X]", m_ulPedPoolBase );
Log::Debug( "Address - Base = [0x%X]", m_ulPedPoolBase - reinterpret_cast< unsigned long >( hMainModule ) );
}
}
void RiotThread::RunScript()
{
SetupSettingsFile();
CreateKeyboard();
CreateMenu();
while( IsThreadAlive() )
{
if( NetworkIsSessionStarted() == false )
{
Ped LocalPed = GetPlayerPed();
if( LocalPed.IsValid() == false )
continue;
Vector3 LocalVector = GetPedVector( &LocalPed );
if( m_bSpawnRandomPedNextFrame && m_SpawnHashes.size() )
{
SpawnNewRioter( LocalPed, Vector3( LocalVector.X + 2.f, LocalVector.Y, LocalVector.Z ) );
m_bSpawnRandomPedNextFrame = false;
}
if( m_bForcePedMultiplier )
{
SetPedDensityMultiplier( m_fPedMultiplier );
}
SwitchMadDrivers( m_bMadDrivers );
AllowEmergencyServices( !m_bEmergencyServicesDisabled );
SetPoliceIgnorePlayer( GetPlayer(), m_bEmergencyServicesDisabled );
if( m_bEmergencyServicesDisabled )
{
SetCharWantedByPolice( LocalPed, false );
ClearWantedLevel( GetPlayer() );
ClearAreaOfCops( LocalVector.X, LocalVector.Y, LocalVector.Z, m_fMaximumRange );
}
if( m_bEnabled )
{
AllowGangRelationshipsToBeChangedByNextCommand( true );
SetupRelationships();
for( u32 i = 0; i < GetPedCount(); i++ )
{
Ped Index;
Index.Set( 0 );
if( GetPedByIndex( i, &Index ) )
{
if( Index.IsNull() )
continue;
if( Index.Get() == LocalPed.Get() )
continue;
if( CausePedToRiot( LocalPed, Index ) == false )
continue;
}
}
}
}
Wait( 10 );
}
}
void RiotThread::OnStart()
{
m_pMenu = NULL;
}
void RiotThread::OnKill()
{
IKeyboardHookService *kbhService = ScriptHookManager::RequestService<IKeyboardHookService>( "KeyboardHook" );
kbhService->RemoveHandler( this );
if( m_pMenu )
{
m_pMenu->Release();
m_pMenu = NULL;
}
ScriptThread::OnKill();
}
void RiotThread::CreateKeyboard()
{
IKeyboardHookService *kbhService = ScriptHookManager::RequestService<IKeyboardHookService>( "KeyboardHook" );
kbhService->AddHandler( this );
}
void RiotThread::CreateMenu()
{
IMenuService *menuService = ScriptHookManager::RequestService< IMenuService >( "Menu" );
m_pMenu = menuService->CreateMenu();
m_pMenu->SetTitle( "Riot mode" );
AddMenuItemCustom( 0, m_bEnabled ? "Disable Riot Mode" : "Enable Riot Mode" );
AddMenuItemCustom( 1, m_bTargetPlayer ? "Disable peds targetting player" : "Enable peds targetting player" );
AddMenuItemCustom( 2, m_bCopsGoCrazy ? "Disable cops rioting" : "Enable cops rioting" );
AddMenuItemCustom( 3, m_bPedsUseCars ? "Disable peds use cars in combat" : "Enable peds use cars in combat" );
AddMenuItemCustom( 4, m_bPedsWanted ? "Disable peds wanted" : "Enable peds wanted" );
AddMenuItemCustom( 5, m_bMadDrivers ? "Disable mad drivers" : "Enable mad drives" );
AddMenuItemCustom( 6, m_bEmergencyServicesDisabled ? "Enable emergancy services" : "Disable emergancy services" );
m_pMenu->SetEventHandler( this );
}
void RiotThread::AddMenuItemCustom( int idx, char *pszFormat, ... )
{
char FormattedBuffer[ 1024 ] = { 0 };
va_list va_alist;
va_start( va_alist, pszFormat );
_vsnprintf(
FormattedBuffer + strlen( FormattedBuffer ),
sizeof( FormattedBuffer ) - strlen( FormattedBuffer ),
pszFormat, va_alist );
va_end( va_alist );
m_pMenu->AddItem( idx, FormattedBuffer );
}
void RiotThread::SetMenuItemCustom( int idx, char *pszFormat, ... )
{
char FormattedBuffer[ 1024 ] = { 0 };
va_list va_alist;
va_start( va_alist, pszFormat );
_vsnprintf(
FormattedBuffer + strlen( FormattedBuffer ),
sizeof( FormattedBuffer ) - strlen( FormattedBuffer ),
pszFormat, va_alist );
va_end( va_alist );
m_pMenu->SetItem( idx, FormattedBuffer );
}
void RiotThread::OnMenuSelectionChanged( IMenu *menu, u32 id )
{
// None?
}
void RiotThread::OnMenuSelected( IMenu *menu, u32 id )
{
switch( id )
{
case 0:
{
m_bEnabled = !m_bEnabled;
SetMenuItemCustom( 0, m_bEnabled ? "Disable Riot Mode" : "Enable Riot Mode" );
break;
}
case 1:
{
m_bTargetPlayer = !m_bTargetPlayer;
SetMenuItemCustom( 1, m_bTargetPlayer ? "Disable peds targetting player" : "Enable peds targetting player" );
break;
}
case 2:
{
m_bCopsGoCrazy = !m_bCopsGoCrazy;
SetMenuItemCustom( 2, m_bCopsGoCrazy ? "Disable cops rioting" : "Enable cops rioting" );
break;
}
case 3:
{
m_bPedsUseCars = !m_bPedsUseCars;
SetMenuItemCustom( 3, m_bPedsUseCars ? "Disable peds use cars in combat" : "Enable peds use cars in combat" );
break;
}
case 4:
{
m_bPedsWanted = !m_bPedsWanted;
SetMenuItemCustom( 4, m_bPedsWanted ? "Disable peds wanted" : "Enable peds wanted" );
break;
}
case 5:
{
m_bMadDrivers = !m_bMadDrivers;
SetMenuItemCustom( 5, m_bMadDrivers ? "Disable mad drivers" : "Enable mad drives" );
break;
}
case 6:
{
m_bEmergencyServicesDisabled = !m_bEmergencyServicesDisabled;
SetMenuItemCustom( 6, m_bEmergencyServicesDisabled ? "Enable emergancy services" : "Disable emergancy services" );
break;
}
}
}
void RiotThread::OnKeyboardHookEvent( const IKeyboardHookHandler::KeyEventArgs &args )
{
if( args.VirtualKey == VK_INSERT && !args.WasKeyDownBefore )
{
if( m_pMenu )
{
m_pMenu->Show();
}
}
if( args.VirtualKey == VK_F3 && !args.WasKeyDownBefore )
{
m_bSpawnRandomPedNextFrame = true;
}
}