mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 22:56:30 -10:00
* Automatically generate the isValid() Lua API function for handle types If an ADE object's stored type has an isValid() member, ade_obj now registers an isValid function for it automatically. An explicit ADE_FUNC(isValid, ...) declared after the ADE_OBJ replaces the generated one, so types with special semantics (e.g. sound) keep their own. The 67 hand-written functions that merely delegated to the member are removed, and 26 types that previously exposed no isValid() gain one. Co-Authored-By: Claude Fable 5.1 <[email protected]> * Add ADE_OBJ_VALIDATOR for index-based Lua handles Int-index handles (shipclass, weaponclass, team, etc.) can now declare their validity predicate with a single ADE_OBJ_VALIDATOR or ADE_OBJ_VALIDATOR_RANGE line after the ADE_OBJ. The registered validator drives the generated isValid() function and is also honored by ade_set_args when the mod table asks for nil instead of invalid objects, which previously never applied to these handles. The 19 hand-written range-check functions are replaced, and loadout_ship, default_primary, default_secondary, and support_rearm_pool_team gain isValid(). Add a validator to HudGauge while we're at it. Also fully qualify the scripting namespace in LuaConvert.h so its templates can be instantiated from the unit tests. Co-Authored-By: Claude Fable 5.1 <[email protected]> --------- Co-authored-by: Claude Fable 5.1 <[email protected]>
602 lines
19 KiB
C++
602 lines
19 KiB
C++
//
|
|
//
|
|
|
|
#include "order.h"
|
|
#include "enums.h"
|
|
#include "object.h"
|
|
#include "subsystem.h"
|
|
#include "waypoint.h"
|
|
|
|
#include "ai/aigoals.h"
|
|
#include "weapon/weapon.h"
|
|
#include "playerman/player.h"
|
|
|
|
namespace scripting {
|
|
namespace api {
|
|
|
|
|
|
order_h::order_h()
|
|
: objh(), odx(-1), sig(-1), aigp(nullptr)
|
|
{}
|
|
order_h::order_h(object* objp, int n_odx)
|
|
{
|
|
objh = object_h(objp);
|
|
if(objh.isValid() && objh.objp()->type == OBJ_SHIP && n_odx > -1 && n_odx < MAX_AI_GOALS)
|
|
{
|
|
odx = n_odx;
|
|
sig = Ai_info[Ships[objh.objp()->instance].ai_index].goals[odx].signature;
|
|
aigp = &Ai_info[Ships[objh.objp()->instance].ai_index].goals[odx];
|
|
}
|
|
else
|
|
{
|
|
odx = -1;
|
|
sig = -1;
|
|
aigp = NULL;
|
|
}
|
|
}
|
|
bool order_h::isValid() const
|
|
{
|
|
if (!objh.isValid() || aigp == NULL)
|
|
return false;
|
|
|
|
return objh.objp()->type == OBJ_SHIP && odx > -1 && odx < MAX_AI_GOALS && sig == Ai_info[Ships[objh.objp()->instance].ai_index].goals[odx].signature;
|
|
}
|
|
|
|
//**********HANDLE: order
|
|
ADE_OBJ(l_Order, order_h, "order", "order handle");
|
|
|
|
ADE_VIRTVAR(Priority, l_Order, "number", "Priority of the given order", "number", "Order priority or 0 if invalid")
|
|
{
|
|
order_h *ohp = NULL;
|
|
int priority = 1;
|
|
|
|
if(!ade_get_args(L, "o|i", l_Order.GetPtr(&ohp), &priority))
|
|
return ade_set_error(L, "i", 0);
|
|
|
|
if(!ohp->isValid())
|
|
return ade_set_error(L, "i", 0);
|
|
|
|
if(ADE_SETTING_VAR) {
|
|
ohp->aigp->priority = priority;
|
|
}
|
|
|
|
return ade_set_args(L, "i", ohp->aigp->priority);
|
|
}
|
|
|
|
ADE_FUNC(remove, l_Order, NULL, "Removes the given order from the ship's priority queue.", "boolean", "True if order was successfully removed, otherwise false or nil.")
|
|
{
|
|
order_h *ohp = NULL;
|
|
if(!ade_get_args(L, "o", l_Order.GetPtr(&ohp)))
|
|
return ADE_RETURN_NIL;
|
|
|
|
if(!ohp->isValid())
|
|
return ADE_RETURN_FALSE;
|
|
|
|
ai_info *aip = &Ai_info[Ships[ohp->objh.objp()->instance].ai_index];
|
|
|
|
ai_remove_ship_goal(aip, ohp->odx);
|
|
|
|
return ADE_RETURN_TRUE;
|
|
}
|
|
|
|
ADE_FUNC(getType, l_Order, NULL, "Gets the type of the order.", "enumeration", "The type of the order as one of the ORDER_* enumerations.")
|
|
{
|
|
order_h *ohp = NULL;
|
|
lua_enum eh_idx = ENUM_INVALID;
|
|
if(!ade_get_args(L, "o", l_Order.GetPtr(&ohp)))
|
|
return ade_set_error(L, "o", l_Enum.Set(enum_h()));
|
|
|
|
if(!ohp->isValid())
|
|
return ade_set_error(L, "o", l_Enum.Set(enum_h()));
|
|
|
|
switch(ohp->aigp->ai_mode){
|
|
case AI_GOAL_NONE:
|
|
case AI_GOAL_SCHROEDINGER:
|
|
case AI_GOAL_NUM_VALUES:
|
|
break;
|
|
case AI_GOAL_DESTROY_SUBSYSTEM:
|
|
case AI_GOAL_CHASE_WEAPON:
|
|
case AI_GOAL_CHASE:
|
|
eh_idx = LE_ORDER_ATTACK;
|
|
break;
|
|
case AI_GOAL_DOCK:
|
|
eh_idx = LE_ORDER_DOCK;
|
|
break;
|
|
case AI_GOAL_WAYPOINTS:
|
|
eh_idx = LE_ORDER_WAYPOINTS;
|
|
break;
|
|
case AI_GOAL_WAYPOINTS_ONCE:
|
|
eh_idx = LE_ORDER_WAYPOINTS_ONCE;
|
|
break;
|
|
case AI_GOAL_WARP:
|
|
eh_idx = LE_ORDER_DEPART;
|
|
break;
|
|
case AI_GOAL_FORM_ON_WING:
|
|
eh_idx = LE_ORDER_FORM_ON_WING;
|
|
break;
|
|
case AI_GOAL_UNDOCK:
|
|
eh_idx = LE_ORDER_UNDOCK;
|
|
break;
|
|
case AI_GOAL_GUARD:
|
|
eh_idx = LE_ORDER_GUARD;
|
|
break;
|
|
case AI_GOAL_DISABLE_SHIP:
|
|
eh_idx = LE_ORDER_DISABLE;
|
|
break;
|
|
case AI_GOAL_DISABLE_SHIP_TACTICAL:
|
|
eh_idx = LE_ORDER_DISABLE_TACTICAL;
|
|
break;
|
|
case AI_GOAL_DISARM_SHIP:
|
|
eh_idx = LE_ORDER_DISARM;
|
|
break;
|
|
case AI_GOAL_DISARM_SHIP_TACTICAL:
|
|
eh_idx = LE_ORDER_DISARM_TACTICAL;
|
|
break;
|
|
case AI_GOAL_CHASE_ANY:
|
|
eh_idx = LE_ORDER_ATTACK_ANY;
|
|
break;
|
|
case AI_GOAL_IGNORE:
|
|
eh_idx = LE_ORDER_IGNORE;
|
|
break;
|
|
case AI_GOAL_IGNORE_NEW:
|
|
eh_idx = LE_ORDER_IGNORE_NEW;
|
|
break;
|
|
case AI_GOAL_EVADE_SHIP:
|
|
eh_idx = LE_ORDER_EVADE;
|
|
break;
|
|
case AI_GOAL_STAY_NEAR_SHIP:
|
|
eh_idx = LE_ORDER_STAY_NEAR;
|
|
break;
|
|
case AI_GOAL_KEEP_SAFE_DISTANCE:
|
|
eh_idx = LE_ORDER_KEEP_SAFE_DISTANCE;
|
|
break;
|
|
case AI_GOAL_REARM_REPAIR:
|
|
eh_idx = LE_ORDER_REARM;
|
|
break;
|
|
case AI_GOAL_STAY_STILL:
|
|
eh_idx = LE_ORDER_STAY_STILL;
|
|
break;
|
|
case AI_GOAL_PLAY_DEAD:
|
|
eh_idx = LE_ORDER_PLAY_DEAD;
|
|
break;
|
|
case AI_GOAL_PLAY_DEAD_PERSISTENT:
|
|
eh_idx = LE_ORDER_PLAY_DEAD_PERSISTENT;
|
|
break;
|
|
case AI_GOAL_FLY_TO_SHIP:
|
|
eh_idx = LE_ORDER_FLY_TO;
|
|
break;
|
|
case AI_GOAL_CHASE_WING:
|
|
eh_idx = LE_ORDER_ATTACK_WING;
|
|
break;
|
|
case AI_GOAL_GUARD_WING:
|
|
eh_idx = LE_ORDER_GUARD_WING;
|
|
break;
|
|
case AI_GOAL_CHASE_SHIP_CLASS:
|
|
eh_idx = LE_ORDER_ATTACK_SHIP_CLASS;
|
|
break;
|
|
case AI_GOAL_CHASE_SHIP_TYPE:
|
|
eh_idx = LE_ORDER_ATTACK_SHIP_TYPE;
|
|
break;
|
|
case AI_GOAL_LUA:
|
|
eh_idx = LE_ORDER_LUA;
|
|
break;
|
|
}
|
|
|
|
return ade_set_args(L, "o", l_Enum.Set(enum_h(eh_idx)));
|
|
}
|
|
|
|
// helper function
|
|
void maybe_start_waypoints(order_h* ohp, bool force)
|
|
{
|
|
Assertion(ohp->isValid(), "The order should be valid at this point!");
|
|
Assertion(ohp->aigp->wp_list_index >= 0, "This function requires wp_list to be assigned already!");
|
|
Assertion(ohp->aigp->target_name && !stricmp(Waypoint_lists[ohp->aigp->wp_list_index].get_name(), ohp->aigp->target_name), "wp_list name does not match target_name!");
|
|
|
|
if (ohp->odx == 0)
|
|
{
|
|
int flags = 0;
|
|
if (ohp->aigp->ai_mode == AI_GOAL_WAYPOINTS)
|
|
flags |= WPF_REPEAT;
|
|
if (ohp->aigp->flags[AI::Goal_Flags::Waypoints_in_reverse])
|
|
flags |= WPF_BACKTRACK;
|
|
ai_start_waypoints(ohp->objh.objp(), ohp->aigp->wp_list_index, flags, ohp->aigp->int_data, force);
|
|
}
|
|
}
|
|
|
|
ADE_VIRTVAR(Target, l_Order, "object", "Target of the order. Value may also be a deriviative of the 'object' class, such as 'ship'.", "object", "Target object or invalid object handle if order handle is invalid or order requires no target.")
|
|
{
|
|
order_h *ohp = NULL;
|
|
object_h *newh = NULL;
|
|
int shipnum = -1, wingnum = -1, objnum = -1;
|
|
if(!ade_get_args(L, "o|o", l_Order.GetPtr(&ohp), l_Object.GetPtr(&newh)))
|
|
return ade_set_error(L, "o", l_Object.Set(object_h()));
|
|
|
|
if(!ohp->isValid())
|
|
return ade_set_error(L, "o", l_Object.Set(object_h()));
|
|
|
|
auto aip = &Ai_info[Ships[ohp->objh.objp()->instance].ai_index];
|
|
|
|
if(ADE_SETTING_VAR){
|
|
if(newh && newh->isValid()){
|
|
switch(ohp->aigp->ai_mode){
|
|
case AI_GOAL_DESTROY_SUBSYSTEM:
|
|
case AI_GOAL_CHASE:
|
|
case AI_GOAL_FORM_ON_WING:
|
|
case AI_GOAL_GUARD:
|
|
case AI_GOAL_DISABLE_SHIP:
|
|
case AI_GOAL_DISABLE_SHIP_TACTICAL:
|
|
case AI_GOAL_DISARM_SHIP:
|
|
case AI_GOAL_DISARM_SHIP_TACTICAL:
|
|
case AI_GOAL_IGNORE:
|
|
case AI_GOAL_IGNORE_NEW:
|
|
case AI_GOAL_EVADE_SHIP:
|
|
case AI_GOAL_STAY_NEAR_SHIP:
|
|
case AI_GOAL_FLY_TO_SHIP:
|
|
case AI_GOAL_STAY_STILL:
|
|
if ((newh->objp()->type == OBJ_SHIP) && stricmp(Ships[newh->objp()->instance].ship_name, ohp->aigp->target_name)) {
|
|
ohp->aigp->target_name = ai_get_goal_target_name(Ships[newh->objp()->instance].ship_name, &ohp->aigp->target_name_index);
|
|
ohp->aigp->time = (ohp->odx == 0) ? Missiontime : 0;
|
|
|
|
if(ohp->odx == 0) {
|
|
aip->ok_to_target_timestamp = timestamp(0);
|
|
set_target_objnum(aip, newh->objnum);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AI_GOAL_REARM_REPAIR:
|
|
case AI_GOAL_DOCK:
|
|
case AI_GOAL_UNDOCK:
|
|
// TODO: these are currently not implemented, but could be implemented in the future
|
|
break;
|
|
|
|
case AI_GOAL_CHASE_WEAPON:
|
|
if ((newh->objp()->type == OBJ_WEAPON) && (ohp->aigp->target_signature != newh->sig)){
|
|
ohp->aigp->target_instance = newh->objp()->instance;
|
|
ohp->aigp->target_signature = Weapons[newh->objp()->instance].objnum;
|
|
ohp->aigp->time = (ohp->odx == 0) ? Missiontime : 0;
|
|
|
|
if(ohp->odx == 0) {
|
|
aip->ok_to_target_timestamp = timestamp(0);
|
|
set_target_objnum(aip, newh->objnum);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AI_GOAL_CHASE_SHIP_CLASS:
|
|
// we can set the ship class from the class of the target
|
|
if (newh->objp()->type == OBJ_SHIP) {
|
|
auto class_name = Ship_info[Ships[newh->objp()->instance].ship_info_index].name;
|
|
if (stricmp(class_name, ohp->aigp->target_name)) {
|
|
ohp->aigp->target_name = ai_get_goal_target_name(class_name, &ohp->aigp->target_name_index);
|
|
ohp->aigp->time = (ohp->odx == 0) ? Missiontime : 0;
|
|
|
|
if (ohp->odx == 0) {
|
|
aip->ok_to_target_timestamp = timestamp(0);
|
|
set_target_objnum(aip, newh->objnum);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AI_GOAL_CHASE_SHIP_TYPE:
|
|
{
|
|
if (newh->objp()->type == OBJ_SHIP) {
|
|
int info_idx = Ships[newh->objp()->instance].ship_info_index;
|
|
int type_index = Ship_info[info_idx].class_type;
|
|
|
|
const char* type_name;
|
|
|
|
if (type_index < 0) {
|
|
type_name = "<none>";
|
|
} else {
|
|
type_name = Ship_types[type_index].name;
|
|
}
|
|
|
|
if (stricmp(type_name, ohp->aigp->target_name) != 0) {
|
|
ohp->aigp->target_name = ai_get_goal_target_name(type_name, &ohp->aigp->target_name_index);
|
|
ohp->aigp->time = (ohp->odx == 0) ? Missiontime : 0;
|
|
|
|
if (ohp->odx == 0) {
|
|
aip->ok_to_target_timestamp = timestamp(0);
|
|
set_target_objnum(aip, newh->objnum);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AI_GOAL_WAYPOINTS:
|
|
case AI_GOAL_WAYPOINTS_ONCE:
|
|
if (newh->objp()->type == OBJ_WAYPOINT) {
|
|
int wpl, idx;
|
|
calc_waypoint_indexes(newh->objp()->instance, wpl, idx);
|
|
if (stricmp(Waypoint_lists[wpl].get_name(), ohp->aigp->target_name) || idx != ohp->aigp->int_data) {
|
|
ohp->aigp->target_name = ai_get_goal_target_name(Waypoint_lists[wpl].get_name(), &ohp->aigp->target_name_index);
|
|
ohp->aigp->wp_list_index = wpl;
|
|
ohp->aigp->int_data = idx;
|
|
ohp->aigp->time = (ohp->odx == 0) ? Missiontime : 0;
|
|
maybe_start_waypoints(ohp, true);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AI_GOAL_CHASE_WING:
|
|
case AI_GOAL_GUARD_WING:
|
|
if ((newh->objp()->type == OBJ_SHIP) && stricmp(Ships[newh->objp()->instance].ship_name, ohp->aigp->target_name)) {
|
|
ship *shipp = &Ships[newh->objp()->instance];
|
|
if (shipp->wingnum != -1){
|
|
ohp->aigp->target_name = ai_get_goal_target_name(Wings[shipp->wingnum].name, &ohp->aigp->target_name_index);
|
|
ohp->aigp->time = (ohp->odx == 0) ? Missiontime : 0;
|
|
|
|
if(ohp->odx == 0) {
|
|
aip->ok_to_target_timestamp = timestamp(0);
|
|
|
|
if (ohp->aigp->ai_mode == AI_GOAL_CHASE_WING)
|
|
ai_attack_wing(ohp->objh.objp(),shipp->wingnum);
|
|
else // AI_GOAL_GUARD_WING
|
|
ai_set_guard_wing(ohp->objh.objp(), shipp->wingnum);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch(ohp->aigp->ai_mode){
|
|
case AI_GOAL_DESTROY_SUBSYSTEM:
|
|
case AI_GOAL_CHASE:
|
|
case AI_GOAL_FORM_ON_WING:
|
|
case AI_GOAL_GUARD:
|
|
case AI_GOAL_DISABLE_SHIP:
|
|
case AI_GOAL_DISABLE_SHIP_TACTICAL:
|
|
case AI_GOAL_DISARM_SHIP:
|
|
case AI_GOAL_DISARM_SHIP_TACTICAL:
|
|
case AI_GOAL_IGNORE:
|
|
case AI_GOAL_IGNORE_NEW:
|
|
case AI_GOAL_EVADE_SHIP:
|
|
case AI_GOAL_STAY_NEAR_SHIP:
|
|
case AI_GOAL_FLY_TO_SHIP:
|
|
case AI_GOAL_STAY_STILL:
|
|
case AI_GOAL_REARM_REPAIR:
|
|
case AI_GOAL_DOCK:
|
|
case AI_GOAL_UNDOCK: {
|
|
auto target_entry = ship_registry_get(ohp->aigp->target_name);
|
|
if (target_entry && target_entry->has_shipp()) {
|
|
shipnum = target_entry->shipnum;
|
|
objnum = target_entry->objnum;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case AI_GOAL_CHASE_WEAPON:
|
|
objnum = Weapons[ohp->aigp->target_instance].objnum;
|
|
break;
|
|
|
|
case AI_GOAL_CHASE_SHIP_CLASS:
|
|
case AI_GOAL_CHASE_SHIP_TYPE:
|
|
// a ship class/type isn't an in-mission object
|
|
return ade_set_args(L, "o", l_Object.Set(object_h()));
|
|
|
|
case AI_GOAL_WAYPOINTS:
|
|
case AI_GOAL_WAYPOINTS_ONCE:
|
|
// check if waypoint order is the current goal (ohp->odx == 0) and if it is valid
|
|
waypoint* wp;
|
|
waypoint_list* wpl;
|
|
if ( (ohp->odx == 0) && (aip->wp_index != INVALID_WAYPOINT_POSITION) && ((wpl = find_waypoint_list_at_index(aip->wp_list_index)) != nullptr) && ((wp = find_waypoint_at_index(wpl, aip->wp_index)) != nullptr) ) {
|
|
objnum = wp->get_objnum();
|
|
} else {
|
|
wpl = find_matching_waypoint_list(ohp->aigp->target_name);
|
|
if (wpl != nullptr) {
|
|
objnum = wpl->get_waypoints()[ohp->aigp->int_data].get_objnum();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AI_GOAL_CHASE_WING:
|
|
case AI_GOAL_GUARD_WING:
|
|
wingnum = wing_name_lookup(ohp->aigp->target_name);
|
|
if (Wings[wingnum].current_count > 0){
|
|
shipnum = Wings[wingnum].ship_index[0];
|
|
objnum = Ships[shipnum].objnum;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ade_set_object_with_breed(L, objnum);
|
|
}
|
|
|
|
ADE_VIRTVAR(TargetSubsystem, l_Order, "subsystem", "Target subsystem of the order.", "subsystem", "Target subsystem, or invalid subsystem handle if order handle is invalid or order requires no subsystem target.")
|
|
{
|
|
order_h *ohp = NULL;
|
|
ship_subsys_h *newh = NULL;
|
|
ai_info *aip = NULL;
|
|
if(!ade_get_args(L, "o|o", l_Order.GetPtr(&ohp), l_Subsystem.GetPtr(&newh)))
|
|
return ade_set_error(L, "o", l_Subsystem.Set(ship_subsys_h()));
|
|
|
|
if(!ohp->isValid())
|
|
return ade_set_error(L, "o", l_Subsystem.Set(ship_subsys_h()));
|
|
|
|
aip = &Ai_info[Ships[ohp->objh.objp()->instance].ai_index];
|
|
|
|
if(ADE_SETTING_VAR)
|
|
{
|
|
if(newh && newh->isValid() && (ohp->aigp->ai_mode == AI_GOAL_DESTROY_SUBSYSTEM))
|
|
{
|
|
auto ss_objp = &Objects[newh->ss->parent_objnum];
|
|
auto ss_shipp = &Ships[ss_objp->instance];
|
|
if (stricmp(ss_shipp->ship_name, ohp->aigp->target_name)) {
|
|
ohp->aigp->target_name = ai_get_goal_target_name(ss_shipp->ship_name, &ohp->aigp->target_name_index);
|
|
ohp->aigp->time = (ohp->odx == 0) ? Missiontime : 0;
|
|
if(ohp->odx == 0) {
|
|
aip->ok_to_target_timestamp = timestamp(0);
|
|
set_target_objnum(aip, OBJ_INDEX(ss_objp));
|
|
}
|
|
}
|
|
ohp->aigp->ai_submode = ship_find_subsys( ss_shipp, newh->ss->system_info->subobj_name );
|
|
if(ohp->odx == 0) {
|
|
set_targeted_subsys(aip, newh->ss, OBJ_INDEX(ss_objp));
|
|
}
|
|
if (aip == Player_ai) {
|
|
ss_shipp->last_targeted_subobject[Player_num] = newh->ss;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(ohp->aigp->ai_mode == AI_GOAL_DESTROY_SUBSYSTEM){
|
|
auto target_entry = ship_registry_get(ohp->aigp->target_name);
|
|
if (target_entry && target_entry->has_shipp() && ohp->aigp->ai_submode >= 0 && ohp->aigp->ai_submode < ship_get_num_subsys(target_entry->shipp())) {
|
|
return ade_set_args(L, "o", l_Subsystem.Set(ship_subsys_h(target_entry->objp(), ship_get_indexed_subsys(target_entry->shipp(), ohp->aigp->ai_submode))));
|
|
}
|
|
}
|
|
return ade_set_error(L, "o", l_Subsystem.Set(ship_subsys_h()));
|
|
}
|
|
|
|
ADE_VIRTVAR(WaypointList, l_Order, "waypointlist", "Waypoint list of the order.", "waypointlist", "Waypoint list, or invalid handle if order handle is invalid or if this is not a waypoints order.")
|
|
{
|
|
order_h* ohp = nullptr;
|
|
waypointlist_h *wplh = nullptr;
|
|
if (!ade_get_args(L, "o|o", l_Order.GetPtr(&ohp), l_WaypointList.GetPtr(&wplh)))
|
|
return ADE_RETURN_NIL;
|
|
|
|
if (!ohp->isValid() || (ohp->aigp->ai_mode != AI_GOAL_WAYPOINTS && ohp->aigp->ai_mode != AI_GOAL_WAYPOINTS_ONCE))
|
|
return ADE_RETURN_NIL;
|
|
|
|
if (ADE_SETTING_VAR && wplh->isValid())
|
|
{
|
|
ohp->aigp->target_name = ai_get_goal_target_name(wplh->getList()->get_name(), &ohp->aigp->target_name_index);
|
|
ohp->aigp->wp_list_index = wplh->wl_index;
|
|
ohp->aigp->time = (ohp->odx == 0) ? Missiontime : 0;
|
|
|
|
if (!SCP_vector_inbounds(Waypoint_lists[ohp->aigp->wp_list_index].get_waypoints(), ohp->aigp->int_data))
|
|
{
|
|
if (ohp->aigp->flags[AI::Goal_Flags::Waypoints_in_reverse])
|
|
ohp->aigp->int_data = static_cast<int>(Waypoint_lists[ohp->aigp->wp_list_index].get_waypoints().size() - 1);
|
|
else
|
|
ohp->aigp->int_data = 0;
|
|
}
|
|
|
|
maybe_start_waypoints(ohp, true);
|
|
}
|
|
|
|
return ade_set_args(L, "o", l_WaypointList.Set(waypointlist_h(ohp->aigp->wp_list_index)));
|
|
}
|
|
|
|
ADE_VIRTVAR(WaypointIndex, l_Order, "number", "Waypoint index of the order.", "number", "Waypoint index, or invalid 0 if order handle is invalid or if this is not a waypoints order.")
|
|
{
|
|
order_h* ohp = nullptr;
|
|
int index = 0;
|
|
if (!ade_get_args(L, "o|i", l_Order.GetPtr(&ohp), &index))
|
|
return ade_set_error(L, "i", 0);
|
|
|
|
if (!ohp->isValid() || (ohp->aigp->ai_mode != AI_GOAL_WAYPOINTS && ohp->aigp->ai_mode != AI_GOAL_WAYPOINTS_ONCE))
|
|
return ade_set_error(L, "i", 0);
|
|
|
|
if (ADE_SETTING_VAR)
|
|
{
|
|
index--; // Lua->FS2
|
|
if (SCP_vector_inbounds(Waypoint_lists[ohp->aigp->wp_list_index].get_waypoints(), index))
|
|
{
|
|
ohp->aigp->int_data = index;
|
|
ohp->aigp->time = (ohp->odx == 0) ? Missiontime : 0;
|
|
maybe_start_waypoints(ohp, true);
|
|
}
|
|
}
|
|
|
|
return ade_set_args(L, "i", ohp->aigp->int_data + 1); // FS2->Lua
|
|
}
|
|
|
|
ADE_VIRTVAR(WaypointsInReverse, l_Order, "boolean", "Waypoint-reverse flag of the order.", "boolean", "Waypoint-reverse flag, or invalid false if order handle is invalid or if this is not a waypoints order.")
|
|
{
|
|
order_h* ohp = nullptr;
|
|
bool reverse = false;
|
|
if (!ade_get_args(L, "o|b", l_Order.GetPtr(&ohp), &reverse))
|
|
return ade_set_error(L, "b", false);
|
|
|
|
if (!ohp->isValid() || (ohp->aigp->ai_mode != AI_GOAL_WAYPOINTS && ohp->aigp->ai_mode != AI_GOAL_WAYPOINTS_ONCE))
|
|
return ade_set_error(L, "b", false);
|
|
|
|
if (ADE_SETTING_VAR)
|
|
{
|
|
ohp->aigp->flags.set(AI::Goal_Flags::Waypoints_in_reverse, reverse);
|
|
ohp->aigp->time = (ohp->odx == 0) ? Missiontime : 0;
|
|
maybe_start_waypoints(ohp, true);
|
|
}
|
|
|
|
return ade_set_args(L, "b", ohp->aigp->flags[AI::Goal_Flags::Waypoints_in_reverse]);
|
|
}
|
|
|
|
int goal_flag_helper(lua_State* L, AI::Goal_Flags flag)
|
|
{
|
|
order_h* ohp = nullptr;
|
|
bool set_it = false;
|
|
if (!ade_get_args(L, "o|b", l_Order.GetPtr(&ohp), &set_it))
|
|
return ade_set_error(L, "b", false);
|
|
|
|
if (!ohp->isValid())
|
|
return ade_set_error(L, "b", false);
|
|
|
|
if (ADE_SETTING_VAR)
|
|
ohp->aigp->flags.set(flag, set_it);
|
|
|
|
return ade_set_args(L, "b", ohp->aigp->flags[flag]);
|
|
}
|
|
|
|
ADE_VIRTVAR(OverridesWhenAchievable, l_Order, "boolean", "Whether this goal pre-empts all other goals when it is achievable", "boolean", "OverridesWhenAchievable flag, or invalid false if order handle is invalid")
|
|
{
|
|
return goal_flag_helper(L, AI::Goal_Flags::Want_override);
|
|
}
|
|
|
|
ADE_VIRTVAR(PurgeWhenNewGoalAdded, l_Order, "boolean", "Whether this goal should be purged (removed) when another goal is assigned", "boolean", "PurgeWhenNewGoalAdded flag, or invalid false if order handle is invalid")
|
|
{
|
|
return goal_flag_helper(L, AI::Goal_Flags::Purge_when_new_goal_added);
|
|
}
|
|
|
|
//**********HANDLE: shiporders
|
|
ADE_OBJ(l_ShipOrders, object_h, "shiporders", "Ship orders");
|
|
|
|
ADE_FUNC(__len, l_ShipOrders, NULL, "Number of ship orders", "number", "Number of ship orders, or 0 if handle is invalid")
|
|
{
|
|
object_h *objh = NULL;
|
|
if(!ade_get_args(L, "o", l_ShipOrders.GetPtr(&objh)))
|
|
return ade_set_error(L, "i", 0);
|
|
|
|
if(!objh->isValid() || objh->objp()->type != OBJ_SHIP || Ships[objh->objp()->instance].ai_index < 0)
|
|
return ade_set_error(L, "i", 0);
|
|
|
|
return ade_set_args(L, "i", ai_goal_num(&Ai_info[Ships[objh->objp()->instance].ai_index].goals[0]));
|
|
}
|
|
|
|
ADE_INDEXER(l_ShipOrders, "number Index", "Array of ship orders", "order", "Order, or invalid order handle on failure")
|
|
{
|
|
object_h *objh = NULL;
|
|
int i;
|
|
|
|
if (!ade_get_args(L, "oi", l_ShipOrders.GetPtr(&objh), &i))
|
|
return ade_set_error(L, "o", l_Order.Set(order_h()));
|
|
|
|
i--; //Lua->FS2
|
|
|
|
if (!objh->isValid() || i < 0 || i >= MAX_AI_GOALS)
|
|
return ade_set_error(L, "o", l_Order.Set(order_h()));
|
|
|
|
ai_info *aip = &Ai_info[Ships[objh->objp()->instance].ai_index];
|
|
|
|
if (aip->goals[i].ai_mode != AI_GOAL_NONE)
|
|
return ade_set_args(L, "o", l_Order.Set(order_h(objh->objp(), i)));
|
|
else
|
|
return ade_set_args(L, "o", l_Order.Set(order_h()));
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|