* avoid undefined behavior from UNREACHABLE on reachable code paths
UNREACHABLE expands to __assume(false) / __builtin_unreachable() in
release builds, which is undefined behavior if the code is ever actually
reached. Many UNREACHABLE sites only guard against unexpected-but-
possible runtime states (values from tables, mission/save files, network
packets, scripting, or user input), so reaching them should degrade
gracefully rather than invoke UB.
Replace UNREACHABLE with Assertion(false, ...) at all such sites. It
still pops an error in debug builds, but in release it is a harmless
no-op that simply falls through to the following code. Sites that are
genuinely unreachable by local invariants (e.g. a switch over a bounded
random number) are left as UNREACHABLE.
A few converted sites had no safe fall-through -- an uninitialized read,
null dereference, or null call would have followed -- so they also get a
minimal recovery: initialize the value, guard the dereference, return
early, or skip the offending item.
Where the converted site was a simple guard of the form
if (x) { Assertion(false, ...); return; }
hoist the check onto the invariant instead:
Assertion(!x, ...);
if (x) { return; }
This states the expectation directly and shows the offending value in
the debug popup, while keeping the same graceful runtime degradation.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* addendum
After consultation with Lafiel, use UNREACHABLE in situations that are effectively impossible even if not provably impossible. UNREACHABLE = "this shouldn't ever happen"; Assertion = "if this ever happens, it's a coding error". Also fix a few other sites and change a couple to Warnings.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Since FS1 retail, a large ship could get permanently stuck circling its
final waypoint. Waypoint completion requires the ship center to pass
within MIN_DIST_TO_WAYPOINT_GOAL + sqrt(radius) of the point (~37m for
a 1000m-radius capital ship), and the check only runs when the ship
covers 0.1m in a single frame (a framerate-dependent minimum speed).
At any speed that passes that gate, a capital ship turning at
2PI/(3*rotation_time) has a turning circle of hundreds of meters, so
once it overshoots a misaligned final leg it can never re-aim into the
completion bubble: an endless overshoot-turnaround-miss loop.
Add ai_profiles flag "$fix big ship waypoint completion:"
(Fix_big_ship_waypoint_completion), enabled by default for mods
targeting 26.2. With the flag set:
- big/huge ships complete a waypoint anywhere within their own radius
of it (matching what the docking path code has always done), while
small ships keep the retail sqrt(radius) tolerance; and
- the completion check runs every frame instead of requiring 0.1m of
travel per frame, removing the framerate-dependent minimum speed
(which at 240fps is 24 m/s, enough to affect fighters too).
With the flag unset, behavior is unchanged from retail.
Co-Authored-By: Claude Fable 5 <[email protected]>
If the carrier a fighter launched from was destroyed earlier in the
mission, its Ships[] entry retains stale bay door state while its
object and model instance are already freed (objnum/model_instance_num
== -1). When the fighter later departs, ai_manage_bay_doors would still
try to animate the dead carrier's bay doors, calling
model_get_instance(-1) and asserting/crashing.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
* framework for support ship rearm pool and flags
* scripting api
* FRED support
* QtFRED support
* do not save non-player-allowed weapons
* tvt and mission versioning
* fix variable name
* minor fixes and clarity
* oops
* add qtfred help docs
* fixes and cleanup
* clang and readability
* address feedback
* address feedback
* new mine weapon type
* some fixes and cleanup
* fix some parsing issues
* make sure mines get plotted
* make mine spawned objects can get a correct target
* some cleanup and additional features
* chasing mines
* some Lua tweaks
* generalize proximity detonations
* minor tweaks
* collapse namespace
* address feedback
Testing in FotG exposed a bug that dates back to retail. During mission parsing, arrival and departure anchors were represented by indexes into `Parse_names`. After parsing, they were converted to ship indexes -- but only in FRED. Since some functions are shared between FRED and FS2, this can produce inconsistent results, which can be hard to detect since parse indexes and ship indexes are usually the same.
This refactors arrival and departure anchors to be represented by ship registry indexes in all cases. It also adds conversion functions for the benefit of FRED and QtFRED since those dialogs were designed with ship indexes in mind.
The `$better combat collision avoidance for fightercraft:` flag is incredibly useful in preventing AI from hitting large ships while attacking, and the ability to tune the collision avoidance aggression factor further helps mods tune behavior based on their ship speeds (tuning that value is via `+combat collision avoidance aggression for fightercraft:` ). Nevertheless, the improve collision avoidance still is not performed for that actual fightercraft's actual target, so there can be situations where a fast fighter attacks a large ship with high speed, and then when that fighter break off from the attack they turn directly into the large ship's hull. Allowing the target to be incorporated into the `better_collision_avoidance_triggered` check prevents this from happening. To allow the target to be a part of the checks we just simply need to not pass an 'ignor_ship` argument to `maybe_avoid_big_ship`.
This PR adds that ability via flag. This new flag is tested and works as expected. For example, without the flag a squadron of TIEs in FotG would literally ram themselves to death attacking some of our larger capital ships because they would break off attack too late, but with this flag they no longer accidentally collide at all. In discussion with Asteroth it was decided to make this enhanced behavior behind a flag, too.
This PR also fixes a small oversight from the original #2810 where `next_check_time` was never actually used within the `else` block of `maybe_avoid_big_ship`.
* Expose value of idle circling with 'attack-any'
The attack-any order is extremely useful and utilized order, and there was one aspect that was ripe for exposure to modders: the `CHASE_CIRCLE_DIST` that was used to determine how big of a circle to fly around while the AI waited for new hostiles to arrive. For ships with high speeds, like FotG and others, the default radius of 200 m was a bit on the small side, which lead to ships flying in tight circles. This was was counterproductive to goals of tuning the AI to have a more cinematic style, such as where AI would fly around in large distances while waiting for enemies to arrive. Fortunately, this value is easy to expose and allow modders to tune with just a new ai_profiles value.
Tested and works as expected.
* improve name and use correct distance
* Back in business hopefully
* Refactor
* Resolve CI issue in aigoals.cpp
* Updating order.cpp
* Whoops
* Updated objecttypes.tbl to incorporate attack ship type
* Fix clang-tidy issue
* Forgot one other instance
---------
Co-authored-by: wookieejedi <[email protected]>
Currently, the `$better combat collision avoidance for fightercraft:` flag only checks for possible big ship collisions when in normal chase mode (`AIM_CHASE`), but strafing a big ship is a separate AI mode (`AIM_STRAFE`). Per discussion with Asteroth, strafe attacking should also check for better collision avoidance, as the modder likely would expect that to fall under this AI flag. Also took the chance to make the collision avoidance check into a function to improve maintenance and usability.
1. Allow "Stay near me" and "Stay near my target" to work even if the teams do not match
2. Allow "Stay near my target" to be issued regardless of team
3. Avoid divide-by-zero errors when running `ai_stay_near()`
* One fix and one enhancement to AI shield behavior
This PR adds one fix and one enhancement to AI shield behavior.
1) Fix: Testing, especially in FotG, revealed an edge-case bug with shield management where the AI will not direct shield energy to a quadrant if it was hit when that quadrant was down. In other words, if a shield quadrant is knocked down to 0 and continues to be shot the AI will not realize it needs to direct energy to that quadrant. This is because `quadrant` is set to -1 if a shield segment is down. This ensures that the resulting effects and damage calculations properly ignore that quadrant downstream, but it also results in `aip->last_hit_quadrant` being set to -1. This PR adds a fix to properly record which shield was actually hit, so that the AI knows to direct energy to that quadrant.
2) Enhancement: In combat situations with fire coming from many directions, it is good practice to keep shields equalized, so this PR adds a flag for the AI to always attempt to balance shields, even if it is being fired upon (instead of the current behavior to always direct energy to the hit shield quadrant).
Both flags are tested and work as expected.
* fix copy paste
* rename to avoid previous confusion
* also make as ai class flag
Two updates to the energy transfer system.
1. Previously, for fighters missing a system (e.g. shields), the energy would be treated as if all energy from that system was routed to the other systems. So, for example, a fighter's typical speed would be faster without shields than with shields. A new AI profile flag now treats the other systems as if the energy had not been transferred.
2. At some point in development, Volition intended the `$Power Output` ships.tbl field to affect ETS. A new AI profile flag now treats the power output as a factor of energy applied to energy systems.
1. Add an explicit `mn.createWaypointList` function
2. Allow `mn.createWaypoint` to be used with an empty waypoint list
3. Update documentation of `mn.createWaypoint` to clarify optional arguments
* Fix some issues preventing various secondary types from firing in the lab
* Add ability to fire turrets from the lab
* const char*, mjn please
* more cleanup
* range-based for loop
* Update comment
Although normalized vectors in FSO are supposed to stay normalized, occasionally small floating point errors can pop up. In most cases these do not cause a problem, but in certain cases these errors can rapidly accumulate and corrupt an otherwise well-behaved matrix. This can most easily be seen with daisy-chained dock sequences or docked ships with angled dockpoints.
This PR audits the usage of `vm_vector_2_matrix_norm` to re-normalize vectors in cases where floating point errors could accumulate. There are also a few new Assertions for extra vigilance.
Follow-up to #6596. Fixes#6634.
Most places in the code that use `vm_vector_2_matrix` do so on already-normalized vectors. Changing these to `vm_vector_2_matrix_norm` avoids redundant normalization.
* Unify AI Usage of Shield Manage Delay
`$AI Shield Manage Delay:` can be set in the AI profiles, but also set for AI specific classes. Currently, The values set for the specific AI class are only used `if (iff_x_attacks_y(Player_ship->team, Ships[objp->instance].team))`, otherwise the values in the AI profile are used.
Being able to have the shield delay work in a unified way according to the AI class values is very useful, especially for friendly wingmen that you want have different shield manage delays.
This PR adds a AI profiles flag to unify Shield Manage Delay behavior, so that it always works in the same way regardless of AI team conditions.
* cleanup unifying behavior along with CM firing chance
* restore what sushi had for now
Make `SCP_vector`, `SCP_list`, `SCP_set`, and `SCP_multiset` subclasses of their STL counterparts. This allows helper functions to be added to each, including the `contains()` function which is not available until C++20. (This has minor effects on other parts of the codebase that treated `SCP_` and `std::` as synonyms.) Since there is now a nontrivial amount of code that needs to be in a .cpp file, create vmallocator.cpp.
Note that `SCP_string` truncation has been refactored to use length rather than C-string size to be consistent with other `std::basic_string` functions.
Also, remove some enum class hashing code which is no longer necessary now that the codebase is at C++17.
Allow waypoint orders to target a specific waypoint and go in reverse. Add script access to waypoints and waypoint lists belonging to orders.
Fix assignment of targets and goal start times. Assign targets when they are different (and thus need to be assigned), not when they are the same.
Several changes related to the volumetrics model fix:
1. Update the `model_load()` prototype with better parameter names, including a new `ErrorType` enum defined in pstypes.h
2. Update the `MIN`, `MAX`, and `CLAMP` macros, all of which were susceptible to performance issues because they duplicated their tokens. This could cause functions or calculations to be re-run unnecessarily. Since the codebase is now on C++17, `std::clamp()` can be used.
3. Update the rest of the codebase to fix any associated issues.
Kamikaze ships entering broadside mode (`ai_cruiser_chase()`) would always point toward the center of the ship. With this fix, they will point toward a subsystem if attacking one. (In practice this situation had not been encountered before, since by default the ai-destroy-subsystem order is only available to fighters and bombers, which do not use broadside mode.)
The FreeSpace AI includes behavior to run away from shockwaves to try to avoid explosion damage. This is implemented for both ships and missiles, although the missile implementation has been buggy ever since retail. There are several issues:
1. The AI attempts to evade a weapon immediately upon launch. Many missiles have a period of free flight before they start homing on their target. During the free flight period, the homing position is not set. Consequently, the AI will not know the position to avoid and will make assumptions.
2. The AI assumes the shockwave-producing weapon will detonate at the center of the ship it is targeting, although missiles detonate on the surface. A capital ship is likely to be significantly larger than the weapon's blast radius, leading the AI to believe the detonation will be much farther away than it actually is.
3. When avoiding ship shockwaves, the `shockwave_object` field is not cleared when avoiding is complete. This can result in stale references (though not crashes, due to object type checks), causing AI to not avoid future explosions in certain cases.
4. When avoiding ship shockwaves, the code does not check the correct ship for the amount of damage caused by the shockwave.
All of these issues are now fixed. Since this is a change in AI behavior, these fixes are tied to a new AI profiles flag.
Several locations in the code (notably `ai_info` and `waypoint_h`) previously stored pointers for waypoints and waypoint lists that need to be valid for more than one frame. Since both waypoint lists and waypoint paths may change size, resulting in vector reallocation, the information should be stored as indexes instead.
Fixes#6353.
Per #4294, this adds two new flag that, together, are equivalent to the legacy "immobile" flag but individually provide more fine-grained control. Since the physics for position and orientation have a bit of overlap, the new flag functionality simply reverts whatever position or orientation changes were done by the physics sim.
In the FRED and qtFRED ship flags dialogs, the "immobile" flag (aka "Does Not Move") is replaced by the two new flags, but in FSO the situation is more complicated. The old and new flags coexist and have overlapping functionality, so the mission designer must be careful to set and unset the appropriate flag (that is, it is possible for both "immobile" and "don't-change-position" to be set at the same time, in which case the ship cannot change position without both flags being unset). The `set-immobile` and `set-mobile` legacy SEXPs ensure that the new flags are removed so as not to conflict with the legacy flag, but the `alter-ship-flag` and `are-ship-flags-set` SEXPs and the `getFlag` and `setFlag` script functions use exactly what the FREDder/scripter specifies and nothing else.
(I attempted to completely replace "immobile" with the two newer flags, but it proved intractable to do this while maintaining reverse compatibility: retrofitting single-flag functions to apply dual flags for one particular situation made the functions harder to understand as well as introducing many edge cases and opportunities for bugs or "gotchas".)
Implements #4294.
The somewhat modern AI flag for `Require_exact_los` is extremely useful in multiple different situations, for example making large capital ship weapons not fire into friendly surrounding ships on accident. When the feature was added, the `check_los` function was provided with a default value of `10.0` meters for minimum radius detection. This PR exposes that value to modders so they can set it to a reasonable value depending on the ship sizes of their specific needs. And as a note, the los value just uses a simple numerical comparison check, so the los detection could be set to a negative value with no consequence (hence why error messages were not added, though if folks would like I can certainly add them).
* base working version of unlimited debris types
* Proper FRED support
* make qtFRED handle asteroids properly
* fix qtFRED assertion
* further clean out MAX_RETAIL_DEBRIS_TYPES
* These should be sizes not POFs
* Move this to it's own method
* convert asteroid subtypes to a vector
* unlimit asteroid subtypes
* fix some global shadowing
* signed/unsigned mismatch
* Address feedback
This refactors the object_h struct to store objects as indexes rather than pointers. This is a prerequisite for making the collection of objects dynamic in the future. See also #6100.
Follow-up to #5800 and #5829.