Skip to content

refactor firing point patterns into a FirepointState class shared between primaries and secondaries - #7772

Draft
Goober5000 wants to merge 5 commits into
scp-fs2open:masterfrom
Goober5000:fix/more_firingpoints
Draft

Goober5000 wants to merge 5 commits into
scp-fs2open:masterfrom
Goober5000:fix/more_firingpoints

Conversation

@Goober5000

@Goober5000 Goober5000 commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

This standardizes firing point management, allows both primary and secondary weapons to select from the same available patterns, and adds the ability for fighter-beams to use firing patterns. Along the way, several design flaws and inconsistencies are fixed. Changes are implemented in four stages corresponding to four commits: refactor for primaries, unify with secondaries, make consistent for multiplayer, and fighter beam support.

Follow-up to #7635. Also a prerequisite for extending the same capability to turrets. In draft pending testing.

@Goober5000 Goober5000 added this to the Release 27.0 milestone Sep 7, 2026
@Goober5000 Goober5000 added enhancement A new feature or upgrade of an existing feature to add additional functionality. refactor A cleanup/restructure of a feature for speed, simplicity, and/or maintainability labels Sep 7, 2026
@Goober5000
Goober5000 force-pushed the fix/more_firingpoints branch 3 times, most recently from ed54c72 to 28e3c11 Compare September 8, 2026 01:05
Extract the per-bank firing point state into a FirepointState class with
next/peek/post_fire, and factor out the pattern resolution, shot count, and
tables parsing that were duplicated between the two ship_fire_primary
branches and the reticle.  Replace STANDARD with ALL_AT_ONCE, defaulting
primaries to ALL_AT_ONCE and secondaries to CYCLE_FORWARD.  Route all
shuffles through one place in FirepointState.

Fixes flushed out along the way: RANDOM_EXHAUSTIVE's partial reshuffle had
an invalid range, and the reticle used the wrong bank under dynamic linking
and previewed CYCLE_REVERSE one point behind.  The fighter beam branch also
never reshuffled the RANDOM_* patterns; that is now fixed too, though it
only matters for beams tabled with +BeamShots: 0, since any other value
forces CYCLE_FORWARD (as it always has).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Goober5000
Goober5000 force-pushed the fix/more_firingpoints branch from 28e3c11 to f0e136b Compare September 8, 2026 02:16
Goober5000 and others added 4 commits September 7, 2026 22:54
Replace the retail secondary_next_slot round-robin with a FirepointState
per secondary bank, so missiles honor $Firing Pattern:, $Shots:, and
$Cycle Multishot: the same way primaries do.  Dual fire becomes a doubling
of the shot count via ship_get_secondary_firepoint_counts(), which also
carries the ignored-rather-than-cleared capability check.  The reload
animation, hardpoints gauge, and AI line-of-sight check now ask the state
which points fire next instead of assuming sequential order.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Drive the RANDOM_* firing patterns from static_rand(), which is rebuilt
each mission from the netgame seed, so every machine produces the same
firing point order.  Each bank seeds from its ship name and bank index,
since the net signature isn't assigned yet when the banks are set up.

Add util::StaticRandGenerator, a UniformRandomBitGenerator over
static_rand(), and util::deterministic_shuffle(), a Fisher-Yates whose
algorithm is fully specified.  std::shuffle can't be used where the
result must match across platforms, since its algorithm is
implementation-defined.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fighter beams predate the firing pattern feature and have always cycled
forward through +Shots: firing points per trigger pull, firing $Shots:
beams from each.  That mapping is kept for any beam that doesn't table a
$Firing Pattern:, but a beam that does table one now behaves exactly like
a gun with the same pattern: the pattern chooses the firing points and
$Shots: / $Cycle Multishot: count them.  The new Firing_pattern_specified
flag records which case applies.  Similarly, secondaries will now obey the
$Shots: field only if assigned a firing pattern.

Note that this reverses a precedence.  Previously +Shots: took priority
over $Firing Pattern: for fighter beams, so a tabled pattern was accepted
and then silently ignored; now the pattern wins.  A beam that tables both,
with +Shots: greater than 1, will fire from $Shots: points rather than
+Shots: points, so such a table should set $Shots: to what +Shots: used to
say.  A beam that leaves +Shots: alone fires the same number of beams
either way, and only the choice of firing point changes, which is the
reason to table a pattern in the first place.

The legacy "cycle" flag deliberately does not count as specifying a
pattern, since it predates the feature and beams using it have always
used the legacy mapping.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The recoil code in ship_fire_primary tracks a firepoint per projectile so
that the whack can be applied at their average position, but the bookkeeping
had three problems, all of which predate the firing pattern work.

The list was allocated for shot_count * multishot_count entries while a
weapon with an external model that doesn't chain its firing points writes
sub_shots entries per projectile, overrunning the allocation.  Hoist
sub_shots above the allocation and include it in the size.

A bank that fires no projectiles at all, which $Shots: 0 now expresses,
left the list empty and then averaged zero of them, dividing by zero and
whacking the ship at a NaN position, which permanently corrupts its rotvel.
Skip the whack when nothing was recorded.

Finally, winfo_p is reassigned to the substituted weapon's class partway
through the loop, so the Apply_Recoil test could give different answers for
the allocation, the per-projectile write, and the cleanup.  A substitution
that removed the flag leaked the array; one that added it wrote through a
null pointer.  Latch the bank weapon's flag once and use it for all three.
The force calculation still reads the current class, so a substituted
projectile of a different mass recoils by its own weight.

Also hold the list in a unique_ptr, so it cannot be leaked again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Goober5000
Goober5000 force-pushed the fix/more_firingpoints branch from f0e136b to c06a5ad Compare September 8, 2026 02:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement A new feature or upgrade of an existing feature to add additional functionality. refactor A cleanup/restructure of a feature for speed, simplicity, and/or maintainability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant