some cleanup

This commit is contained in:
ineed bots 2023-07-11 22:15:11 -06:00
parent 280ec4c318
commit f5753789db
6 changed files with 271 additions and 257 deletions

View File

@ -285,6 +285,7 @@ AtScriptGoal()
{
return false;
}
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;
}
/*
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
*/
@ -712,6 +697,9 @@ float( num )
return GetDvarFloat( "temp_dvar_bot_util" );
}
/*
returns nodes in playable area
*/
get_nodes_in_playable_area()
{
total_nodes = getAllNodes();
@ -735,6 +723,9 @@ get_nodes_in_playable_area()
return filtered_nodes;
}
/*
is the point in playable area?
*/
is_point_in_playable_area( point )
{
playable_area = getentarray( "playable_area", "targetname" );
@ -1075,195 +1066,3 @@ targetIsGibbed()
{
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 );
}

View File

@ -2,11 +2,12 @@
#include maps\_utility;
#include maps\bots\_bot_utility;
#include maps\bots\script_objectives\_obj_common;
#include maps\bots\script_objectives\_obj_utility;
bot_post_think_common( state )
{
obj = bot_objective_history_get_current();
switch ( state )
{
case "completed":
@ -20,7 +21,7 @@ bot_post_think_common( state )
self thread ClearScriptGoal();
self ClearScriptAimPos();
self ClearPriorityObjective();
self bot_clear_objective();
self bot_clear_objective();
}
bot_obj_timeout( objective_group, time )
@ -28,7 +29,7 @@ bot_obj_timeout( objective_group, time )
self endon( objective_group + "_end_think" );
wait time;
self.obj_cancel_reason = "Obj timeout";
self notify( objective_group + "_cancel" );
self notify( objective_group + "_cancel" );
}
bot_grab_powerup()
@ -77,7 +78,7 @@ bot_grab_powerup()
powerup_obj.bad = true;
}
}
else
else
{
powerup_obj.bad = true;
}
@ -187,7 +188,7 @@ bot_revive_player()
level endon( "end_game" );
player_to_revive_obj = self.available_revives[ 0 ];
player_to_revive = player_to_revive_obj.target_ent;
self bot_set_objective( "revive", player_to_revive );
self bot_set_objective_owner( "revive", player_to_revive );
@ -332,7 +333,7 @@ bot_magicbox_purchase()
level endon( "end_game" );
magicbox_obj = self.available_chests[ 0 ];
magicbox = magicbox_obj.target_ent;
self bot_set_objective( "magicbox", magicbox );
//self bot_set_objective_owner( "magicbox", magicbox );
@ -470,7 +471,7 @@ bot_should_purchase_magicbox()
return self.available_chests.size > 0;
}
bot_check_complete_purchase_magicbox()
bot_check_complete_purchase_magicbox()
{
return self.successfully_grabbed_magicbox_weapon;
}
@ -492,13 +493,13 @@ bot_magicbox_purchase_should_cancel()
goal_canceled = true;
}
*/
if ( isDefined( obj.magicbox_weapon_spawn_time )
&& isDefined( obj.target_ent.chest_user )
if ( isDefined( obj.magicbox_weapon_spawn_time )
&& isDefined( obj.target_ent.chest_user )
&& obj.target_ent.chest_user == self
&& ( getTime() >= ( obj.magicbox_weapon_spawn_time + 12000 ) ) )
{
self.obj_cancel_reason = "Weapon timed out";
goal_canceled = true;
goal_canceled = true;
}
return goal_canceled;
}
@ -537,7 +538,7 @@ bot_perk_purchase()
level endon( "end_game" );
perk_obj = self.available_perks[ 0 ];
perk_ent = perk_obj.target_ent;
self bot_set_objective( "perk", perk_ent );
self bot_objective_print( "perk", perk_obj.id, "Bot <" + self.playername + "> Attempting to purchase " + perk_ent.script_noteworthy, "bot_perk_purchase" );
@ -660,7 +661,7 @@ bot_door_purchase()
level endon( "end_game" );
door_obj = self.available_doors[ 0 ];
door_ent = door_obj.target_ent;
self bot_set_objective( "door", door_ent );
self bot_objective_print( "door", door_obj.id, "Bot <" + self.playername + "> Attempting to purchase " + door_ent.target, "bot_door_purchase" );
@ -759,7 +760,7 @@ bot_debris_purchase()
level endon( "end_game" );
debris_obj = self.available_debris[ 0 ];
debris_ent = debris_obj.target_ent;
self bot_set_objective( "debris", debris_ent );
self bot_objective_print( "debris", debris_obj.id, "Bot <" + self.playername + "> Attempting to purchase " + debris_ent.target, "bot_debris_purchase" );
@ -852,7 +853,7 @@ bot_wallbuy_purchase()
level endon( "end_game" );
wallbuy_obj = self.available_wallbuys[ 0 ];
wallbuy_ent = wallbuy_obj.target_ent;
self bot_set_objective( "wallbuy", wallbuy_ent );
self bot_objective_print( "wallbuy", wallbuy_obj.id, "Bot <" + self.playername + "> Attempting to purchase " + wallbuy_ent.zombie_weapon_upgrade, "bot_wallbuy_purchase" );
@ -952,7 +953,7 @@ bot_wallbuy_ammo_purchase()
level endon( "end_game" );
wallbuy_obj = self.available_wallbuyammos[ 0 ];
wallbuy_ent = wallbuy_obj.target_ent;
self bot_set_objective( "wallbuyammo", wallbuy_ent );
self bot_objective_print( "wallbuyammo", wallbuy_obj.id, "Bot <" + self.playername + "> Attempting to purchase " + wallbuy_ent.zombie_weapon_upgrade, "bot_wallbuy_ammo_purchase" );
@ -1057,7 +1058,7 @@ bot_packapunch_purchase()
level endon( "end_game" );
packapunch_obj = self.available_packapunchs[ 0 ];
packapunch = packapunch_obj.target_ent;
self bot_set_objective( "packapunch", packapunch );
//self bot_set_objective_owner( "magicbox", magicbox );
@ -1086,7 +1087,7 @@ bot_packapunch_purchase()
waittillframeend;
//self ClearScriptAimPos();
//self ClearScriptGoal();
packapunch waittill( "pap_pickup_ready" );
self bot_objective_print( "packapunch", packapunch_obj.id, "Bot <" + self.playername + "> pap_pickup_ready", "bot_packapunch_purchase" );
@ -1160,13 +1161,13 @@ bot_packapunch_purchase_should_cancel()
obj = self bot_get_objective();
goal_canceled = false;
if ( isDefined( obj.target_ent.packapunch_weapon_spawn_time )
&& isDefined( obj.target_ent.packapunch_user )
&& obj.target_ent.packapunch_user == self
if ( isDefined( obj.target_ent.packapunch_weapon_spawn_time )
&& isDefined( obj.target_ent.packapunch_user )
&& obj.target_ent.packapunch_user == self
&& ( getTime() >= ( obj.target_ent_packapunch_weapon_spawn_time + ( level.packapunch_timeout * 1000 ) ) ) )
{
self.obj_cancel_reason = "Weapon timed out";
goal_canceled = true;
goal_canceled = true;
}
return goal_canceled;
}
@ -1188,10 +1189,10 @@ bot_power_activate()
level endon( "end_game" );
power_obj = self.available_powers[ 0 ];
power_ent = power_obj.target_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;
goal_org = power_ent.bot_use_node;
@ -1220,7 +1221,7 @@ bot_power_activate_init()
self.successfully_activated_power = false;
}
bot_power_activate_post_think()
bot_power_activate_post_think(state)
{
self bot_post_think_common( state );
self.successfully_activated_power = false;
@ -1263,7 +1264,7 @@ bot_power_activate_should_cancel()
if ( isDefined( level.flag[ "power_on" ] ) && level.flag[ "power_on" ] )
{
self.obj_cancel_reason = "Power is already on";
goal_canceled = true;
goal_canceled = true;
}
return goal_canceled;
}
@ -1271,4 +1272,4 @@ bot_power_activate_should_cancel()
bot_power_activate_priority()
{
return 0;
}
}

