Skip to content

Commit 76c00db

Browse files
authored
Add set-guard-range sexp (scp-fs2open#7251)
* set-guard-range * make sure old path remains unchanged * address feedback again
1 parent 5f6309b commit 76c00db

5 files changed

Lines changed: 70 additions & 7 deletions

File tree

code/ai/aicode.cpp

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,18 @@ void ai_cleanup_dock_mode_objective(object *objp);
323323
// the "autopilot"
324324
object *Autopilot_flight_leader = NULL;
325325

326+
static inline float ai_guard_threshold(const object* guarded_objp, float threshold)
327+
{
328+
if (guarded_objp != nullptr && guarded_objp->type == OBJ_SHIP && guarded_objp->instance >= 0) {
329+
const float configured = Ships[guarded_objp->instance].max_guard_radius;
330+
if (configured > 0.0f) {
331+
return configured;
332+
}
333+
}
334+
335+
return threshold;
336+
}
337+
326338
/**
327339
* Sets the timestamp used to tell is it is a good time for this team to rearm.
328340
* Ends a 'bad rearm time'
@@ -5117,9 +5129,10 @@ int maybe_resume_previous_mode(object *objp, ai_info *aip)
51175129

51185130
// If guarding ship is far away from guardee and enemy is far away from guardee,
51195131
// then stop chasing and resume guarding.
5120-
if (dist > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
5132+
if (dist > ai_guard_threshold(guard_objp, (MAX_GUARD_DIST + guard_objp->radius) * 6))
5133+
{
51215134
if ((En_objp != NULL) && (En_objp->type == OBJ_SHIP)) {
5122-
if (vm_vec_dist_quick(&guard_objp->pos, &En_objp->pos) > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
5135+
if (vm_vec_dist_quick(&guard_objp->pos, &En_objp->pos) > ai_guard_threshold(guard_objp, (MAX_GUARD_DIST + guard_objp->radius) * 6)) {
51235136
Assert(aip->previous_mode == AIM_GUARD);
51245137
aip->mode = aip->previous_mode;
51255138
aip->submode = AIS_GUARD_PATROL;
@@ -10517,7 +10530,7 @@ int ai_guard_find_nearby_bomb(object *guarding_objp, object *guarded_objp)
1051710530

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

10520-
if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
10533+
if (dist < ai_guard_threshold(guarded_objp, (MAX_GUARD_DIST + guarded_objp->radius) * 3)) {
1052110534
dist_to_guarding_obj = vm_vec_dist_quick(&bomb_objp->pos, &guarding_objp->pos);
1052210535
if ( dist_to_guarding_obj < closest_dist_to_guarding_obj ) {
1052310536
closest_dist_to_guarding_obj = dist_to_guarding_obj;
@@ -10561,11 +10574,11 @@ void ai_guard_find_nearby_ship(object *guarding_objp, object *guarded_objp)
1056110574
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]))
1056210575
{
1056310576
dist = vm_vec_dist_quick(&enemy_objp->pos, &guarded_objp->pos);
10564-
if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3)
10577+
if (dist < ai_guard_threshold(guarded_objp, (MAX_GUARD_DIST + guarded_objp->radius) * 3))
1056510578
{
1056610579
guard_object_was_hit(guarding_objp, enemy_objp);
10567-
}
10568-
else if ((dist < 3000.0f) && (Ai_info[eshipp->ai_index].target_objnum == guarding_aip->guard_objnum))
10580+
} else if ((dist < ai_guard_threshold(guarded_objp, 3000.0f)) &&
10581+
(Ai_info[eshipp->ai_index].target_objnum == guarding_aip->guard_objnum))
1056910582
{
1057010583
guard_object_was_hit(guarding_objp, enemy_objp);
1057110584
}
@@ -10592,7 +10605,7 @@ void ai_guard_find_nearby_asteroid(object *guarding_objp, object *guarded_objp)
1059210605
if ( asteroid_objp->type == OBJ_ASTEROID ) {
1059310606
// Attack asteroid if near guarded ship
1059410607
dist = vm_vec_dist_quick(&asteroid_objp->pos, &guarded_objp->pos);
10595-
if ( dist < (MAX_GUARD_DIST + guarded_objp->radius)*2) {
10608+
if (dist < ai_guard_threshold(guarded_objp, (MAX_GUARD_DIST + guarded_objp->radius) * 2)) {
1059610609
dist_to_self = vm_vec_dist_quick(&asteroid_objp->pos, &guarding_objp->pos);
1059710610
if ( OBJ_INDEX(guarded_objp) == asteroid_collide_objnum(asteroid_objp) ) {
1059810611
if( dist_to_self < closest_danger_asteroid_dist ) {

code/parse/sexp.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@ SCP_vector<sexp_oper> Operators = {
525525
{ "ship-no-guardian", OP_SHIP_NO_GUARDIAN, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
526526
{ "ship-guardian-threshold", OP_SHIP_GUARDIAN_THRESHOLD, 2, INT_MAX, SEXP_ACTION_OPERATOR, },
527527
{ "ship-subsys-guardian-threshold", OP_SHIP_SUBSYS_GUARDIAN_THRESHOLD, 3, INT_MAX, SEXP_ACTION_OPERATOR, },
528+
{ "set-guard-range", OP_SET_GUARD_RANGE, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // MjnMixael
528529
{ "self-destruct", OP_SELF_DESTRUCT, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
529530
{ "destroy-instantly", OP_DESTROY_INSTANTLY, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Admiral MS
530531
{ "destroy-instantly-with-debris", OP_DESTROY_INSTANTLY_WITH_DEBRIS, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Asteroth
@@ -19521,6 +19522,29 @@ void sexp_ship_guardian_threshold(int node)
1952119522
}
1952219523
}
1952319524

19525+
// MjnMixael
19526+
void sexp_set_guard_range(int node)
19527+
{
19528+
int range, n = node;
19529+
bool is_nan, is_nan_forever;
19530+
19531+
range = eval_num(n, is_nan, is_nan_forever);
19532+
if (is_nan || is_nan_forever)
19533+
return;
19534+
n = CDR(n);
19535+
19536+
for (; n != -1; n = CDR(n)) {
19537+
auto ship_entry = eval_ship(n);
19538+
if (!ship_entry || !ship_entry->has_shipp()) {
19539+
continue;
19540+
}
19541+
19542+
// Intentionally no lower bound validation beyond disabling at <= 0.
19543+
// Mission authors may choose very small positive values for highly restrictive escort behavior.
19544+
ship_entry->shipp()->max_guard_radius = (range > 0) ? static_cast<float>(range) : -1.0f;
19545+
}
19546+
}
19547+
1952419548
// Goober5000
1952519549
void sexp_ship_subsys_guardian_threshold(int node)
1952619550
{
@@ -28925,6 +28949,11 @@ int eval_sexp(int cur_node, int referenced_node)
2892528949
sexp_val = SEXP_TRUE;
2892628950
break;
2892728951

28952+
case OP_SET_GUARD_RANGE:
28953+
sexp_set_guard_range(node);
28954+
sexp_val = SEXP_TRUE;
28955+
break;
28956+
2892828957
case OP_SHIP_SUBSYS_TARGETABLE:
2892928958
sexp_ship_deal_with_subsystem_flag(cur_node, node, Ship::Subsystem_Flags::Untargetable, true, false);
2893028959
sexp_val = SEXP_TRUE;
@@ -31676,6 +31705,7 @@ int query_operator_return_type(int op)
3167631705
case OP_SHIP_NO_GUARDIAN:
3167731706
case OP_SHIP_GUARDIAN_THRESHOLD:
3167831707
case OP_SHIP_SUBSYS_GUARDIAN_THRESHOLD:
31708+
case OP_SET_GUARD_RANGE:
3167931709
case OP_SHIP_VANISH:
3168031710
case OP_PROP_VANISH:
3168131711
case OP_DESTROY_INSTANTLY:
@@ -32390,6 +32420,12 @@ int query_operator_argument_type(int op_index, int argnum)
3239032420
else
3239132421
return OPF_SUBSYS_OR_GENERIC;
3239232422

32423+
case OP_SET_GUARD_RANGE:
32424+
if (argnum == 0)
32425+
return OPF_NUMBER;
32426+
else
32427+
return OPF_SHIP;
32428+
3239332429
case OP_SHIP_SUBSYS_TARGETABLE:
3239432430
case OP_SHIP_SUBSYS_UNTARGETABLE:
3239532431
if (argnum == 0)
@@ -36975,6 +37011,7 @@ int get_category(int op_id)
3697537011
case OP_JUMP_NODE_HIDE_JUMPNODE:
3697637012
case OP_SHIP_GUARDIAN_THRESHOLD:
3697737013
case OP_SHIP_SUBSYS_GUARDIAN_THRESHOLD:
37014+
case OP_SET_GUARD_RANGE:
3697837015
case OP_SET_SKYBOX_MODEL:
3697937016
case OP_SHIP_CREATE:
3698037017
case OP_PROP_CREATE:
@@ -37334,6 +37371,7 @@ int get_subcategory(int op_id)
3733437371

3733537372
case OP_ALTER_SHIP_FLAG:
3733637373
case OP_ALTER_WING_FLAG:
37374+
case OP_SET_GUARD_RANGE:
3733737375
case OP_PROTECT_SHIP:
3733837376
case OP_UNPROTECT_SHIP:
3733937377
case OP_BEAM_PROTECT_SHIP:
@@ -40530,6 +40568,15 @@ SCP_vector<sexp_help_struct> Sexp_help = {
4053040568
"\t2:\tShip housing the subsystem(s) (ships must be in-mission).\r\n"
4053140569
"\t3+:\tSubsystems to make unkillable." },
4053240570

40571+
// MjnMixael
40572+
{ OP_SET_GUARD_RANGE, "set-guard-range\r\n"
40573+
"\tSets the max range in meters at which any ships guarding this ship will engage with threats.\r\n"
40574+
"This range will override the default dynamic range behavior for ships obeying a guard order.\r\n"
40575+
"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"
40576+
"Takes 2 or more arguments...\r\n"
40577+
"\t1:\tGuard range cap in meters (<= 0 disables cap).\r\n"
40578+
"\t2+:\tShip(s) to apply the cap to (ships must be in-mission)." },
40579+
4053340580
// Goober5000
4053440581
{ OP_SHIP_STEALTHY, "ship-stealthy\r\n"
4053540582
"\tCauses the ships listed in this sexpression to become stealth ships (i.e. invisible to radar).\r\n\r\n"

code/parse/sexp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,7 @@ enum : int {
698698
OP_JUMP_NODE_HIDE_JUMPNODE, // WMC
699699
OP_SHIP_GUARDIAN_THRESHOLD, // Goober5000
700700
OP_SHIP_SUBSYS_GUARDIAN_THRESHOLD, // Goober5000
701+
OP_SET_GUARD_RANGE, //MjnMixael
701702
OP_SET_SKYBOX_MODEL, // taylor
702703
OP_SHIP_CREATE,
703704
OP_PROP_CREATE, // MjnMixael

code/ship/ship.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7229,6 +7229,7 @@ void ship::clear()
72297229
ship_max_hull_strength = 0.0f;
72307230

72317231
ship_guardian_threshold = 0;
7232+
max_guard_radius = -1.0f;
72327233

72337234
ship_name[0] = 0;
72347235
display_name.clear();

code/ship/ship.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,7 @@ class ship
622622
float max_weapon_regen_per_second; // wookieejedi - make this a ship object variable
623623

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

626627

627628
char ship_name[NAME_LENGTH];

0 commit comments

Comments
 (0)