mirror of
https://github.com/JezuzLizard/t4sp_bot_warfare.git
synced 2025-04-23 06:55:44 +00:00
some cleanup
This commit is contained in:
parent
280ec4c318
commit
f5753789db
@ -285,6 +285,7 @@ AtScriptGoal()
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return distanceSquared( self.origin, self.bot.script_goal ) <= ( self.bot.script_goal_dist * self.bot.script_goal_dist );
|
return distanceSquared( self.origin, self.bot.script_goal ) <= ( self.bot.script_goal_dist * self.bot.script_goal_dist );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,22 +314,6 @@ ClearPriorityObjective()
|
|||||||
self.bot.prio_objective = false;
|
self.bot.prio_objective = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Checks whether the path generated by the ASTAR path finding is inaccessible
|
|
||||||
*/
|
|
||||||
GetPathIsInaccessible( to, best_effort )
|
|
||||||
{
|
|
||||||
if ( isDefined( best_effort ) )
|
|
||||||
{
|
|
||||||
path = generatePath( self.origin, to, self.team, level.bot_allowed_negotiation_links, best_effort );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
path = generatePath( self.origin, to, self.team, level.bot_allowed_negotiation_links );
|
|
||||||
}
|
|
||||||
return !isDefined( path ) || ( path.size <= 0) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Sets the aim position of the bot
|
Sets the aim position of the bot
|
||||||
*/
|
*/
|
||||||
@ -712,6 +697,9 @@ float( num )
|
|||||||
return GetDvarFloat( "temp_dvar_bot_util" );
|
return GetDvarFloat( "temp_dvar_bot_util" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
returns nodes in playable area
|
||||||
|
*/
|
||||||
get_nodes_in_playable_area()
|
get_nodes_in_playable_area()
|
||||||
{
|
{
|
||||||
total_nodes = getAllNodes();
|
total_nodes = getAllNodes();
|
||||||
@ -735,6 +723,9 @@ get_nodes_in_playable_area()
|
|||||||
return filtered_nodes;
|
return filtered_nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
is the point in playable area?
|
||||||
|
*/
|
||||||
is_point_in_playable_area( point )
|
is_point_in_playable_area( point )
|
||||||
{
|
{
|
||||||
playable_area = getentarray( "playable_area", "targetname" );
|
playable_area = getentarray( "playable_area", "targetname" );
|
||||||
@ -1075,195 +1066,3 @@ targetIsGibbed()
|
|||||||
{
|
{
|
||||||
return isDefined( self.gibbed ) && self.gibbed;
|
return isDefined( self.gibbed ) && self.gibbed;
|
||||||
}
|
}
|
||||||
|
|
||||||
swap_array_index( array, index1, index2 )
|
|
||||||
{
|
|
||||||
temp = array[ index1 ];
|
|
||||||
array[ index1 ] = array[ index2 ];
|
|
||||||
array[ index2 ] = temp;
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
quickSort(array, compare_func, compare_func_arg1)
|
|
||||||
{
|
|
||||||
return quickSortMid( array, 0, array.size - 1, compare_func, compare_func_arg1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Quicksort algorithm copied from T7 modified for T4
|
|
||||||
*/
|
|
||||||
quickSortMid( array, start, end, compare_func, compare_func_arg1 )
|
|
||||||
{
|
|
||||||
i = start;
|
|
||||||
k = end;
|
|
||||||
|
|
||||||
if(!IsDefined(compare_func))
|
|
||||||
compare_func = ::quicksort_compare;
|
|
||||||
|
|
||||||
if (end - start >= 1)
|
|
||||||
{
|
|
||||||
pivot = array[start];
|
|
||||||
|
|
||||||
while (k > i)
|
|
||||||
{
|
|
||||||
while ( [[ compare_func ]](array[i], pivot, compare_func_arg1) && i <= end && k > i)
|
|
||||||
i++;
|
|
||||||
while ( ![[ compare_func ]](array[k], pivot, compare_func_arg1) && k >= start && k >= i)
|
|
||||||
k--;
|
|
||||||
if (k > i)
|
|
||||||
array = swap_array_index(array, i, k);
|
|
||||||
}
|
|
||||||
array = swap_array_index(array, start, k);
|
|
||||||
array = quickSortMid(array, start, k - 1, compare_func);
|
|
||||||
array = quickSortMid(array, k + 1, end, compare_func);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return array;
|
|
||||||
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
quicksort_compare(left, right, compare_func_arg1)
|
|
||||||
{
|
|
||||||
return left <= right;
|
|
||||||
}
|
|
||||||
|
|
||||||
quicksort_compare_priority_field(left, right, compare_func_arg1)
|
|
||||||
{
|
|
||||||
return left.priority <= right.priority;
|
|
||||||
}
|
|
||||||
|
|
||||||
quicksort_compare_pers_value_highest_to_lowest( left, right, compare_func_arg1 )
|
|
||||||
{
|
|
||||||
return left.pers[ compare_func_arg1 ] <= right.pers[ compare_func_arg1 ];
|
|
||||||
}
|
|
||||||
|
|
||||||
quicksort_compare_pers_value_lowest_to_highest( left, right, compare_func_arg1 )
|
|
||||||
{
|
|
||||||
return left.pers[ compare_func_arg1 ] >= right.pers[ compare_func_arg1 ];
|
|
||||||
}
|
|
||||||
|
|
||||||
assign_priority_to_powerup( powerup )
|
|
||||||
{
|
|
||||||
if ( !isDefined( powerup ) )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
priority = 0;
|
|
||||||
powerup_is_max_ammo = false;
|
|
||||||
switch ( powerup.powerup_name )
|
|
||||||
{
|
|
||||||
case "zombie_blood":
|
|
||||||
case "insta_kill":
|
|
||||||
case "nuke":
|
|
||||||
priority += 2;
|
|
||||||
break;
|
|
||||||
case "full_ammo":
|
|
||||||
powerup_is_max_ammo = true;
|
|
||||||
priority += 1;
|
|
||||||
break;
|
|
||||||
case "double_points":
|
|
||||||
case "fire_sale":
|
|
||||||
case "carpenter":
|
|
||||||
case "free_perk":
|
|
||||||
priority += 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
priority += 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ( powerup_is_max_ammo )
|
|
||||||
{
|
|
||||||
LOW_AMMO_THRESHOLD = 0.3;
|
|
||||||
|
|
||||||
players = getPlayers();
|
|
||||||
|
|
||||||
for ( i = 0; i < players.size; i++ )
|
|
||||||
{
|
|
||||||
weapons = players[ i ] getWeaponsListPrimaries();
|
|
||||||
for ( j = 0; j < weapons.size; j++ )
|
|
||||||
{
|
|
||||||
if ( players[ i ] getWeaponAmmoStock( weapons[ j ] ) <= int( weaponmaxammo( weapons[ j ] ) * LOW_AMMO_THRESHOLD ) )
|
|
||||||
{
|
|
||||||
priority += 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( priority > 3 )
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( maps\_laststand::player_any_player_in_laststand() )
|
|
||||||
{
|
|
||||||
switch ( powerup.powerup_name )
|
|
||||||
{
|
|
||||||
case "zombie_blood":
|
|
||||||
case "insta_kill":
|
|
||||||
case "nuke":
|
|
||||||
priority += 1;
|
|
||||||
break;
|
|
||||||
case "full_ammo":
|
|
||||||
priority += 0;
|
|
||||||
break;
|
|
||||||
case "double_points":
|
|
||||||
case "fire_sale":
|
|
||||||
case "carpenter":
|
|
||||||
case "free_perk":
|
|
||||||
priority -= 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
priority += 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( powerup.time_left_until_timeout < 10.0 )
|
|
||||||
{
|
|
||||||
priority += 1;
|
|
||||||
}
|
|
||||||
if ( priority < 0 )
|
|
||||||
{
|
|
||||||
priority = 0;
|
|
||||||
}
|
|
||||||
powerup.priority = priority;
|
|
||||||
}
|
|
||||||
|
|
||||||
sort_array_by_priority_field( array, item )
|
|
||||||
{
|
|
||||||
if ( isDefined( item ) )
|
|
||||||
{
|
|
||||||
array[ array.size ] = item;
|
|
||||||
}
|
|
||||||
|
|
||||||
array = quickSort( array, ::quicksort_compare_priority_field, undefined );
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
get_players_sorted_by_highest_pers_value( pers_name )
|
|
||||||
{
|
|
||||||
players = getPlayers();
|
|
||||||
|
|
||||||
if ( !isDefined( players[ 0 ].pers[ pers_name ] ) )
|
|
||||||
{
|
|
||||||
assertMsg( "Uninitialized pers value: " + pers_name );
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return quickSort( players, ::quicksort_compare_pers_value_highest_to_lowest, pers_name );
|
|
||||||
}
|
|
||||||
|
|
||||||
get_players_sorted_by_lowest_pers_value( pers_name )
|
|
||||||
{
|
|
||||||
players = getPlayers();
|
|
||||||
|
|
||||||
if ( !isDefined( players[ 0 ].pers[ pers_name ] ) )
|
|
||||||
{
|
|
||||||
assertMsg( "Uninitialized pers value: " + pers_name );
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return quickSort( players, ::quicksort_compare_pers_value_lowest_to_highest, pers_name );
|
|
||||||
}
|
|
@ -2,6 +2,7 @@
|
|||||||
#include maps\_utility;
|
#include maps\_utility;
|
||||||
#include maps\bots\_bot_utility;
|
#include maps\bots\_bot_utility;
|
||||||
#include maps\bots\script_objectives\_obj_common;
|
#include maps\bots\script_objectives\_obj_common;
|
||||||
|
#include maps\bots\script_objectives\_obj_utility;
|
||||||
|
|
||||||
bot_post_think_common( state )
|
bot_post_think_common( state )
|
||||||
{
|
{
|
||||||
@ -1191,7 +1192,7 @@ bot_power_activate()
|
|||||||
|
|
||||||
power_ent = power_obj.target_ent;
|
power_ent = power_obj.target_ent;
|
||||||
self bot_set_objective( "power", power_ent );
|
self bot_set_objective( "power", power_ent );
|
||||||
self bot_objective_print( "power", power_obj.id, "Bot <" + self.playername + "> Attempting to activate power" "bot_power_activate" );
|
self bot_objective_print( "power", power_obj.id, "Bot <" + self.playername + "> Attempting to activate power", "bot_power_activate" );
|
||||||
|
|
||||||
lookat_org = power_ent.origin;
|
lookat_org = power_ent.origin;
|
||||||
goal_org = power_ent.bot_use_node;
|
goal_org = power_ent.bot_use_node;
|
||||||
@ -1220,7 +1221,7 @@ bot_power_activate_init()
|
|||||||
self.successfully_activated_power = false;
|
self.successfully_activated_power = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bot_power_activate_post_think()
|
bot_power_activate_post_think(state)
|
||||||
{
|
{
|
||||||
self bot_post_think_common( state );
|
self bot_post_think_common( state );
|
||||||
self.successfully_activated_power = false;
|
self.successfully_activated_power = false;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include common_scripts\utility;
|
#include common_scripts\utility;
|
||||||
#include maps\_utility;
|
#include maps\_utility;
|
||||||
#include maps\bots\_bot_utility;
|
#include maps\bots\_bot_utility;
|
||||||
|
#include maps\bots\script_objectives\_obj_utility;
|
||||||
|
|
||||||
register_bot_objective( objective_group )
|
register_bot_objective( objective_group )
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include common_scripts\utility;
|
#include common_scripts\utility;
|
||||||
#include maps\_utility;
|
#include maps\_utility;
|
||||||
#include maps\bots\_bot_utility;
|
#include maps\bots\_bot_utility;
|
||||||
|
#include maps\bots\script_objectives\_obj_utility;
|
||||||
#include maps\bots\script_objectives\_obj_common;
|
#include maps\bots\script_objectives\_obj_common;
|
||||||
#include maps\bots\script_objectives\_obj_actions;
|
#include maps\bots\script_objectives\_obj_actions;
|
||||||
#include maps\bots\script_objectives\_obj_trackers;
|
#include maps\bots\script_objectives\_obj_trackers;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include maps\_utility;
|
#include maps\_utility;
|
||||||
#include maps\bots\_bot_utility;
|
#include maps\bots\_bot_utility;
|
||||||
#include maps\bots\script_objectives\_obj_common;
|
#include maps\bots\script_objectives\_obj_common;
|
||||||
|
#include maps\bots\script_objectives\_obj_utility;
|
||||||
|
|
||||||
create_static_objectives()
|
create_static_objectives()
|
||||||
{
|
{
|
||||||
@ -141,7 +142,7 @@ create_static_objectives()
|
|||||||
{
|
{
|
||||||
obj = add_possible_bot_objective( "power", master_switch, false );
|
obj = add_possible_bot_objective( "power", master_switch, false );
|
||||||
master_switch.bot_use_node = master_switch get_angle_offset_node( getDvarInt( "power_node_forward_size" ), ( 0, getDvarInt( "power_node_angle" ), 0 ), ( 0, 0, getDvarInt( "power_node_vertical_offset" ) ) );
|
master_switch.bot_use_node = master_switch get_angle_offset_node( getDvarInt( "power_node_forward_size" ), ( 0, getDvarInt( "power_node_angle" ), 0 ), ( 0, 0, getDvarInt( "power_node_vertical_offset" ) ) );
|
||||||
model thread power_debug();
|
//model thread power_debug();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isDefined( level.chests ) && level.chests.size > 0 )
|
if ( isDefined( level.chests ) && level.chests.size > 0 )
|
||||||
|
211
maps/bots/script_objectives/_obj_utility.gsc
Normal file
211
maps/bots/script_objectives/_obj_utility.gsc
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
#include common_scripts\utility;
|
||||||
|
#include maps\_utility;
|
||||||
|
#include maps\bots\_bot_utility;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Checks whether the path generated by the ASTAR path finding is inaccessible
|
||||||
|
*/
|
||||||
|
GetPathIsInaccessible( to, best_effort )
|
||||||
|
{
|
||||||
|
if ( isDefined( best_effort ) )
|
||||||
|
{
|
||||||
|
path = generatePath( self.origin, to, self.team, level.bot_allowed_negotiation_links, best_effort );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
path = generatePath( self.origin, to, self.team, level.bot_allowed_negotiation_links );
|
||||||
|
}
|
||||||
|
return !isDefined( path ) || ( path.size <= 0) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
swap_array_index( array, index1, index2 )
|
||||||
|
{
|
||||||
|
temp = array[ index1 ];
|
||||||
|
array[ index1 ] = array[ index2 ];
|
||||||
|
array[ index2 ] = temp;
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
quickSort(array, compare_func, compare_func_arg1)
|
||||||
|
{
|
||||||
|
return quickSortMid( array, 0, array.size - 1, compare_func, compare_func_arg1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Quicksort algorithm copied from T7 modified for T4
|
||||||
|
*/
|
||||||
|
quickSortMid( array, start, end, compare_func, compare_func_arg1 )
|
||||||
|
{
|
||||||
|
i = start;
|
||||||
|
k = end;
|
||||||
|
|
||||||
|
if(!IsDefined(compare_func))
|
||||||
|
compare_func = ::quicksort_compare;
|
||||||
|
|
||||||
|
if (end - start >= 1)
|
||||||
|
{
|
||||||
|
pivot = array[start];
|
||||||
|
|
||||||
|
while (k > i)
|
||||||
|
{
|
||||||
|
while ( [[ compare_func ]](array[i], pivot, compare_func_arg1) && i <= end && k > i)
|
||||||
|
i++;
|
||||||
|
while ( ![[ compare_func ]](array[k], pivot, compare_func_arg1) && k >= start && k >= i)
|
||||||
|
k--;
|
||||||
|
if (k > i)
|
||||||
|
array = swap_array_index(array, i, k);
|
||||||
|
}
|
||||||
|
array = swap_array_index(array, start, k);
|
||||||
|
array = quickSortMid(array, start, k - 1, compare_func);
|
||||||
|
array = quickSortMid(array, k + 1, end, compare_func);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return array;
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
quicksort_compare(left, right, compare_func_arg1)
|
||||||
|
{
|
||||||
|
return left <= right;
|
||||||
|
}
|
||||||
|
|
||||||
|
quicksort_compare_priority_field(left, right, compare_func_arg1)
|
||||||
|
{
|
||||||
|
return left.priority <= right.priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
quicksort_compare_pers_value_highest_to_lowest( left, right, compare_func_arg1 )
|
||||||
|
{
|
||||||
|
return left.pers[ compare_func_arg1 ] <= right.pers[ compare_func_arg1 ];
|
||||||
|
}
|
||||||
|
|
||||||
|
quicksort_compare_pers_value_lowest_to_highest( left, right, compare_func_arg1 )
|
||||||
|
{
|
||||||
|
return left.pers[ compare_func_arg1 ] >= right.pers[ compare_func_arg1 ];
|
||||||
|
}
|
||||||
|
|
||||||
|
assign_priority_to_powerup( powerup )
|
||||||
|
{
|
||||||
|
if ( !isDefined( powerup ) )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
priority = 0;
|
||||||
|
powerup_is_max_ammo = false;
|
||||||
|
switch ( powerup.powerup_name )
|
||||||
|
{
|
||||||
|
case "zombie_blood":
|
||||||
|
case "insta_kill":
|
||||||
|
case "nuke":
|
||||||
|
priority += 2;
|
||||||
|
break;
|
||||||
|
case "full_ammo":
|
||||||
|
powerup_is_max_ammo = true;
|
||||||
|
priority += 1;
|
||||||
|
break;
|
||||||
|
case "double_points":
|
||||||
|
case "fire_sale":
|
||||||
|
case "carpenter":
|
||||||
|
case "free_perk":
|
||||||
|
priority += 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
priority += 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ( powerup_is_max_ammo )
|
||||||
|
{
|
||||||
|
LOW_AMMO_THRESHOLD = 0.3;
|
||||||
|
|
||||||
|
players = getPlayers();
|
||||||
|
|
||||||
|
for ( i = 0; i < players.size; i++ )
|
||||||
|
{
|
||||||
|
weapons = players[ i ] getWeaponsListPrimaries();
|
||||||
|
for ( j = 0; j < weapons.size; j++ )
|
||||||
|
{
|
||||||
|
if ( players[ i ] getWeaponAmmoStock( weapons[ j ] ) <= int( weaponmaxammo( weapons[ j ] ) * LOW_AMMO_THRESHOLD ) )
|
||||||
|
{
|
||||||
|
priority += 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( priority > 3 )
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( maps\_laststand::player_any_player_in_laststand() )
|
||||||
|
{
|
||||||
|
switch ( powerup.powerup_name )
|
||||||
|
{
|
||||||
|
case "zombie_blood":
|
||||||
|
case "insta_kill":
|
||||||
|
case "nuke":
|
||||||
|
priority += 1;
|
||||||
|
break;
|
||||||
|
case "full_ammo":
|
||||||
|
priority += 0;
|
||||||
|
break;
|
||||||
|
case "double_points":
|
||||||
|
case "fire_sale":
|
||||||
|
case "carpenter":
|
||||||
|
case "free_perk":
|
||||||
|
priority -= 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
priority += 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( powerup.time_left_until_timeout < 10.0 )
|
||||||
|
{
|
||||||
|
priority += 1;
|
||||||
|
}
|
||||||
|
if ( priority < 0 )
|
||||||
|
{
|
||||||
|
priority = 0;
|
||||||
|
}
|
||||||
|
powerup.priority = priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
sort_array_by_priority_field( array, item )
|
||||||
|
{
|
||||||
|
if ( isDefined( item ) )
|
||||||
|
{
|
||||||
|
array[ array.size ] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
array = quickSort( array, ::quicksort_compare_priority_field, undefined );
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_players_sorted_by_highest_pers_value( pers_name )
|
||||||
|
{
|
||||||
|
players = getPlayers();
|
||||||
|
|
||||||
|
if ( !isDefined( players[ 0 ].pers[ pers_name ] ) )
|
||||||
|
{
|
||||||
|
assertMsg( "Uninitialized pers value: " + pers_name );
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return quickSort( players, ::quicksort_compare_pers_value_highest_to_lowest, pers_name );
|
||||||
|
}
|
||||||
|
|
||||||
|
get_players_sorted_by_lowest_pers_value( pers_name )
|
||||||
|
{
|
||||||
|
players = getPlayers();
|
||||||
|
|
||||||
|
if ( !isDefined( players[ 0 ].pers[ pers_name ] ) )
|
||||||
|
{
|
||||||
|
assertMsg( "Uninitialized pers value: " + pers_name );
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return quickSort( players, ::quicksort_compare_pers_value_lowest_to_highest, pers_name );
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user