#include <amxmodx>
#include <engine>
#include <fakemeta>
#define VERSION "1.2"
#define MAX_DOORS 500
#define TSK_BHOP 50
#define TSK_CLEAR_FAIL 100
//func_doors[x]{ id, speed, angles }
new door_count = 0, func_doors[MAX_DOORS][3], Float:door_tp_pos[MAX_DOORS][3]
new bhop_failid[32], bool:bhop_fail[32]
new p_enabled
new MAXPLAYERS
public plugin_init( )
{
MAXPLAYERS = get_maxplayers( )
register_plugin( "MP Bhops", VERSION, "Ian Cammarata" )
register_cvar( "mpbhops_version", VERSION, FCVAR_SERVER )
p_enabled = register_cvar( "mpbhops", "0", FCVAR_SERVER )
}
public pfn_keyvalue( ent )
{
static last_ent
new class[31], key[31], val[31]
copy_keyvalue( class, 30, key, 30, val, 30 )
if( ent != last_ent && func_doors[door_count][0] && door_count < MAX_DOORS )
door_count++
if( equal( class, "func_door" ) )
{
if( ent != last_ent ) func_doors[door_count][0] = ent
if( equal( key, "speed" ) )
func_doors[door_count][1] = str_to_num(val)
if( equal( key, "dmg" ) )
func_doors[door_count][0] = 0
if( equal( key, "angles" ) )
{
new angles[5]
parse( val, angles, 4 )
func_doors[door_count][2] = str_to_num( angles )
}
last_ent = ent
}
return PLUGIN_CONTINUE
}
public plugin_cfg( )
{
if( func_doors[door_count][0] && door_count < MAX_DOORS )
door_count++
new ent, ent2, tmpstr[33]
new Float:dmins[3], Float:dmaxs[3]
//Find tp spots for doors, in case they're used for bhop
for( new i = 0; i < door_count; i++ )
{
ent = func_doors[i][0]
if( !is_valid_ent( ent ) ) func_doors[i][0] = 0
else
{
entity_get_vector( ent, EV_VEC_mins, dmins )
entity_get_vector( ent, EV_VEC_maxs, dmaxs )
new dwid = floatround( dmaxs[0] - dmins[0] )
new dlen = floatround( dmaxs[1] - dmins[1] )
//If the door moves up, or is thin, remove it's id from the array
if( func_doors[i][2] < 0 || dwid < 24 || dlen < 24 )
func_doors[i][0] = 0
//Otherwise find a safe tp spot in case it's a bhop door
else
{
//If it has a targetname, change the id in array to targeter
entity_get_string( ent, EV_SZ_targetname, tmpstr, 32 )
if( strlen( tmpstr ) )
{
ent2 = find_ent_by_target( -1, tmpstr )
if( ent2 )
{
func_doors[i][0] = ent2
//If targeter is a button, remove it's id from the array
entity_get_string( ent2, EV_SZ_classname, tmpstr, 32 )
if( equal( tmpstr, "func_button" ) )
func_doors[i][0] = 0
}
}
new Float:tmpvec[3], Float:tmpvec2[3]
new Float:dr_tc[3]
dr_tc[0] = ( dmaxs[0] + dmins[0] ) / 2
dr_tc[1] = ( dmaxs[1] + dmins[1] ) / 2
dr_tc[2] = dmaxs[2]
tmpvec[0] = ( dmaxs[0] + dmins[0] ) / 2
tmpvec[1] = dmaxs[1] + 20
tmpvec[2] = dmaxs[2] + 20
trace_line( ent, dr_tc, tmpvec, tmpvec2 )
if( !trace_hull( tmpvec, HULL_HUMAN ) && tmpvec2[2] == tmpvec[2] )
door_tp_pos[i] = tmpvec
else
{
tmpvec[1] = dmins[1] - 20
trace_line( ent, dr_tc, tmpvec, tmpvec2 )
if( !trace_hull( tmpvec, HULL_HUMAN ) && tmpvec2[2] == tmpvec[2] )
door_tp_pos[i] = tmpvec
else
{
tmpvec[0] = dmaxs[0] + 20
tmpvec[1] = ( dmaxs[1] + dmins[1] ) / 2
trace_line( ent, dr_tc, tmpvec, tmpvec2 )
if( !trace_hull( tmpvec, HULL_HUMAN ) && tmpvec2[2] == tmpvec[2] )
door_tp_pos[i] = tmpvec
else
{
tmpvec[0] = dmins[0] - 20
door_tp_pos[i] = tmpvec
}
}
}
}
}
}
}
//This is a semiclip fix
public client_PreThink( id )
{
//If they're on the ground and not solid...
if( ( pev( id, pev_flags ) & FL_ONGROUND ) && !pev( id, pev_solid ) )
{
new Float:orig[3]
entity_get_vector( id, EV_VEC_origin, orig )
//do a hull trace 1 unit below their origin
orig[2] -= 1
engfunc( EngFunc_TraceHull, orig, orig, DONT_IGNORE_MONSTERS, HULL_HUMAN, id, 0 )
new ent = get_tr2( 0, TR_pHit )
//if all we hit is world or another player, who cares, exit
if( ent <= MAXPLAYERS ) return PLUGIN_CONTINUE
//if we hit a door in the array, send it to the handler then exit
new dpos = door_in_array( ent )
if( dpos > -1 )
{
bhop_check_fail( id, ent, dpos )
return PLUGIN_CONTINUE
}
//if we hit a BCM entity, force touch so the BCM plugin can handle it
new class[32]
entity_get_string( ent, EV_SZ_classname, class, 31 )
if( equal( class, "bcm" ) || equal( class, "bm_block" ) )
fake_touch( ent, id )
}
return PLUGIN_CONTINUE
}
public pfn_touch( ent, id )
{
if( !get_pcvar_num( p_enabled ) || !ent || !id )
return PLUGIN_CONTINUE
//Make sure id is player and ent is object
if( 0 < ent <= MAXPLAYERS )
{
new tmp = id
id = ent
ent = tmp
}
else if( !( 0 < id <= MAXPLAYERS ) )
return PLUGIN_CONTINUE
//Bhop stuff
new dpos = door_in_array( ent )
if( dpos > -1 )
{
bhop_check_fail( id, ent, dpos )
return PLUGIN_HANDLED
}
return PLUGIN_CONTINUE
}
public bhop_check_fail( id, ent, dpos )
{
if( bhop_failid[id-1] != ent )
{
bhop_failid[id-1] = ent
bhop_fail[id-1] = false
new tskid = TSK_BHOP + id
if( task_exists( tskid ) )
remove_task( tskid )
set_task( 0.2, "bhop_set_fail", tskid )
tskid = TSK_CLEAR_FAIL + id
if( task_exists( tskid ) )
remove_task( tskid )
set_task( 0.7, "bhop_clear_fail", tskid )
}
else if( bhop_fail[id-1] )
{
//Teleport to fail position
entity_set_vector( id, EV_VEC_velocity, Float:{0.0, 0.0, 0.0} )
entity_set_vector( id, EV_VEC_origin, door_tp_pos[dpos] )
//Reset fail vars
bhop_failid[id-1] = 0
bhop_fail[id-1] = false
}
}
public door_in_array( door )
{
for( new i = 0; i < door_count; i++ )
if( func_doors[i][0] == door )
return i
return -1
}
public bhop_set_fail( tskid )
{
bhop_fail[ tskid - TSK_BHOP - 1 ] = true
return PLUGIN_HANDLED
}
public bhop_clear_fail( tskid )
{
new id = tskid - TSK_CLEAR_FAIL
bhop_failid[id-1] = 0
bhop_fail[id-1] = false
return PLUGIN_HANDLED
}