Загрузка данных
#include < amxmisc >
#include < fakemeta >
#include < hamsandwich >
#include < json >
#include < reapi >
native bio_get_user_money( id );
native bio_set_user_money( id, amount, bool: flash = true );
#if AMXX_VERSION_NUM < 190
#assert Outdated version of AMX Mod X (AMXX_VERSION_NUM), please update in order to use this plugin properly.
#endif
#pragma semicolon 1
#include < bitmask >
new const TEXT_MESSAGE[ ][ ] =
{
"#Game_Commencing",
"#Cstrike_Tutor_Round_Over",
"#Round_Draw",
"#Game_will_restart_in",
"#CTs_Win",
"#Terrorists_Win"
};
new const SEND_AUDIO[ ][ ] =
{
"%!MRAD_rounddraw",
"%!MRAD_terwin",
"%!MRAD_ctwin"
};
new const ZOMBIE_SCREAM[ ][ ] =
{
"biohazard/scream1.wav",
"biohazard/scream2.wav",
"biohazard/scream3.wav"
};
new const IMMUTABLE_CVARS[ ][ ][ ] =
{
{ "mp_limitteams", "0" },
{ "mp_autoteambalance", "0" },
{ "mp_round_infinite", "b" },
{ "mp_roundover", "1" },
{ "mp_auto_join_team", "1" },
{ "humans_join_team", "CT" },
{ "mp_autokick", "0" }
};
new m_cImmutableCvars[ sizeof( IMMUTABLE_CVARS ) ];
enum ( += 100 )
{
TASKID_WARMUP,
TASKID_LAUNCHGAME,
TASKID_AFK_CHECK
};
enum _: ClassData
{
ClassName[ 32 ],
Float: ClassHealth,
Float: ClassSpeed,
Float: ClassGravity,
ClassModel[ 64 ],
ClassClaw[ 64 ]
};
new Array: m_arrClassesVec = Invalid_Array,
m_iNumClasses;
new m_iMenuPosition[ MAX_CLIENTS + 1 ];
new Array: m_arrSpawnPoints = Invalid_Array,
m_iLastSpawn;
// Plugin's hash maps.
new Trie: m_trUselessEntities = Invalid_Trie,
Trie: m_trTextMessage = Invalid_Trie,
Trie: m_trSendAudio = Invalid_Trie;
// Plugin's vars.
new bool: m_bWarmup = true,
bool: m_bZombieRespawnAllowed;
// Plugin's cvars.
new m_pCvar_FreezeTime,
m_pCvar_RoundTime,
m_pCvar_OverHumansWin,
m_pCvar_ZombieRespawnTime,
m_pCvar_ZombieRespawnMinDistance,
m_pCvar_ZombieRespawnLimit,
m_pCvar_ZombieRespawnChanceDecay,
m_pCvar_ZombieBalanceAfkTime,
m_pCvar_MoneyDamageRatio,
m_pCvar_MoneyInfection;
// Plugin's forwards.
new m_fwdSpawn;
// Client's vars.
new m_varClass[ MAX_CLIENTS + 1 ],
m_iZombieRespawn[ MAX_CLIENTS + 1 ],
m_iMoney[ MAX_CLIENTS + 1 ];
// Client's bits.
new m_bitZombie,
m_bitZombieRespawn,
m_bitJoinAsZombie;
public plugin_precache( )
{
register_plugin( "Biohazard", "0.0.1", "oxsi. x Vision" );
m_arrClassesVec = ArrayCreate( ClassData );
m_arrSpawnPoints = ArrayCreate( 1, 0 );
m_trTextMessage = TrieCreate( );
for ( new i = 0; i < sizeof( TEXT_MESSAGE ); i++ )
TrieSetCell( m_trTextMessage, TEXT_MESSAGE[ i ], 0 );
m_trSendAudio = TrieCreate( );
for ( new i = 0; i < sizeof( SEND_AUDIO ); i++ )
TrieSetCell( m_trSendAudio, SEND_AUDIO[ i ], 0 );
for ( new i = 0; i < sizeof( ZOMBIE_SCREAM ); i++ )
precache_sound( ZOMBIE_SCREAM[ i ] );
for ( new i = 0; i < sizeof( IMMUTABLE_CVARS ); i++ )
{
m_cImmutableCvars[ i ] = get_cvar_pointer( IMMUTABLE_CVARS[ i ][ 0 ] );
set_pcvar_string( m_cImmutableCvars[ i ], IMMUTABLE_CVARS[ i ][ 1 ] );
hook_cvar_change( m_cImmutableCvars[ i ], "@Hook_ForcingCvarChange" );
}
m_pCvar_FreezeTime = get_cvar_pointer( "mp_freezetime" );
m_pCvar_RoundTime = get_cvar_pointer( "mp_roundtime" );
set_pcvar_flags( m_pCvar_FreezeTime, get_pcvar_flags( m_pCvar_FreezeTime ) | FCVAR_UNLOGGED );
set_pcvar_flags( m_pCvar_RoundTime, get_pcvar_flags( m_pCvar_RoundTime ) | FCVAR_UNLOGGED );
if ( m_bWarmup )
{
set_pcvar_num( m_pCvar_FreezeTime, 0 );
set_pcvar_float( m_pCvar_RoundTime, 20.0 / 60.0 );
set_task_ex( 1.0, "@Task_Warmup", TASKID_WARMUP, .flags = SetTask_Repeat );
}
else
{
set_pcvar_num( m_pCvar_FreezeTime, 15 );
set_pcvar_float( m_pCvar_RoundTime, 180.0 / 60.0 );
}
if ( !Read_JsonField( "biohazard" ) )
set_fail_state( "Failed read '%s' file." );
}
public plugin_init( )
{
m_pCvar_OverHumansWin = register_cvar( "bio_over_humans_win", "1" );
m_pCvar_ZombieRespawnTime = register_cvar( "bio_zombie_respawn", "5.0" );
m_pCvar_ZombieRespawnMinDistance = register_cvar( "bio_zombie_respawn_min_distance", "1200.0" );
m_pCvar_ZombieRespawnLimit = register_cvar( "bio_zombie_respawn_limit", "4" );
m_pCvar_ZombieRespawnChanceDecay = register_cvar( "bio_zombie_chance_decay", "25.0" );
m_pCvar_ZombieBalanceAfkTime = register_cvar( "bio_balance_afk_time", "45.0" );
m_pCvar_MoneyDamageRatio = register_cvar( "bio_money_damage_ratio", "0.5" );
m_pCvar_MoneyInfection = register_cvar( "bio_money_infection", "150" );
register_clcmd( "say /class", "@Open_ClassesMenu" );
register_clcmd( "say_team /class", "@Open_ClassesMenu" );
register_menucmd( register_menuid( "@Display_ClassesMenu" ), ( 1 << 0 | 1 << 1 | 1 << 2 | 1 << 3 | 1 << 4 | 1 << 5 | 1 << 6 | 1 << 7 | 1 << 8 | 1 << 9 ), "@Handle_ClassesMenu" );
register_message( get_user_msgid( "TextMsg" ), "@Message_TextMsg" );
register_message( get_user_msgid( "SendAudio" ), "@Message_SendAudio" );
register_message( get_user_msgid( "Money" ), "@Message_Money" );
RegisterHookChain( RH_SV_ActivateServer, "@SV_ActivateServer_Post", true );
RegisterHookChain( RG_RoundEnd, "@RoundEnd_Pre" );
RegisterHookChain( RG_RoundEnd, "@RoundEnd_Post", true );
RegisterHookChain( RG_CSGameRules_RestartRound, "@CSGameRules_RestartRound_Pre" );
RegisterHookChain( RG_CSGameRules_RestartRound, "@CSGameRules_RestartRound_Post", true );
RegisterHookChain( RG_CSGameRules_CheckWinConditions, "@CSGameRules_CheckWinConditions_Pre" );
RegisterHookChain( RG_CSGameRules_FPlayerCanTakeDamage, "@CSGameRules_FPlayerCanTakeDamage_Pre" );
RegisterHookChain( RG_CSGameRules_OnRoundFreezeEnd, "@CSGameRules_OnRoundFreezeEnd_Pre" );
RegisterHookChain( RG_CSGameRules_GetPlayerSpawnSpot, "@CSGameRules_GetPlayerSpawnSpot_Pre" );
RegisterHookChain( RG_CBasePlayer_Spawn, "@CBasePlayer_Spawn_Pre" );
RegisterHookChain( RG_CBasePlayer_Spawn, "@CBasePlayer_Spawn_Post", true );
RegisterHookChain( RG_CBasePlayer_HasRestrictItem, "@CBasePlayer_HasRestrictItem_Pre" );
RegisterHookChain( RG_CBasePlayer_TakeDamage, "@CBasePlayer_TakeDamage_Pre" );
RegisterHookChain( RG_CBasePlayer_ResetMaxSpeed, "@CBasePlayer_ResetMaxSpeed_Pre" );
RegisterHookChain( RG_CBasePlayer_Killed, "@CBasePlayer_Killed_Post", true );
@Forcing_LevelInitialize( );
}
public plugin_natives( )
{
register_native( "bio_get_user_money", "@native_get_user_money" );
register_native( "bio_set_user_money", "@native_set_user_money" );
}
public plugin_end( )
{
if ( m_trUselessEntities != Invalid_Trie )
{
TrieDestroy( m_trUselessEntities );
m_trUselessEntities = Invalid_Trie;
}
if ( m_trTextMessage != Invalid_Trie )
{
TrieDestroy( m_trTextMessage );
m_trTextMessage = Invalid_Trie;
}
if ( m_trSendAudio != Invalid_Trie )
{
TrieDestroy( m_trSendAudio );
m_trSendAudio = Invalid_Trie;
}
if ( m_arrClassesVec != Invalid_Array )
{
ArrayDestroy( m_arrClassesVec );
m_arrClassesVec = Invalid_Array;
}
}
public client_putinserver( id )
{
m_varClass[ id ] = 0;
BIT_SUB( m_bitJoinAsZombie, BIT_PLAYER( id ) );
RequestFrame( "@RequestFrame_TryJoinAsZombie", id );
}
public client_disconnected( id )
{
m_iZombieRespawn[ id ] = 0;
m_varClass[ id ] = 0;
BIT_SUB( m_bitZombieRespawn, BIT_PLAYER( id ) );
BIT_SUB( m_bitZombie, BIT_PLAYER( id ) );
BIT_SUB( m_bitJoinAsZombie, BIT_PLAYER( id ) );
}
@Open_ClassesMenu( id )
{
if ( !m_iNumClasses )
{
client_print( id, print_chat, "Классы зомби не найдены!" );
return PLUGIN_HANDLED;
}
return @Display_ClassesMenu( id, m_iMenuPosition[ id ] = 0 );
}
@Display_ClassesMenu( id, pos )
{
if ( pos < 0 )
return PLUGIN_HANDLED;
new start;
if ( ( start = pos * 7 ) > m_iNumClasses )
start = m_iNumClasses;
start = start - ( start % 7 );
pos = start / 7;
new end;
if ( ( end = start + 7 ) > m_iNumClasses )
end = m_iNumClasses;
new menu[ 512 ], classData[ ClassData ], page = ( m_iNumClasses / 7 + ( ( m_iNumClasses % 7 ) ? 1 : 0 ) ), key = ( 1 << 9 ), k,
len = add( menu, charsmax( menu ), fmt( "\yClasses %d/%d^n^n", pos + 1, page ) );
while ( start < end )
{
ArrayGetArray( m_arrClassesVec, start, classData );
if ( m_varClass[ id ] != start )
key |= ( 1 << k );
if ( m_varClass[ id ] == start )
len += add( menu, charsmax( menu ) - len, fmt( "\y%d. \d%s^n", ++k, classData[ ClassName ] ) );
else
len += add( menu, charsmax( menu ) - len, fmt( "\y%d. \w%s^n", ++k, classData[ ClassName ] ) );
start++;
}
if ( pos )
key |= ( 1 << 7 );
if ( end < m_iNumClasses )
key |= ( 1 << 8 );
len += add( menu, charsmax( menu ) - len, fmt( "^n\y8. %sНазад", pos ? "\w" : "\d" ) );
len += add( menu, charsmax( menu ) - len, fmt( "^n\y9. %sВперёд", end < m_iNumClasses ? "\w" : "\d" ) );
add( menu, charsmax( menu ), "^n\y0. \wВыход" );
return show_menu( id, key, menu, -1, "@Display_ClassesMenu" );
}
@Handle_ClassesMenu( id, key )
{
switch ( key )
{
case 7: @Display_ClassesMenu( id, --m_iMenuPosition[ id ] );
case 8: @Display_ClassesMenu( id, ++m_iMenuPosition[ id ] );
case 9: return PLUGIN_HANDLED;
default:
{
new class = m_iMenuPosition[ id ] * 7 + key;
if ( class >= m_iNumClasses )
return PLUGIN_HANDLED;
m_varClass[ id ] = class;
new classData[ ClassData ];
ArrayGetArray( m_arrClassesVec, class, classData );
client_print( id, print_chat, "Вы выбрали класс %s %d", classData[ ClassName ], m_varClass[ id ] );
}
}
return PLUGIN_CONTINUE;
}
@Hook_ForcingCvarChange( pcvar, old_value[ ], new_value[ ] )
{
for ( new i = 0; i < sizeof( IMMUTABLE_CVARS ); i++ )
{
if ( m_cImmutableCvars[ i ] != pcvar )
continue;
if ( equal( new_value, IMMUTABLE_CVARS[ i ][ 1 ] ) )
continue;
set_pcvar_string( pcvar, IMMUTABLE_CVARS[ i ][ 1 ] );
break;
}
}
@Message_TextMsg( msg_id, msg_dest, receiver )
{
new text[ 32 ], value;
get_msg_arg_string( 2, text, charsmax( text ) );
if ( TrieGetCell( m_trTextMessage, text, value ) )
return PLUGIN_HANDLED;
return PLUGIN_CONTINUE;
}
@Message_SendAudio( msg_id, msg_dest, receiver )
{
new text[ 32 ], value;
get_msg_arg_string( 2, text, charsmax( text ) );
if ( TrieGetCell( m_trSendAudio, text, value ) )
return PLUGIN_HANDLED;
return PLUGIN_CONTINUE;
}
@Message_Money( msg_id, msg_dest, receiver )
{
set_member( receiver, m_iAccount, m_iMoney[ receiver ] );
set_msg_arg_int( 1, get_msg_argtype( 1 ), m_iMoney[ receiver ] );
}
@Task_Warmup( task )
{
if ( !get_member_game( m_bRoundTerminating ) )
{
new timeLeft = floatround( ( float( get_member_game( m_iRoundTimeSecs ) ) - get_gametime( ) + Float: get_member_game( m_fRoundStartTimeReal ) ) );
if ( timeLeft == 11 )
client_cmd( 0, "spk ^"plats/elevbell1.wav^"" );
else if ( timeLeft <= 4 && timeLeft > 1 )
client_cmd( 0, "spk ^"buttons/bell1.wav^"" );
if ( timeLeft < 1 )
rg_round_end( 3.0, WINSTATUS_DRAW, ROUND_GAME_COMMENCE, .trigger = true );
}
}
@SV_ActivateServer_Post( )
{
if ( m_fwdSpawn )
{
unregister_forward( FM_Spawn, m_fwdSpawn );
m_fwdSpawn = 0;
TrieDestroy( m_trUselessEntities );
m_trUselessEntities = Invalid_Trie;
}
}
@RoundEnd_Pre( WinStatus: status, ScenarioEventEndRound: event, Float: delay )
{
if ( m_bWarmup )
{
SetHookChainArg( 2, ATYPE_INTEGER, ROUND_GAME_COMMENCE );
SetHookChainArg( 3, ATYPE_FLOAT, 3.0 );
return;
}
if ( !get_member_game( m_bGameStarted ) )
return;
stop_zombie_respawn( );
if ( status == WINSTATUS_NONE )
status = WINSTATUS_DRAW;
if ( status == WINSTATUS_DRAW )
{
if ( get_pcvar_bool( m_pCvar_OverHumansWin ) )
status = WINSTATUS_CTS;
}
SetHookChainArg( 1, ATYPE_INTEGER, status );
}
@RoundEnd_Post( WinStatus: status, ScenarioEventEndRound: event, Float: delay )
{
if ( m_bWarmup )
return;
switch ( status )
{
case WINSTATUS_CTS:
{
client_print( 0, print_center, "Humans won!" );
client_cmd( 0, " spk ^"ambience/3dmstart.wav^"" );
}
case WINSTATUS_TERRORISTS:
{
client_print( 0, print_center, "Zombies won!" );
client_cmd( 0, "spk ^"ambience/the_horror1.wav^"" );
}
case WINSTATUS_DRAW:
{
client_print( 0, print_center, "No one won!" );
client_cmd( 0, "spk ^"ambience/3dmstart.wav^"" );
}
}
}
@CSGameRules_RestartRound_Pre( )
{
if ( m_bWarmup )
{
m_bWarmup = false;
remove_task( TASKID_WARMUP );
}
set_pcvar_num( m_pCvar_FreezeTime, 15 );
set_pcvar_float( m_pCvar_RoundTime, 180.0 / 60.0 );
}
@CSGameRules_RestartRound_Post( )
{
if ( !m_bWarmup )
set_member_game( m_bGameStarted, true );
stop_zombie_respawn( );
for ( new i = 1; i <= MaxClients; i++ )
{
if ( !is_user_connected( i ) )
continue;
m_iZombieRespawn[ i ] = 0;
BIT_SUB( m_bitZombie, BIT_PLAYER( i ) );
BIT_SUB( m_bitZombieRespawn, BIT_PLAYER( i ) );
set_member( i, m_flRespawnPending, 0.0 );
}
@Forcing_LevelInitialize( );
}
@CSGameRules_CheckWinConditions_Pre( )
{
if ( m_bWarmup )
return HC_SUPERCEDE;
if ( !get_member_game( m_bGameStarted ) )
return HC_SUPERCEDE;
if ( get_member_game( m_bRoundTerminating ) )
return HC_SUPERCEDE;
return HC_SUPERCEDE;
}
@CSGameRules_FPlayerCanTakeDamage_Pre( id, attacker )
{
if ( get_member_game( m_bRoundTerminating ) )
{
SetHookChainReturn( ATYPE_INTEGER, false );
return HC_SUPERCEDE;
}
if ( m_bWarmup )
{
SetHookChainReturn( ATYPE_INTEGER, false );
return HC_SUPERCEDE;
}
return HC_CONTINUE;
}
@CSGameRules_OnRoundFreezeEnd_Pre( )
{
if ( !get_member_game( m_bGameStarted ) )
return;
m_bZombieRespawnAllowed = true;
new Array: alives = ArrayCreate( 1, 0 );
for ( new i = 1; i <= MaxClients; i++ )
{
if ( !is_user_alive( i ) )
continue;
ArrayPushCell( alives, i );
}
new num = ArraySize( alives );
if ( num >= 2 )
{
new need_zombies = floatround( float( num ) * 0.20, floatround_ceil );
new id, pos;
while ( need_zombies-- )
{
pos = random( ArraySize( alives ) );
id = ArrayGetCell( alives, pos );
ArrayDeleteItem( alives, pos );
@ZombieAttributes( id, 0 );
}
}
ArrayDestroy( alives );
}
@CSGameRules_GetPlayerSpawnSpot_Pre( id )
{
if ( TEAM_TERRORIST > get_member( id, m_iTeam ) > TEAM_CT )
return HC_CONTINUE;
new spot;
if ( BIT_VALID( m_bitZombie, BIT_PLAYER( id ) ) )
spot = @Select_ZombieSpawnPoint( id );
else
spot = @Select_SpawnPoint( id );
if ( is_nullent( spot ) )
return HC_CONTINUE;
new Float: vecOrigin[ 3 ], Float: vecAngles[ 3 ];
get_entvar( spot, var_origin, vecOrigin );
get_entvar( spot, var_angles, vecAngles );
vecOrigin[ 2 ] += 1.0;
set_entvar( id, var_origin, vecOrigin );
set_entvar( id, var_v_angle, NULL_VECTOR );
set_entvar( id, var_velocity, NULL_VECTOR );
set_entvar( id, var_angles, vecAngles );
set_entvar( id, var_punchangle, NULL_VECTOR );
set_entvar( id, var_fixangle, 1 );
SetHookChainReturn( ATYPE_INTEGER, spot );
return HC_SUPERCEDE;
}
@CBasePlayer_Spawn_Pre( id )
{
if ( get_member( id, m_bJustConnected ) )
return;
if ( TEAM_TERRORIST > get_member( id, m_iTeam ) > TEAM_CT )
return;
RequestFrame( "@RequestFrame_ApplyPlayerAttributes", id );
}
@CBasePlayer_Spawn_Post( id )
{
if ( get_member( id, m_bJustConnected ) )
{
set_member( id, m_fNextSuicideTime, 999999999.0 );
return;
}
if ( TEAM_TERRORIST > get_member( id, m_iTeam ) > TEAM_CT )
return;
if ( BIT_VALID( m_bitJoinAsZombie, BIT_PLAYER( id ) ) )
{
BIT_SUB( m_bitJoinAsZombie, BIT_PLAYER( id ) );
@ZombieAttributes( id, 0 );
return;
}
if ( get_member_game( m_bFreezePeriod ) )
set_member( id, m_bCanShootOverride, true );
if ( BIT_VALID( m_bitZombieRespawn, BIT_PLAYER( id ) ) )
BIT_SUB( m_bitZombieRespawn, BIT_PLAYER( id ) );
}
@CBasePlayer_HasRestrictItem_Pre( id )
{
if ( get_member( id, m_iTeam ) != TEAM_TERRORIST )
return HC_CONTINUE;
SetHookChainReturn( ATYPE_BOOL, true );
return HC_SUPERCEDE;
}
@CBasePlayer_TakeDamage_Pre( victim, inflictor, attacker, Float: damage, damage_type )
{
if ( victim == attacker || !is_user_connected( attacker ) )
return;
if ( !rg_is_player_can_takedamage( victim, attacker ) )
return;
if ( BIT_VALID( m_bitZombie, BIT_PLAYER( attacker ) ) )
{
new activeItem = get_member( attacker, m_pActiveItem );
if ( !is_nullent( activeItem ) && get_member( activeItem, m_iId ) == WEAPON_KNIFE )
{
@ZombieAttributes( victim, attacker );
bio_set_user_money( attacker, bio_get_user_money( attacker ) + get_pcvar_num( m_pCvar_MoneyInfection ) );
SetHookChainArg( 4, ATYPE_FLOAT, 0.0 );
return;
}
}
new moneyRatio = floatround( damage * get_pcvar_float( m_pCvar_MoneyDamageRatio ), floatround_floor );
bio_set_user_money( attacker, bio_get_user_money( attacker ) + moneyRatio );
}
@CBasePlayer_ResetMaxSpeed_Pre( id )
{
if ( !is_user_alive( id ) )
return HC_CONTINUE;
if ( BIT_VALID( m_bitZombie, BIT_PLAYER( id ) ) )
{
new classData[ ClassData ];
ArrayGetArray( m_arrClassesVec, m_varClass[ id ], classData );
set_entvar( id, var_maxspeed, classData[ ClassSpeed ] );
return HC_SUPERCEDE;
}
new activeItem = get_member( id, m_pActiveItem ), Float: speed;
if ( !is_nullent( activeItem ) )
ExecuteHamB( Ham_CS_Item_GetMaxSpeed, activeItem, speed );
else
speed = 240.0;
set_entvar( id, var_maxspeed, speed );
return HC_SUPERCEDE;
}
@CBasePlayer_Killed_Post( id, attacker, gib )
{
if ( is_user_alive( id ) )
return;
new TeamName: team = get_member( id, m_iTeam );
if ( team == TEAM_SPECTATOR || team == TEAM_UNASSIGNED )
return;
if ( get_member_game( m_bRoundTerminating ) )
return;
if ( !get_member_game( m_bGameStarted ) )
return;
if ( BIT_VALID( m_bitZombie, BIT_PLAYER( id ) ) )
{
if ( m_bZombieRespawnAllowed && CanZombieRespawn( id ) )
{
new Float: respawnTime = get_pcvar_float( m_pCvar_ZombieRespawnTime );
if ( respawnTime > 0.0 )
{
BIT_ADD( m_bitZombieRespawn, BIT_PLAYER( id ) );
m_iZombieRespawn[ id ]++;
set_member( id, m_flRespawnPending, get_gametime( ) + respawnTime );
return;
}
}
}
RequestFrame( "@RequestFrame_CheckChangeClassWinConditions" );
}
@FM_Spawn_Pre( entity )
{
new className[ 32 ];
get_entvar( entity, var_classname, className, charsmax( className ) );
if ( TrieKeyExists( m_trUselessEntities, className ) )
{
forward_return( FMV_CELL, NULLENT );
return FMRES_SUPERCEDE;
}
return FMRES_IGNORED;
}
@ZombieAttributes( id, attacker )
{
BIT_ADD( m_bitZombie, BIT_PLAYER( id ) );
new classData[ ClassData ];
ArrayGetArray( m_arrClassesVec, m_varClass[ id ], classData );
set_entvar( id, var_health, classData[ ClassHealth ] );
set_entvar( id, var_gravity, classData[ ClassGravity ] );
set_entvar( id, var_armorvalue, 0.0 );
set_member( id, m_iKevlar, ARMOR_NONE );
rg_set_user_model( id, classData[ ClassModel ] );
rg_remove_all_items( id );
rg_give_item( id, "weapon_knife" );
set_entvar( id, var_viewmodel, classData[ ClassClaw ] );
set_entvar( id, var_weaponmodel, NULL_STRING );
rh_emit_sound2( id, 0, CHAN_VOICE, ZOMBIE_SCREAM[ random( sizeof( ZOMBIE_SCREAM ) ) ] );
if ( attacker )
{
@UTIL_DeathMsg( attacker, id, 0, "infection" );
@UTIL_ScoreAttrib( id, 0 );
rg_set_user_team( id, TEAM_TERRORIST );
ExecuteHamB( Ham_AddPoints, id, 0, true );
ExecuteHamB( Ham_AddPoints, attacker, 1, true );
set_member( id, m_iDeaths, get_member( id, m_iDeaths ) + 1 );
}
else
{
@UTIL_DeathMsg( id, id, 0, "infection" );
@UTIL_ScoreAttrib( id, 0 );
rg_set_user_team( id, TEAM_TERRORIST );
}
if ( get_member_game( m_bGameStarted ) && !get_member_game( m_bFreezePeriod ) )
RequestFrame( "@RequestFrame_CheckChangeClassWinConditions" );
}
@HuamnAttributes( id, attacker )
{
rg_set_user_team( id, TEAM_CT );
BIT_SUB( m_bitZombie, BIT_PLAYER( id ) );
set_entvar( id, var_health, 100.0 );
set_entvar( id, var_gravity, 1.0 );
rg_reset_user_model( id );
set_entvar( id, var_viewmodel, "models/v_knife.mdl" );
set_entvar( id, var_weaponmodel, "models/p_knife.mdl" );
rg_remove_all_items( id );
rg_give_item( id, "weapon_knife" );
rg_give_item( id, "weapon_g3sg1" );
rg_set_user_bpammo( id, WEAPON_G3SG1, 200 );
if ( get_member_game( m_bGameStarted ) && !get_member_game( m_bFreezePeriod ) )
RequestFrame( "@RequestFrame_CheckChangeClassWinConditions" );
}
@RequestFrame_CheckChangeClassWinConditions( )
{
if ( get_member_game( m_iRoundWinStatus ) != WINSTATUS_NONE )
return;
if ( !get_member_game( m_bGameStarted ) )
return;
new aliveZombies = get_alive_zombies( );
new aliveHumans = get_alive_humans( );
if ( aliveZombies <= 0 )
{
stop_zombie_respawn( );
rg_round_end( 3.0, WINSTATUS_CTS, ROUND_CTS_WIN, .trigger = true );
return;
}
if ( aliveHumans <= 0 )
{
stop_zombie_respawn( );
rg_round_end( 3.0, WINSTATUS_TERRORISTS, ROUND_TERRORISTS_WIN, .trigger = true );
return;
}
new alive_t, dead_t,
alive_ct, dead_ct;
rg_initialize_player_counts( alive_t, alive_ct, dead_t, dead_ct );
if ( alive_t + alive_ct + dead_t + dead_ct >= 2 )
{
if ( !alive_t )
rg_round_end( 3.0, WINSTATUS_CTS, ROUND_CTS_WIN, .trigger = true );
else if ( !alive_ct )
rg_round_end( 3.0, WINSTATUS_TERRORISTS, ROUND_TERRORISTS_WIN, .trigger = true );
}
}
@RequestFrame_ApplyPlayerAttributes( id )
{
if ( BIT_VALID( m_bitZombie, BIT_PLAYER( id ) ) )
@ZombieAttributes( id, 0 );
else
@HuamnAttributes( id, 0 );
}
@RequestFrame_TryJoinAsZombie( id )
{
if ( !is_user_connected( id ) )
return;
if ( get_member_game( m_bFreezePeriod ) )
{
RequestFrame( "@RequestFrame_TryJoinAsZombie", id );
return;
}
if ( !can_join_as_zombie( ) )
return;
BIT_ADD( m_bitJoinAsZombie, BIT_PLAYER( id ) );
rg_set_user_team( id, TEAM_TERRORIST );
if ( !is_user_alive( id ) )
rg_round_respawn( id );
}
@Select_ZombieSpawnPoint( id )
{
new numPoints = ArraySize( m_arrSpawnPoints );
if ( !numPoints )
return 0;
new bestSpot = 0, safeSpot = 0;
new Float: safeDistance = -1.0;
new farthestSpot = 0;
new Float: farthestDistance = -1.0;
new Float: spawnOrigin[ 3 ];
new Float: playerOrigin[ 3 ];
new Float: minRequiredDistance = get_pcvar_float( m_pCvar_ZombieRespawnMinDistance ) * get_pcvar_float( m_pCvar_ZombieRespawnMinDistance );
for ( new i = 0; i < numPoints; i++ )
{
new spot = ArrayGetCell( m_arrSpawnPoints, i );
if ( is_nullent( spot ) )
continue;
get_entvar( spot, var_origin, spawnOrigin );
if ( !IsHullVacant( id, spawnOrigin, HULL_HUMAN ) )
continue;
new Float: nearestHumanDistance = 999999999.0;
new bool: hasHuman;
for ( new j = 1; j <= MaxClients; j++ )
{
if ( !is_user_alive( j ) )
continue;
if ( is_player_afk( j ) )
continue;
if ( BIT_VALID( m_bitZombie, BIT_PLAYER( j ) ) )
continue;
hasHuman = true;
get_entvar( j, var_origin, playerOrigin );
new Float: distance = get_distance_squared( spawnOrigin, playerOrigin );
if ( distance < nearestHumanDistance )
nearestHumanDistance = distance;
}
if ( !hasHuman )
nearestHumanDistance = 999999999.0;
if ( nearestHumanDistance > farthestDistance )
{
farthestDistance = nearestHumanDistance;
farthestSpot = spot;
}
if ( nearestHumanDistance < minRequiredDistance )
continue;
if ( nearestHumanDistance > safeDistance )
{
safeDistance = nearestHumanDistance;
safeSpot = spot;
}
}
if ( !is_nullent( safeSpot ) )
return safeSpot;
if ( !is_nullent( farthestSpot ) )
return farthestSpot;
bestSpot = @Select_SpawnPoint( id );
return bestSpot;
}
@Select_SpawnPoint( id )
{
new spot, lastSpot = m_iLastSpawn, numPoints = ArraySize( m_arrSpawnPoints ), Float: vecOrigin[ 3 ];
do
{
if ( ++lastSpot >= numPoints )
lastSpot = 0;
spot = ArrayGetCell( m_arrSpawnPoints, lastSpot );
if ( is_nullent( spot ) )
continue;
get_entvar( spot, var_origin, vecOrigin );
if ( !IsHullVacant( id, vecOrigin, HULL_HUMAN ) )
continue;
break;
}
while ( lastSpot != m_iLastSpawn );
if ( is_nullent( spot ) )
return 0;
m_iLastSpawn = lastSpot;
return spot;
}
@Forcing_LevelInitialize( )
{
if ( !ArraySize( m_arrSpawnPoints ) )
{
new entity = NULLENT;
while ( ( entity = rg_find_ent_by_class( entity, "info_player_start", true ) ) )
ArrayPushCell( m_arrSpawnPoints, entity );
while ( ( entity = rg_find_ent_by_class( entity, "info_player_deathmatch", true ) ) )
ArrayPushCell( m_arrSpawnPoints, entity );
}
new numPoints = ArraySize( m_arrSpawnPoints );
set_member_game( m_iSpawnPointCount_Terrorist, numPoints );
set_member_game( m_iSpawnPointCount_CT, numPoints );
set_member_game( m_bLevelInitialized, true );
}
static stock Float: get_distance_squared( const Float: vecOrigin1[ 3 ], const Float: vecOrigin2[ 3 ] )
{
new Float: x = vecOrigin1[ 0 ] - vecOrigin2[ 0 ];
new Float: y = vecOrigin1[ 1 ] - vecOrigin2[ 1 ];
new Float: z = vecOrigin1[ 2 ] - vecOrigin2[ 2 ];
return x * x + y * y + z * z;
}
static stock bool: IsHullVacant( id, Float: vecOrigin[ 3 ], hull )
{
engfunc( EngFunc_TraceHull, vecOrigin, vecOrigin, 0, hull, id, 0 );
if ( get_tr2( 0, TR_StartSolid ) || get_tr2( 0, TR_AllSolid ) || !get_tr2( 0, TR_InOpen ) )
return false;
return true;
}
static stock bool: CanZombieRespawn( id )
{
new alive = get_alive_players( );
new zombies = get_alive_zombies( );
new humans = get_alive_humans( );
alive++;
if ( alive <= 1 )
return false;
if ( humans <= 0 )
return false;
if ( humans == 1 )
return false;
if ( zombies >= humans )
return false;
new maxZombies = floatround( float( alive ) * get_max_zombie_count( ), floatround_ceil );
if ( zombies + 1 > maxZombies )
return false;
if ( m_iZombieRespawn[ id ] >= get_pcvar_num( m_pCvar_ZombieRespawnLimit ) )
return false;
new respawnNumber = m_iZombieRespawn[ id ];
new Float: chance = 100.0 - float( respawnNumber ) * get_pcvar_float( m_pCvar_ZombieRespawnChanceDecay );
if ( chance <= 0.0 )
return false;
if ( random_float( 0.0, 100.0 ) > chance )
return false;
return true;
}
static stock bool: can_join_as_zombie( )
{
if ( !get_member_game( m_bGameStarted ) )
return false;
if ( get_member_game( m_bRoundTerminating ) )
return false;
if ( !m_bZombieRespawnAllowed )
return false;
new alive = get_alive_players( );
new zombies = get_alive_zombies( );
new humans = get_alive_humans( );
if ( humans <= 1 )
return false;
if ( zombies >= humans )
return false;
alive++;
new maxZombies = floatround( float( alive ) * float( get_zombie_player_ratio( ) ) / 100.0, floatround_ceil );
if ( zombies + 1 > maxZombies )
return false;
return true;
}
static stock bool: is_player_afk( id )
{
if ( !is_user_connected( id ) )
return true;
new Float: lastMovement = get_member( id, m_fLastMovement );
if ( lastMovement <= 0.0 )
return false;
return ( get_gametime( ) - lastMovement ) >= get_pcvar_float( m_pCvar_ZombieBalanceAfkTime );
}
static stock get_alive_players( )
{
new count;
for ( new i = 1; i <= MaxClients; i++ )
{
if ( !is_user_alive( i ) )
continue;
if ( is_player_afk( i ) )
continue;
count++;
}
return count;
}
static stock get_alive_zombies( )
{
new count;
for ( new i = 1; i <= MaxClients; i++ )
{
if ( !is_user_alive( i ) )
continue;
if ( is_player_afk( i ) )
continue;
if ( !BIT_VALID( m_bitZombie, BIT_PLAYER( i ) ) )
continue;
count++;
}
return count;
}
static stock get_alive_humans( )
{
new count;
for ( new i = 1; i <= MaxClients; i++ )
{
if ( !is_user_alive( i ) )
continue;
if ( is_player_afk( i ) )
continue;
if ( BIT_VALID( m_bitZombie, BIT_PLAYER( i ) ) )
continue;
count++;
}
return count;
}
static stock get_zombie_player_ratio( )
{
new activePlayers = get_alive_players( );
if ( activePlayers <= 4 )
return 33;
if ( activePlayers <= 9 )
return 40;
if ( activePlayers <= 19 )
return 45;
return 50;
}
static stock get_zombie_time_ratio( )
{
new Float: roundStart = get_member_game( m_fRoundStartTime );
if ( roundStart <= 0.0 )
return 50;
new Float: roundTime = get_gametime( ) - roundStart;
if ( roundTime < 30.0 )
return 50;
if ( roundTime < 60.0 )
return 45;
if ( roundTime < 120.0 )
return 40;
return 35;
}
static stock get_max_zombie_count( )
{
new activePlayers = get_alive_players( );
if ( activePlayers <= 0 )
return 0;
new playerRatio = get_zombie_player_ratio( );
new timeRatio = get_zombie_time_ratio( );
new ratio = playerRatio;
if ( timeRatio < ratio )
ratio = timeRatio;
new maxZombies = floatround( float( activePlayers ) * float( ratio ) / 100.0, floatround_ceil );
if ( maxZombies < 1 )
maxZombies = 1;
return maxZombies;
}
static stock stop_zombie_respawn( )
{
m_bZombieRespawnAllowed = false;
for ( new i = 1; i <= MaxClients; i++ )
{
if ( !is_user_connected( i ) )
continue;
set_member( i, m_flRespawnPending, 0.0 );
BIT_SUB( m_bitZombieRespawn, BIT_PLAYER( i ) );
}
}
@UTIL_DeathMsg( killer, victim, headshot, const weapon[ ] )
{
message_begin( MSG_ALL, get_user_msgid( "DeathMsg" ) );
write_byte( killer );
write_byte( victim );
write_byte( headshot );
write_string( weapon );
message_end( );
}
@UTIL_ScoreAttrib( id, flags )
{
message_begin( MSG_ALL, get_user_msgid( "ScoreAttrib" ) );
write_byte( id );
write_byte( flags );
message_end( );
}
static stock bool: Read_JsonField( const fileName[ ] )
{
new cfgsDir[ PLATFORM_MAX_PATH ];
get_localinfo( "amxx_configsdir", cfgsDir, charsmax( cfgsDir ) );
add( cfgsDir, charsmax( cfgsDir ), "/biohazard" );
if ( !dir_exists( cfgsDir ) )
{
set_fail_state( "Directory '%s' not found.", cfgsDir );
return false;
}
strcat( cfgsDir, fmt( "/%s.json", fileName ), charsmax( cfgsDir ) );
if ( !file_exists( cfgsDir ) )
{
set_fail_state( "File '%s' not found.", cfgsDir );
return false;
}
new JSON: jsonParse = json_parse( cfgsDir, true );
if ( jsonParse == Invalid_JSON )
{
set_fail_state( "Parsing file '%s' error." );
return false;
}
new JSON: jsonValue = json_object_get_value( jsonParse, "useless_entities" );
if ( json_is_array( jsonValue ) )
{
m_trUselessEntities = TrieCreate( );
new temp[ 32 ];
for ( new i = 0; i < json_array_get_count( jsonValue ); i++ )
{
json_array_get_string( jsonValue, i, temp, charsmax( temp ) );
TrieSetCell( m_trUselessEntities, temp, 1 );
}
json_free( jsonValue );
if ( TrieGetSize( m_trUselessEntities ) )
m_fwdSpawn = register_forward( FM_Spawn, "@FM_Spawn_Pre" );
else
{
TrieDestroy( m_trUselessEntities );
m_trUselessEntities = Invalid_Trie;
}
}
new JSON: jsonClasses = json_object_get_value( jsonParse, "classes" );
if ( json_is_array( jsonClasses ) )
{
new tempClass[ ClassData ], JSON: jsonItem;
for ( new i = 0; i < json_array_get_count( jsonClasses ); i++ )
{
jsonItem = json_array_get_value( jsonClasses, i );
json_object_get_string( jsonItem, "name", tempClass[ ClassName ], charsmax( tempClass[ ClassName ] ) );
tempClass[ ClassHealth ] = _: json_object_get_real( jsonItem, "health" );
tempClass[ ClassSpeed ] = _: json_object_get_real( jsonItem, "speed" );
tempClass[ ClassGravity ] = _: json_object_get_real( jsonItem, "gravity" );
json_object_get_string( jsonItem, "model", tempClass[ ClassModel ], charsmax( tempClass[ ClassModel ] ) );
precache_model( fmt( "models/player/%s/%s.mdl", tempClass[ ClassModel ], tempClass[ ClassModel ] ) );
json_object_get_string( jsonItem, "claw", tempClass[ ClassClaw ], charsmax( tempClass[ ClassClaw ] ) );
precache_model( tempClass[ ClassClaw ] );
ArrayPushArray( m_arrClassesVec, tempClass );
json_free( jsonItem );
}
m_iNumClasses = ArraySize( m_arrClassesVec );
json_free( jsonClasses );
}
json_free( jsonParse );
return true;
}
@native_get_user_money( amxx, argc )
{
enum { arg_id = 1 };
new id = get_param( arg_id );
if ( !is_user_connected( id ) )
{
log_error( AMX_ERR_NATIVE, "Invalid index '%d'.", id );
return -1;
}
return m_iMoney[ id ];
}
@native_set_user_money( amxx, argc )
{
enum { arg_id = 1, arg_amount, arg_flash };
new id = get_param( arg_id );
if ( !is_user_connected( id ) )
{
log_error( AMX_ERR_NATIVE, "Invalid index '%d'.", id );
return 0;
}
new amount = get_param( arg_amount );
if ( amount < 0 )
amount = 0;
else if ( amount > 12000 )
amount = 12000;
message_begin( MSG_ONE, get_user_msgid( "Money" ), .player = id );
write_long( amount );
write_byte( get_param( arg_flash ) );
message_end( );
set_member( id, m_iAccount, amount );
m_iMoney[ id ] = amount;
return 1;
}