View File

@ -1,6 +1,7 @@
#include common_scripts\utility;
#include maps\_utility;
#include maps\bots\_bot_utility;
#include maps\bots\script_objectives\_obj_utility;
register_bot_objective( objective_group )
{
@ -95,7 +96,7 @@ bot_set_objective( objective_group, ent, id )
{
self.bot_obj_history_index = 0;
}
self.obj_history[ self.bot_obj_history_index ] = new_obj_history;
self.zbot_current_objective = objective;
}
@ -167,7 +168,7 @@ bot_set_objective_owner( objective_group, ent, id )
{
id = ent getEntityNumber();
}
if ( !objective.is_global_shared )
{
objective_assert( objective_group, id, "bot_set_objective_owner", "Objective with " + id + " id number cannot be set to have an owner because is_global_shared field is false in group " + objective_group );
@ -489,7 +490,7 @@ bot_action_think()
{
wait 1;
}
else
else
{
wait 1;
}
@ -505,7 +506,7 @@ bot_action_think()
}
continue;
}
self bot_process_action();
while ( !maps\so\zm_common\_zm_utility::is_player_valid( self ) )
@ -542,7 +543,7 @@ bot_action_pump()
else if ( self bot_check_if_action_should_be_canceled_in_group( action_name ) )
{
}
while ( !maps\so\zm_common\_zm_utility::is_player_valid( self ) )
{
wait 1;
@ -624,4 +625,4 @@ clamp_to_ground( org )
{
trace = playerPhysicsTrace( org + ( 0, 0, 20 ), org - ( 0, 0, 2000 ) );
return trace;
}
}

View File

