Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 19 additions & 7 deletions code/ai/aicode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,18 @@ void ai_cleanup_dock_mode_objective(object *objp);
// the "autopilot"
object *Autopilot_flight_leader = NULL;

static inline float ai_guard_threshold(const object* guarded_objp, float threshold)
{
if (guarded_objp != nullptr && guarded_objp->type == OBJ_SHIP && guarded_objp->instance >= 0) {
const float configured = Ships[guarded_objp->instance].max_guard_radius;
if (configured > 0.0f) {
return configured;
}
}

return threshold;
}

/**
* Sets the timestamp used to tell is it is a good time for this team to rearm.
* Ends a 'bad rearm time'
Expand Down Expand Up @@ -5115,9 +5127,9 @@ int maybe_resume_previous_mode(object *objp, ai_info *aip)

// If guarding ship is far away from guardee and enemy is far away from guardee,
// then stop chasing and resume guarding.
if (dist > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
if (dist > ai_guard_threshold(guard_objp, 6.0f)) {
if ((En_objp != NULL) && (En_objp->type == OBJ_SHIP)) {
if (vm_vec_dist_quick(&guard_objp->pos, &En_objp->pos) > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
if (vm_vec_dist_quick(&guard_objp->pos, &En_objp->pos) > ai_guard_threshold(guard_objp, 6.0f)) {
Assert(aip->previous_mode == AIM_GUARD);
aip->mode = aip->previous_mode;
aip->submode = AIS_GUARD_PATROL;
Expand Down Expand Up @@ -10515,7 +10527,7 @@ int ai_guard_find_nearby_bomb(object *guarding_objp, object *guarded_objp)

dist = vm_vec_dist_quick(&bomb_objp->pos, &guarded_objp->pos);

if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
if (dist < ai_guard_threshold(guarded_objp, 3.0f)) {
dist_to_guarding_obj = vm_vec_dist_quick(&bomb_objp->pos, &guarding_objp->pos);
if ( dist_to_guarding_obj < closest_dist_to_guarding_obj ) {
closest_dist_to_guarding_obj = dist_to_guarding_obj;
Expand Down Expand Up @@ -10559,11 +10571,11 @@ void ai_guard_find_nearby_ship(object *guarding_objp, object *guarded_objp)
if (Ship_info[eshipp->ship_info_index].class_type >= 0 && (Ship_types[Ship_info[eshipp->ship_info_index].class_type].flags[Ship::Type_Info_Flags::AI_guards_attack]))
{
dist = vm_vec_dist_quick(&enemy_objp->pos, &guarded_objp->pos);
if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3)
if (dist < ai_guard_threshold(guarded_objp, 3.0f))
{
guard_object_was_hit(guarding_objp, enemy_objp);
}
else if ((dist < 3000.0f) && (Ai_info[eshipp->ai_index].target_objnum == guarding_aip->guard_objnum))
} else if ((dist < ai_guard_threshold(guarded_objp, 3000.0f)) &&
(Ai_info[eshipp->ai_index].target_objnum == guarding_aip->guard_objnum))
{
guard_object_was_hit(guarding_objp, enemy_objp);
}
Expand All @@ -10590,7 +10602,7 @@ void ai_guard_find_nearby_asteroid(object *guarding_objp, object *guarded_objp)
if ( asteroid_objp->type == OBJ_ASTEROID ) {
// Attack asteroid if near guarded ship
dist = vm_vec_dist_quick(&asteroid_objp->pos, &guarded_objp->pos);
if ( dist < (MAX_GUARD_DIST + guarded_objp->radius)*2) {
if (dist < ai_guard_threshold(guarded_objp, 2.0f)) {
dist_to_self = vm_vec_dist_quick(&asteroid_objp->pos, &guarding_objp->pos);
if ( OBJ_INDEX(guarded_objp) == asteroid_collide_objnum(asteroid_objp) ) {
if( dist_to_self < closest_danger_asteroid_dist ) {
Expand Down
47 changes: 47 additions & 0 deletions code/parse/sexp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,7 @@ SCP_vector<sexp_oper> Operators = {
{ "ship-no-guardian", OP_SHIP_NO_GUARDIAN, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
{ "ship-guardian-threshold", OP_SHIP_GUARDIAN_THRESHOLD, 2, INT_MAX, SEXP_ACTION_OPERATOR, },
{ "ship-subsys-guardian-threshold", OP_SHIP_SUBSYS_GUARDIAN_THRESHOLD, 3, INT_MAX, SEXP_ACTION_OPERATOR, },
{ "set-guard-range", OP_SET_GUARD_RANGE, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // MjnMixael
{ "self-destruct", OP_SELF_DESTRUCT, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
{ "destroy-instantly", OP_DESTROY_INSTANTLY, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Admiral MS
{ "destroy-instantly-with-debris", OP_DESTROY_INSTANTLY_WITH_DEBRIS, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Asteroth
Expand Down Expand Up @@ -19467,6 +19468,29 @@ void sexp_ship_guardian_threshold(int node)
}
}

// MjnMixael
void sexp_set_guard_range(int node)
{
int range, n = node;
bool is_nan, is_nan_forever;

range = eval_num(n, is_nan, is_nan_forever);
if (is_nan || is_nan_forever)
return;
n = CDR(n);

for (; n != -1; n = CDR(n)) {
auto ship_entry = eval_ship(n);
if (!ship_entry || !ship_entry->has_shipp()) {
continue;
}

// Intentionally no lower bound validation beyond disabling at <= 0.
// Mission authors may choose very small positive values for highly restrictive escort behavior.
ship_entry->shipp()->max_guard_radius = (range > 0) ? static_cast<float>(range) : -1.0f;
}
}

// Goober5000
void sexp_ship_subsys_guardian_threshold(int node)
{
Expand Down Expand Up @@ -28860,6 +28884,11 @@ int eval_sexp(int cur_node, int referenced_node)
sexp_val = SEXP_TRUE;
break;

case OP_SET_GUARD_RANGE:
sexp_set_guard_range(node);
sexp_val = SEXP_TRUE;
break;

case OP_SHIP_SUBSYS_TARGETABLE:
sexp_ship_deal_with_subsystem_flag(cur_node, node, Ship::Subsystem_Flags::Untargetable, true, false);
sexp_val = SEXP_TRUE;
Expand Down Expand Up @@ -31597,6 +31626,7 @@ int query_operator_return_type(int op)
case OP_SHIP_NO_GUARDIAN:
case OP_SHIP_GUARDIAN_THRESHOLD:
case OP_SHIP_SUBSYS_GUARDIAN_THRESHOLD:
case OP_SET_GUARD_RANGE:
case OP_SHIP_VANISH:
case OP_PROP_VANISH:
case OP_DESTROY_INSTANTLY:
Expand Down Expand Up @@ -32308,6 +32338,12 @@ int query_operator_argument_type(int op, int argnum)
else
return OPF_SUBSYS_OR_GENERIC;

case OP_SET_GUARD_RANGE:
if (argnum == 0)
return OPF_NUMBER;
else
return OPF_SHIP;

case OP_SHIP_SUBSYS_TARGETABLE:
case OP_SHIP_SUBSYS_UNTARGETABLE:
if (argnum == 0)
Expand Down Expand Up @@ -36895,6 +36931,7 @@ int get_category(int op_id)
case OP_JUMP_NODE_HIDE_JUMPNODE:
case OP_SHIP_GUARDIAN_THRESHOLD:
case OP_SHIP_SUBSYS_GUARDIAN_THRESHOLD:
case OP_SET_GUARD_RANGE:
case OP_SET_SKYBOX_MODEL:
case OP_SHIP_CREATE:
case OP_PROP_CREATE:
Expand Down Expand Up @@ -37252,6 +37289,7 @@ int get_subcategory(int op_id)

case OP_ALTER_SHIP_FLAG:
case OP_ALTER_WING_FLAG:
case OP_SET_GUARD_RANGE:
case OP_PROTECT_SHIP:
case OP_UNPROTECT_SHIP:
case OP_BEAM_PROTECT_SHIP:
Expand Down Expand Up @@ -40447,6 +40485,15 @@ SCP_vector<sexp_help_struct> Sexp_help = {
"\t2:\tShip housing the subsystem(s) (ships must be in-mission).\r\n"
"\t3+:\tSubsystems to make unkillable." },

// MjnMixael
{ OP_SET_GUARD_RANGE, "set-guard-range\r\n"
"\tSets the max range in meters at which any ships guarding this ship will engage with threats.\r\n"
"This range will override the default dynamic range behavior for ships obeying a guard order.\r\n"
"If the value is <= 0, regular dynamic guard range behavior will resume. Positive values are used as is with no size validation based on ship class.\r\n\r\n"
"Takes 2 or more arguments...\r\n"
"\t1:\tGuard range cap in meters (<= 0 disables cap).\r\n"
"\t2+:\tShip(s) to apply the cap to (ships must be in-mission)." },

// Goober5000
{ OP_SHIP_STEALTHY, "ship-stealthy\r\n"
"\tCauses the ships listed in this sexpression to become stealth ships (i.e. invisible to radar).\r\n\r\n"
Expand Down
1 change: 1 addition & 0 deletions code/parse/sexp.h
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,7 @@ enum : int {
OP_JUMP_NODE_HIDE_JUMPNODE, // WMC
OP_SHIP_GUARDIAN_THRESHOLD, // Goober5000
OP_SHIP_SUBSYS_GUARDIAN_THRESHOLD, // Goober5000
OP_SET_GUARD_RANGE, //MjnMixael
OP_SET_SKYBOX_MODEL, // taylor
OP_SHIP_CREATE,
OP_PROP_CREATE, // MjnMixael
Expand Down
1 change: 1 addition & 0 deletions code/ship/ship.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7184,6 +7184,7 @@ void ship::clear()
ship_max_hull_strength = 0.0f;

ship_guardian_threshold = 0;
max_guard_radius = -1.0f;

ship_name[0] = 0;
display_name.clear();
Expand Down
1 change: 1 addition & 0 deletions code/ship/ship.h
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,7 @@ class ship
float max_weapon_regen_per_second; // wookieejedi - make this a ship object variable

int ship_guardian_threshold; // Goober5000 - now also determines whether ship is guardian'd
float max_guard_radius; // Optional clamp for guard engagement/resume ranges; <= 0 means unused


char ship_name[NAME_LENGTH];
Expand Down