s1-scripts-dev/raw/common_scripts/_createfxmenu.gsc
2025-05-21 16:23:17 +02:00

968 lines
24 KiB
Plaintext

#include common_scripts\utility;
#include common_scripts\_createfx;
//---------------------------------------------------------
// Menu init/loop section
//---------------------------------------------------------
init_menu()
{
level._createfx.options = [];
// each option has a type, a name its stored under, a description, a default, and a mask it uses to determine
// which types of fx can have this option
addOption( "vector", "origin", "Origin", (0,0,0), "fx", 1 );
addOption( "vector", "angles", "Angles", (0,0,0), "fx", 1 );
addOption( "string", "fxid", "FX id", "nil", "fx" );
addOption( "float", "delay", "Repeat rate/start delay", 0.5, "fx" );
addOption( "string", "flag", "Flag", "nil", "exploder" );
addOption( "string", "platform", "Platform", "all", "all" );
if ( !level.mp_createfx )
{
addOption( "string", "firefx", "2nd FX id", "nil", "exploder" );
addOption( "float", "firefxdelay", "2nd FX id repeat rate", 0.5, "exploder" );
addOption( "float", "firefxtimeout", "2nd FX timeout", 5, "exploder" );
addOption( "string", "firefxsound", "2nd FX soundalias", "nil", "exploder" );
addOption( "float", "damage", "Radius damage", 150, "exploder" );
addOption( "float", "damage_radius", "Radius of radius damage", 250, "exploder" );
addOption( "string", "earthquake", "Earthquake", "nil", "exploder" );
addOption( "string", "ender", "Level notify for ending 2nd FX", "nil", "exploder" );
}
addOption( "float", "delay_min", "Minimimum time between repeats", 1, "soundfx_interval" );
addOption( "float", "delay_max", "Maximum time between repeats", 2, "soundfx_interval" );
addOption( "int", "repeat", "Number of times to repeat", 5, "exploder" );
addOption( "string", "exploder", "Exploder", "1", "exploder" );
setup_help_keys();
addOption( "string", "soundalias", "Soundalias", "nil", "all" );
addOption( "string", "loopsound", "Loopsound", "nil", "exploder" );
addOption( "int", "reactive_radius", "Reactive Radius", 100, "reactive_fx", undefined, ::input_reactive_radius );
addOption( "string", "ambiencename", "Ambience Name", "nil", "soundfx_dynamic" );
addOption( "int", "dynamic_distance", "Dynamic Max Distance", 1000, "soundfx_dynamic" );
if( !level.mp_createfx )
{
addOption( "string", "rumble", "Rumble", "nil", "exploder" );
addOption( "int", "stoppable", "Can be stopped from script", "1", "all" );
}
level.effect_list_offset = 0;
level.effect_list_offset_max = 10;
level.effect_list_current_size = 0;
level.help_list_offset = 0;
level.help_list_offset_max = 20;
level.createfx_help_active = false;
level.createfx_menu_list_active = false;
// creates mask groups. For example if the above says its mask is "fx", then all the types under "fx" can use the option
level.createfxMasks = [];
level.createfxMasks[ "all" ] = [];
level.createfxMasks[ "all" ][ "exploder" ] = true;
level.createfxMasks[ "all" ][ "oneshotfx" ] = true;
level.createfxMasks[ "all" ][ "loopfx" ] = true;
level.createfxMasks[ "all" ][ "soundfx" ] = true;
level.createfxMasks[ "all" ][ "soundfx_interval" ] = true;
level.createfxMasks[ "all" ][ "reactive_fx" ] = true;
level.createfxMasks[ "all" ][ "soundfx_dynamic" ] = true;
level.createfxMasks[ "fx" ] = [];
level.createfxMasks[ "fx" ][ "exploder" ] = true;
level.createfxMasks[ "fx" ][ "oneshotfx" ] = true;
level.createfxMasks[ "fx" ][ "loopfx" ] = true;
level.createfxMasks[ "exploder" ] = [];
level.createfxMasks[ "exploder" ][ "exploder" ] = true;
level.createfxMasks[ "loopfx" ] = [];
level.createfxMasks[ "loopfx" ][ "loopfx" ] = true;
level.createfxMasks[ "oneshotfx" ] = [];
level.createfxMasks[ "oneshotfx" ][ "oneshotfx" ] = true;
level.createfxMasks[ "soundfx" ] = [];
level.createfxMasks[ "soundfx" ][ "soundalias" ] = true;
level.createfxMasks[ "soundfx_interval" ] = [];
level.createfxMasks[ "soundfx_interval" ][ "soundfx_interval" ] = true;
level.createfxMasks[ "reactive_fx" ] = [];
level.createfxMasks[ "reactive_fx" ][ "reactive_fx" ] = true;
level.createfxMasks[ "soundfx_dynamic" ] = [];
level.createfxMasks[ "soundfx_dynamic" ][ "soundfx_dynamic" ] = true;
// Mainly used for input of a menu
menus = [];
menus[ "creation" ] = ::menu_create_select;
menus[ "create_oneshot" ] = ::menu_create;
menus[ "create_loopfx" ] = ::menu_create;
menus[ "change_fxid" ] = ::menu_create;
menus[ "none" ] = ::menu_none;
menus[ "add_options" ] = ::menu_add_options;
menus[ "select_by_name" ] = ::menu_select_by_name;
level._createfx.menus = menus;
}
menu( name )
{
return level.create_fx_menu == name;
}
setmenu( name )
{
level.create_fx_menu = name;
}
create_fx_menu()
{
if ( button_is_clicked( "escape", "x" ) )
{
_exit_menu();
return;
}
if ( IsDefined( level._createfx.menus[ level.create_fx_menu ] ) )
{
[[ level._createfx.menus[ level.create_fx_menu ] ]]();
}
}
menu_create_select()
{
if ( button_is_clicked( "1" ) )
{
setmenu( "create_oneshot" );
draw_effects_list();
return;
}
else if ( button_is_clicked( "2" ) )
{
setmenu( "create_loopfx" );
draw_effects_list();
return;
}
else if ( button_is_clicked( "3" ) )
{
setmenu( "create_loopsound" );
ent = createLoopSound();
finish_creating_entity( ent );
return;
}
else if ( button_is_clicked( "4" ) )
{
setmenu( "create_exploder" );
ent = createNewExploder();
finish_creating_entity( ent );
return;
}
else if ( button_is_clicked( "5" ) )
{
setmenu( "create_interval_sound" );
ent = createIntervalSound();
finish_creating_entity( ent );
return;
}
else if ( button_is_clicked( "6" ) )
{
ent = createReactiveEnt();
finish_creating_entity( ent );
return;
}
else if ( button_is_clicked( "7" ) )
{
ent = createDynamicAmbience();
finish_creating_entity( ent );
return;
}
}
menu_create()
{
level.createfx_menu_list_active = true;
if ( next_button() )
{
increment_list_offset();
draw_effects_list();
}
else if ( previous_button() )
{
decrement_list_offset();
draw_effects_list();
}
menu_fx_creation();
}
menu_none()
{
if ( button_is_clicked( "m" ) )
increment_list_offset();
// change selected entities
menu_change_selected_fx();
// if there's a selected ent then display the info on the last one to be selected
if ( entities_are_selected() )
{
last_selected_ent = get_last_selected_ent();
// only update hudelems when we have new info
if ( !IsDefined( level.last_displayed_ent ) || last_selected_ent != level.last_displayed_ent || level._createfx.justConvertedOneshot == 1 )
{
display_fx_info( last_selected_ent );
level.last_displayed_ent = last_selected_ent;
level._createfx.justConvertedOneshot = 0;
}
if ( button_is_clicked( "a" ) )
{
clear_settable_fx();
setMenu( "add_options" );
}
}
else
{
level.last_displayed_ent = undefined;
}
}
menu_add_options()
{
if ( !entities_are_selected() )
{
clear_fx_hudElements();
setMenu( "none" );
return;
}
display_fx_add_options( get_last_selected_ent() );
if ( next_button() )
{
increment_list_offset();
// draw_effects_list();
}
}
menu_select_by_name()
{
if ( next_button() )
{
increment_list_offset();
draw_effects_list( "Select by name" );
}
else if ( previous_button() )
{
decrement_list_offset();
draw_effects_list( "Select by name" );
}
select_by_name();
}
next_button()
{
return button_is_clicked( "rightarrow" );
}
previous_button()
{
return button_is_clicked( "leftarrow" );
}
_exit_menu()
{
clear_fx_hudElements();
clear_entity_selection();
update_selected_entities();
setmenu( "none" );
}
//---------------------------------------------------------
// Create FX Section (button presses)
//---------------------------------------------------------
menu_fx_creation()
{
count = 0;
picked_fx = undefined;
keys = func_get_level_fx();
for ( i = level.effect_list_offset; i < keys.size; i++ )
{
count = count + 1;
button_to_check = count;
if ( button_to_check == 10 )
button_to_check = 0;
if ( button_is_clicked( button_to_check + "" ) )
{
picked_fx = keys[ i ];
break;
}
if ( count > level.effect_list_offset_max )
break;
}
if ( !isdefined( picked_fx ) )
return;
if ( menu( "change_fxid" ) )
{
apply_option_to_selected_fx( get_option( "fxid" ), picked_fx );
level.effect_list_offset = 0;
clear_fx_hudElements();
setMenu( "none" );
level.createfx_menu_list_active = false;
level.createfx_last_movement_timer = 0;
return;
}
ent = undefined;
if ( menu( "create_loopfx" ) )
ent = createLoopEffect( picked_fx );
if ( menu( "create_oneshot" ) )
ent = createOneshotEffect( picked_fx );
finish_creating_entity( ent );
}
finish_creating_entity( ent )
{
assert( isdefined( ent ) );
ent.v[ "angles" ] = vectortoangles( ( ent.v[ "origin" ] + ( 0, 0, 100 ) ) - ent.v[ "origin" ] );
ent post_entity_creation_function();// for createfx dev purposes
clear_entity_selection();
select_last_entity();
move_selection_to_cursor();
update_selected_entities();
setMenu( "none" );
level.createfx_menu_list_active = false;
}
entities_are_selected()
{
return level._createfx.selected_fx_ents.size > 0;
}
menu_change_selected_fx()
{
if ( !level._createfx.selected_fx_ents.size )
{
return;
}
count = 0;
drawnCount = 0;
ent = get_last_selected_ent();
for ( i = 0; i < level._createfx.options.size; i++ )
{
option = level._createfx.options[ i ];
if ( !isdefined( ent.v[ option[ "name" ] ] ) )
continue;
count++ ;
if ( count < level.effect_list_offset )
continue;
drawnCount++ ;
button_to_check = drawnCount;
if ( button_to_check == 10 )
button_to_check = 0;
if ( button_is_clicked( button_to_check + "" ) )
{
prepare_option_for_change( option, drawnCount );
break;
}
if ( drawnCount > level.effect_list_offset_max )
{
more = true;
break;
}
}
}
prepare_option_for_change( option, drawnCount )
{
if ( option[ "name" ] == "fxid" )
{
setMenu( "change_fxid" );
draw_effects_list();
return;
}
level.createfx_inputlocked = true;
level._createfx.hudelems[ drawnCount + 1 ][ 0 ].color = ( 1, 1, 0 );
if ( IsDefined( option[ "input_func" ] ) )
{
thread [[ option[ "input_func" ] ]]( drawnCount + 1 );
}
else
{
createfx_centerprint( "To set " + option[ "description" ] + ", type /fx newvalue. To remove "+ option[ "description" ] +", type /fx del" );
}
set_option_index( option[ "name" ] );
setdvar( "fx", "nil" );
}
menu_fx_option_set()
{
if ( getdvar( "fx" ) == "nil" )
return;
if ( getdvar( "fx" ) == "del" )
{
remove_selected_option();
return;
}
option = get_selected_option();
setting = undefined;
if ( option[ "type" ] == "string" )
setting = getdvar( "fx" );
if ( option[ "type" ] == "int" )
setting = getdvarint( "fx" );
if ( option[ "type" ] == "float" )
setting = getdvarfloat( "fx" );
if ( option[ "type" ] == "vector" )
setting = getdvarvector( "fx" );
if(isDefined(setting))
apply_option_to_selected_fx( option, setting );
else
setdvar( "fx" , "nil");
}
apply_option_to_selected_fx( option, setting )
{
save_undo_buffer();
for ( i = 0; i < level._createfx.selected_fx_ents.size; i++ )
{
ent = level._createfx.selected_fx_ents[ i ];
if ( mask( option[ "mask" ], ent.v[ "type" ] ))
ent.v[ option[ "name" ] ] = setting;
}
level.last_displayed_ent = undefined; // needed to force a redraw of the last display ent
update_selected_entities();
clear_settable_fx();
//if we moved it, restart and focus camera on new position
if( option[ "name" ] == "origin")
{
level.createfx_last_movement_timer = 0;
frame_selected();
}
//if we changed angles, restart fx
if( option[ "name" ] == "angles")
{
level.createfx_last_movement_timer = 0;
}
save_redo_buffer();
}
set_option_index( name )
{
for ( i = 0; i < level._createfx.options.size; i++ )
{
if ( level._createfx.options[ i ][ "name" ] != name )
continue;
level._createfx.selected_fx_option_index = i;
return;
}
}
get_selected_option()
{
return level._createfx.options[ level._createfx.selected_fx_option_index ];
}
mask( type, name )
{
return isdefined( level.createfxMasks[ type ][ name ] );
}
addOption( type, name, description, defaultSetting, mask, nowrite, input_func )
{
option = [];
option[ "type" ] = type;
option[ "name" ] = name;
option[ "description" ] = description;
option[ "default" ] = defaultSetting;
option[ "mask" ] = mask;
if ( isdefined(nowrite) && nowrite )
option[ "nowrite" ] = 1;
else
option[ "nowrite" ] = 0;
if ( IsDefined( input_func ) )
{
option[ "input_func" ] = input_func;
}
level._createfx.options[ level._createfx.options.size ] = option;
}
get_option( name )
{
for ( i = 0; i < level._createfx.options.size; i++ )
{
if ( level._createfx.options[ i ][ "name" ] == name )
return level._createfx.options[ i ];
}
}
//---------------------------------------------------------
// Reactive Radius
//---------------------------------------------------------
input_reactive_radius( menu_index )
{
level._createfx.hudelems[ menu_index ][ 0 ] SetDevText( "Reactive Radius, Press: + OR -" );
while ( 1 )
{
wait( 0.05 );
if ( level.player ButtonPressed( "escape" ) || level.player ButtonPressed( "x" ) )
break;
val = 0;
if ( level.player ButtonPressed( "-" ) )
val = -10;
else if ( level.player ButtonPressed( "=" ) )
val = 10;
if ( val != 0 )
{
foreach ( ent in level._createfx.selected_fx_ents )
{
if ( IsDefined( ent.v[ "reactive_radius" ] ) )
{
ent.v[ "reactive_radius" ] += val;
ent.v[ "reactive_radius" ] = Clamp( ent.v[ "reactive_radius" ], 10, 1000 );
}
}
}
}
level.last_displayed_ent = undefined; // needed to force a redraw of the last display ent
update_selected_entities();
clear_settable_fx();
}
//---------------------------------------------------------
// Display FX Add Options
//---------------------------------------------------------
display_fx_add_options( ent )
{
// are we doing the create fx menu right now?
assert( menu( "add_options" ) );
assert( entities_are_selected() );
level.createfx_menu_list_active = true;
clear_fx_hudElements();
set_fx_hudElement( "Name: " + ent.v[ "fxid" ] );
set_fx_hudElement( "Type: " + ent.v[ "type" ] );
set_fx_hudElement( "Origin: " + ent.v[ "origin" ] );
set_fx_hudElement( "Angles: " + ent.v[ "angles" ] );
// if entities are selected then we make the entity stats modifiable
count = 0;
drawnCount = 0;
more = false;
if ( level.effect_list_offset >= level._createfx.options.size )
level.effect_list_offset = 0;
for ( i = 0; i < level._createfx.options.size; i++ )
{
option = level._createfx.options[ i ];
if ( isdefined( ent.v[ option[ "name" ] ] ) )
continue;
// does this type of effect get this kind of option?
if ( !mask( option[ "mask" ], ent.v[ "type" ] ) )
continue;
count++ ;
if ( count < level.effect_list_offset )
continue;
if ( drawnCount >= level.effect_list_offset_max )
continue;
drawnCount++ ;
button_to_check = drawnCount;
if ( button_to_check == 10 )
button_to_check = 0;
if ( button_is_clicked( button_to_check + "" ) )
{
add_option_to_selected_entities( option );
// prepare_option_for_change( option, drawnCount );
menuNone();
level.last_displayed_ent = undefined; // needed to force a redraw of the last display ent
return;
}
set_fx_hudElement( button_to_check + ". " + option[ "description" ] );
}
if ( count > level.effect_list_offset_max )
{
level.effect_list_current_size = count;
set_fx_hudElement( "(->) More >" );
}
set_fx_hudElement( "(x) Exit >" );
}
add_option_to_selected_entities( option )
{
setting = undefined;
for ( i = 0; i < level._createfx.selected_fx_ents.size; i++ )
{
ent = level._createfx.selected_fx_ents[ i ];
if ( mask( option[ "mask" ], ent.v[ "type" ] ) )
ent.v[ option[ "name" ] ] = option[ "default" ];
}
}
menuNone()
{
level.effect_list_offset = 0;
clear_fx_hudElements();
setMenu( "none" );
}
//---------------------------------------------------------
// Display FX info
//---------------------------------------------------------
display_fx_info( ent )
{
// are we doing the create fx menu right now?
if ( !menu( "none" ) )
return;
if ( level.createfx_help_active )
return;
clear_fx_hudElements();
set_fx_hudElement( "Name: " + ent.v[ "fxid" ] );
set_fx_hudElement( "Type: " + ent.v[ "type" ] );
if ( entities_are_selected() )
{
// if entities are selected then we make the entity stats modifiable
count = 0;
drawnCount = 0;
more = false;
for ( i = 0; i < level._createfx.options.size; i++ )
{
option = level._createfx.options[ i ];
if ( !isdefined( ent.v[ option[ "name" ] ] ) )
continue;
count++ ;
if ( count < level.effect_list_offset )
continue;
drawnCount++ ;
set_fx_hudElement( drawnCount + ". " + option[ "description" ] + ": " + ent.v[ option[ "name" ] ] );
if ( drawnCount > level.effect_list_offset_max )
{
more = true;
break;
}
}
if ( count > level.effect_list_offset_max )
{
level.effect_list_current_size = count;
set_fx_hudElement( "(->) More >" );
}
set_fx_hudElement( "(a) Add >" );
set_fx_hudElement( "(x) Exit >" );
}
else
{
count = 0;
more = false;
for ( i = 0; i < level._createfx.options.size; i++ )
{
option = level._createfx.options[ i ];
if ( !isdefined( ent.v[ option[ "name" ] ] ) )
continue;
count++ ;
set_fx_hudElement( option[ "description" ] + ": " + ent.v[ option[ "name" ] ] );
if ( count > level._createfx.hudelem_count )
break;
}
}
}
display_current_translation()
{
ent = get_last_selected_ent();
if(isdefined(ent))
display_fx_info(ent);
}
//---------------------------------------------------------
// Draw Effects Section
//---------------------------------------------------------
draw_effects_list( title )
{
clear_fx_hudElements();
count = 0;
more = false;
keys = func_get_level_fx();
level.effect_list_current_size = keys.size;
if( !IsDefined( title ) )
{
title = "Pick an effect";
}
set_fx_hudElement( title + " [" + level.effect_list_offset + " - " + keys.size + "]:" );
// if ( level.effect_list_offset >= keys.size )
// level.effect_list_offset = 0;
for ( i = level.effect_list_offset; i < keys.size; i++ )
{
count = count + 1;
set_fx_hudElement( count + ". " + keys[ i ] );
if ( count >= level.effect_list_offset_max )
{
more = true;
break;
}
}
if ( keys.size > level.effect_list_offset_max )
{
set_fx_hudElement( "(->) More >" );
set_fx_hudElement( "(<-) Previous >" );
}
}
increment_list_offset()
{
if ( level.effect_list_offset >= level.effect_list_current_size - level.effect_list_offset_max )
{
level.effect_list_offset = 0;
}
else
{
level.effect_list_offset += level.effect_list_offset_max;
}
}
decrement_list_offset()
{
if ( level.effect_list_current_size < level.effect_list_offset_max )
{
level.effect_list_offset = 0;
}
else
{
level.effect_list_offset -= level.effect_list_offset_max;
if ( level.effect_list_offset < 0 )
{
//keys = func_get_level_fx();
level.effect_list_offset = level.effect_list_current_size - level.effect_list_offset_max;
}
}
}
//---------------------------------------------------------
// Draw Help
//---------------------------------------------------------
draw_help_list( title )
{
clear_fx_hudElements();
count = 0;
keys = level.createfx_help_keys;
if( !IsDefined( title ) )
{
title = "Help";
}
set_fx_hudElement( "[" + title + "]");// + " [" + level.help_list_offset + " - " + keys.size + "]:" );
// if ( level.effect_list_offset >= keys.size )
// level.effect_list_offset = 0;
for ( i = level.help_list_offset; i < keys.size; i++ )
{
count = count + 1;
set_fx_hudElement( keys[ i ] );
if ( count >= level.help_list_offset_max )
{
more = true;
break;
}
}
if ( keys.size > level.help_list_offset_max )
{
level.effect_list_current_size = keys.size;
set_fx_hudElement( "(->) More >" );
set_fx_hudElement( "(<-) Previous >" );
}
}
increment_help_list_offset()
{
keys = level.createfx_help_keys;
if ( level.help_list_offset >= keys.size - level.help_list_offset_max )
{
level.help_list_offset = 0;
}
else
{
level.help_list_offset += level.help_list_offset_max;
}
}
decrement_help_list_offset()
{
level.help_list_offset -= level.help_list_offset_max;
if ( level.help_list_offset < 0 )
{
keys = level.createfx_help_keys;
level.help_list_offset = keys.size - level.help_list_offset_max;
}
}
help_navigation_buttons()
{
while(level.createfx_help_active == true)
{
if ( next_button() )
{
increment_help_list_offset();
draw_help_list();
wait 0.1;
}
else if ( previous_button() )
{
decrement_help_list_offset();
draw_help_list();
wait 0.1;
}
waitframe();
}
}
setup_help_keys()
{
level.createfx_help_keys = [
"Insert Insert entity",
"F2 Toggle createfx dot and text drawing",
"F5 SAVES your work",
"Z Undo",
"Shift-Z Redo",
"F Frames currently selected entities in camera view",
"END Drop selected entities to the ground",
"A Add option to the selected entities",
"P Reset the rotation of the selected entities",
"V Copy the angles from the most recently selected fx onto all selected fx.",
"O Orient all selected fx to point at most recently selected fx.",
"S Toggle Snap2Normal mode.",
"L Toggle 90deg Snap mode.",
"G Select all effects in level of same exploder or flag as selected.",
"U Select by name list.",
"C Convert One-Shot to Exploder.",
"Delete Kill the selected entities",
"ESCAPE Cancel out of option-modify-mode, must have console open",
"SPACE or -> Turn on exploders",
"<- Turn off exploders",
"Dpad Move selected entities on X/Y or rotate pitch/yaw",
"A button Toggle the selection of the current entity",
"X button Toggle entity rotation mode",
"Y button Move selected entites up or rotate roll",
"B button Move selected entites down or rotate roll",
"R Shoulder Move selected entities to the cursor",
"L Shoulder Hold to select multiple entites",
"L JoyClick Copy",
"R JoyClick Paste",
"Ctrl-C Copy",
"Ctrl-V Paste",
"N UFO",
"T Toggle Timescale FAST",
"Y Toggle Timescale SLOW",
"[ Toggle FX Visibility",
"] Toggle ShowTris",
"F11 Toggle FX Profile"];
}
//---------------------------------------------------------
// Select by Name Section
//---------------------------------------------------------
select_by_name()
{
count = 0;
picked_fx = undefined;
keys = func_get_level_fx();
for ( i = level.effect_list_offset; i < keys.size; i++ )
{
count = count + 1;
button_to_check = count;
if ( button_to_check == 10 )
button_to_check = 0;
if ( button_is_clicked( button_to_check + "" ) )
{
picked_fx = keys[ i ];
break;
}
if ( count > level.effect_list_offset_max )
break;
}
if ( !IsDefined( picked_fx ) )
return;
index_array = [];
foreach ( i, ent in level.createFXent )
{
if ( IsSubStr( ent.v[ "fxid" ], picked_fx ) )
{
index_array[ index_array.size ] = i;
}
}
deselect_all_ents();
select_index_array( index_array );
level._createfx.select_by_name = true;
}
//---------------------------------------------------------
// Utility Section
//---------------------------------------------------------
get_last_selected_ent()
{
return level._createfx.selected_fx_ents[ level._createfx.selected_fx_ents.size - 1 ];
}