@ -1,13 +1,14 @@
#include common_scripts\utility;
#include maps\_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_actions;
#include maps\bots\script_objectives\_obj_trackers;
init()
{
register_bot_action( "powerup",
register_bot_action( "powerup",
::bot_grab_powerup,
::bot_powerup_init,
::bot_powerup_post_think,
@ -16,7 +17,7 @@ init()
::bot_powerup_should_cancel,
::bot_powerup_priority );
register_bot_action( "revive",
register_bot_action( "revive",
::bot_revive_player,
::bot_revive_player_init,
::bot_revive_player_post_think,
@ -25,7 +26,7 @@ init()
::bot_revive_player_should_cancel,
::bot_revive_player_priority );
register_bot_action( "magicbox",
register_bot_action( "magicbox",
::bot_magicbox_purchase,
::bot_magicbox_purchase_init,
::bot_magicbox_purchase_post_think,
@ -34,7 +35,7 @@ init()
::bot_magicbox_purchase_should_cancel,
::bot_magicbox_purchase_priority );
register_bot_action( "perk",
register_bot_action( "perk",
::bot_perk_purchase,
::bot_perk_purchase_init,
::bot_perk_purchase_post_think,
@ -43,7 +44,7 @@ init()
::bot_perk_purchase_should_cancel,
::bot_perk_purchase_priority );
register_bot_action( "door",
register_bot_action( "door",
::bot_door_purchase,
::bot_door_purchase_init,
::bot_door_purchase_post_think,
@ -52,7 +53,7 @@ init()
::bot_door_purchase_should_cancel,
::bot_door_purchase_priority );
register_bot_action( "debris",
register_bot_action( "debris",
::bot_debris_purchase,
::bot_debris_purchase_init,
::bot_debris_purchase_post_think,
@ -61,7 +62,7 @@ init()
::bot_debris_purchase_should_cancel,
::bot_debris_purchase_priority );
register_bot_action( "wallbuy",
register_bot_action( "wallbuy",
::bot_wallbuy_purchase,
::bot_wallbuy_purchase_init,
::bot_wallbuy_purchase_post_think,
@ -70,7 +71,7 @@ init()
::bot_wallbuy_purchase_should_cancel,
::bot_wallbuy_purchase_priority );
register_bot_action( "wallbuyammo",
register_bot_action( "wallbuyammo",
::bot_wallbuy_ammo_purchase,
::bot_wallbuy_ammo_purchase_init,
::bot_wallbuy_ammo_purchase_post_think,
@ -79,7 +80,7 @@ init()
::bot_wallbuy_ammo_purchase_should_cancel,
::bot_wallbuy_ammo_purchase_priority );
register_bot_action( "packapunch",
register_bot_action( "packapunch",
::bot_packapunch_purchase,
::bot_packapunch_purchase_init,
::bot_packapunch_purchase_post_think,
@ -88,7 +89,7 @@ init()
::bot_packapunch_purchase_should_cancel,
::bot_packapunch_purchase_priority );
register_bot_action( "power",
register_bot_action( "power",
::bot_power_activate,
::bot_power_activate_init,
::bot_power_activate_post_think,

View File

@ -2,6 +2,7 @@
#include maps\_utility;
#include maps\bots\_bot_utility;
#include maps\bots\script_objectives\_obj_common;
#include maps\bots\script_objectives\_obj_utility;
create_static_objectives()
{
@ -65,7 +66,7 @@ create_static_objectives()
{
setDvar( "power_node_angle", -90 );
}
weapon_spawns = GetEntArray( "weapon_upgrade", "targetname" );
weapon_spawns = GetEntArray( "weapon_upgrade", "targetname" );
if ( isDefined( weapon_spawns ) && weapon_spawns.size > 0 )
{
@ -93,8 +94,8 @@ create_static_objectives()
}
//TODO: See if its possible to automatically detect if a door is blocking an objective
zombie_doors = GetEntArray( "zombie_door", "targetname" );
zombie_doors = GetEntArray( "zombie_door", "targetname" );
if ( isDefined( zombie_doors ) && zombie_doors.size > 0 )
{
for ( i = 0; i < zombie_doors.size; i++ )
@ -108,7 +109,7 @@ create_static_objectives()
level thread watch_door_objectives( zombie_doors );
}
zombie_debris = GetEntArray( "zombie_debris", "targetname" );
zombie_debris = GetEntArray( "zombie_debris", "targetname" );
if ( isDefined( zombie_debris ) && zombie_debris.size > 0 )
{
@ -141,7 +142,7 @@ create_static_objectives()
{
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" ) ) );
model thread power_debug();
//model thread power_debug();
}
if ( isDefined( level.chests ) && level.chests.size > 0 )
@ -346,7 +347,7 @@ packapunch_debug()
self.bot_use_node = node;
line( self.origin, node, ( 1.0, 1.0, 1.0 ) );
wait 0.05;
}
}
}
bot_on_powerup_grab( powerup )
@ -397,4 +398,4 @@ bot_on_wallbuy_ammo_purchase_func( trigger, weapon )
{
self bot_objective_print( "wallbuyammo", trigger getEntityNumber(), "bot_on_wallbuy_ammo_purchase_func", "Bot <" + self.playername + "> purchased wallbuy ammo <" + weapon + ">" );
self.successfully_bought_wallbuy_ammo = true;
}
}

View 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 );
}