boiii-scripts/cp/_objectives.gsc
2023-04-13 17:30:38 +02:00

874 lines
27 KiB
Plaintext

#using scripts\codescripts\struct;
#using scripts\shared\array_shared;
#using scripts\shared\flag_shared;
#using scripts\shared\flagsys_shared;
#using scripts\shared\gameobjects_shared;
#using scripts\shared\lui_shared;
#using scripts\shared\objpoints_shared;
#using scripts\shared\system_shared;
#using scripts\shared\trigger_shared;
#using scripts\shared\util_shared;
#using scripts\shared\callbacks_shared;
#using scripts\cp\_util;
#precache( "material", "compass_waypoint_target" );
#precache( "material", "compass_waypoint_captureneutral" );
#precache( "material", "compass_waypoint_capture" );
#precache( "material", "compass_waypoint_defend" );
#precache( "material", "compass_waypoint_captureneutral_a" );
#precache( "material", "compass_waypoint_capture_a" );
#precache( "material", "compass_waypoint_defend_a" );
#precache( "material", "compass_waypoint_captureneutral_b" );
#precache( "material", "compass_waypoint_capture_b" );
#precache( "material", "compass_waypoint_defend_b" );
#precache( "material", "compass_waypoint_captureneutral_c" );
#precache( "material", "compass_waypoint_capture_c" );
#precache( "material", "compass_waypoint_defend_c" );
#precache( "material", "waypoint_return" );
#precache( "material", "objective_arrow" );
#precache( "eventstring", "comms_event_message" );
#precache( "string", "CP_SH_CAIRO_PLAYER_READY" );
#precache( "lui_menu_data", "obj_x" );
#precache( "lui_menu_data", "obj_y" );
#namespace objectives;
class cObjective
{
var m_str_type;
var m_a_game_obj;
var m_a_targets;
var m_str_lui_menu;
constructor()
{
}
function init( str_type, a_target_list, b_done = false )
{
m_a_targets = [];
m_a_game_obj = [];
m_str_type = str_type;
if ( b_done )
{
gobj_id = gameobjects::get_next_obj_id();
m_a_game_obj = array( gobj_id );
Objective_Add( gobj_id, "done", (0,0,0), iString(str_type) );
}
else
{
if ( isdefined( a_target_list ) && a_target_list.size > 0 )
{
foreach( target in a_target_list )
{
add_target( target );
}
}
else
{
gobj_id = gameobjects::get_next_obj_id();
m_a_game_obj = array( gobj_id );
Objective_Add( gobj_id, "active", (0,0,0), iString(str_type) );
}
}
}
function update_value( str_menu_data_name, value )
{
gobj_id = m_a_game_obj[0];
Objective_SetUIModelValue( gobj_id, str_menu_data_name, value );
}
function update_counter( x_val, y_val )
{
update_value( "obj_x", x_val );
if ( isdefined( y_val ) )
{
update_value( "obj_y", y_val );
}
}
function add_target( target )
{
// do not add a target twice.
if ( IsInArray( m_a_targets, target ) )
{
return;
}
gobj_id = undefined;
if ( m_a_targets.size < m_a_game_obj.size )
{
gobj_id = m_a_game_obj[m_a_game_obj.size-1];
}
else
{
gobj_id = gameobjects::get_next_obj_id();
array::add( m_a_game_obj, gobj_id );
}
//Objective_Add can take in an entity or a vector for its 'target'
if ( IsVec( target ) || IsEntity( target ) )
{
Objective_Add( gobj_id, "active", target, iString(m_str_type) );
}
else
{
Objective_Add( gobj_id, "active", target.origin, iString(m_str_type) );
}
array::add( m_a_targets, target );
assert( m_a_targets.size == m_a_game_obj.size );
}
function complete( a_target_or_list )
{
if ( a_target_or_list.size > 0 )
{
// find specific targets to remove their objective
foreach ( target in a_target_or_list )
{
for ( i = m_a_targets.size - 1; i >= 0; i-- )
{
if ( m_a_targets[ i ] == target )
{
Objective_State( m_a_game_obj[ i ], "done" );
ArrayRemoveIndex( m_a_game_obj, i );
ArrayRemoveIndex( m_a_targets, i );
break;
}
}
}
}
else
{
foreach ( n_gobj_id in m_a_game_obj )
{
Objective_State( n_gobj_id, "done" );
}
for ( i = m_a_targets.size - 1; i >= 0; i-- )
{
ArrayRemoveIndex( m_a_game_obj, i );
ArrayRemoveIndex( m_a_targets, i );
}
}
if ( m_a_game_obj.size == 0 )
{
ArrayRemoveValue( level.a_objectives, self, true );
}
}
//hide waypoint
function hide( e_player )
{
if(isdefined( e_player) )
{
Assert( IsPlayer( e_player ), "Passed a non-player entity into cObjective::hide()" );
foreach( obj_id in m_a_game_obj )
{
Objective_SetInvisibleToPlayer( obj_id, e_player );
}
}
else
{
foreach( obj_id in m_a_game_obj )
{
Objective_SetInvisibleToAll( obj_id );
}
}
}
//show waypoint
function show( e_player )
{
if(isdefined( e_player) )
{
Assert( IsPlayer( e_player ), "Passed a non-player entity into cObjective::show()" );
foreach( obj_id in m_a_game_obj )
{
Objective_SetVisibleToPlayer( obj_id, e_player );
}
}
else
{
foreach( obj_id in m_a_game_obj )
{
Objective_SetVisibleToAll( obj_id );
}
}
}
function hide_for_target( e_target )
{
foreach( i, obj_id in m_a_game_obj )
{
ent = m_a_targets[i];
if ( isdefined( ent ) && ent == e_target )
{
Objective_State( obj_id, "invisible" );
return;
}
}
}
function show_for_target( e_target )
{
foreach( i, obj_id in m_a_game_obj )
{
ent = m_a_targets[i];
if ( isdefined( ent ) && ent == e_target )
{
Objective_State( obj_id, "active" );
return;
}
}
}
function get_id_for_target( e_target )
{
foreach( i, obj_id in m_a_game_obj )
{
ent = m_a_targets[i];
if ( isdefined( ent ) && ent == e_target )
{
return obj_id;
}
}
return -1;
}
function is_breadcrumb()
{
return false;
}
}
class cBreadcrumbObjective : cObjective
{
var m_a_player_game_obj; // array of objective ids, one per player
var m_str_first_trig_targetname; // the targetname of the first trigger in the breadcrumb sequence
var m_done; // at least one of the players has reached the end of their breadcrumb trail
constructor()
{
}
function init( str_type, a_target_list, b_done = false )
{
// call the parent version
cObjective::init( str_type, a_target_list, b_done );
// init some fields
m_str_first_trig_targetname = "";
m_done = b_done;
// create objectives for max players
m_a_player_game_obj = [];
for ( i = 0; i < 4; i++ )
{
obj_id = gameobjects::get_next_obj_id();
m_a_player_game_obj[i] = obj_id;
if ( m_done )
{
Objective_Add( obj_id, "done", ( 0, 0, 0 ), iString( m_str_type ) );
}
else
{
Objective_Add( obj_id, "empty", ( 0, 0, 0 ), iString( m_str_type ) );
}
}
// hide the actual objective, since each player now has their own
obj_id = m_a_game_obj[0];
Objective_SetInvisibleToAll( obj_id );
}
function complete( a_target_or_list )
{
// kill the threads
level notify( "breadcrumb_" + m_str_type + "_complete" );
// destroy the objectives
for ( i = 0; i < 4; i++ )
{
obj_id = m_a_player_game_obj[i];
Objective_State( obj_id, "done" );
}
foreach( player in level.players )
{
player.v_current_active_breadcrumb = undefined;
}
// call the parent version
cObjective::complete( a_target_or_list );
}
function hide( e_player )
{
if ( isdefined( e_player ) )
{
Assert( IsPlayer( e_player ), "Passed a non-player entity into cBreadcrumbObjective::hide()" );
entnum = e_player GetEntityNumber();
obj_id = m_a_player_game_obj[entnum];
Objective_SetInvisibleToPlayer( obj_id, e_player );
}
else
{
for ( i = 0; i < 4; i++ )
{
obj_id = m_a_player_game_obj[i];
Objective_SetInvisibleToPlayerByIndex( obj_id, i );
}
}
}
function show( e_player )
{
if ( isdefined( e_player ) )
{
Assert( IsPlayer( e_player ), "Passed a non-player entity into cBreadcrumbObjective::hide()" );
entnum = e_player GetEntityNumber();
obj_id = m_a_player_game_obj[entnum];
Objective_SetVisibleToPlayer( obj_id, e_player );
}
else
{
for ( i = 0; i < 4; i++ )
{
obj_id = m_a_player_game_obj[i];
Objective_SetVisibleToPlayerByIndex( obj_id, i );
}
}
}
function start( str_trig_targetname )
{
// remember the name of the first trigger
m_str_first_trig_targetname = str_trig_targetname;
m_done = false;
// create individual objectives for each player
foreach( player in level.players )
{
add_player( player );
}
}
function add_player( player )
{
// activate the objective
entnum = player GetEntityNumber();
obj_id = m_a_player_game_obj[entnum];
Objective_SetInvisibleToAll( obj_id );
Objective_SetVisibleToPlayer( obj_id, player );
Objective_State( obj_id, "active" );
// spawn a thread to track the player's movement along the breadcrumb trail
thread do_player_breadcrumb( player );
}
function private set_player_objective( player, target )
{
entnum = player GetEntityNumber();
obj_id = m_a_player_game_obj[entnum];
n_breadcrumb_height = 72;
v_pos = target;
if ( !IsVec( target ) )
{
v_pos = target.origin;
if ( isdefined( target.script_height ) )
{
n_breadcrumb_height = target.script_height;
}
}
v_pos = util::ground_position( v_pos, 300, n_breadcrumb_height );
// cache off the breadcrumb for other systems look up
player.v_current_active_breadcrumb = v_pos;
Objective_Position( obj_id, v_pos );
Objective_State( obj_id, "active" );
}
function do_player_breadcrumb( player )
{
level endon( "breadcrumb_" + m_str_type );
level endon( "breadcrumb_" + m_str_type + "_complete" );
player endon( "death" );
str_trig_targetname = m_str_first_trig_targetname;
entnum = player GetEntityNumber();
obj_id = m_a_player_game_obj[entnum];
Objective_SetVisibleToPlayer( obj_id, player );
do
{
t_current = GetEnt( str_trig_targetname, "targetname" );
if ( isdefined( t_current ) )
{
if ( isdefined( t_current.target ) )
{
if ( isdefined( t_current.script_flag_true ) )
{
Objective_SetInvisibleToPlayer( obj_id, player );
level flag::wait_till( t_current.script_flag_true );
Objective_SetVisibleToPlayer( obj_id, player );
}
s_current = struct::get( t_current.target, "targetname" );
if ( isdefined( s_current ) )
{
set_player_objective( player, s_current );
}
else
{
set_player_objective( player, t_current );
}
}
else
{
set_player_objective( player, t_current );
}
str_trig_targetname = t_current.target;
//assert( !trigger::is_trigger_once( t_current ), "Trigger at " + t_current.origin + " in breadcrumb chain '" + m_str_first_trig_targetname + "' should not be set to TRIGGER_ONCE." );
t_current trigger::wait_till( undefined, undefined, player );
}
else
{
str_trig_targetname = undefined;
}
}
while ( isdefined( str_trig_targetname ) );
Objective_SetInvisibleToPlayer( obj_id, player );
foreach( player in level.players )
{
player.v_current_active_breadcrumb = undefined;
}
// done
m_done = true;
}
function is_breadcrumb()
{
return true;
}
function is_done()
{
// we're done if at least one of the players has made it to the final breadcrumb
return m_done;
}
}
function autoexec __init__sytem__() { system::register("objectives",&__init__,undefined,undefined); }
function __init__()
{
level.a_objectives = []; // Declares an array of objectives to hold the objective strings.
level.n_obj_index = 0; // This is the objective number associated with the objective strings.
callback::on_spawned( &on_player_spawned );
}
/@
"Name: set( <str_obj_type>, <a_target_or_list>, <b_breadcrumb> )"
"Summary: Used for all objective-related tasks."
"CallOn: NA"
"Example: objectives::set( "cp_level_lotus_hakim", level.ai_hakim );"
"SPMP: singleplayer"
@/
function set( str_obj_type, a_target_or_list, b_breadcrumb )
{
if(!isdefined(level.a_objectives))level.a_objectives=[];
if(!isdefined(b_breadcrumb))b_breadcrumb=false;
// Make an array out if it, if it's only one objective.
if ( !isdefined( a_target_or_list ) ) a_target_or_list = []; else if ( !IsArray( a_target_or_list ) ) a_target_or_list = array( a_target_or_list );;
o_objective = undefined;
if ( isdefined( level.a_objectives[str_obj_type] ) )
{
// TODO Slone: Clear old targets here?
o_objective = level.a_objectives[str_obj_type];
if ( isdefined( a_target_or_list ) )
{
foreach( target in a_target_or_list )
{
[[o_objective]]->add_target( target );
}
}
}
else
{
if ( b_breadcrumb )
{
o_objective = new cBreadcrumbObjective();
}
else
{
o_objective = new cObjective();
}
[[o_objective]]->init( str_obj_type, a_target_or_list );
level.a_objectives[ str_obj_type ] = o_objective;
}
return o_objective;
}
// Mark just one of many targets for this objective as complete.
//
// str_obj_type: the objective string ID we'd like to remove.
// a_target_or_list: the object from which we'd like to remove the marker.
//
/@
"Name: complete( <str_obj_type>, <a_target_or_list> )"
"Summary: Mark specific targets for this objective as complete."
"CallOn: NA"
"MandatoryArg: <str_obj_type> The objective string ID we'd like to remove."
"OptionalArg: <a_target_or_list> The target or targets from which we'd like to remove the marker."
"Example: objectives::complete( "cp_level_lotus_minigun", self );"
"SPMP: singleplayer"
@/
function complete( str_obj_type, a_target_or_list )
{
// Make an array out if it, if it's only one objective.
if ( !isdefined( a_target_or_list ) ) a_target_or_list = []; else if ( !IsArray( a_target_or_list ) ) a_target_or_list = array( a_target_or_list );;
if ( isdefined( level.a_objectives[str_obj_type] ) )
{
o_objective = level.a_objectives[str_obj_type];
[[o_objective]]->complete( a_target_or_list );
}
else
{
if ( str_obj_type == "cp_waypoint_breadcrumb" )
{
o_objective = new cBreadcrumbObjective();
}
else
{
o_objective = new cObjective();
}
[[o_objective]]->init( str_obj_type, undefined, true );
level.a_objectives[ str_obj_type ] = o_objective;
}
}
/@
"Name: set( <str_obj_id>, <a_target_or_list> )"
"Summary: Set the objective and automatically set the counter to [0 of a_targets.size]"
"CallOn: NA"
"Example: objectives::set( "objective_kill_targets", a_targets );"
"SPMP: singleplayer"
@/
function set_with_counter( str_obj_id, a_targets )
{
if ( !isdefined( a_targets ) ) a_targets = []; else if ( !IsArray( a_targets ) ) a_targets = array( a_targets );;
o_obj = set( str_obj_id, a_targets );
[[o_obj]]->update_counter( 0, a_targets.size );
}
/@
"Name: update_counter( <str_obj_id>, <x_val>, <y_val> )"
"Summary: Set the values on an objective with a counter."
"CallOn: NA"
"Example: objectives::update_counter( "objective_kill_targets", 2, a_targets.size );"
"SPMP: singleplayer"
@/
function update_counter( str_obj_id, x_val, y_val )
{
o_obj = level.a_objectives[ str_obj_id ];
if ( isdefined( o_obj ) )
{
[[o_obj]]->update_counter( x_val, y_val );
}
}
/@
"Name: set_value( <str_obj_id>, <str_menu_data_name>, <value> )"
"Summary: Set the LUI model values on an objective."
"CallOn: NA"
"Example: objectives::set_value( "objective_kill_targets", "num_targets", a_targets.size );"
"SPMP: singleplayer"
@/
function set_value( str_obj_id, str_menu_data_name, value )
{
o_obj = level.a_objectives[ str_obj_id ];
if ( isdefined( o_obj ) )
{
[[o_obj]]->update_value( str_menu_data_name, value );
}
}
/@
"Name: breadcrumb( <str_trig_targetname>, [str_obj_id] )"
"Summary: Automatically advances a chain of triggered objective structs."
"Module: Objectives"
"CallOn: Level"
"MandatoryArg: <str_trig_targetname> The first trigger in the string of breadcrumbs."
"OptionalArg: [str_obj_id] The objective string ID we'd like to hide the marker and menu entry of. Default behavior hides for ALL players."
"Example: objectives::breadcrumb( "trig_breadcrumb" );"
"SPMP: singleplayer"
@/
function breadcrumb( str_trig_targetname, str_obj_id = "cp_waypoint_breadcrumb", b_complete_on_first_player_finish = true )
{
level notify( "breadcrumb_" + str_obj_id );
level endon( "breadcrumb_" + str_obj_id );
if ( isdefined( level.a_objectives[ str_obj_id ] ) )
{
complete( str_obj_id );
}
o_objective = set( str_obj_id, undefined, true );
[[o_objective]]->start( str_trig_targetname );
while ( ![[o_objective]]->is_done() )
{
wait 0.05;
}
if ( b_complete_on_first_player_finish )
{
complete( str_obj_id );
}
}
/@
"Name: hide( <str_obj_type>, <e_player> )"
"Summary: Hide the objective marker and menu entry for an objective."
"CallOn: NA"
"MandatoryArg: <str_obj_type> The objective string ID we'd like to hide the marker and menu entry of. Default behavior hides for ALL players"
"OptionalArg: <e_player> The specific player you would like to hide the objective waypoint from."
"Example: objectives::hide( "objective_to_do_things", e_player_2 );"
"SPMP: singleplayer"
@/
function hide( str_obj_type, e_player )
{
if ( isdefined( level.a_objectives[str_obj_type] ) )
{
o_objective = level.a_objectives[str_obj_type];
[[o_objective]]->hide( e_player );
}
else
{
Assert( false, "Attempting to hide a marker on an objective that does not exist." );
}
}
/@
"Name: hide_for_target( <str_obj_type>, <e_target> )"
"Summary: Hide the objective marker for a specific target."
"CallOn: NA"
"MandatoryArg: <str_obj_type> The objective string ID we'd like to hide the marker."
"MandatoryArg: <e_target> The target for which you want to hide the objective marker."
"Example: objectives::hide_for_target( "tank_marker", vh_tank_3 );"
"SPMP: singleplayer"
@/
function hide_for_target( str_obj_type, e_target )
{
if ( isdefined( level.a_objectives[str_obj_type] ) )
{
o_objective = level.a_objectives[str_obj_type];
[[o_objective]]->hide_for_target( e_target );
}
else
{
Assert( false, "Attempting to hide a marker on an objective that does not exist." );
}
}
/@
"Name: show( <str_obj_type>, <e_player> )"
"Summary: Show the objective marker and menu entry for an objective."
"CallOn: NA"
"MandatoryArg: <str_obj_type> The objective string ID we'd like to show the marker and menu entry of. Default behavior shows for ALL players"
"OptionalArg: <e_player> The specific player you would like to show the objective waypoint to."
"Example: objectives::show( "objective_to_do_things", e_player_2 );"
"SPMP: singleplayer"
@/
function show( str_obj_type, e_player )
{
if ( isdefined( level.a_objectives[str_obj_type] ) )
{
o_objective = level.a_objectives[str_obj_type];
[[o_objective]]->show( e_player );
}
else
{
Assert( false, "Attempting to show a marker on an objective that does not exist." );
}
}
/@
"Name: show_for_target( <str_obj_type>, <e_target> )"
"Summary: Show the objective marker for a specific target."
"CallOn: NA"
"MandatoryArg: <str_obj_type> The objective string ID we'd like to show the marker."
"MandatoryArg: <e_target> The target for which you want to show the objective marker."
"Example: objectives::show_for_target( "tank_marker", vh_tank_3 );"
"SPMP: singleplayer"
@/
function show_for_target( str_obj_type, e_target )
{
if ( isdefined( level.a_objectives[str_obj_type] ) )
{
o_objective = level.a_objectives[str_obj_type];
[[o_objective]]->show_for_target( e_target );
}
else
{
Assert( false, "Attempting to hide a marker on an objective that does not exist." );
}
}
/@
"Name: get_id_for_target( <str_obj_type>, <e_target> )"
"Summary: Get objective ID for a specific target."
"CallOn: NA"
"MandatoryArg: <str_obj_type> The objective string ID."
"MandatoryArg: <e_target> The target for which you want the ID."
"Example: id = objectives::get_id_for_target( "tank_marker", vh_tank_3 );"
"SPMP: singleplayer"
@/
function get_id_for_target( str_obj_type, e_target )
{
id = -1;
if ( isdefined( level.a_objectives[str_obj_type] ) )
{
o_objective = level.a_objectives[str_obj_type];
id = [[o_objective]]->get_id_for_target( e_target );
}
if ( id < 0 )
{
Assert( false, "get_id_for_target called for an objective that does not exist." );
}
return id;
}
/@
"Name: event_message( <istr_message> )"
"Summary: Show an event message on the HUD."
"MandatoryArg: <istr_message> The localized string to show."
"Example: objectives::event_message( "Kill the quadtank" );"
@/
function event_message( istr_message )
{
foreach ( player in level.players )
{
util::show_event_message( player, istring( istr_message ) );
}
}
//str_obj_type - the icon type ( target, capture, defend, defend_b, etc )
//str_obj_name - a unique identifier for this icon. Creating a new icon with a name already in use, will destroy/override the previous icon
//v_pos - the position the icon will be placed
function create_temp_icon( str_obj_type, str_obj_name, v_pos, v_offset=(0,0,0) )
{
switch( str_obj_type )
{
case "target":
str_shader = "waypoint_targetneutral";
break;
case "capture":
str_shader = "waypoint_capture";
break;
case "capture_a":
str_shader = "waypoint_capture_a";
break;
case "capture_b":
str_shader = "waypoint_capture_b";
break;
case "capture_c":
str_shader = "waypoint_capture_c";
break;
case "defend":
str_shader = "waypoint_defend";
break;
case "defend_a":
str_shader = "waypoint_defend_a";
break;
case "defend_b":
str_shader = "waypoint_defend_b";
break;
case "defend_c":
str_shader = "waypoint_defend_c";
break;
case "return":
str_shader = "waypoint_return";
break;
default:
AssertMsg( "Type '" + str_obj_type + "' not supported. Please see create_temp_icon() in _objectives.gsc for supported types." );
break;
}
nextObjPoint = objpoints::create( str_obj_name, v_pos + v_offset, "all", str_shader );
nextObjPoint setWayPoint( true, str_shader );
return nextObjPoint;
}
function destroy_temp_icon()
{
objpoints::delete( self );
}
function private on_player_spawned() // self = player
{
// find any active breadcrumbs, and initialize them on this player
if ( isdefined( level.a_objectives ) )
{
foreach( o_objective in level.a_objectives )
{
if ( [[o_objective]]->is_breadcrumb() && ![[o_objective]]->is_done() )
{
[[o_objective]]->add_player( self );
}
}
}
}