Skip to content

Remove the ship class limit - #7746

Draft
Goober5000 wants to merge 11 commits into
scp-fs2open:masterfrom
Goober5000:feature/remove_ship_class_limits
Draft

Remove the ship class limit#7746
Goober5000 wants to merge 11 commits into
scp-fs2open:masterfrom
Goober5000:feature/remove_ship_class_limits

Conversation

@Goober5000

Copy link
Copy Markdown
Contributor

With every fixed-size ship-class-indexed structure now converted to dynamic containers, we can finally remove the MAX_SHIP_CLASSES limit!

This depends on PRs #7737, #7739, #7742, and #7743. It is in draft until they are merged.

Goober5000 and others added 11 commits August 24, 2026 20:04
team_data was the last persistence-adjacent structure sized by MAX_SHIP_CLASSES: four parallel ship arrays plus four parallel weapon arrays (one slot per mission-file loadout line, holding a class or a sexp variable name, and a count or a sexp variable name) with manual num_*_choices counters, plus a dense per-class weapon_required flag array.  Convert all of it to the sparse-container family:

  ship_list / ship_list_variables / ship_count / ship_count_variables
    -> SCP_vector<loadout_entry> ship_choices
  weaponry_pool / weaponry_count / weaponry_pool_variable /
  weaponry_amount_variable
    -> SCP_vector<loadout_entry> weapon_choices
  bool weapon_required[MAX_WEAPON_TYPES]
    -> SCP_set<int> required_weapons

where loadout_entry is {class_index, count, class_variable, count_variable}, with empty variable strings meaning the value was given literally.  num_ship_choices / num_weapon_choices become .size().

Intentional behavior fixes, all previously latent memory errors or
data bugs:

 * is_ship_assignable stored an alt ship CLASS in a variable used as a
   loadout list INDEX when default_to_this_class was false, then
   swapped to ship_list[class] - garbage for any mission where the two
   domains diverge.  It now swaps to the alt class itself, per the
   evident intent.

 * The default-ship fallback read ship_list[0] even when a team had
   zero ship choices; it now leaves default_ship at -1, and
   ss_fixup_team_data guards its append of the default ship accordingly.

 * FRED2's dumpstats indexed Ships[] with a ship class when printing
   loadout entries; it now prints the entry's class or variable name
   directly.

 * Team parse now clears the previous mission's entries explicitly
   rather than relying on overwrite-in-place.

This clears the largest remaining cluster of MAX_SHIP_CLASSES references on the way to lifting the ship class limit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
FRED2, qtFRED, and the shared mission save code each carried their own copy of the loadout usage builders.  The weaponry pair already had shared versions in missioneditor/common; this converts those to the SCP_map<int,int> signature the editors now use, adds the missing ship builder alongside them, and deletes both editors' private copies.

FRED2's playerstarteditor/freddoc and qtFRED's Editor/ErrorChecker all call the shared functions now, following the update_custom_wing_indexes consolidation precedent.  missionsave's used_pool becomes a map as well, which removes its MAX_WEAPON_TYPES stack array.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Convert the mission ship/weapon select screens' per-class UI state from dense MAX_SHIP_CLASSES / MAX_WEAPON_TYPES arrays to sparse containers keyed by loadout-pool membership, matching the Ss_pool / Wl_pool maps they shadow:

  ss_icon_info Ss_icons_teams[][MAX_SHIP_CLASSES] -> SCP_map<int, ss_icon_info>[]
  wl_icon_info Wl_icons_teams[][MAX_WEAPON_TYPES] -> SCP_map<int, wl_icon_info>[]
  wl_ship_class_info Wl_ships[MAX_SHIP_CLASSES]   -> SCP_map<int, wl_ship_class_info>
  int Plist[] / Slist[] + manual size counters    -> SCP_vector<int>

The ship-select active list is replaced outright by a vector of ship classes rebuilt on each interface sync, plus the existing scroll offset.  active_list_remove() was dead code and is deleted.

Behavior fixes required by the conversion:

 * wl_set_disabled_weapons now computes usability flags for the pool
   weapons plus every weapon currently in a slot's banks, instead of all
   weapon classes.  (Bank weapons are not guaranteed pool members.)

 * The two mouse-region handlers that read Plist[Plist_start+index] for
   raw indices 0..3 now bounds-check against the list size (as does
   ss_get_ship_class_from_list).

 * wl_unload_icons and the ss unloaders early-return on a null team
   pointer instead of asserting and dereferencing.

The ship side loads icons for every pool class including exhausted 0-count entries while the weapon side loads only positive counts, preserving the existing asymmetry.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fix bitmap and model handle leakage in the ship/weapon select screens, and make release symmetric with load:

 * Initialize each team only once in `ship_select_common_init`
 * Unload every team's map in `ss_unload_team_icons`, not just the active one.
 * Add a per-team unload helper and use it in the load-all functions

Also drop the stray per-icon Cur_Anim unload, and call `wl_free_ship_class_data` before loading in `wl_init_ship_class_data`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Shield_sys_types (the per-ship-class shield status in the FRED2/qtFRED Shield System dialog) was a dense array/vector sized to MAX_SHIP_CLASSES, one of the last cap-blocking structures.  It is a poor fit for a dense container: it is derived, ephemeral editor state, and sparse in practice, since only the ship classes present in the mission ever get a meaningful value.  Its default (HasShields) is exactly the "absent" case.

Convert it to a map keyed by ship class:

  FRED2:  int Shield_sys_types[MAX_SHIP_CLASSES]            -> SCP_map<int, int>
  qtFRED: SCP_vector<GlobalShieldStatus> Shield_sys_types   -> SCP_map<int, GlobalShieldStatus>
          (and the ShieldSystemDialogModel::_types copy likewise)

No file-format or behavior change: shield status is still derived from and applied to the same ship flags.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
FRED2's AltShipClassDlg kept two fixed-size parallel arrays mapping combobox rows back to engine indices:

  int ship_class_indices[MAX_SHIP_CLASSES]       // row -> Ship_info index
  int string_variable_indices[MAX_SEXP_VARIABLES] // row -> Sexp_variables index

Both are pure UI plumbing. Delete them and attach each index to its combobox row via CComboBox::SetItemData/GetItemData.  Besides removing the arrays, this drops the num_string_variables index offset math the parallel arrays forced and bounds the reverse lookups by the combobox item count instead of MAX_SHIP_CLASSES.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
With every fixed-size ship-class-indexed structure now converted to dynamic containers, we can finally remove the `MAX_SHIP_CLASSES` limit!

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Goober5000 Goober5000 added this to the Release 26.2 milestone Aug 27, 2026
@Goober5000 Goober5000 added the feature A totally new sort of functionality label Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature A totally new sort of functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant